From e52e67767076b29cb01939aa7bdd8fee9d205cc1 Mon Sep 17 00:00:00 2001
From: Ashlee Young <ashlee@onosfw.com>
Date: Fri, 23 Oct 2015 08:31:31 -0700
Subject: Update ONOS src to commit id 69b36d5d11e81e28e56b46ba44e4b8cd701c5867

Change-Id: I9c13045711dbf9c0181106b66a6bf22c72bcf330
Signed-off-by: Ashlee Young <ashlee@onosfw.com>
---
 .../aaa/src/main/java/org/onosproject/aaa/AAA.java | 244 +++++++-------
 .../main/java/org/onosproject/aaa/AAAConfig.java   |   4 +-
 .../org/onosproject/aaa/AAAIntegrationTest.java    | 151 +++++++++
 .../src/test/java/org/onosproject/aaa/AAATest.java | 375 ++++-----------------
 .../test/java/org/onosproject/aaa/AAATestBase.java | 224 ++++++++++++
 .../java/org/onosproject/aaa/StateMachineTest.java |   4 +-
 framework/src/onos/apps/cordvtn/pom.xml            |  10 +
 .../main/java/org/onosproject/cordvtn/CordVtn.java | 143 +++++---
 .../onosproject/cordvtn/CordVtnConfigManager.java  |   1 -
 .../java/org/onosproject/cordvtn/OvsdbNode.java    |   9 +
 .../cordvtn/cli/OvsdbNodeAddCommand.java           |  65 ++++
 .../cordvtn/cli/OvsdbNodeConnectCommand.java       |  60 ++++
 .../cordvtn/cli/OvsdbNodeDeleteCommand.java        |  57 ++++
 .../cordvtn/cli/OvsdbNodeDisconnectCommand.java    |  60 ++++
 .../cordvtn/cli/OvsdbNodeListCommand.java          |  74 ++++
 .../org/onosproject/cordvtn/cli/package-info.java  |  20 ++
 .../java/org/onosproject/cordvtn/package-info.java |  20 ++
 .../resources/OSGI-INF/blueprint/shell-config.xml  |  35 ++
 framework/src/onos/apps/olt/pom.xml                |   9 +
 .../org/onosproject/olt/AccessDeviceConfig.java    |  43 +++
 .../java/org/onosproject/olt/AccessDeviceData.java |  76 +++++
 .../org/onosproject/olt/AccessDeviceService.java   |  41 +++
 .../olt/src/main/java/org/onosproject/olt/OLT.java | 144 +++++++-
 .../org/onosproject/olt/SubscriberAddCommand.java  |  58 ++++
 .../resources/OSGI-INF/blueprint/shell-config.xml  |  29 ++
 .../segmentrouting/DefaultRoutingHandler.java      |  18 +-
 .../segmentrouting/DeviceConfiguration.java        |  12 +-
 .../segmentrouting/ECMPShortestPathGraph.java      |   8 +-
 .../org/onosproject/segmentrouting/IpHandler.java  |   2 +-
 .../segmentrouting/RoutingRulePopulator.java       |   8 +-
 .../segmentrouting/SegmentRoutingManager.java      |   3 +-
 .../grouphandler/DefaultEdgeGroupHandler.java      |   9 +-
 .../vtnrsc/virtualport/VirtualPortService.java     |   9 +
 .../virtualport/impl/VirtualPortManager.java       |  23 ++
 .../cli/net/ConnectivityIntentCommand.java         |   8 +
 .../onosproject/cli/net/InterfaceAddCommand.java   |   6 +-
 .../resources/OSGI-INF/blueprint/shell-config.xml  |   2 +
 .../net/flow/DefaultTrafficTreatment.java          |   6 +
 .../org/onosproject/net/flow/TrafficTreatment.java |  30 +-
 .../net/flow/criteria/ArpPaCriterion.java          |  80 +++++
 .../onosproject/net/flow/criteria/Criteria.java    |  13 +-
 .../net/flow/instructions/Instruction.java         |   6 +
 .../net/flow/instructions/Instructions.java        |  55 +++
 .../org/onosproject/ui/topo/DeviceHighlight.java   |  12 +-
 .../java/org/onosproject/ui/topo/NodeBadge.java    | 220 ++++++++++++
 .../org/onosproject/ui/topo/NodeHighlight.java     |  27 ++
 .../org/onosproject/ui/topo/TopoConstants.java     |   3 +
 .../java/org/onosproject/ui/topo/TopoJson.java     |  19 ++
 .../org/onosproject/ui/topo/NodeBadgeTest.java     | 112 ++++++
 .../java/org/onosproject/ui/topo/TopoJsonTest.java |  46 +++
 framework/src/onos/docs/internal-apps              |   1 +
 framework/src/onos/docs/internal-netconf           |   1 +
 .../driver/pipeline/CpqdOFDPA1Pipeline.java        |   2 +-
 .../onosproject/driver/pipeline/OLTPipeline.java   |  25 +-
 .../driver/pipeline/OpenVSwitchPipeline.java       |  68 +++-
 framework/src/onos/etc/init/onos.conf              |  37 --
 framework/src/onos/etc/org.ops4j.pax.url.mvn.cfg   | 101 ------
 framework/src/onos/etc/org.ops4j.pax.web.cfg       |  12 -
 framework/src/onos/etc/samples/linkGraph.cfg       |  27 --
 .../org.onosproject.fwd.ReactiveForwarding.cfg     |  79 -----
 ...ect.provider.host.impl.HostLocationProvider.cfg |  13 -
 ...project.provider.lldp.impl.LLDPLinkProvider.cfg |  21 --
 ...r.netconf.device.impl.NetconfDeviceProvider.cfg |  11 -
 ...provider.nil.device.impl.NullDeviceProvider.cfg |  11 -
 ...ect.provider.nil.link.impl.NullLinkProvider.cfg |  16 -
 ...provider.nil.packet.impl.NullPacketProvider.cfg |   4 -
 .../samples/org.onosproject.proxyarp.ProxyArp.cfg  |   8 -
 ...g.onosproject.routing.bgp.BgpSessionManager.cfg |   8 -
 .../onos/etc/samples/org.onosproject.xos.XOS.cfg   |   0
 framework/src/onos/etc/users.properties            |  34 --
 .../net/config/basics/InterfaceConfig.java         |  15 +-
 .../onosproject/incubator/net/intf/Interface.java  |   9 +-
 .../openflow/controller/OpenFlowSwitch.java        |   8 +
 .../controller/driver/AbstractOpenFlowSwitch.java  |  26 +-
 .../openflow/controller/impl/OFMessageEncoder.java |   5 +-
 framework/src/onos/opt/onos/.empty                 |   0
 .../controller/driver/DefaultOvsdbClient.java      |   9 +-
 .../of/device/impl/OpenFlowDeviceProvider.java     |  13 +-
 .../provider/of/flow/impl/FlowEntryBuilder.java    |   6 +-
 .../provider/of/flow/impl/FlowModBuilderVer13.java |   8 +
 .../of/group/impl/OpenFlowGroupProvider.java       |   8 +
 .../tools/package/config/samples/network-cfg.json  |   4 +-
 .../config/samples/segmentrouting_dell.conf        |  93 -----
 .../tools/test/scenarios/sequential-example.xml    |  27 ++
 .../src/main/java/org/onlab/packet/EthType.java    |   2 +-
 .../src/main/java/org/onlab/packet/MacAddress.java |   1 +
 .../misc/src/main/java/org/onlab/packet/PIM.java   |   3 +
 .../java/org/onlab/packet/pim/PIMAddrGroup.java    |  16 +-
 .../java/org/onlab/packet/pim/PIMAddrSource.java   |  17 +-
 .../java/org/onlab/packet/pim/PIMAddrUnicast.java  |  23 +-
 .../stc/src/main/java/org/onlab/stc/Compiler.java  |  69 +++-
 .../src/test/java/org/onlab/stc/CompilerTest.java  |   4 +-
 .../src/test/resources/org/onlab/stc/scenario.xml  |   7 +
 .../web/gui/src/main/webapp/app/fw/svg/glyph.js    |   2 +
 .../gui/src/main/webapp/app/view/topo/topoD3.js    |  19 ++
 .../gui/src/main/webapp/app/view/topo/topoForce.js |  12 +
 .../src/main/webapp/app/view/topo/topoOverlay.js   |   8 +-
 .../test/_karma/ev/badges/ev_1_addInstance.json    |  14 +
 .../test/_karma/ev/badges/ev_2_addDevice_s1.json   |  18 +
 .../test/_karma/ev/badges/ev_3_addDevice_s2.json   |  18 +
 .../test/_karma/ev/badges/ev_4_addLink_1_2.json    |  16 +
 .../ev/badges/ev_5_showHighlights_clear.json       |   8 +
 .../ev/badges/ev_6_showHighlights_stuff.json       |  30 ++
 .../ev/badges/ev_7_showHighlights_clear.json       |   8 +
 .../gui/src/test/_karma/ev/badges/scenario.json    |  12 +
 .../gui/src/test/_karma/ev/traffic/scenario.json   |   4 +-
 106 files changed, 2617 insertions(+), 1097 deletions(-)
 create mode 100644 framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java
 create mode 100644 framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATestBase.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeAddCommand.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeConnectCommand.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDeleteCommand.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDisconnectCommand.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeListCommand.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/package-info.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/package-info.java
 create mode 100644 framework/src/onos/apps/cordvtn/src/main/resources/OSGI-INF/blueprint/shell-config.xml
 create mode 100644 framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceConfig.java
 create mode 100644 framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceData.java
 create mode 100644 framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceService.java
 create mode 100644 framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/SubscriberAddCommand.java
 create mode 100644 framework/src/onos/apps/olt/src/main/resources/OSGI-INF/blueprint/shell-config.xml
 create mode 100644 framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/ArpPaCriterion.java
 create mode 100644 framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java
 create mode 100644 framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/NodeBadgeTest.java
 delete mode 100644 framework/src/onos/etc/init/onos.conf
 delete mode 100644 framework/src/onos/etc/org.ops4j.pax.url.mvn.cfg
 delete mode 100644 framework/src/onos/etc/org.ops4j.pax.web.cfg
 delete mode 100644 framework/src/onos/etc/samples/linkGraph.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.fwd.ReactiveForwarding.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.provider.host.impl.HostLocationProvider.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.provider.lldp.impl.LLDPLinkProvider.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.provider.netconf.device.impl.NetconfDeviceProvider.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.provider.nil.device.impl.NullDeviceProvider.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.provider.nil.link.impl.NullLinkProvider.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.provider.nil.packet.impl.NullPacketProvider.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.proxyarp.ProxyArp.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.routing.bgp.BgpSessionManager.cfg
 delete mode 100644 framework/src/onos/etc/samples/org.onosproject.xos.XOS.cfg
 delete mode 100644 framework/src/onos/etc/users.properties
 delete mode 100644 framework/src/onos/opt/onos/.empty
 delete mode 100644 framework/src/onos/tools/package/config/samples/segmentrouting_dell.conf
 create mode 100644 framework/src/onos/tools/test/scenarios/sequential-example.xml
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_1_addInstance.json
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_2_addDevice_s1.json
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_3_addDevice_s2.json
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_4_addLink_1_2.json
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_5_showHighlights_clear.json
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_6_showHighlights_stuff.json
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_7_showHighlights_clear.json
 create mode 100644 framework/src/onos/web/gui/src/test/_karma/ev/badges/scenario.json

diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java
index 479ec7ed..72a5b122 100644
--- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java
+++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAA.java
@@ -15,10 +15,13 @@
  */
 package org.onosproject.aaa;
 
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
-import java.util.Optional;
-import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -30,20 +33,13 @@ import org.onlab.packet.EAP;
 import org.onlab.packet.EAPOL;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.RADIUS;
 import org.onlab.packet.RADIUSAttribute;
-import org.onlab.packet.TpPort;
-import org.onlab.packet.UDP;
-import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.config.ConfigFactory;
 import org.onosproject.net.config.NetworkConfigEvent;
@@ -53,7 +49,6 @@ import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostService;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.OutboundPacket;
@@ -63,6 +58,8 @@ import org.onosproject.net.packet.PacketService;
 import org.onosproject.xosintegration.VoltTenantService;
 import org.slf4j.Logger;
 
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+
 import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
 import static org.onosproject.net.packet.PacketPriority.CONTROL;
 import static org.slf4j.LoggerFactory.getLogger;
@@ -85,10 +82,6 @@ public class AAA {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
-    // end host information
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected VoltTenantService voltTenantService;
 
@@ -121,6 +114,12 @@ public class AAA {
     // our unique identifier
     private ApplicationId appId;
 
+    // Socket used for UDP communications with RADIUS server
+    private DatagramSocket radiusSocket;
+
+    // Executor for RADIUS communication thread
+    private ExecutorService executor;
+
     // Configuration properties factory
     private final ConfigFactory factory =
             new ConfigFactory<ApplicationId, AAAConfig>(APP_SUBJECT_FACTORY,
@@ -184,7 +183,16 @@ public class AAA {
 
         StateMachine.initializeMaps();
 
-        hostService.startMonitoringIp(IpAddress.valueOf(radiusIpAddress));
+        try {
+            radiusSocket = new DatagramSocket(radiusServerPort);
+        } catch (Exception ex) {
+            log.error("Can't open RADIUS socket", ex);
+        }
+
+        executor = Executors.newSingleThreadExecutor(
+                new ThreadFactoryBuilder()
+                        .setNameFormat("AAA-radius-%d").build());
+        executor.execute(radiusListener);
     }
 
     @Deactivate
@@ -195,6 +203,24 @@ public class AAA {
         packetService.removeProcessor(processor);
         processor = null;
         StateMachine.destroyMaps();
+        radiusSocket.close();
+        executor.shutdownNow();
+    }
+
+    protected void sendRADIUSPacket(RADIUS radiusPacket) {
+
+        try {
+            final byte[] data = radiusPacket.serialize();
+            final DatagramSocket socket = radiusSocket;
+
+            DatagramPacket packet =
+                    new DatagramPacket(data, data.length,
+                                       radiusIpAddress, radiusServerPort);
+
+            socket.send(packet);
+        } catch (IOException e) {
+            log.info("Cannot send packet to RADIUS server", e);
+        }
     }
 
     /**
@@ -205,14 +231,6 @@ public class AAA {
         selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
         packetService.requestPackets(selector.build(),
                                      CONTROL, appId);
-
-        TrafficSelector radSelector = DefaultTrafficSelector.builder()
-                .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
-                .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(radiusServerPort))
-                .matchUdpSrc(TpPort.tpPort(radiusServerPort))
-                .build();
-        packetService.requestPackets(radSelector, CONTROL, appId);
     }
 
     /**
@@ -222,14 +240,19 @@ public class AAA {
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
         selector.matchEthType(EthType.EtherType.EAPOL.ethType().toShort());
         packetService.cancelPackets(selector.build(), CONTROL, appId);
+    }
 
-        TrafficSelector radSelector = DefaultTrafficSelector.builder()
-                .matchEthType(EthType.EtherType.IPV4.ethType().toShort())
-                .matchIPProtocol(IPv4.PROTOCOL_UDP)
-                .matchUdpDst(TpPort.tpPort(radiusServerPort))
-                .matchUdpSrc(TpPort.tpPort(radiusServerPort))
-                .build();
-        packetService.cancelPackets(radSelector, CONTROL, appId);
+    /**
+     * Send the ethernet packet to the supplicant.
+     *
+     * @param ethernetPkt  the ethernet packet
+     * @param connectPoint the connect point to send out
+     */
+    private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
+        OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
+                                                          treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
+        packetService.emit(packet);
     }
 
     // our handler defined as a private inner class
@@ -252,27 +275,12 @@ public class AAA {
                 switch (EthType.EtherType.lookup(ethPkt.getEtherType())) {
                     case EAPOL:
                         handleSupplicantPacket(context.inPacket());
-                        break;
-                    case IPV4:
-                        IPv4 ipv4Packet = (IPv4) ethPkt.getPayload();
-                        Ip4Address srcIp = Ip4Address.valueOf(ipv4Packet.getSourceAddress());
-                        Ip4Address radiusIp4Address = Ip4Address.valueOf(radiusIpAddress);
-                        if (srcIp.equals(radiusIp4Address) && ipv4Packet.getProtocol() == IPv4.PROTOCOL_UDP) {
-                            // TODO: check for port as well when it's configurable
-                            UDP udpPacket = (UDP) ipv4Packet.getPayload();
-
-                            byte[] datagram = udpPacket.getPayload().serialize();
-                            RADIUS radiusPacket;
-                            radiusPacket = RADIUS.deserializer().deserialize(datagram, 0, datagram.length);
-                            handleRadiusPacket(radiusPacket);
-                        }
-
                         break;
                     default:
                         log.trace("Skipping Ethernet packet type {}",
                                   EthType.EtherType.lookup(ethPkt.getEtherType()));
                 }
-            } catch (DeserializationException | StateMachineException e) {
+            } catch (StateMachineException e) {
                 log.warn("Unable to process RADIUS packet:", e);
             }
         }
@@ -280,23 +288,26 @@ public class AAA {
         /**
          * Creates and initializes common fields of a RADIUS packet.
          *
-         * @param identifier RADIUS identifier
+         * @param stateMachine state machine for the request
          * @param eapPacket  EAP packet
          * @return RADIUS packet
          */
-        private RADIUS getRadiusPayload(byte identifier, EAP eapPacket) {
+        private RADIUS getRadiusPayload(StateMachine stateMachine, byte identifier, EAP eapPacket) {
             RADIUS radiusPayload =
                     new RADIUS(RADIUS.RADIUS_CODE_ACCESS_REQUEST,
                                eapPacket.getIdentifier());
+
+            // set Request Authenticator in StateMachine
+            stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
+
             radiusPayload.setIdentifier(identifier);
             radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME,
-                                       eapPacket.getData());
+                                       stateMachine.username());
 
             radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP,
                                        AAA.this.nasIpAddress.getAddress());
 
             radiusPayload.encapsulateMessage(eapPacket);
-            radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
 
             return radiusPayload;
         }
@@ -329,13 +340,13 @@ public class AAA {
 
                     //send an EAP Request/Identify to the supplicant
                     EAP eapPayload = new EAP(EAP.REQUEST, stateMachine.identifier(), EAP.ATTR_IDENTITY, null);
-                    Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(1L),
+                    Ethernet eth = buildEapolResponse(srcMAC, MacAddress.valueOf(nasMacAddress),
                                                       ethPkt.getVlanID(), EAPOL.EAPOL_PACKET,
                                                       eapPayload);
                     stateMachine.setSupplicantAddress(srcMAC);
                     stateMachine.setVlanId(ethPkt.getVlanID());
 
-                    this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
 
                     break;
                 case EAPOL.EAPOL_PACKET:
@@ -350,11 +361,10 @@ public class AAA {
                             // request id access to RADIUS
                             stateMachine.setUsername(eapPacket.getData());
 
-                            radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
+                            radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
+                            radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
 
-                            // set Request Authenticator in StateMachine
-                            stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
-                            sendRadiusMessage(radiusPayload);
+                            sendRADIUSPacket(radiusPayload);
 
                             // change the state to "PENDING"
                             stateMachine.requestAccess();
@@ -365,24 +375,31 @@ public class AAA {
                             // machine.
                             if (eapPacket.getIdentifier() == stateMachine.challengeIdentifier()) {
                                 //send the RADIUS challenge response
-                                radiusPayload = getRadiusPayload(stateMachine.challengeIdentifier(), eapPacket);
+                                radiusPayload =
+                                        getRadiusPayload(stateMachine,
+                                                         stateMachine.identifier(),
+                                                         eapPacket);
 
                                 radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
                                                            stateMachine.challengeState());
-                                sendRadiusMessage(radiusPayload);
+                                radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+                                sendRADIUSPacket(radiusPayload);
                             }
                             break;
                         case EAP.ATTR_TLS:
                             // request id access to RADIUS
-                            radiusPayload = getRadiusPayload(stateMachine.identifier(), eapPacket);
+                            radiusPayload = getRadiusPayload(stateMachine, stateMachine.identifier(), eapPacket);
 
                             radiusPayload.setAttribute(RADIUSAttribute.RADIUS_ATTR_STATE,
                                                        stateMachine.challengeState());
                             stateMachine.setRequestAuthenticator(radiusPayload.generateAuthCode());
 
-                            sendRadiusMessage(radiusPayload);
-                            // TODO: this gets called on every fragment, should only be called at TLS-Start
-                            stateMachine.requestAccess();
+                            radiusPayload.addMessageAuthenticator(AAA.this.radiusSecret);
+                            sendRADIUSPacket(radiusPayload);
+
+                            if (stateMachine.state() != StateMachine.STATE_PENDING) {
+                                stateMachine.requestAccess();
+                            }
 
                             break;
                         default:
@@ -392,14 +409,18 @@ public class AAA {
                 default:
                     log.trace("Skipping EAPOL message {}", eapol.getEapolType());
             }
+
         }
+    }
+
+    class RadiusListener implements Runnable {
 
         /**
          * Handles RADIUS packets.
          *
          * @param radiusPacket RADIUS packet coming from the RADIUS server.
          */
-        private void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
+        protected void handleRadiusPacket(RADIUS radiusPacket) throws StateMachineException {
             StateMachine stateMachine = StateMachine.lookupStateMachineById(radiusPacket.getIdentifier());
             if (stateMachine == null) {
                 log.error("Invalid session identifier, exiting...");
@@ -410,13 +431,16 @@ public class AAA {
             Ethernet eth;
             switch (radiusPacket.getCode()) {
                 case RADIUS.RADIUS_CODE_ACCESS_CHALLENGE:
-                    byte[] challengeState = radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
+                    byte[] challengeState =
+                            radiusPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_STATE).getValue();
                     eapPayload = radiusPacket.decapsulateMessage();
                     stateMachine.setChallengeInfo(eapPayload.getIdentifier(), challengeState);
                     eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                                             MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
+                                             MacAddress.valueOf(nasMacAddress),
+                                             stateMachine.vlanId(),
+                                             EAPOL.EAPOL_PACKET,
                                              eapPayload);
-                    this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
                     break;
                 case RADIUS.RADIUS_CODE_ACCESS_ACCEPT:
                     //send an EAPOL - Success to the supplicant.
@@ -425,9 +449,11 @@ public class AAA {
                     eapPayload = new EAP();
                     eapPayload = (EAP) eapPayload.deserialize(eapMessage, 0, eapMessage.length);
                     eth = buildEapolResponse(stateMachine.supplicantAddress(),
-                                             MacAddress.valueOf(1L), stateMachine.vlanId(), EAPOL.EAPOL_PACKET,
+                                             MacAddress.valueOf(nasMacAddress),
+                                             stateMachine.vlanId(),
+                                             EAPOL.EAPOL_PACKET,
                                              eapPayload);
-                    this.sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
+                    sendPacketToSupplicant(eth, stateMachine.supplicantConnectpoint());
 
                     stateMachine.authorizeAccess();
                     break;
@@ -439,59 +465,45 @@ public class AAA {
             }
         }
 
-        private void sendRadiusMessage(RADIUS radiusMessage) {
-            Set<Host> hosts = hostService.getHostsByIp(IpAddress.valueOf(radiusIpAddress));
-            Optional<Host> odst = hosts.stream().filter(h -> h.vlan().toShort() == VlanId.UNTAGGED).findFirst();
-
-            Host dst;
-            if (!odst.isPresent()) {
-                log.info("Radius server {} is not present", radiusIpAddress);
-                return;
-            } else {
-                dst = odst.get();
-            }
-
-            UDP udp = new UDP();
-            IPv4 ip4Packet = new IPv4();
-            Ethernet ethPkt = new Ethernet();
-            radiusMessage.setParent(udp);
-            udp.setDestinationPort(radiusServerPort);
-            udp.setSourcePort(radiusServerPort);
-            udp.setPayload(radiusMessage);
-            udp.setParent(ip4Packet);
-            ip4Packet.setSourceAddress(AAA.this.nasIpAddress.getHostAddress());
-            ip4Packet.setDestinationAddress(AAA.this.radiusIpAddress.getHostAddress());
-            ip4Packet.setProtocol(IPv4.PROTOCOL_UDP);
-            ip4Packet.setPayload(udp);
-            ip4Packet.setParent(ethPkt);
-            ethPkt.setDestinationMACAddress(radiusMacAddress);
-            ethPkt.setSourceMACAddress(nasMacAddress);
-            ethPkt.setEtherType(Ethernet.TYPE_IPV4);
-            ethPkt.setPayload(ip4Packet);
-
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setOutput(PortNumber.portNumber(radiusPort)).build();
-            OutboundPacket packet = new DefaultOutboundPacket(DeviceId.deviceId(radiusSwitch),
-                                                              treatment, ByteBuffer.wrap(ethPkt.serialize()));
-            packetService.emit(packet);
 
-        }
+        @Override
+        public void run() {
+            boolean done = false;
+            int packetNumber = 1;
+
+            log.info("UDP listener thread starting up");
+            RADIUS inboundRadiusPacket;
+            while (!done) {
+                try {
+                    byte[] packetBuffer = new byte[RADIUS.RADIUS_MAX_LENGTH];
+                    DatagramPacket inboundBasePacket =
+                            new DatagramPacket(packetBuffer, packetBuffer.length);
+                    DatagramSocket socket = radiusSocket;
+                    socket.receive(inboundBasePacket);
+                    log.info("Packet #{} received", packetNumber++);
+                    try {
+                        inboundRadiusPacket =
+                                RADIUS.deserializer()
+                                        .deserialize(inboundBasePacket.getData(),
+                                                     0,
+                                                     inboundBasePacket.getLength());
+                        handleRadiusPacket(inboundRadiusPacket);
+                    } catch (DeserializationException dex) {
+                        log.error("Cannot deserialize packet", dex);
+                    } catch (StateMachineException sme) {
+                        log.error("Illegal state machine operation", sme);
+                    }
 
-        /**
-         * Send the ethernet packet to the supplicant.
-         *
-         * @param ethernetPkt  the ethernet packet
-         * @param connectPoint the connect point to send out
-         */
-        private void sendPacketToSupplicant(Ethernet ethernetPkt, ConnectPoint connectPoint) {
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder().setOutput(connectPoint.port()).build();
-            OutboundPacket packet = new DefaultOutboundPacket(connectPoint.deviceId(),
-                                                              treatment, ByteBuffer.wrap(ethernetPkt.serialize()));
-            packetService.emit(packet);
+                } catch (IOException e) {
+                    log.info("Socket was closed, exiting listener thread");
+                    done = true;
+                }
+            }
         }
-
     }
 
+    RadiusListener radiusListener = new RadiusListener();
+
     private class InternalConfigListener implements NetworkConfigListener {
 
         /**
diff --git a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java
index c18d2bf2..73be7691 100644
--- a/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java
+++ b/framework/src/onos/apps/aaa/src/main/java/org/onosproject/aaa/AAAConfig.java
@@ -37,13 +37,13 @@ public class AAAConfig extends Config<ApplicationId> {
     private static final String RADIUS_PORT = "radiusPort";
 
     // RADIUS server IP address
-    protected static final String DEFAULT_RADIUS_IP = "192.168.1.10";
+    protected static final String DEFAULT_RADIUS_IP = "10.128.10.4";
 
     // RADIUS MAC address
     protected static final String DEFAULT_RADIUS_MAC = "00:00:00:00:01:10";
 
     // NAS IP address
-    protected static final String DEFAULT_NAS_IP = "192.168.1.11";
+    protected static final String DEFAULT_NAS_IP = "10.128.9.244";
 
     // NAS MAC address
     protected static final String DEFAULT_NAS_MAC = "00:00:00:00:10:01";
diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java
new file mode 100644
index 00000000..fb513ced
--- /dev/null
+++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAAIntegrationTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.onosproject.aaa;
+
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.packet.EAP;
+import org.onlab.packet.EAPOL;
+import org.onlab.packet.Ethernet;
+import org.onosproject.core.CoreServiceAdapter;
+import org.onosproject.net.config.Config;
+import org.onosproject.net.config.NetworkConfigRegistryAdapter;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Set of tests of the ONOS application component. These use an existing RADIUS
+ * server and sends live packets over the network to it.
+ */
+@Ignore ("This should not be run as part of the standard build")
+public class AAAIntegrationTest extends AAATestBase {
+
+    private AAA aaa;
+
+    /**
+     * Mocks the network config registry.
+     */
+    @SuppressWarnings("unchecked")
+    static final class TestNetworkConfigRegistry
+            extends NetworkConfigRegistryAdapter {
+        @Override
+        public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
+            return (C) new AAAConfig();
+        }
+    }
+
+    /**
+     * Sets up the services required by the AAA application.
+     */
+    @Before
+    public void setUp() {
+        aaa = new AAA();
+        aaa.netCfgService = new TestNetworkConfigRegistry();
+        aaa.coreService = new CoreServiceAdapter();
+        aaa.packetService = new MockPacketService();
+        aaa.activate();
+    }
+
+    /**
+     * Fetches the sent packet at the given index. The requested packet
+     * must be the last packet on the list.
+     *
+     * @param index index into sent packets array
+     * @return packet
+     */
+    private Ethernet fetchPacket(int index) {
+        for (int iteration = 0; iteration < 20; iteration++) {
+            if (savedPackets.size() > index) {
+                return (Ethernet) savedPackets.get(index);
+            } else {
+                try {
+                    Thread.sleep(250);
+                } catch (Exception ex) {
+                    return null;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Tests the authentication path through the AAA application by sending
+     * packets to the RADIUS server and checking the state machine
+     * transitions.
+     *
+     * @throws Exception when an unhandled error occurs
+     */
+    @Test
+    public void testAuthentication()  throws Exception {
+
+        //  (1) Supplicant start up
+
+        Ethernet startPacket = constructSupplicantStartPacket();
+        sendPacket(startPacket);
+
+        Ethernet responsePacket = fetchPacket(0);
+        assertThat(responsePacket, notNullValue());
+        checkRadiusPacket(aaa, responsePacket, EAP.REQUEST);
+
+        //  (2) Supplicant identify
+
+        Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
+        sendPacket(identifyPacket);
+
+        //  State machine should have been created by now
+
+        StateMachine stateMachine =
+                StateMachine.lookupStateMachineBySessionId(SESSION_ID);
+        assertThat(stateMachine, notNullValue());
+        assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
+
+        // (3) RADIUS MD5 challenge
+
+        Ethernet radiusChallengeMD5Packet = fetchPacket(1);
+        assertThat(radiusChallengeMD5Packet, notNullValue());
+        checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.REQUEST);
+
+
+        // (4) Supplicant MD5 response
+
+        Ethernet md5RadiusPacket =
+                constructSupplicantIdentifyPacket(stateMachine,
+                                                  EAP.ATTR_MD5,
+                                                  stateMachine.challengeIdentifier(),
+                                                  radiusChallengeMD5Packet);
+        sendPacket(md5RadiusPacket);
+
+
+        // (5) RADIUS Success
+
+        Ethernet successRadiusPacket = fetchPacket(2);
+        assertThat(successRadiusPacket, notNullValue());
+        EAPOL successEAPOL = (EAPOL) successRadiusPacket.getPayload();
+        EAP successEAP = (EAP) successEAPOL.getPayload();
+        assertThat(successEAP.getCode(), is(EAP.SUCCESS));
+
+        //  State machine should be in authorized state
+
+        assertThat(stateMachine, notNullValue());
+        assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
+
+    }
+
+}
+
diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java
index 75a60336..860a7dbd 100644
--- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java
+++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATest.java
@@ -15,163 +15,60 @@
  */
 package org.onosproject.aaa;
 
-import java.nio.ByteBuffer;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
-import org.onlab.packet.Data;
+import org.onlab.packet.BasePacket;
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.EAP;
-import org.onlab.packet.EAPOL;
-import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv4;
 import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
 import org.onlab.packet.RADIUS;
 import org.onlab.packet.RADIUSAttribute;
-import org.onlab.packet.UDP;
-import org.onlab.packet.VlanId;
 import org.onosproject.core.CoreServiceAdapter;
-import org.onosproject.net.Annotations;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
 import org.onosproject.net.config.Config;
 import org.onosproject.net.config.NetworkConfigRegistryAdapter;
-import org.onosproject.net.host.HostServiceAdapter;
-import org.onosproject.net.packet.DefaultInboundPacket;
-import org.onosproject.net.packet.DefaultPacketContext;
-import org.onosproject.net.packet.InboundPacket;
-import org.onosproject.net.packet.OutboundPacket;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketServiceAdapter;
-import org.onosproject.net.provider.ProviderId;
 
 import com.google.common.base.Charsets;
-import com.google.common.collect.ImmutableSet;
 
-import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-import static org.onosproject.net.NetTestTools.connectPoint;
 
 /**
  * Set of tests of the ONOS application component.
  */
-public class AAATest {
+public class AAATest extends AAATestBase {
 
-    MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a");
-    MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
+    static final String BAD_IP_ADDRESS = "198.51.100.0";
 
-    PacketProcessor packetProcessor;
     private AAA aaa;
-    List<Ethernet> savedPackets = new LinkedList<>();
 
-    /**
-     * Saves the given packet onto the saved packets list.
-     *
-     * @param eth packet to save
-     */
-    private void savePacket(Ethernet eth) {
-        savedPackets.add(eth);
+    class AAAWithoutRadiusServer extends AAA {
+        protected void sendRADIUSPacket(RADIUS radiusPacket) {
+            savePacket(radiusPacket);
+        }
     }
 
     /**
-     * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
+     * Mocks the AAAConfig class to force usage of an unroutable address for the
+     * RADIUS server.
      */
-    private class MockPacketService extends PacketServiceAdapter {
-
-        @Override
-        public void addProcessor(PacketProcessor processor, int priority) {
-            packetProcessor = processor;
-        }
-
+    static class MockAAAConfig extends AAAConfig {
         @Override
-        public void emit(OutboundPacket packet) {
+        public InetAddress radiusIp() {
             try {
-                Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
-                                                                   0, packet.data().array().length);
-                savePacket(eth);
-            } catch (Exception e) {
-                fail(e.getMessage());
+                return InetAddress.getByName(BAD_IP_ADDRESS);
+            } catch (UnknownHostException ex) {
+                // can't happen
+                throw new IllegalStateException(ex);
             }
         }
     }
 
-    /**
-     * Mocks the DefaultPacketContext.
-     */
-    private final class TestPacketContext extends DefaultPacketContext {
-
-        private TestPacketContext(long time, InboundPacket inPkt,
-                                  OutboundPacket outPkt, boolean block) {
-            super(time, inPkt, outPkt, block);
-        }
-
-        @Override
-        public void send() {
-            // We don't send anything out.
-        }
-    }
-
-    /**
-     * Mocks a host to allow locating the Radius server.
-     */
-    private static final class MockHost implements Host {
-        @Override
-        public HostId id() {
-            return null;
-        }
-
-        @Override
-        public MacAddress mac() {
-            return null;
-        }
-
-        @Override
-        public VlanId vlan() {
-            return VlanId.vlanId(VlanId.UNTAGGED);
-        }
-
-        @Override
-        public Set<IpAddress> ipAddresses() {
-            return null;
-        }
-
-        @Override
-        public HostLocation location() {
-            return null;
-        }
-
-        @Override
-        public Annotations annotations() {
-            return null;
-        }
-
-        @Override
-        public ProviderId providerId() {
-            return null;
-        }
-    }
-
-    /**
-     * Mocks the Host service.
-     */
-    private static final class MockHostService extends HostServiceAdapter {
-        @Override
-        public Set<Host> getHostsByIp(IpAddress ip) {
-            return ImmutableSet.of(new MockHost());
-        }
-    }
-
     /**
      * Mocks the network config registry.
      */
@@ -180,82 +77,11 @@ public class AAATest {
             extends NetworkConfigRegistryAdapter {
         @Override
         public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
-            return (C) new AAAConfig();
+            AAAConfig aaaConfig = new MockAAAConfig();
+            return (C) aaaConfig;
         }
     }
 
-    /**
-     * Sends an Ethernet packet to the process method of the Packet Processor.
-     *
-     * @param reply Ethernet packet
-     */
-    private void sendPacket(Ethernet reply) {
-        final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
-        InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
-                                                          reply,
-                                                          byteBuffer);
-
-        PacketContext context = new TestPacketContext(127L, inPacket, null, false);
-        packetProcessor.process(context);
-    }
-
-    /**
-     * Constructs an Ethernet packet containing a EAPOL_START Payload.
-     *
-     * @return Ethernet packet
-     */
-    private Ethernet constructSupplicantStartPacket() {
-        Ethernet eth = new Ethernet();
-        eth.setDestinationMACAddress(clientMac.toBytes());
-        eth.setSourceMACAddress(serverMac.toBytes());
-        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
-        eth.setVlanID((short) 2);
-
-        EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 1, EAPOL.EAPOL_START, null);
-
-        //eapol header
-        EAPOL eapol = new EAPOL();
-        eapol.setEapolType(EAPOL.EAPOL_START);
-        eapol.setPacketLength(eap.getLength());
-
-        //eap part
-        eapol.setPayload(eap);
-
-        eth.setPayload(eapol);
-        eth.setPad(true);
-        return eth;
-    }
-
-    /**
-     * Constructs an Ethernet packet containing identification payload.
-     *
-     * @return Ethernet packet
-     */
-    private Ethernet constructSupplicantIdentifyPacket(byte type) {
-        Ethernet eth = new Ethernet();
-        eth.setDestinationMACAddress(clientMac.toBytes());
-        eth.setSourceMACAddress(serverMac.toBytes());
-        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
-        eth.setVlanID((short) 2);
-
-        String username = "user";
-        EAP eap = new EAP(EAP.REQUEST, (byte) 1, type,
-                          username.getBytes(Charsets.US_ASCII));
-        eap.setIdentifier((byte) 1);
-
-        // eapol header
-        EAPOL eapol = new EAPOL();
-        eapol.setEapolType(EAPOL.EAPOL_PACKET);
-        eapol.setPacketLength(eap.getLength());
-
-        // eap part
-        eapol.setPayload(eap);
-
-        eth.setPayload(eapol);
-        eth.setPad(true);
-        return eth;
-    }
-
     /**
      * Constructs an Ethernet packet containing a RADIUS challenge
      * packet.
@@ -264,18 +90,9 @@ public class AAATest {
      * @param challengeType type to use in challenge packet
      * @return Ethernet packet
      */
-    private Ethernet constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
-        Ethernet eth = new Ethernet();
-        eth.setDestinationMACAddress(clientMac.toBytes());
-        eth.setSourceMACAddress(serverMac.toBytes());
-        eth.setEtherType(EthType.EtherType.IPV4.ethType().toShort());
-        eth.setVlanID((short) 2);
-
-        IPv4 ipv4 = new IPv4();
-        ipv4.setProtocol(IPv4.PROTOCOL_UDP);
-        ipv4.setSourceAddress(aaa.radiusIpAddress.getHostAddress());
+    private RADIUS constructRADIUSCodeAccessChallengePacket(byte challengeCode, byte challengeType) {
 
-        String challenge = "1234";
+        String challenge = "12345678901234567";
 
         EAP eap = new EAP(challengeType, (byte) 1, challengeType,
                           challenge.getBytes(Charsets.US_ASCII));
@@ -291,13 +108,7 @@ public class AAATest {
         radius.setAttribute(RADIUSAttribute.RADIUS_ATTR_EAP_MESSAGE,
                             eap.serialize());
 
-        UDP udp = new UDP();
-        udp.setPayload(radius);
-        ipv4.setPayload(udp);
-
-        eth.setPayload(ipv4);
-        eth.setPad(true);
-        return eth;
+        return radius;
     }
 
     /**
@@ -305,11 +116,10 @@ public class AAATest {
      */
     @Before
     public void setUp() {
-        aaa = new AAA();
+        aaa = new AAAWithoutRadiusServer();
         aaa.netCfgService = new TestNetworkConfigRegistry();
         aaa.coreService = new CoreServiceAdapter();
         aaa.packetService = new MockPacketService();
-        aaa.hostService = new MockHostService();
         aaa.activate();
     }
 
@@ -324,60 +134,16 @@ public class AAATest {
     /**
      * Extracts the RADIUS packet from a packet sent by the supplicant.
      *
-     * @param supplicantPacket packet sent by the supplicant
-     * @return RADIUS packet
+     * @param radius RADIUS packet sent by the supplicant
      * @throws DeserializationException if deserialization of the packet contents
      *         fails.
      */
-    private RADIUS checkAndFetchRADIUSPacketFromSupplicant(Ethernet supplicantPacket)
+    private void checkRADIUSPacketFromSupplicant(RADIUS radius)
             throws DeserializationException {
-        assertThat(supplicantPacket, notNullValue());
-        assertThat(supplicantPacket.getVlanID(), is(VlanId.UNTAGGED));
-        assertThat(supplicantPacket.getSourceMAC().toString(), is(aaa.nasMacAddress));
-        assertThat(supplicantPacket.getDestinationMAC().toString(), is(aaa.radiusMacAddress));
-
-        assertThat(supplicantPacket.getPayload(), instanceOf(IPv4.class));
-        IPv4 ipv4 = (IPv4) supplicantPacket.getPayload();
-        assertThat(ipv4, notNullValue());
-        assertThat(IpAddress.valueOf(ipv4.getSourceAddress()).toString(),
-                   is(aaa.nasIpAddress.getHostAddress()));
-        assertThat(IpAddress.valueOf(ipv4.getDestinationAddress()).toString(),
-                   is(aaa.radiusIpAddress.getHostAddress()));
-
-        assertThat(ipv4.getPayload(), instanceOf(UDP.class));
-        UDP udp = (UDP) ipv4.getPayload();
-        assertThat(udp, notNullValue());
-
-        assertThat(udp.getPayload(), instanceOf(Data.class));
-        Data data = (Data) udp.getPayload();
-        RADIUS radius = RADIUS.deserializer()
-                .deserialize(data.getData(), 0, data.getData().length);
         assertThat(radius, notNullValue());
-        return radius;
-    }
-
-    /**
-     * Checks the contents of a RADIUS packet being sent to the RADIUS server.
-     *
-     * @param radiusPacket packet to check
-     * @param code expected code
-     */
-    private void checkRadiusPacket(Ethernet radiusPacket, byte code) {
-        assertThat(radiusPacket.getVlanID(), is((short) 2));
-
-        // TODO: These address values seem wrong, but are produced by the current AAA implementation
-        assertThat(radiusPacket.getSourceMAC(), is(MacAddress.valueOf(1L)));
-        assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
 
-        assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class));
-        EAPOL eapol = (EAPOL) radiusPacket.getPayload();
-        assertThat(eapol, notNullValue());
-
-        assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
-        assertThat(eapol.getPayload(), instanceOf(EAP.class));
-        EAP eap = (EAP) eapol.getPayload();
+        EAP eap = radius.decapsulateMessage();
         assertThat(eap, notNullValue());
-        assertThat(eap.getCode(), is(code));
     }
 
     /**
@@ -387,11 +153,10 @@ public class AAATest {
      * @param index index into sent packets array
      * @return packet
      */
-    private Ethernet fetchPacket(int index) {
-        assertThat(savedPackets.size(), is(index + 1));
-        Ethernet eth = savedPackets.get(index);
-        assertThat(eth, notNullValue());
-        return eth;
+    private BasePacket fetchPacket(int index) {
+        BasePacket packet = savedPackets.get(index);
+        assertThat(packet, notNullValue());
+        return packet;
     }
 
     /**
@@ -400,61 +165,64 @@ public class AAATest {
      * @throws DeserializationException if packed deserialization fails.
      */
     @Test
-    public void testAuthentication()  throws DeserializationException {
-
-        // Our session id will be the device ID ("of:1") with the port ("1") concatenated
-        String sessionId = "of:11";
+    public void testAuthentication()  throws Exception {
 
         //  (1) Supplicant start up
 
         Ethernet startPacket = constructSupplicantStartPacket();
         sendPacket(startPacket);
 
-        Ethernet responsePacket = fetchPacket(0);
-        checkRadiusPacket(responsePacket, EAP.ATTR_IDENTITY);
+        Ethernet responsePacket = (Ethernet) fetchPacket(0);
+        checkRadiusPacket(aaa, responsePacket, EAP.ATTR_IDENTITY);
 
         //  (2) Supplicant identify
 
-        Ethernet identifyPacket = constructSupplicantIdentifyPacket(EAP.ATTR_IDENTITY);
+        Ethernet identifyPacket = constructSupplicantIdentifyPacket(null, EAP.ATTR_IDENTITY, (byte) 1, null);
         sendPacket(identifyPacket);
 
-        Ethernet radiusIdentifyPacket = fetchPacket(1);
+        RADIUS radiusIdentifyPacket = (RADIUS) fetchPacket(1);
+
+        checkRADIUSPacketFromSupplicant(radiusIdentifyPacket);
 
-        RADIUS radiusAccessRequest = checkAndFetchRADIUSPacketFromSupplicant(radiusIdentifyPacket);
-        assertThat(radiusAccessRequest, notNullValue());
-        assertThat(radiusAccessRequest.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
-        assertThat(new String(radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
-                   is("user"));
+        assertThat(radiusIdentifyPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
+        assertThat(new String(radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_USERNAME).getValue()),
+                   is("testuser"));
 
         IpAddress nasIp =
                 IpAddress.valueOf(IpAddress.Version.INET,
-                                  radiusAccessRequest.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP)
+                                  radiusIdentifyPacket.getAttribute(RADIUSAttribute.RADIUS_ATTR_NAS_IP)
                                           .getValue());
         assertThat(nasIp.toString(), is(aaa.nasIpAddress.getHostAddress()));
 
         //  State machine should have been created by now
 
         StateMachine stateMachine =
-                StateMachine.lookupStateMachineBySessionId(sessionId);
+                StateMachine.lookupStateMachineBySessionId(SESSION_ID);
         assertThat(stateMachine, notNullValue());
         assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
 
         // (3) RADIUS MD5 challenge
 
-        Ethernet radiusCodeAccessChallengePacket =
+        RADIUS radiusCodeAccessChallengePacket =
                 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_MD5);
-        sendPacket(radiusCodeAccessChallengePacket);
+        aaa.radiusListener.handleRadiusPacket(radiusCodeAccessChallengePacket);
 
-        Ethernet radiusChallengeMD5Packet = fetchPacket(2);
-        checkRadiusPacket(radiusChallengeMD5Packet, EAP.ATTR_MD5);
+        Ethernet radiusChallengeMD5Packet = (Ethernet) fetchPacket(2);
+        checkRadiusPacket(aaa, radiusChallengeMD5Packet, EAP.ATTR_MD5);
 
         // (4) Supplicant MD5 response
 
-        Ethernet md5RadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_MD5);
+        Ethernet md5RadiusPacket =
+                constructSupplicantIdentifyPacket(stateMachine,
+                                                  EAP.ATTR_MD5,
+                                                  stateMachine.challengeIdentifier(),
+                                                  radiusChallengeMD5Packet);
         sendPacket(md5RadiusPacket);
-        Ethernet supplicantMD5ResponsePacket = fetchPacket(3);
-        RADIUS responseMd5RadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantMD5ResponsePacket);
-        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 1));
+
+        RADIUS responseMd5RadiusPacket = (RADIUS) fetchPacket(3);
+
+        checkRADIUSPacketFromSupplicant(responseMd5RadiusPacket);
+        assertThat(responseMd5RadiusPacket.getIdentifier(), is((byte) 0));
         assertThat(responseMd5RadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
 
         //  State machine should be in pending state
@@ -462,37 +230,20 @@ public class AAATest {
         assertThat(stateMachine, notNullValue());
         assertThat(stateMachine.state(), is(StateMachine.STATE_PENDING));
 
-        // (5) RADIUS TLS Challenge
-
-        Ethernet radiusCodeAccessChallengeTLSPacket =
-                constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_CHALLENGE, EAP.ATTR_TLS);
-        sendPacket(radiusCodeAccessChallengeTLSPacket);
+        // (5) RADIUS Success
 
-        Ethernet radiusChallengeTLSPacket = fetchPacket(4);
-        checkRadiusPacket(radiusChallengeTLSPacket, EAP.ATTR_TLS);
-
-        // (6) Supplicant TLS response
-
-        Ethernet tlsRadiusPacket = constructSupplicantIdentifyPacket(EAP.ATTR_TLS);
-        sendPacket(tlsRadiusPacket);
-        Ethernet supplicantTLSResponsePacket = fetchPacket(5);
-        RADIUS responseTLSRadiusPacket = checkAndFetchRADIUSPacketFromSupplicant(supplicantTLSResponsePacket);
-        assertThat(responseTLSRadiusPacket.getIdentifier(), is((byte) 0));
-        assertThat(responseTLSRadiusPacket.getCode(), is(RADIUS.RADIUS_CODE_ACCESS_REQUEST));
-
-        // (7) RADIUS Success
-
-        Ethernet successPacket =
+        RADIUS successPacket =
                 constructRADIUSCodeAccessChallengePacket(RADIUS.RADIUS_CODE_ACCESS_ACCEPT, EAP.SUCCESS);
-        sendPacket(successPacket);
-        Ethernet supplicantSuccessPacket = fetchPacket(6);
+        aaa.radiusListener.handleRadiusPacket((successPacket));
+        Ethernet supplicantSuccessPacket = (Ethernet) fetchPacket(4);
 
-        checkRadiusPacket(supplicantSuccessPacket, EAP.SUCCESS);
+        checkRadiusPacket(aaa, supplicantSuccessPacket, EAP.SUCCESS);
 
         //  State machine should be in authorized state
 
         assertThat(stateMachine, notNullValue());
         assertThat(stateMachine.state(), is(StateMachine.STATE_AUTHORIZED));
+
     }
 
     /**
@@ -502,7 +253,7 @@ public class AAATest {
     public void testConfig() {
         assertThat(aaa.nasIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_NAS_IP));
         assertThat(aaa.nasMacAddress, is(AAAConfig.DEFAULT_NAS_MAC));
-        assertThat(aaa.radiusIpAddress.getHostAddress(), is(AAAConfig.DEFAULT_RADIUS_IP));
+        assertThat(aaa.radiusIpAddress.getHostAddress(), is(BAD_IP_ADDRESS));
         assertThat(aaa.radiusMacAddress, is(AAAConfig.DEFAULT_RADIUS_MAC));
     }
 }
diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATestBase.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATestBase.java
new file mode 100644
index 00000000..dffcba2f
--- /dev/null
+++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/AAATestBase.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2015 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.onosproject.aaa;
+
+import java.nio.ByteBuffer;
+import java.security.MessageDigest;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.onlab.packet.BasePacket;
+import org.onlab.packet.EAP;
+import org.onlab.packet.EAPOL;
+import org.onlab.packet.EthType;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.MacAddress;
+import org.onosproject.net.packet.DefaultInboundPacket;
+import org.onosproject.net.packet.DefaultPacketContext;
+import org.onosproject.net.packet.InboundPacket;
+import org.onosproject.net.packet.OutboundPacket;
+import org.onosproject.net.packet.PacketContext;
+import org.onosproject.net.packet.PacketProcessor;
+import org.onosproject.net.packet.PacketServiceAdapter;
+
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.fail;
+import static org.onosproject.net.NetTestTools.connectPoint;
+
+/**
+ * Common methods for AAA app testing.
+ */
+public class AAATestBase {
+
+    MacAddress clientMac = MacAddress.valueOf("1a:1a:1a:1a:1a:1a");
+    MacAddress serverMac = MacAddress.valueOf("2a:2a:2a:2a:2a:2a");
+
+    // Our session id will be the device ID ("of:1") with the port ("1") concatenated
+    static final String SESSION_ID = "of:11";
+
+    List<BasePacket> savedPackets = new LinkedList<>();
+    PacketProcessor packetProcessor;
+
+    /**
+     * Saves the given packet onto the saved packets list.
+     *
+     * @param packet packet to save
+     */
+    void savePacket(BasePacket packet) {
+        savedPackets.add(packet);
+    }
+
+    /**
+     * Keeps a reference to the PacketProcessor and saves the OutboundPackets.
+     */
+    class MockPacketService extends PacketServiceAdapter {
+
+        @Override
+        public void addProcessor(PacketProcessor processor, int priority) {
+            packetProcessor = processor;
+        }
+
+        @Override
+        public void emit(OutboundPacket packet) {
+            try {
+                Ethernet eth = Ethernet.deserializer().deserialize(packet.data().array(),
+                                                                   0, packet.data().array().length);
+                savePacket(eth);
+            } catch (Exception e) {
+                fail(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Mocks the DefaultPacketContext.
+     */
+    final class TestPacketContext extends DefaultPacketContext {
+
+        private TestPacketContext(long time, InboundPacket inPkt,
+                                  OutboundPacket outPkt, boolean block) {
+            super(time, inPkt, outPkt, block);
+        }
+
+        @Override
+        public void send() {
+            // We don't send anything out.
+        }
+    }
+
+    /**
+     * Sends an Ethernet packet to the process method of the Packet Processor.
+     *
+     * @param reply Ethernet packet
+     */
+    void sendPacket(Ethernet reply) {
+        final ByteBuffer byteBuffer = ByteBuffer.wrap(reply.serialize());
+        InboundPacket inPacket = new DefaultInboundPacket(connectPoint("1", 1),
+                                                          reply,
+                                                          byteBuffer);
+
+        PacketContext context = new TestPacketContext(127L, inPacket, null, false);
+        packetProcessor.process(context);
+    }
+
+    /**
+     * Constructs an Ethernet packet containing identification payload.
+     *
+     * @return Ethernet packet
+     */
+    Ethernet constructSupplicantIdentifyPacket(StateMachine stateMachine,
+                                                       byte type,
+                                                       byte id,
+                                                       Ethernet radiusChallenge)
+            throws Exception {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(clientMac.toBytes());
+        eth.setSourceMACAddress(serverMac.toBytes());
+        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        eth.setVlanID((short) 2);
+
+        String username = "testuser";
+        byte[] data = username.getBytes();
+
+
+        if (type == EAP.ATTR_MD5) {
+            String password = "testpassword";
+            EAPOL eapol = (EAPOL) radiusChallenge.getPayload();
+            EAP eap = (EAP) eapol.getPayload();
+
+            byte[] identifier = new byte[password.length() + eap.getData().length];
+
+            identifier[0] = stateMachine.challengeIdentifier();
+            System.arraycopy(password.getBytes(), 0, identifier, 1, password.length());
+            System.arraycopy(eap.getData(), 1, identifier, 1 + password.length(), 16);
+
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] hash = md.digest(identifier);
+            data = new byte[17];
+            data[0] = (byte) 16;
+            System.arraycopy(hash, 0, data, 1, 16);
+        }
+        EAP eap = new EAP(EAP.RESPONSE, (byte) 1, type,
+                          data);
+        eap.setIdentifier(id);
+
+        // eapol header
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(EAPOL.EAPOL_PACKET);
+        eapol.setPacketLength(eap.getLength());
+
+        // eap part
+        eapol.setPayload(eap);
+
+        eth.setPayload(eapol);
+        eth.setPad(true);
+        return eth;
+    }
+
+    /**
+     * Constructs an Ethernet packet containing a EAPOL_START Payload.
+     *
+     * @return Ethernet packet
+     */
+    Ethernet constructSupplicantStartPacket() {
+        Ethernet eth = new Ethernet();
+        eth.setDestinationMACAddress(clientMac.toBytes());
+        eth.setSourceMACAddress(serverMac.toBytes());
+        eth.setEtherType(EthType.EtherType.EAPOL.ethType().toShort());
+        eth.setVlanID((short) 2);
+
+        EAP eap = new EAP(EAPOL.EAPOL_START, (byte) 2, EAPOL.EAPOL_START, null);
+
+        // eapol header
+        EAPOL eapol = new EAPOL();
+        eapol.setEapolType(EAPOL.EAPOL_START);
+        eapol.setPacketLength(eap.getLength());
+
+        // eap part
+        eapol.setPayload(eap);
+
+        eth.setPayload(eapol);
+        eth.setPad(true);
+        return eth;
+    }
+
+    /**
+     * Checks the contents of a RADIUS packet being sent to the RADIUS server.
+     *
+     * @param radiusPacket packet to check
+     * @param code expected code
+     */
+    void checkRadiusPacket(AAA aaa, Ethernet radiusPacket, byte code) {
+
+        assertThat(radiusPacket.getSourceMAC(),
+                   is(MacAddress.valueOf(aaa.nasMacAddress)));
+        assertThat(radiusPacket.getDestinationMAC(), is(serverMac));
+
+        assertThat(radiusPacket.getPayload(), instanceOf(EAPOL.class));
+        EAPOL eapol = (EAPOL) radiusPacket.getPayload();
+        assertThat(eapol, notNullValue());
+
+        assertThat(eapol.getEapolType(), is(EAPOL.EAPOL_PACKET));
+        assertThat(eapol.getPayload(), instanceOf(EAP.class));
+        EAP eap = (EAP) eapol.getPayload();
+        assertThat(eap, notNullValue());
+
+        assertThat(eap.getCode(), is(code));
+    }
+}
diff --git a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java
index 04837e85..1838c63e 100644
--- a/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java
+++ b/framework/src/onos/apps/aaa/src/test/java/org/onosproject/aaa/StateMachineTest.java
@@ -21,7 +21,9 @@ import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
-import static org.junit.Assert.*;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 
 
 public class StateMachineTest {
diff --git a/framework/src/onos/apps/cordvtn/pom.xml b/framework/src/onos/apps/cordvtn/pom.xml
index b8e913d4..1d96108b 100644
--- a/framework/src/onos/apps/cordvtn/pom.xml
+++ b/framework/src/onos/apps/cordvtn/pom.xml
@@ -54,6 +54,16 @@
             <artifactId>onos-ovsdb-api</artifactId>
             <version>${project.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+            <version>3.0.3</version>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
index ba707800..4b28a14b 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtn.java
@@ -27,7 +27,6 @@ import org.onlab.util.KryoNamespace;
 import org.onosproject.cluster.ClusterService;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
@@ -52,6 +51,7 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -75,7 +75,8 @@ public class CordVtn implements CordVtnService {
             .register(KryoNamespaces.API)
             .register(DefaultOvsdbNode.class);
     private static final String DEFAULT_BRIDGE_NAME = "br-int";
-    private static final Map<String, String> VXLAN_OPTIONS = new HashMap<String, String>() {
+    private static final String DEFAULT_TUNNEL = "vxlan";
+    private static final Map<String, String> DEFAULT_TUNNEL_OPTIONS = new HashMap<String, String>() {
         {
             put("key", "flow");
             put("local_ip", "flow");
@@ -97,9 +98,6 @@ public class CordVtn implements CordVtnService {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected HostService hostService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MastershipService mastershipService;
-
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OvsdbController controller;
 
@@ -117,11 +115,10 @@ public class CordVtn implements CordVtnService {
     private final VmHandler vmHandler = new VmHandler();
 
     private ConsistentMap<DeviceId, OvsdbNode> nodeStore;
-    private ApplicationId appId;
 
     @Activate
     protected void activate() {
-        appId = coreService.registerApplication("org.onosproject.cordvtn");
+        ApplicationId appId = coreService.registerApplication("org.onosproject.cordvtn");
         nodeStore = storageService.<DeviceId, OvsdbNode>consistentMapBuilder()
                 .withSerializer(Serializer.using(NODE_SERIALIZER.build()))
                 .withName("cordvtn-nodestore")
@@ -148,38 +145,59 @@ public class CordVtn implements CordVtnService {
     @Override
     public void addNode(OvsdbNode ovsdb) {
         checkNotNull(ovsdb);
-        nodeStore.put(ovsdb.deviceId(), ovsdb);
+
+        nodeStore.putIfAbsent(ovsdb.deviceId(), ovsdb);
+
+        if (isNodeConnected(ovsdb)) {
+            init(ovsdb);
+        } else {
+            connect(ovsdb);
+        }
     }
 
     @Override
     public void deleteNode(OvsdbNode ovsdb) {
         checkNotNull(ovsdb);
 
+        if (deviceService.getDevice(ovsdb.deviceId()) != null) {
+            if (deviceService.isAvailable(ovsdb.deviceId())) {
+                log.warn("Cannot delete connected node {}", ovsdb.host());
+                return;
+            }
+        }
+        nodeStore.remove(ovsdb.deviceId());
+    }
+
+    @Override
+    public void connect(OvsdbNode ovsdb) {
+        checkNotNull(ovsdb);
+
         if (!nodeStore.containsKey(ovsdb.deviceId())) {
+            log.warn("Node {} does not exist", ovsdb.host());
             return;
         }
 
-        // check ovsdb and integration bridge connection state first
-        if (isNodeConnected(ovsdb)) {
-            log.warn("Cannot delete connected node {}", ovsdb.host());
-        } else {
-            nodeStore.remove(ovsdb.deviceId());
+        if (!isNodeConnected(ovsdb)) {
+            controller.connect(ovsdb.ip(), ovsdb.port());
         }
     }
 
     @Override
-    public void connect(OvsdbNode ovsdb) {
+    public void disconnect(OvsdbNode ovsdb) {
         checkNotNull(ovsdb);
 
         if (!nodeStore.containsKey(ovsdb.deviceId())) {
             log.warn("Node {} does not exist", ovsdb.host());
             return;
         }
-        controller.connect(ovsdb.ip(), ovsdb.port());
+
+        if (isNodeConnected(ovsdb)) {
+            OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
+            ovsdbClient.disconnect();
+        }
     }
 
-    @Override
-    public void disconnect(OvsdbNode ovsdb) {
+    private void init(OvsdbNode ovsdb) {
         checkNotNull(ovsdb);
 
         if (!nodeStore.containsKey(ovsdb.deviceId())) {
@@ -187,11 +205,16 @@ public class CordVtn implements CordVtnService {
             return;
         }
 
-        OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
-        checkNotNull(ovsdbClient);
+        if (!isNodeConnected(ovsdb)) {
+            log.warn("Node {} is not connected", ovsdb.host());
+            return;
+        }
 
-        if (ovsdbClient.isConnected()) {
-            ovsdbClient.disconnect();
+        if (deviceService.getDevice(ovsdb.intBrId()) == null ||
+                !deviceService.isAvailable(ovsdb.intBrId())) {
+            createIntegrationBridge(ovsdb);
+        } else if (!checkVxlanPort(ovsdb)) {
+            createVxlanPort(ovsdb);
         }
     }
 
@@ -235,11 +258,45 @@ public class CordVtn implements CordVtnService {
         OvsdbClientService ovsdbClient = controller.getOvsdbClient(
                 new OvsdbNodeId(ovsdb.ip(), ovsdb.port().toInt()));
         if (ovsdbClient == null) {
-            log.warn("Couldn't find ovsdb client of node {}", ovsdb.host());
+            log.debug("Couldn't find ovsdb client for {}", ovsdb.host());
         }
         return ovsdbClient;
     }
 
+    private void createIntegrationBridge(OvsdbNode ovsdb) {
+        List<ControllerInfo> controllers = new ArrayList<>();
+        Sets.newHashSet(clusterService.getNodes())
+                .forEach(controller -> {
+                    ControllerInfo ctrlInfo = new ControllerInfo(controller.ip(), OFPORT, "tcp");
+                    controllers.add(ctrlInfo);
+                });
+        String dpid = ovsdb.intBrId().toString().substring(DPID_BEGIN);
+
+        // TODO change to use bridge config
+        OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
+        ovsdbClient.createBridge(DEFAULT_BRIDGE_NAME, dpid, controllers);
+    }
+
+    private void createVxlanPort(OvsdbNode ovsdb) {
+        // TODO change to use tunnel config and tunnel description
+        OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
+        ovsdbClient.createTunnel(DEFAULT_BRIDGE_NAME, DEFAULT_TUNNEL,
+                                 DEFAULT_TUNNEL, DEFAULT_TUNNEL_OPTIONS);
+    }
+
+    private boolean checkVxlanPort(OvsdbNode ovsdb) {
+        // TODO change to use tunnel config
+        OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
+        try {
+            ovsdbClient.getPorts().stream()
+                    .filter(p -> p.portName().value().equals(DEFAULT_TUNNEL))
+                    .findFirst().get();
+        } catch (NoSuchElementException e) {
+            return false;
+        }
+        return true;
+    }
+
     private class InternalDeviceListener implements DeviceListener {
 
         @Override
@@ -252,8 +309,11 @@ public class CordVtn implements CordVtnService {
                     eventExecutor.submit(() -> handler.connected(device));
                     break;
                 case DEVICE_AVAILABILITY_CHANGED:
-                    eventExecutor.submit(() -> handler.disconnected(device));
-                    // TODO handle the case that the device is recovered
+                    if (deviceService.isAvailable(device.id())) {
+                        eventExecutor.submit(() -> handler.connected(device));
+                    } else {
+                        eventExecutor.submit(() -> handler.disconnected(device));
+                    }
                     break;
                 default:
                     break;
@@ -286,20 +346,10 @@ public class CordVtn implements CordVtnService {
         public void connected(Device device) {
             log.info("Ovsdb {} is connected", device.id());
 
-            if (!mastershipService.isLocalMaster(device.id())) {
-                return;
-            }
-
-            // TODO change to use bridge config
             OvsdbNode ovsdb = getNode(device.id());
-            OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
-
-            List<ControllerInfo> controllers = new ArrayList<>();
-            Sets.newHashSet(clusterService.getNodes()).forEach(controller ->
-                        controllers.add(new ControllerInfo(controller.ip(), OFPORT, "tcp")));
-            String dpid = ovsdb.intBrId().toString().substring(DPID_BEGIN);
-
-            ovsdbClient.createBridge(DEFAULT_BRIDGE_NAME, dpid, controllers);
+            if (ovsdb != null) {
+                init(ovsdb);
+            }
         }
 
         @Override
@@ -314,22 +364,19 @@ public class CordVtn implements CordVtnService {
         public void connected(Device device) {
             log.info("Integration Bridge {} is detected", device.id());
 
-            OvsdbNode ovsdb = getNodes().stream()
-                    .filter(node -> node.intBrId().equals(device.id()))
-                    .findFirst().get();
-
-            if (ovsdb == null) {
+            OvsdbNode ovsdb;
+            try {
+                ovsdb = getNodes().stream()
+                        .filter(node -> node.intBrId().equals(device.id()))
+                        .findFirst().get();
+            } catch (NoSuchElementException e) {
                 log.warn("Couldn't find OVSDB associated with {}", device.id());
                 return;
             }
 
-            if (!mastershipService.isLocalMaster(ovsdb.deviceId())) {
-                return;
+            if (!checkVxlanPort(ovsdb)) {
+                createVxlanPort(ovsdb);
             }
-
-            // TODO change to use tunnel config and tunnel description
-            OvsdbClientService ovsdbClient = getOvsdbClient(ovsdb);
-            ovsdbClient.createTunnel(DEFAULT_BRIDGE_NAME, "vxlan", "vxlan", VXLAN_OPTIONS);
         }
 
         @Override
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
index f276c7ca..287f2a34 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/CordVtnConfigManager.java
@@ -94,7 +94,6 @@ public class CordVtnConfigManager {
             DefaultOvsdbNode ovsdb = new DefaultOvsdbNode(
                     node.host(), node.ip(), node.port(), node.bridgeId());
             cordVtnService.addNode(ovsdb);
-            cordVtnService.connect(ovsdb);
         });
     }
 
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java
index c5b7a078..7a9a06a6 100644
--- a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/OvsdbNode.java
@@ -19,11 +19,20 @@ import org.onlab.packet.IpAddress;
 import org.onlab.packet.TpPort;
 import org.onosproject.net.DeviceId;
 
+import java.util.Comparator;
+
 /**
  * Representation of a node with ovsdb server.
  */
 public interface OvsdbNode {
 
+    Comparator<OvsdbNode> OVSDB_NODE_COMPARATOR = new Comparator<OvsdbNode>() {
+        @Override
+        public int compare(OvsdbNode ovsdb1, OvsdbNode ovsdb2) {
+            return ovsdb1.host().compareTo(ovsdb2.host());
+        }
+    };
+
     /**
      * Returns the IP address of the ovsdb server.
      *
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeAddCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeAddCommand.java
new file mode 100644
index 00000000..88d16341
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeAddCommand.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2015 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.onosproject.cordvtn.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordvtn.CordVtnService;
+import org.onosproject.cordvtn.DefaultOvsdbNode;
+import org.onosproject.cordvtn.OvsdbNode;
+import org.onosproject.net.DeviceId;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * Adds a new OVSDB nodes.
+ */
+@Command(scope = "onos", name = "ovsdb-add",
+        description = "Adds a new OVSDB node to cordvtn")
+public class OvsdbNodeAddCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "host", description = "Hostname or IP",
+            required = true, multiValued = false)
+    private String host = null;
+
+    @Argument(index = 1, name = "address",
+            description = "OVSDB server listening address (ip:port)",
+            required = true, multiValued = false)
+    private String address = null;
+
+    @Argument(index = 2, name = "bridgeId",
+            description = "Device ID of integration bridge",
+            required = true, multiValued = false)
+    private String bridgeId = null;
+
+    @Override
+    protected void execute() {
+        checkArgument(address.contains(":"), "address should be ip:port format");
+        checkArgument(bridgeId.startsWith("of:"), "bridgeId should be of:dpid format");
+
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+        String[] ipPort = address.split(":");
+        OvsdbNode ovsdb = new DefaultOvsdbNode(host,
+                                               IpAddress.valueOf(ipPort[0]),
+                                               TpPort.tpPort(Integer.parseInt(ipPort[1])),
+                                               DeviceId.deviceId(bridgeId));
+        service.addNode(ovsdb);
+    }
+}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeConnectCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeConnectCommand.java
new file mode 100644
index 00000000..e4ca0f3c
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeConnectCommand.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 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.onosproject.cordvtn.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordvtn.CordVtnService;
+import org.onosproject.cordvtn.OvsdbNode;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Connects to OVSDBs.
+ */
+@Command(scope = "onos", name = "ovsdb-connect",
+        description = "Connects to OVSDBs")
+public class OvsdbNodeConnectCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "hosts", description = "Hostname(s) or IP(s)",
+            required = true, multiValued = true)
+    private String[] hosts = null;
+
+    @Override
+    protected void execute() {
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+
+        for (String host : hosts) {
+            OvsdbNode ovsdb;
+            try {
+                ovsdb = service.getNodes().stream()
+                        .filter(node -> node.host().equals(host))
+                        .findFirst().get();
+            } catch (NoSuchElementException e) {
+                print("Unable to find %s", host);
+                continue;
+            }
+
+            if (service.isNodeConnected(ovsdb)) {
+                print("OVSDB %s is already in connected state, do nothing", host);
+            } else {
+                service.connect(ovsdb);
+            }
+        }
+    }
+}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDeleteCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDeleteCommand.java
new file mode 100644
index 00000000..a500d0d8
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDeleteCommand.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2015 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.onosproject.cordvtn.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordvtn.CordVtnService;
+import org.onosproject.cordvtn.OvsdbNode;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Deletes OVSDB nodes from cordvtn.
+ */
+@Command(scope = "onos", name = "ovsdb-delete",
+        description = "Deletes OVSDB nodes from cordvtn")
+public class OvsdbNodeDeleteCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "hosts", description = "Hostname(s) or IP(s)",
+            required = true, multiValued = true)
+    private String[] hosts = null;
+
+    @Override
+    protected void execute() {
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+
+        for (String host : hosts) {
+            OvsdbNode ovsdb;
+            try {
+                ovsdb = service.getNodes().stream()
+                        .filter(node -> node.host().equals(host))
+                        .findFirst().get();
+
+            } catch (NoSuchElementException e) {
+                print("Unable to find %s", host);
+                continue;
+            }
+
+            service.deleteNode(ovsdb);
+        }
+    }
+}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDisconnectCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDisconnectCommand.java
new file mode 100644
index 00000000..14e44e08
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeDisconnectCommand.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2015 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.onosproject.cordvtn.cli;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordvtn.CordVtnService;
+import org.onosproject.cordvtn.OvsdbNode;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Disconnects OVSDBs.
+ */
+@Command(scope = "onos", name = "ovsdb-disconnect",
+        description = "Disconnects OVSDBs")
+public class OvsdbNodeDisconnectCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "hosts", description = "Hostname(s) or IP(s)",
+            required = true, multiValued = true)
+    private String[] hosts = null;
+
+    @Override
+    protected void execute() {
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+
+        for (String host : hosts) {
+            OvsdbNode ovsdb;
+            try {
+                ovsdb = service.getNodes().stream()
+                        .filter(node -> node.host().equals(host))
+                        .findFirst().get();
+            } catch (NoSuchElementException e) {
+                print("Unable to find %s", host);
+                continue;
+            }
+
+            if (!service.isNodeConnected(ovsdb)) {
+                print("OVSDB %s is already in disconnected state, do nothing", host);
+            } else {
+                service.disconnect(ovsdb);
+            }
+        }
+    }
+}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeListCommand.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeListCommand.java
new file mode 100644
index 00000000..7d125ca6
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/OvsdbNodeListCommand.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015 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.onosproject.cordvtn.cli;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.cordvtn.CordVtnService;
+import org.onosproject.cordvtn.OvsdbNode;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Lists all OVSDB nodes.
+ */
+@Command(scope = "onos", name = "ovsdbs",
+        description = "Lists all OVSDB nodes registered in cordvtn application")
+public class OvsdbNodeListCommand extends AbstractShellCommand {
+
+    @Override
+    protected void execute() {
+        CordVtnService service = AbstractShellCommand.get(CordVtnService.class);
+        List<OvsdbNode> ovsdbs = service.getNodes();
+        Collections.sort(ovsdbs, OvsdbNode.OVSDB_NODE_COMPARATOR);
+
+        if (outputJson()) {
+            print("%s", json(service, ovsdbs));
+        } else {
+            for (OvsdbNode ovsdb : ovsdbs) {
+                print("host=%s, address=%s, br-int=%s, state=%s",
+                      ovsdb.host(),
+                      ovsdb.ip().toString() + ":" + ovsdb.port().toString(),
+                      ovsdb.intBrId().toString(),
+                      getState(service, ovsdb));
+            }
+            print("Total %s nodes", service.getNodeCount());
+        }
+    }
+
+    private JsonNode json(CordVtnService service, List<OvsdbNode> ovsdbs) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode result = mapper.createArrayNode();
+        for (OvsdbNode ovsdb : ovsdbs) {
+            String ipPort = ovsdb.ip().toString() + ":" + ovsdb.port().toString();
+            result.add(mapper.createObjectNode()
+                               .put("host", ovsdb.host())
+                               .put("address", ipPort)
+                               .put("brInt", ovsdb.intBrId().toString())
+                               .put("state", getState(service, ovsdb)));
+        }
+        return result;
+    }
+
+    private String getState(CordVtnService service, OvsdbNode ovsdb) {
+        return service.isNodeConnected(ovsdb) ? "CONNECTED" : "DISCONNECTED";
+    }
+}
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/package-info.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/package-info.java
new file mode 100644
index 00000000..686172ce
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/cli/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Console commands to manage OVSDB nodes for cordvtn.
+ */
+package org.onosproject.cordvtn.cli;
\ No newline at end of file
diff --git a/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/package-info.java b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/package-info.java
new file mode 100644
index 00000000..1c13737f
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/java/org/onosproject/cordvtn/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2015 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.
+ */
+
+/**
+ * Application for provisioning virtual tenant networks.
+ */
+package org.onosproject.cordvtn;
\ No newline at end of file
diff --git a/framework/src/onos/apps/cordvtn/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/cordvtn/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 00000000..6e172387
--- /dev/null
+++ b/framework/src/onos/apps/cordvtn/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,35 @@
+<!--
+  ~ Copyright 2015 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.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.onosproject.cordvtn.cli.OvsdbNodeListCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.cordvtn.cli.OvsdbNodeAddCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.cordvtn.cli.OvsdbNodeDeleteCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.cordvtn.cli.OvsdbNodeConnectCommand"/>
+        </command>
+        <command>
+            <action class="org.onosproject.cordvtn.cli.OvsdbNodeDisconnectCommand"/>
+        </command>
+    </command-bundle>
+</blueprint>
diff --git a/framework/src/onos/apps/olt/pom.xml b/framework/src/onos/apps/olt/pom.xml
index 180e026a..f803a61a 100644
--- a/framework/src/onos/apps/olt/pom.xml
+++ b/framework/src/onos/apps/olt/pom.xml
@@ -36,6 +36,15 @@
     </properties>
 
     <dependencies>
+        <dependency>
+            <groupId>org.onosproject</groupId>
+            <artifactId>onos-cli</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.console</artifactId>
+        </dependency>
       <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceConfig.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceConfig.java
new file mode 100644
index 00000000..90ed7403
--- /dev/null
+++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceConfig.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015 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.onosproject.olt;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.Config;
+
+/**
+ * Config object for access device data.
+ */
+public class AccessDeviceConfig extends Config<DeviceId> {
+
+    private static final String UPLINK = "uplink";
+    private static final String VLAN = "vlan";
+
+    /**
+     * Gets the access device configuration for this device.
+     *
+     * @return access device configuration
+     */
+    public AccessDeviceData getOlt() {
+        PortNumber uplink = PortNumber.portNumber(node.path(UPLINK).asText());
+        VlanId vlan = VlanId.vlanId(Short.parseShort(node.path(VLAN).asText()));
+
+        return new AccessDeviceData(subject(), uplink, vlan);
+    }
+}
diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceData.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceData.java
new file mode 100644
index 00000000..f7e40e30
--- /dev/null
+++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceData.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015 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.onosproject.olt;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Information about an access device.
+ */
+public class AccessDeviceData {
+    private static final String DEVICE_ID_MISSING = "Device ID cannot be null";
+    private static final String UPLINK_MISSING = "Uplink cannot be null";
+    private static final String VLAN_MISSING = "VLAN ID cannot be null";
+
+    private final DeviceId deviceId;
+    private final PortNumber uplink;
+    private final VlanId vlan;
+
+    /**
+     * Class constructor.
+     *
+     * @param deviceId access device ID
+     * @param uplink uplink port number
+     * @param vlan device VLAN ID
+     */
+    public AccessDeviceData(DeviceId deviceId, PortNumber uplink, VlanId vlan) {
+        this.deviceId = checkNotNull(deviceId, DEVICE_ID_MISSING);
+        this.uplink = checkNotNull(uplink, UPLINK_MISSING);
+        this.vlan = checkNotNull(vlan, VLAN_MISSING);
+    }
+
+    /**
+     * Retrieves the access device ID.
+     *
+     * @return device ID
+     */
+    public DeviceId deviceId() {
+        return deviceId;
+    }
+
+    /**
+     * Retrieves the uplink port number.
+     *
+     * @return port number
+     */
+    public PortNumber uplink() {
+        return uplink;
+    }
+
+    /**
+     * Retrieves the VLAN ID assigned to the device.
+     *
+     * @return vlan ID
+     */
+    public VlanId vlan() {
+        return vlan;
+    }
+}
diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceService.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceService.java
new file mode 100644
index 00000000..bd82f489
--- /dev/null
+++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/AccessDeviceService.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2015 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.onosproject.olt;
+
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
+
+/**
+ * Service for interacting with an access device (OLT).
+ */
+public interface AccessDeviceService {
+
+    /**
+     * Provisions connectivity for a subscriber on an access device.
+     *
+     * @param port subscriber's connection point
+     * @param vlan VLAN ID to provision for subscriber
+     */
+    void provisionSubscriber(ConnectPoint port, VlanId vlan);
+
+    /**
+     * Removes provisioned connectivity for a subscriber from an access device.
+     *
+     * @param port subscriber's connection point
+     */
+    void removeSubscriber(ConnectPoint port);
+}
diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java
index c92f47a2..9aa8865a 100644
--- a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java
+++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/OLT.java
@@ -15,7 +15,6 @@
  */
 package org.onosproject.olt;
 
-
 import com.google.common.base.Strings;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
@@ -24,13 +23,20 @@ import org.apache.felix.scr.annotations.Modified;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
 import org.onlab.packet.VlanId;
 import org.onlab.util.Tools;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.config.ConfigFactory;
+import org.onosproject.net.config.NetworkConfigEvent;
+import org.onosproject.net.config.NetworkConfigListener;
+import org.onosproject.net.config.NetworkConfigRegistry;
+import org.onosproject.net.config.basics.SubjectFactories;
 import org.onosproject.net.device.DeviceEvent;
 import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
@@ -45,15 +51,17 @@ import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 
 import java.util.Dictionary;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Sample mobility application. Cleans up flowmods when a host moves.
+ * Provisions rules on access devices.
  */
+@Service
 @Component(immediate = true)
-public class OLT {
-
+public class OLT implements AccessDeviceService {
     private final Logger log = getLogger(getClass());
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -65,10 +73,14 @@ public class OLT {
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected NetworkConfigRegistry networkConfig;
+
     private final DeviceListener deviceListener = new InternalDeviceListener();
 
     private ApplicationId appId;
 
+    private static final VlanId DEFAULT_VLAN = VlanId.vlanId((short) 0);
     public static final int OFFSET = 200;
 
     public static final int UPLINK_PORT = 129;
@@ -94,11 +106,39 @@ public class OLT {
             label = "The gfast device id")
     private String gfastDevice = GFAST_DEVICE;
 
+    private Map<DeviceId, AccessDeviceData> oltData = new ConcurrentHashMap<>();
+
+    private InternalNetworkConfigListener configListener =
+            new InternalNetworkConfigListener();
+    private static final Class<AccessDeviceConfig> CONFIG_CLASS =
+            AccessDeviceConfig.class;
+
+    private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory =
+            new ConfigFactory<DeviceId, AccessDeviceConfig>(
+                    SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
+        @Override
+        public AccessDeviceConfig createConfig() {
+            return new AccessDeviceConfig();
+        }
+    };
 
     @Activate
     public void activate() {
         appId = coreService.registerApplication("org.onosproject.olt");
 
+        networkConfig.registerConfigFactory(configFactory);
+        networkConfig.addListener(configListener);
+
+        networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach(
+                subject -> {
+                    AccessDeviceConfig config = networkConfig.getConfig(subject, AccessDeviceConfig.class);
+                    if (config != null) {
+                        AccessDeviceData data = config.getOlt();
+                        oltData.put(data.deviceId(), data);
+                    }
+                }
+        );
+
         /*deviceService.addListener(deviceListener);
 
         deviceService.getPorts(DeviceId.deviceId(oltDevice)).stream().forEach(
@@ -129,6 +169,8 @@ public class OLT {
 
     @Deactivate
     public void deactivate() {
+        networkConfig.removeListener(configListener);
+        networkConfig.unregisterConfigFactory(configFactory);
         log.info("Stopped");
     }
 
@@ -136,16 +178,13 @@ public class OLT {
     public void modified(ComponentContext context) {
         Dictionary<?, ?> properties = context.getProperties();
 
-
         String s = Tools.get(properties, "uplinkPort");
         uplinkPort = Strings.isNullOrEmpty(s) ? UPLINK_PORT : Integer.parseInt(s);
 
         s = Tools.get(properties, "oltDevice");
         oltDevice = Strings.isNullOrEmpty(s) ? OLT_DEVICE : s;
-
     }
 
-
     private short fetchVlanId(PortNumber port) {
         long p = port.toLong() + OFFSET;
         if (p > 4095) {
@@ -155,7 +194,6 @@ public class OLT {
         return (short) p;
     }
 
-
     private void provisionVlanOnPort(String deviceId, int uplinkPort, PortNumber p, short vlanId) {
         DeviceId did = DeviceId.deviceId(deviceId);
 
@@ -198,7 +236,73 @@ public class OLT {
 
         flowObjectiveService.forward(did, upFwd);
         flowObjectiveService.forward(did, downFwd);
+    }
+
+    @Override
+    public void provisionSubscriber(ConnectPoint port, VlanId vlan) {
+        AccessDeviceData olt = oltData.get(port.deviceId());
+
+        if (olt == null) {
+            log.warn("No data found for OLT device {}", port.deviceId());
+            return;
+        }
+
+        provisionVlans(olt.deviceId(), olt.uplink(), port.port(), vlan, olt.vlan());
+    }
+
+    private void provisionVlans(DeviceId deviceId, PortNumber uplinkPort,
+                                PortNumber subscriberPort,
+                                VlanId subscriberVlan, VlanId deviceVlan) {
+
+        TrafficSelector upstream = DefaultTrafficSelector.builder()
+                .matchVlanId(DEFAULT_VLAN)
+                .matchInPort(subscriberPort)
+                .build();
+
+        TrafficSelector downstream = DefaultTrafficSelector.builder()
+                .matchVlanId(deviceVlan)
+                .matchInPort(uplinkPort)
+                .build();
+
+        TrafficTreatment upstreamTreatment = DefaultTrafficTreatment.builder()
+                .setVlanId(subscriberVlan)
+                .pushVlan()
+                .setVlanId(deviceVlan)
+                .setOutput(uplinkPort)
+                .build();
 
+        TrafficTreatment downstreamTreatment = DefaultTrafficTreatment.builder()
+                .popVlan()
+                .setVlanId(DEFAULT_VLAN)
+                .setOutput(subscriberPort)
+                .build();
+
+
+        ForwardingObjective upFwd = DefaultForwardingObjective.builder()
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(1000)
+                .makePermanent()
+                .withSelector(upstream)
+                .fromApp(appId)
+                .withTreatment(upstreamTreatment)
+                .add();
+
+        ForwardingObjective downFwd = DefaultForwardingObjective.builder()
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(1000)
+                .makePermanent()
+                .withSelector(downstream)
+                .fromApp(appId)
+                .withTreatment(downstreamTreatment)
+                .add();
+
+        flowObjectiveService.forward(deviceId, upFwd);
+        flowObjectiveService.forward(deviceId, downFwd);
+    }
+
+    @Override
+    public void removeSubscriber(ConnectPoint port) {
+        throw new UnsupportedOperationException("Not yet implemented");
     }
 
     private class InternalDeviceListener implements DeviceListener {
@@ -226,7 +330,27 @@ public class OLT {
         }
     }
 
+    private class InternalNetworkConfigListener implements NetworkConfigListener {
+        @Override
+        public void event(NetworkConfigEvent event) {
+            switch (event.type()) {
 
-}
-
+            case CONFIG_ADDED:
+            case CONFIG_UPDATED:
+                if (event.configClass().equals(CONFIG_CLASS)) {
+                    AccessDeviceConfig config =
+                            networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS);
+                    if (config != null) {
+                        oltData.put(config.getOlt().deviceId(), config.getOlt());
+                    }
+                }
+                break;
+            case CONFIG_UNREGISTERED:
+            case CONFIG_REMOVED:
+            default:
+                break;
+            }
+        }
+    }
 
+}
diff --git a/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/SubscriberAddCommand.java b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/SubscriberAddCommand.java
new file mode 100644
index 00000000..d9b4559b
--- /dev/null
+++ b/framework/src/onos/apps/olt/src/main/java/org/onosproject/olt/SubscriberAddCommand.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015 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.onosproject.olt;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.packet.VlanId;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.PortNumber;
+
+/**
+ * Adds a subscriber to an access device.
+ */
+@Command(scope = "onos", name = "add-subscriber-access",
+        description = "Adds a subscriber to an access device")
+public class SubscriberAddCommand extends AbstractShellCommand {
+
+    @Argument(index = 0, name = "deviceId", description = "Access device ID",
+            required = true, multiValued = false)
+    private String strDeviceId = null;
+
+    @Argument(index = 1, name = "port", description = "Subscriber port number",
+            required = true, multiValued = false)
+    private String strPort = null;
+
+    @Argument(index = 2, name = "vlanId",
+            description = "VLAN ID to add",
+            required = true, multiValued = false)
+    private String strVlanId = null;
+
+    @Override
+    protected void execute() {
+        AccessDeviceService service = AbstractShellCommand.get(AccessDeviceService.class);
+
+        DeviceId deviceId = DeviceId.deviceId(strDeviceId);
+        PortNumber port = PortNumber.portNumber(strPort);
+        VlanId vlan = VlanId.vlanId(Short.parseShort(strVlanId));
+        ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
+
+        service.provisionSubscriber(connectPoint, vlan);
+    }
+}
diff --git a/framework/src/onos/apps/olt/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/apps/olt/src/main/resources/OSGI-INF/blueprint/shell-config.xml
new file mode 100644
index 00000000..00ebe9d1
--- /dev/null
+++ b/framework/src/onos/apps/olt/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -0,0 +1,29 @@
+<!--
+  ~ Copyright 2015 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.
+  -->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <command-bundle xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0">
+        <command>
+            <action class="org.onosproject.olt.SubscriberAddCommand"/>
+            <completers>
+                <ref component-id="deviceIdCompleter"/>
+                <null/>
+            </completers>
+        </command>
+    </command-bundle>
+
+    <bean id="deviceIdCompleter" class="org.onosproject.cli.net.DeviceIdCompleter"/>
+</blueprint>
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
index 8b447af4..40ee55fc 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
@@ -93,7 +93,7 @@ public class DefaultRoutingHandler {
         try {
             populationStatus = Status.STARTED;
             rulePopulator.resetCounter();
-            log.info("Starts to populate routing rules");
+            log.info("Starting to populate segment-routing rules");
             log.debug("populateAllRoutingRules: populationStatus is STARTED");
 
             for (Device sw : srManager.deviceService.getDevices()) {
@@ -117,7 +117,7 @@ public class DefaultRoutingHandler {
 
             log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
             populationStatus = Status.SUCCEEDED;
-            log.info("Completes routing rule population. Total # of rules pushed : {}",
+            log.info("Completed routing rule population. Total # of rules pushed : {}",
                     rulePopulator.getCounter());
             return true;
         } finally {
@@ -426,7 +426,7 @@ public class DefaultRoutingHandler {
                     .get(itrIdx);
             for (DeviceId targetSw : swViaMap.keySet()) {
                 Set<DeviceId> nextHops = new HashSet<>();
-
+                log.debug("** Iter: {} root: {} target: {}", itrIdx, destSw, targetSw);
                 for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
                     if (via.isEmpty()) {
                         nextHops.add(destSw);
@@ -456,7 +456,7 @@ public class DefaultRoutingHandler {
         // rule for both subnet and router IP.
         if (config.isEdgeDevice(targetSw) && config.isEdgeDevice(destSw)) {
             List<Ip4Prefix> subnets = config.getSubnets(destSw);
-            log.debug("populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
+            log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for subnets {}",
                     targetSw, destSw, subnets);
             result = rulePopulator.populateIpRuleForSubnet(targetSw,
                                                            subnets,
@@ -468,7 +468,7 @@ public class DefaultRoutingHandler {
 
             Ip4Address routerIp = config.getRouterIp(destSw);
             IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
-            log.debug("populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
+            log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
                     targetSw, destSw, routerIpPrefix);
             result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
             if (!result) {
@@ -479,7 +479,7 @@ public class DefaultRoutingHandler {
         } else if (config.isEdgeDevice(targetSw)) {
             Ip4Address routerIp = config.getRouterIp(destSw);
             IpPrefix routerIpPrefix = IpPrefix.valueOf(routerIp, IpPrefix.MAX_INET_MASK_LENGTH);
-            log.debug("populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
+            log.debug("* populateEcmpRoutingRulePartial in device {} towards {} for router IP {}",
                     targetSw, destSw, routerIpPrefix);
             result = rulePopulator.populateIpRuleForRouter(targetSw, routerIpPrefix, destSw, nextHops);
             if (!result) {
@@ -488,7 +488,7 @@ public class DefaultRoutingHandler {
         }
 
         // Populates MPLS rules to all routers
-        log.debug("populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
+        log.debug("* populateEcmpRoutingRulePartial in device{} towards {} for all MPLS rules",
                 targetSw, destSw);
         result = rulePopulator.populateMplsRule(targetSw, destSw, nextHops);
         if (!result) {
@@ -500,7 +500,7 @@ public class DefaultRoutingHandler {
 
     /**
      * Populates table miss entries for all tables, and pipeline rules for VLAN
-     * and TACM tables.
+     * and TCAM tables. XXX rename/rethink
      *
      * @param deviceId Switch ID to set the rules
      */
@@ -534,6 +534,8 @@ public class DefaultRoutingHandler {
     /**
      * Resume the flow rule population process if it was aborted for any reason.
      * Mostly the process is aborted when the groups required are not set yet.
+     *  XXX is this called?
+     *
      */
     public void resumePopulationProcess() {
         statusLock.lock();
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
index eef1b147..0bc155b8 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/DeviceConfiguration.java
@@ -134,7 +134,7 @@ public class DeviceConfiguration implements DeviceProperties {
     @Override
     public int getSegmentId(DeviceId deviceId) {
         if (deviceConfigMap.get(deviceId) != null) {
-            log.debug("getSegmentId for device{} is {}",
+            log.trace("getSegmentId for device{} is {}",
                     deviceId,
                     deviceConfigMap.get(deviceId).nodeSid);
             return deviceConfigMap.get(deviceId).nodeSid;
@@ -189,7 +189,7 @@ public class DeviceConfiguration implements DeviceProperties {
     @Override
     public MacAddress getDeviceMac(DeviceId deviceId) {
         if (deviceConfigMap.get(deviceId) != null) {
-            log.debug("getDeviceMac for device{} is {}",
+            log.trace("getDeviceMac for device{} is {}",
                     deviceId,
                     deviceConfigMap.get(deviceId).mac);
             return deviceConfigMap.get(deviceId).mac;
@@ -209,7 +209,7 @@ public class DeviceConfiguration implements DeviceProperties {
      */
     public Ip4Address getRouterIp(DeviceId deviceId) {
         if (deviceConfigMap.get(deviceId) != null) {
-            log.debug("getDeviceIp for device{} is {}",
+            log.trace("getDeviceIp for device{} is {}",
                     deviceId,
                     deviceConfigMap.get(deviceId).ip);
             return deviceConfigMap.get(deviceId).ip;
@@ -231,7 +231,7 @@ public class DeviceConfiguration implements DeviceProperties {
     @Override
     public boolean isEdgeDevice(DeviceId deviceId) {
         if (deviceConfigMap.get(deviceId) != null) {
-            log.debug("isEdgeDevice for device{} is {}",
+            log.trace("isEdgeDevice for device{} is {}",
                     deviceId,
                     deviceConfigMap.get(deviceId).isEdge);
             return deviceConfigMap.get(deviceId).isEdge;
@@ -297,7 +297,7 @@ public class DeviceConfiguration implements DeviceProperties {
      */
     public List<Ip4Address> getSubnetGatewayIps(DeviceId deviceId) {
         if (deviceConfigMap.get(deviceId) != null) {
-            log.debug("getSubnetGatewayIps for device{} is {}",
+            log.trace("getSubnetGatewayIps for device{} is {}",
                     deviceId,
                     deviceConfigMap.get(deviceId).gatewayIps.values());
             return new ArrayList<>(deviceConfigMap.get(deviceId).gatewayIps.values());
@@ -314,7 +314,7 @@ public class DeviceConfiguration implements DeviceProperties {
      */
     public List<Ip4Prefix> getSubnets(DeviceId deviceId) {
         if (deviceConfigMap.get(deviceId) != null) {
-            log.debug("getSubnets for device{} is {}",
+            log.trace("getSubnets for device{} is {}",
                     deviceId,
                     deviceConfigMap.get(deviceId).subnets.values());
             return new ArrayList<>(deviceConfigMap.get(deviceId).subnets.values());
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
index 2e2041c4..e9a59bae 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/ECMPShortestPathGraph.java
@@ -31,7 +31,7 @@ import java.util.List;
 
 /**
  * This class creates bandwidth constrained breadth first tree and returns paths
- * from root Device to leaf Devicees which satisfies the bandwidth condition. If
+ * from root Device to leaf Devices which satisfies the bandwidth condition. If
  * bandwidth parameter is not specified, the normal breadth first tree will be
  * calculated. The paths are snapshot paths at the point of the class
  * instantiation.
@@ -281,7 +281,7 @@ public class ECMPShortestPathGraph {
      * Return the complete info of the computed ECMP paths for each Device
      * learned in multiple iterations from the root Device.
      *
-     * @return the hash table of Devicees learned in multiple Dijkstra
+     * @return the hash table of Devices learned in multiple Dijkstra
      *         iterations and corresponding ECMP paths to it from the root
      *         Device
      */
@@ -305,8 +305,8 @@ public class ECMPShortestPathGraph {
      * Return the complete info of the computed ECMP paths for each Device
      * learned in multiple iterations from the root Device.
      *
-     * @return the hash table of Devicees learned in multiple Dijkstra
-     *         iterations and corresponding ECMP paths in terms of Devicees to
+     * @return the hash table of Devices learned in multiple Dijkstra
+     *         iterations and corresponding ECMP paths in terms of Devices to
      *         be traversed to it from the root Device
      */
     public HashMap<Integer, HashMap<DeviceId,
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java
index e37fe52a..14ce679b 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/IpHandler.java
@@ -115,7 +115,7 @@ public class IpHandler {
     /**
      * Forwards IP packets in the buffer to the destination IP address.
      * It is called when the controller finds the destination MAC address
-     * via ARP responsees.
+     * via ARP responses.
      *
      * @param deviceId switch device ID
      * @param destIpAddress destination IP address
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index d802be91..7641571d 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -150,7 +150,7 @@ public class RoutingRulePopulator {
     /**
      * Populates IP flow rules for the router IP address.
      *
-     * @param deviceId device ID to set the rules
+     * @param deviceId target device ID to set the rules
      * @param ipPrefix the IP address of the destination router
      * @param destSw device ID of the destination router
      * @param nextHops next hop switch ID list
@@ -210,9 +210,9 @@ public class RoutingRulePopulator {
     }
 
     /**
-     * Populates MPLS flow rules to all transit routers.
+     * Populates MPLS flow rules to all routers.
      *
-     * @param deviceId device ID of the switch to set the rules
+     * @param deviceId target device ID of the switch to set the rules
      * @param destSwId destination switch device ID
      * @param nextHops next hops switch ID list
      * @return true if all rules are set successfully, false otherwise
@@ -379,7 +379,7 @@ public class RoutingRulePopulator {
                 .addCondition(Criteria.matchEthDst(config
                                       .getDeviceMac(deviceId)));
         fob.permit().fromApp(srManager.appId);
-        log.debug("populateTableVlan: Installing filtering objective for router mac");
+        log.debug("populateTableTMac: Installing filtering objective for router mac");
         srManager.flowObjectiveService.
             filter(deviceId,
                    fob.add(new SRObjectiveContext(deviceId,
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 9011160c..eb2cf569 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -296,7 +296,8 @@ public class SegmentRoutingManager implements SegmentRoutingService {
     }
 
     /**
-     * Returns the GrouopKey object for the device and the NighborSet given.
+     * Returns the GroupKey object for the device and the NeighborSet given.
+     * XXX is this called
      *
      * @param ns NeightborSet object for the GroupKey
      * @return GroupKey object for the NeighborSet
diff --git a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
index 3dc312df..c960adca 100644
--- a/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
+++ b/framework/src/onos/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultEdgeGroupHandler.java
@@ -35,13 +35,14 @@ import org.onosproject.store.service.EventuallyConsistentMap;
  * where D0 and D3 are edge devices and D1 and D2 are transit devices.
  * Assume device D0 is connected to 2 neighbors (D1 and D2 ).
  * The following groups will be created in D0:
- * 1) all ports to D1 + with no label push,
- * 2) all ports to D1 + with label 102 pushed,
- * 3) all ports to D1 + with label 103 pushed,
+ * 1) all ports to D1 + with no label push, // direct attach case, seen
+ * 2) all ports to D1 + with label 102 pushed, // this is not needed
+ * 3) all ports to D1 + with label 103 pushed, // maybe needed, sometimes seen
  * 4) all ports to D2 + with no label push,
  * 5) all ports to D2 + with label 101 pushed,
  * 6) all ports to D2 + with label 103 pushed,
- * 7) all ports to D1 and D2 + with label 103 pushed
+ * 7) all ports to D1 and D2 + with label 103 pushed // ecmp case
+ * 8) what about ecmp no label case
  */
 public class DefaultEdgeGroupHandler extends DefaultGroupHandler {
 
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java
index 05ebccf9..0a3ea2cd 100644
--- a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/VirtualPortService.java
@@ -18,6 +18,7 @@ package org.onosproject.vtnrsc.virtualport;
 import java.util.Collection;
 
 import org.onosproject.net.DeviceId;
+import org.onosproject.vtnrsc.FixedIp;
 import org.onosproject.vtnrsc.TenantId;
 import org.onosproject.vtnrsc.TenantNetworkId;
 import org.onosproject.vtnrsc.VirtualPort;
@@ -43,6 +44,14 @@ public interface VirtualPortService {
      */
     VirtualPort getPort(VirtualPortId virtualPortId);
 
+    /**
+     * Returns the virtualPort associated with the fixedIP.
+     *
+     * @param fixedIP the fixedIP identifier
+     * @return virtualPort.
+     */
+    VirtualPort getPort(FixedIp fixedIP);
+
     /**
      * Returns the collection of the currently known virtualPort.
      * @return collection of VirtualPort.
diff --git a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java
index 926809c9..c45373b9 100644
--- a/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java
+++ b/framework/src/onos/apps/vtn/vtnrsc/src/main/java/org/onosproject/vtnrsc/virtualport/impl/VirtualPortManager.java
@@ -17,9 +17,12 @@ package org.onosproject.vtnrsc.virtualport.impl;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
 
@@ -68,6 +71,7 @@ public class VirtualPortManager implements VirtualPortService {
     private static final String TENANTID_NOT_NULL = "TenantId  cannot be null";
     private static final String NETWORKID_NOT_NULL = "NetworkId  cannot be null";
     private static final String DEVICEID_NOT_NULL = "DeviceId  cannot be null";
+    private static final String FIXEDIP_NOT_NULL = "FixedIp  cannot be null";
 
     protected Map<VirtualPortId, VirtualPort> vPortStore;
     protected ApplicationId appId;
@@ -124,6 +128,25 @@ public class VirtualPortManager implements VirtualPortService {
         return vPortStore.get(vPortId);
     }
 
+    @Override
+    public VirtualPort getPort(FixedIp fixedIP) {
+        checkNotNull(fixedIP, FIXEDIP_NOT_NULL);
+        List<VirtualPort> vPorts = new ArrayList<>();
+        vPortStore.values().stream().forEach(p -> {
+            Iterator<FixedIp> fixedIps = p.fixedIps().iterator();
+            while (fixedIps.hasNext()) {
+                if (fixedIps.next().equals(fixedIP)) {
+                    vPorts.add(p);
+                    break;
+                }
+            }
+        });
+        if (vPorts.size() == 0) {
+            return null;
+        }
+        return vPorts.get(0);
+    }
+
     @Override
     public Collection<VirtualPort> getPorts() {
         return Collections.unmodifiableCollection(vPortStore.values());
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
index 6c33f45c..62cf042a 100644
--- a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
+++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/ConnectivityIntentCommand.java
@@ -166,6 +166,10 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand {
             required = false, multiValued = false)
     private String pushVlan = null;
 
+    @Option(name = "--setQueue", description = "Set Queue ID",
+            required = false, multiValued = false)
+    private String setQueue = null;
+
     // Priorities
     @Option(name = "-p", aliases = "--priority", description = "Priority",
             required = false, multiValued = false)
@@ -327,6 +331,10 @@ public abstract class ConnectivityIntentCommand extends AbstractShellCommand {
             treatmentBuilder.setVlanId(VlanId.vlanId(Short.parseShort(pushVlan)));
             emptyTreatment = false;
         }
+        if (!isNullOrEmpty(setQueue)) {
+            treatmentBuilder.setQueue(Long.parseLong(setQueue));
+            emptyTreatment = false;
+        }
 
         if (emptyTreatment) {
             return DefaultTrafficTreatment.emptyTreatment();
diff --git a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java
index eefb711a..4fd9b0df 100644
--- a/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java
+++ b/framework/src/onos/cli/src/main/java/org/onosproject/cli/net/InterfaceAddCommand.java
@@ -43,7 +43,7 @@ public class InterfaceAddCommand extends AbstractShellCommand {
 
     @Option(name = "-m", aliases = "--mac",
             description = "MAC address of the interface",
-            required = true, multiValued = false)
+            required = false, multiValued = false)
     private String mac = null;
 
     @Option(name = "-i", aliases = "--ip",
@@ -68,10 +68,12 @@ public class InterfaceAddCommand extends AbstractShellCommand {
             }
         }
 
+        MacAddress macAddr = mac == null ? null : MacAddress.valueOf(mac);
+
         VlanId vlanId = vlan == null ? VlanId.NONE : VlanId.vlanId(Short.parseShort(vlan));
 
         Interface intf = new Interface(ConnectPoint.deviceConnectPoint(connectPoint),
-                ipAddresses, MacAddress.valueOf(mac), vlanId);
+                ipAddresses, macAddr, vlanId);
 
         interfaceService.add(intf);
     }
diff --git a/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 28461e27..cf76febe 100644
--- a/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/framework/src/onos/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -146,6 +146,8 @@
             <action class="org.onosproject.cli.net.AnnotateDeviceCommand"/>
             <completers>
                 <ref component-id="deviceIdCompleter"/>
+                <null/>
+                <null/>
             </completers>
         </command>
 
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
index a628725c..6174cef6 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/DefaultTrafficTreatment.java
@@ -237,6 +237,7 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
                 case NOACTION:
                 case OUTPUT:
                 case GROUP:
+                case QUEUE:
                 case L0MODIFICATION:
                 case L2MODIFICATION:
                 case L3MODIFICATION:
@@ -380,6 +381,11 @@ public final class DefaultTrafficTreatment implements TrafficTreatment {
             return add(Instructions.createGroup(groupId));
         }
 
+        @Override
+        public Builder setQueue(long queueId) {
+            return add(Instructions.setQueue(queueId));
+        }
+
         @Override
         public TrafficTreatment.Builder meter(MeterId meterId) {
             return add(Instructions.meterTraffic(meterId));
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
index 33753afa..c7fe8b85 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/TrafficTreatment.java
@@ -190,14 +190,14 @@ public interface TrafficTreatment {
         /**
          * Push MPLS ether type.
          *
-         * @return a treatment builder.
+         * @return a treatment builder
          */
         Builder pushMpls();
 
         /**
          * Pops MPLS ether type.
          *
-         * @return a treatment builder.
+         * @return a treatment builder
          */
         Builder popMpls();
 
@@ -205,7 +205,7 @@ public interface TrafficTreatment {
          * Pops MPLS ether type and set the new ethertype.
          *
          * @param etherType an ether type
-         * @return a treatment builder.
+         * @return a treatment builder
          * @deprecated in Drake Release
          */
         @Deprecated
@@ -215,22 +215,22 @@ public interface TrafficTreatment {
          * Pops MPLS ether type and set the new ethertype.
          *
          * @param etherType an ether type
-         * @return a treatment builder.
+         * @return a treatment builder
          */
         Builder popMpls(EthType etherType);
 
         /**
          * Sets the mpls label.
          *
-         * @param mplsLabel MPLS label.
-         * @return a treatment builder.
+         * @param mplsLabel MPLS label
+         * @return a treatment builder
          */
         Builder setMpls(MplsLabel mplsLabel);
 
         /**
          * Sets the mpls bottom-of-stack indicator bit.
          *
-         * @param mplsBos boolean to set BOS=1 (true) or BOS=0 (false).
+         * @param mplsBos boolean to set BOS=1 (true) or BOS=0 (false)
          * @return a treatment builder.
          */
         Builder setMplsBos(boolean mplsBos);
@@ -260,6 +260,14 @@ public interface TrafficTreatment {
          */
         Builder group(GroupId groupId);
 
+        /**
+         * Sets the Queue ID.
+         *
+         * @param queueId a queue ID
+         * @return a treatment builder
+         */
+        Builder setQueue(long queueId);
+
         /**
          * Sets a meter to be used by this flow.
          *
@@ -280,14 +288,14 @@ public interface TrafficTreatment {
         /**
          * Pops outermost VLAN tag.
          *
-         * @return a treatment builder.
+         * @return a treatment builder
          */
         Builder popVlan();
 
         /**
          * Pushes a new VLAN tag.
          *
-         * @return a treatment builder.
+         * @return a treatment builder
          */
         Builder pushVlan();
 
@@ -327,8 +335,8 @@ public interface TrafficTreatment {
         /**
          * Sets the tunnel id.
          *
-         * @param tunnelId a tunnel id.
-         * @return a treatment builder.
+         * @param tunnelId a tunnel id
+         * @return a treatment builder
          */
         Builder setTunnelId(long tunnelId);
 
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/ArpPaCriterion.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/ArpPaCriterion.java
new file mode 100644
index 00000000..ba5a03d8
--- /dev/null
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/ArpPaCriterion.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015 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.onosproject.net.flow.criteria;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+import java.util.Objects;
+
+import org.onlab.packet.Ip4Address;
+
+/**
+ * Implementation of arp spa or tpa address criterion.
+ */
+public final class ArpPaCriterion implements Criterion {
+    private final Ip4Address ip;
+    private final Type type;
+
+    /**
+     * Constructor.
+     *
+     * @param ip the Ip4 Address to match.
+     * @param type the match type. Should be one of the following:
+     * Type.ARP_SPA, Type.ARP_TPA
+     */
+    ArpPaCriterion(Ip4Address ip, Type type) {
+        this.ip = ip;
+        this.type = type;
+    }
+
+    @Override
+    public Type type() {
+        return this.type;
+    }
+
+    /**
+     * Gets the Ip4 Address to match.
+     *
+     * @return the Ip4 Address to match
+     */
+    public Ip4Address ip() {
+        return this.ip;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(type().toString())
+                .add("ip", ip).toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(type().ordinal(), ip);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof ArpPaCriterion) {
+            ArpPaCriterion that = (ArpPaCriterion) obj;
+            return Objects.equals(ip, that.ip) &&
+                    Objects.equals(type, that.type);
+        }
+        return false;
+    }
+}
\ No newline at end of file
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
index ae940bdc..778d50a5 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/criteria/Criteria.java
@@ -16,6 +16,7 @@
 package org.onosproject.net.flow.criteria;
 
 import org.onlab.packet.EthType;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
@@ -25,11 +26,11 @@ import org.onlab.packet.VlanId;
 import org.onosproject.net.IndexedLambda;
 import org.onosproject.net.Lambda;
 import org.onosproject.net.OchSignal;
+import org.onosproject.net.OchSignalType;
 import org.onosproject.net.OduSignalId;
 import org.onosproject.net.OduSignalType;
 import org.onosproject.net.PortNumber;
 import org.onosproject.net.flow.criteria.Criterion.Type;
-import org.onosproject.net.OchSignalType;
 
 /**
  * Factory class to create various traffic selection criteria.
@@ -508,6 +509,16 @@ public final class Criteria {
         return new OduSignalTypeCriterion(signalType);
     }
 
+    /**
+     * Creates a match on IPv4 source field using the specified value.
+     *
+     * @param ip ipv4 source value
+     * @return match criterion
+     */
+    public static Criterion matchArpTpa(Ip4Address ip) {
+        return new ArpPaCriterion(ip, Type.ARP_TPA);
+    }
+
     public static Criterion dummy() {
         return new DummyCriterion();
     }
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
index eddbbb71..2f6a1cc1 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instruction.java
@@ -48,6 +48,12 @@ public interface Instruction {
          */
         GROUP,
 
+        /**
+         * Signifies that the traffic should be enqueued to an already-configured
+         queue on a port.
+         */
+        QUEUE,
+
         /**
          * Signifies that traffic should be metered according to a meter.
          */
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
index 26981e5e..8868bf7c 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/net/flow/instructions/Instructions.java
@@ -94,6 +94,17 @@ public final class Instructions {
         return new GroupInstruction(groupId);
     }
 
+    /**
+     * Creates a set-queue instruction.
+     *
+     * @param queueId Queue Id
+     * @return set-queue instruction
+     */
+    public static SetQueueInstruction setQueue(final long queueId) {
+        checkNotNull(queueId, "queue ID cannot be null");
+        return new SetQueueInstruction(queueId);
+    }
+
     public static MeterInstruction meterTraffic(final MeterId meterId) {
         checkNotNull(meterId, "meter id cannot be null");
         return new MeterInstruction(meterId);
@@ -624,6 +635,50 @@ public final class Instructions {
         }
     }
 
+    /**
+     *  Set-Queue Instruction.
+     */
+    public static final class SetQueueInstruction implements Instruction {
+        private final long queueId;
+
+        private SetQueueInstruction(long queueId) {
+            this.queueId = queueId;
+        }
+
+        public long queueId() {
+            return queueId;
+        }
+
+        @Override
+        public Type type() {
+            return Type.QUEUE;
+        }
+
+        @Override
+        public String toString() {
+            return toStringHelper(type().toString())
+                    .add("queueId", queueId).toString();
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type().ordinal(), queueId);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof SetQueueInstruction) {
+                SetQueueInstruction that = (SetQueueInstruction) obj;
+                return Objects.equals(queueId, that.queueId);
+
+            }
+            return false;
+        }
+    }
+
     /**
      * A meter instruction.
      */
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/DeviceHighlight.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/DeviceHighlight.java
index 0ce6592c..0cc15475 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/DeviceHighlight.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/DeviceHighlight.java
@@ -17,16 +17,16 @@
 package org.onosproject.ui.topo;
 
 /**
- * Denotes the highlighting to apply to a device.
+ * Denotes the highlighting to be applied to a device.
  */
 public class DeviceHighlight extends NodeHighlight {
 
+    /**
+     * Constructs a device highlight entity.
+     *
+     * @param deviceId the device identifier
+     */
     public DeviceHighlight(String deviceId) {
         super(TopoElementType.DEVICE, deviceId);
     }
-
-    // TODO: implement device highlighting:
-    //   - visual highlight
-    //   - badging
-
 }
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java
new file mode 100644
index 00000000..7b517111
--- /dev/null
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeBadge.java
@@ -0,0 +1,220 @@
+/*
+ *  Copyright 2015 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.onosproject.ui.topo;
+
+/**
+ * Designates a badge to be applied to a node in the topology view.
+ */
+public final class NodeBadge {
+
+    private static final String EMPTY = "";
+
+    /** Designates the badge status. */
+    public enum Status {
+        INFO("i"),
+        WARN("w"),
+        ERROR("e");
+
+        private String code;
+
+        Status(String code) {
+            this.code = code;
+        }
+
+        @Override
+        public String toString() {
+            return "{" + code + "}";
+        }
+
+        /** Returns the status code in string form. */
+        public String code() {
+            return code;
+        }
+    }
+
+    private final Status status;
+    private final boolean isGlyph;
+    private final String text;
+    private final String message;
+
+    // only instantiated through static methods.
+    private NodeBadge(Status status, boolean isGlyph, String text, String message) {
+        this.status = status == null ? Status.INFO : status;
+        this.isGlyph = isGlyph;
+        this.text = text;
+        this.message = message;
+    }
+
+    @Override
+    public String toString() {
+        return "{Badge " + status +
+                " (" + text + ")" +
+                (isGlyph ? "*G " : " ") +
+                "\"" + message + "\"}";
+    }
+
+    /**
+     * Returns the badge status.
+     *
+     * @return badge status
+     */
+    public Status status() {
+        return status;
+    }
+
+    /**
+     * Returns true if the text for this badge designates a glyph ID.
+     *
+     * @return true if badge uses glyph
+     */
+    public boolean isGlyph() {
+        return isGlyph;
+    }
+
+    /**
+     * Returns the text for the badge.
+     * Note that if {@link #isGlyph} is true, the text is a glyph ID, otherwise
+     * the text is displayed verbatim in the badge.
+     *
+     * @return text for badge
+     */
+    public String text() {
+        return text;
+    }
+
+    /**
+     * Returns the message associated with the badge.
+     *
+     * @return associated message
+     */
+    public String message() {
+        return message;
+    }
+
+    private static String nonNull(String s) {
+        return s == null ? EMPTY : s;
+    }
+
+    /**
+     * Returns an arbitrary text badge, with default status.
+     *
+     * @param txt the text
+     * @return node badge to display text
+     */
+    public static NodeBadge text(String txt) {
+        // TODO: consider length constraint on txt (3 chars?)
+        return new NodeBadge(Status.INFO, false, nonNull(txt), null);
+    }
+
+    /**
+     * Returns a glyph badge, with default status.
+     *
+     * @param gid the glyph ID
+     * @return node badge to display glyph
+     */
+    public static NodeBadge glyph(String gid) {
+        return new NodeBadge(Status.INFO, true, nonNull(gid), null);
+    }
+
+    /**
+     * Returns a numeric badge, with default status.
+     *
+     * @param n the number
+     * @return node badge to display a number
+     */
+    public static NodeBadge number(int n) {
+        // TODO: consider constraints, e.g. 1 <= n <= 999
+        return new NodeBadge(Status.INFO, false, Integer.toString(n), null);
+    }
+
+    /**
+     * Returns an arbitrary text badge, with the given status.
+     *
+     * @param s the status
+     * @param txt the text
+     * @return node badge to display text
+     */
+    public static NodeBadge text(Status s, String txt) {
+        // TODO: consider length constraint on txt (3 chars?)
+        return new NodeBadge(s, false, nonNull(txt), null);
+    }
+
+    /**
+     * Returns a glyph badge, with the given status.
+     *
+     * @param s the status
+     * @param gid the glyph ID
+     * @return node badge to display glyph
+     */
+    public static NodeBadge glyph(Status s, String gid) {
+        return new NodeBadge(s, true, nonNull(gid), null);
+    }
+
+
+    /**
+     * Returns a numeric badge, with the given status and optional message.
+     *
+     * @param s the status
+     * @param n the number
+     * @return node badge to display a number
+     */
+    public static NodeBadge number(Status s, int n) {
+        // TODO: consider constraints, e.g. 1 <= n <= 999
+        return new NodeBadge(s, false, Integer.toString(n), null);
+    }
+
+    /**
+     * Returns an arbitrary text badge, with the given status and optional
+     * message.
+     *
+     * @param s the status
+     * @param txt the text
+     * @param msg the optional message
+     * @return node badge to display text
+     */
+    public static NodeBadge text(Status s, String txt, String msg) {
+        // TODO: consider length constraint on txt (3 chars?)
+        return new NodeBadge(s, false, nonNull(txt), msg);
+    }
+
+    /**
+     * Returns a glyph badge, with the given status and optional message.
+     *
+     * @param s the status
+     * @param gid the glyph ID
+     * @param msg the optional message
+     * @return node badge to display glyph
+     */
+    public static NodeBadge glyph(Status s, String gid, String msg) {
+        return new NodeBadge(s, true, nonNull(gid), msg);
+    }
+
+
+    /**
+     * Returns a numeric badge, with the given status and optional message.
+     *
+     * @param s the status
+     * @param n the number
+     * @param msg the optional message
+     * @return node badge to display a number
+     */
+    public static NodeBadge number(Status s, int n, String msg) {
+        // TODO: consider constraints, e.g. 1 <= n <= 999
+        return new NodeBadge(s, false, Integer.toString(n), msg);
+    }
+
+}
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeHighlight.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeHighlight.java
index 61e10c56..69235089 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeHighlight.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/NodeHighlight.java
@@ -20,7 +20,34 @@ package org.onosproject.ui.topo;
  * Parent class of {@link DeviceHighlight} and {@link HostHighlight}.
  */
 public abstract class NodeHighlight extends AbstractHighlight {
+
+    private NodeBadge badge;
+
+    /**
+     * Constructs a node highlight entity.
+     *
+     * @param type element type
+     * @param elementId element identifier
+     */
     public NodeHighlight(TopoElementType type, String elementId) {
         super(type, elementId);
     }
+
+    /**
+     * Sets the badge for this node.
+     *
+     * @param badge badge to apply
+     */
+    public void setBadge(NodeBadge badge) {
+        this.badge = badge;
+    }
+
+    /**
+     * Returns the badge for this node, if any.
+     *
+     * @return badge, or null
+     */
+    public NodeBadge badge() {
+        return badge;
+    }
 }
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java
index 0f42b628..e2034fa7 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoConstants.java
@@ -32,14 +32,17 @@ public final class TopoConstants {
     public static final class Glyphs {
         public static final String UNKNOWN = "unknown";
         public static final String BIRD = "bird";
+        public static final String QUERY = "query";
         public static final String NODE = "node";
         public static final String SWITCH = "switch";
         public static final String ROADM = "roadm";
         public static final String ENDSTATION = "endstation";
         public static final String ROUTER = "router";
         public static final String BGP_SPEAKER = "bgpSpeaker";
+        public static final String MICROWAVE = "microwave";
         public static final String CHAIN = "chain";
         public static final String CROWN = "crown";
+        public static final String LOCK = "lock";
         public static final String TOPO = "topo";
         public static final String REFRESH = "refresh";
         public static final String GARBAGE = "garbage";
diff --git a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
index 8df03169..4030abdc 100644
--- a/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
+++ b/framework/src/onos/core/api/src/main/java/org/onosproject/ui/topo/TopoJson.java
@@ -37,6 +37,11 @@ public final class TopoJson {
     static final String ID = "id";
     static final String LABEL = "label";
     static final String CSS = "css";
+    static final String BADGE = "badge";
+    static final String STATUS = "status";
+    static final String TXT = "txt";
+    static final String GID = "gid";
+    static final String MSG = "msg";
 
     static final String TITLE = "title";
     static final String TYPE = "type";
@@ -97,12 +102,26 @@ public final class TopoJson {
         return payload;
     }
 
+    private static ObjectNode json(NodeBadge b) {
+        ObjectNode n = objectNode()
+                .put(STATUS, b.status().code())
+                .put(b.isGlyph() ? GID : TXT, b.text());
+        if (b.message() != null) {
+            n.put(MSG, b.message());
+        }
+        return n;
+    }
+
     private static ObjectNode json(DeviceHighlight dh) {
         ObjectNode n = objectNode()
                 .put(ID, dh.elementId());
         if (dh.subdued()) {
             n.put(SUBDUE, true);
         }
+        NodeBadge badge = dh.badge();
+        if (badge != null) {
+            n.set(BADGE, json(badge));
+        }
         return n;
     }
 
diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/NodeBadgeTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/NodeBadgeTest.java
new file mode 100644
index 00000000..c8243695
--- /dev/null
+++ b/framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/NodeBadgeTest.java
@@ -0,0 +1,112 @@
+/*
+ *  Copyright 2015 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.onosproject.ui.topo;
+
+import org.junit.Test;
+import org.onosproject.ui.topo.NodeBadge.Status;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Unit tests for {@link NodeBadge}.
+ */
+public class NodeBadgeTest {
+
+    private static final String MSG = "a msg";
+    private static final String TXT = "text";
+    private static final String GID = "glyph-id";
+    private static final int NUM = 42;
+    private static final String NUM_STR = Integer.toString(NUM);
+
+    private static final String WR_S = "wrong status";
+    private static final String WR_B = "wrong boolean";
+    private static final String WR_T = "wrong text";
+    private static final String WR_M = "wrong message";
+    private static final String WR_SF = "wrong string format";
+
+    private NodeBadge badge;
+
+    private void checkFields(NodeBadge b, Status s, boolean g,
+                             String txt, String msg) {
+        assertEquals(WR_S, s, b.status());
+        assertEquals(WR_B, g, b.isGlyph());
+        assertEquals(WR_T, txt, b.text());
+        assertEquals(WR_M, msg, b.message());
+    }
+
+    @Test
+    public void badgeTypes() {
+        assertEquals(WR_SF, "i", Status.INFO.code());
+        assertEquals(WR_SF, "w", Status.WARN.code());
+        assertEquals(WR_SF, "e", Status.ERROR.code());
+        assertEquals("unexpected size", 3, Status.values().length);
+    }
+
+    @Test
+    public void textOnly() {
+        badge = NodeBadge.text(TXT);
+        checkFields(badge, Status.INFO, false, TXT, null);
+    }
+
+    @Test
+    public void glyphOnly() {
+        badge = NodeBadge.glyph(GID);
+        checkFields(badge, Status.INFO, true, GID, null);
+    }
+
+    @Test
+    public void numberOnly() {
+        badge = NodeBadge.number(NUM);
+        checkFields(badge, Status.INFO, false, NUM_STR, null);
+    }
+
+    @Test
+    public void textInfo() {
+        badge = NodeBadge.text(Status.INFO, TXT);
+        checkFields(badge, Status.INFO, false, TXT, null);
+    }
+
+    @Test
+    public void glyphWarn() {
+        badge = NodeBadge.glyph(Status.WARN, GID);
+        checkFields(badge, Status.WARN, true, GID, null);
+    }
+
+    @Test
+    public void numberError() {
+        badge = NodeBadge.number(Status.ERROR, NUM);
+        checkFields(badge, Status.ERROR, false, NUM_STR, null);
+    }
+
+    @Test
+    public void textInfoMsg() {
+        badge = NodeBadge.text(Status.INFO, TXT, MSG);
+        checkFields(badge, Status.INFO, false, TXT, MSG);
+    }
+
+    @Test
+    public void glyphWarnMsg() {
+        badge = NodeBadge.glyph(Status.WARN, GID, MSG);
+        checkFields(badge, Status.WARN, true, GID, MSG);
+    }
+
+    @Test
+    public void numberErrorMsg() {
+        badge = NodeBadge.number(Status.ERROR, NUM, MSG);
+        checkFields(badge, Status.ERROR, false, NUM_STR, MSG);
+    }
+}
diff --git a/framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/TopoJsonTest.java b/framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/TopoJsonTest.java
index ac0051cd..50aabf8e 100644
--- a/framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/TopoJsonTest.java
+++ b/framework/src/onos/core/api/src/test/java/org/onosproject/ui/topo/TopoJsonTest.java
@@ -21,14 +21,22 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.junit.Test;
 import org.onosproject.ui.JsonUtils;
 import org.onosproject.ui.topo.Highlights.Amount;
+import org.onosproject.ui.topo.NodeBadge.Status;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 
 /**
  * Unit tests for {@link TopoJson}.
  */
 public class TopoJsonTest {
 
+    private static final String DEV1 = "device-1";
+    private static final String DEV2 = "device-2";
+    private static final String SOME_MSG = "Hello there";
+    private static final String GID = "glyph-ID";
+
     private ObjectNode payload;
 
     private void checkArrayLength(String key, int expLen) {
@@ -68,4 +76,42 @@ public class TopoJsonTest {
         String subdue = JsonUtils.string(payload, TopoJson.SUBDUE);
         assertEquals("not max", "max", subdue);
     }
+
+    @Test
+    public void badgedDevice() {
+        Highlights h = new Highlights();
+        DeviceHighlight dh = new DeviceHighlight(DEV1);
+        dh.setBadge(NodeBadge.number(7));
+        h.add(dh);
+
+        dh = new DeviceHighlight(DEV2);
+        dh.setBadge(NodeBadge.glyph(Status.WARN, GID, SOME_MSG));
+        h.add(dh);
+
+        payload = TopoJson.json(h);
+//        System.out.println(payload);
+
+        // dig into the payload, and verify the badges are set on the devices
+        ArrayNode a = (ArrayNode) payload.get(TopoJson.DEVICES);
+
+        ObjectNode d = (ObjectNode) a.get(0);
+        assertEquals("wrong device id", DEV1, d.get(TopoJson.ID).asText());
+
+        ObjectNode b = (ObjectNode) d.get(TopoJson.BADGE);
+        assertNotNull("missing badge", b);
+        assertEquals("wrong status code", "i", b.get(TopoJson.STATUS).asText());
+        assertEquals("wrong text", "7", b.get(TopoJson.TXT).asText());
+        assertNull("glyph?", b.get(TopoJson.GID));
+        assertNull("msg?", b.get(TopoJson.MSG));
+
+        d = (ObjectNode) a.get(1);
+        assertEquals("wrong device id", DEV2, d.get(TopoJson.ID).asText());
+
+        b = (ObjectNode) d.get(TopoJson.BADGE);
+        assertNotNull("missing badge", b);
+        assertEquals("wrong status code", "w", b.get(TopoJson.STATUS).asText());
+        assertNull("text?", b.get(TopoJson.TXT));
+        assertEquals("wrong text", GID, b.get(TopoJson.GID).asText());
+        assertEquals("wrong message", SOME_MSG, b.get(TopoJson.MSG).asText());
+    }
 }
diff --git a/framework/src/onos/docs/internal-apps b/framework/src/onos/docs/internal-apps
index 71aacd03..c930c05f 100644
--- a/framework/src/onos/docs/internal-apps
+++ b/framework/src/onos/docs/internal-apps
@@ -27,4 +27,5 @@ org.onosproject.vtn*
 org.onosproject.cord*
 org.onosproject.mcast*
 org.onosproject.mfwd*
+org.onosproject.mlb*
 org.onosproject.igmp.impl
diff --git a/framework/src/onos/docs/internal-netconf b/framework/src/onos/docs/internal-netconf
index 40c159f4..161ad9cc 100644
--- a/framework/src/onos/docs/internal-netconf
+++ b/framework/src/onos/docs/internal-netconf
@@ -1 +1,2 @@
+org.onosproject.netconf*
 org.onosproject.provider.netconf*
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA1Pipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA1Pipeline.java
index f72bde09..c9ef451e 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA1Pipeline.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/CpqdOFDPA1Pipeline.java
@@ -169,7 +169,7 @@ public class CpqdOFDPA1Pipeline extends OFDPA1Pipeline {
         }));
     }
 
-    private void processAclTable() {
+    protected void processAclTable() {
         //table miss entry - catch all to executed action-set
         FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
         TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OLTPipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OLTPipeline.java
index eebb2e22..c735af3f 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OLTPipeline.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OLTPipeline.java
@@ -30,7 +30,6 @@ import org.onosproject.net.device.DefaultDeviceDescription;
 import org.onosproject.net.device.DeviceDescription;
 import org.onosproject.net.device.DeviceProvider;
 import org.onosproject.net.device.DeviceProviderRegistry;
-import org.onosproject.net.device.DeviceProviderService;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.AbstractHandlerBehaviour;
 import org.onosproject.net.flow.DefaultFlowRule;
@@ -86,13 +85,13 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
         flowRuleService = serviceDirectory.get(FlowRuleService.class);
         coreService = serviceDirectory.get(CoreService.class);
 
-        try {
+        /*try {
             DeviceProviderService providerService = registry.register(provider);
             providerService.deviceConnected(deviceId,
                                             description(deviceId, DEVICE, OLT));
         } finally {
             registry.unregister(provider);
-        }
+        }*/
 
         appId = coreService.registerApplication(
                 "org.onosproject.driver.OLTPipeline");
@@ -109,12 +108,12 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
                                                 PacketPriority.CONTROL.priorityValue(),
                                                 appId, 0, true, null);
 
-        flowRuleService.applyFlowRules(flowRule);
+        //flowRuleService.applyFlowRules(flowRule);
     }
 
     @Override
     public void filter(FilteringObjective filter) {
-        throw new UnsupportedOperationException("Single table does not filter.");
+        throw new UnsupportedOperationException("OLT does not filter.");
     }
 
     @Override
@@ -140,19 +139,11 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
 
         TrafficSelector selector = fwd.selector();
         TrafficTreatment treatment = fwd.treatment();
-        if ((fwd.treatment().deferred().size() == 0) &&
-                (fwd.treatment().immediate().size() == 0) &&
-                (fwd.treatment().tableTransition() == null) &&
-                (!fwd.treatment().clearedDeferred())) {
-            TrafficTreatment.Builder flowTreatment = DefaultTrafficTreatment.builder();
-            flowTreatment.add(Instructions.createDrop());
-            treatment = flowTreatment.build();
-        }
 
         FlowRule.Builder ruleBuilder = DefaultFlowRule.builder()
                 .forDevice(deviceId)
                 .withSelector(selector)
-                .withTreatment(fwd.treatment())
+                .withTreatment(treatment)
                 .fromApp(fwd.appId())
                 .withPriority(fwd.priority());
 
@@ -162,9 +153,7 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
             ruleBuilder.makeTemporary(fwd.timeout());
         }
 
-
         switch (fwd.op()) {
-
             case ADD:
                 flowBuilder.add(ruleBuilder.build());
                 break;
@@ -190,16 +179,16 @@ public class OLTPipeline extends AbstractHandlerBehaviour implements Pipeliner {
                 }
             }
         }));
-
     }
 
     @Override
     public void next(NextObjective nextObjective) {
-        throw new UnsupportedOperationException("Single table does not next hop.");
+        throw new UnsupportedOperationException("OLT does not next hop.");
     }
 
     /**
      * Build a device description.
+     *
      * @param deviceId a deviceId
      * @param key the key of the annotation
      * @param value the value for the annotation
diff --git a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java
index 270e76a2..5d098390 100644
--- a/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java
+++ b/framework/src/onos/drivers/src/main/java/org/onosproject/driver/pipeline/OpenVSwitchPipeline.java
@@ -21,12 +21,14 @@ import java.util.Collection;
 import java.util.Collections;
 
 import org.onlab.osgi.ServiceDirectory;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.behaviour.Pipeliner;
 import org.onosproject.net.behaviour.PipelinerContext;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.flow.DefaultFlowRule;
+import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.FlowRule;
 import org.onosproject.net.flow.FlowRuleOperations;
@@ -56,11 +58,13 @@ public class OpenVSwitchPipeline extends DefaultSingleTablePipeline
     private ServiceDirectory serviceDirectory;
     protected FlowObjectiveStore flowObjectiveStore;
     protected DeviceId deviceId;
+    protected ApplicationId appId;
     protected FlowRuleService flowRuleService;
     protected DeviceService deviceService;
     private static final int TIME_OUT = 0;
-    private static final int MAC_TABLE = 40;
-    private static final int PORT_TABLE = 0;
+    private static final int CLASSIFIER_TABLE = 0;
+    private static final int MAC_TABLE = 50;
+    private static final int TABLE_MISS_PRIORITY = 0;
 
     @Override
     public void init(DeviceId deviceId, PipelinerContext context) {
@@ -71,9 +75,9 @@ public class OpenVSwitchPipeline extends DefaultSingleTablePipeline
         coreService = serviceDirectory.get(CoreService.class);
         flowRuleService = serviceDirectory.get(FlowRuleService.class);
         flowObjectiveStore = context.store();
-        coreService
+        appId = coreService
                 .registerApplication("org.onosproject.driver.OpenVSwitchPipeline");
-
+        initializePipeline();
     }
 
     @Override
@@ -125,6 +129,60 @@ public class OpenVSwitchPipeline extends DefaultSingleTablePipeline
         super.next(nextObjective);
     }
 
+    private void initializePipeline() {
+        processClassifierTable(true);
+        processMacTable(true);
+    }
+
+    private void processClassifierTable(boolean install) {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+
+        treatment.transition(MAC_TABLE);
+
+        FlowRule rule;
+        rule = DefaultFlowRule.builder().forDevice(deviceId)
+                .withSelector(selector.build())
+                .withTreatment(treatment.build())
+                .withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
+                .makePermanent().forTable(CLASSIFIER_TABLE).build();
+
+        applyRules(install, rule);
+    }
+
+    private void processMacTable(boolean install) {
+        TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
+
+        treatment.drop();
+
+        FlowRule rule;
+        rule = DefaultFlowRule.builder().forDevice(deviceId)
+                .withSelector(selector.build())
+                .withTreatment(treatment.build())
+                .withPriority(TABLE_MISS_PRIORITY).fromApp(appId)
+                .makePermanent().forTable(MAC_TABLE).build();
+
+        applyRules(install, rule);
+    }
+
+    private void applyRules(boolean install, FlowRule rule) {
+        FlowRuleOperations.Builder ops = FlowRuleOperations.builder();
+
+        ops = install ? ops.add(rule) : ops.remove(rule);
+        flowRuleService.apply(ops.build(new FlowRuleOperationsContext() {
+            @Override
+            public void onSuccess(FlowRuleOperations ops) {
+                log.info("ONOSW provisioned " + rule.tableId() + " table");
+            }
+
+            @Override
+            public void onError(FlowRuleOperations ops) {
+                log.info("ONOSW failed to provision " + rule.tableId() + " table");
+            }
+        }));
+    }
+
     private Collection<FlowRule> processForward(ForwardingObjective fwd) {
         switch (fwd.flag()) {
         case SPECIFIC:
@@ -164,7 +222,7 @@ public class OpenVSwitchPipeline extends DefaultSingleTablePipeline
             tb.allInstructions().forEach(t -> newTraffic.add(t));
             newTraffic.transition(MAC_TABLE);
             ruleBuilder.withTreatment(newTraffic.build());
-            ruleBuilder.forTable(PORT_TABLE);
+            ruleBuilder.forTable(CLASSIFIER_TABLE);
         }
         return Collections.singletonList(ruleBuilder.build());
     }
diff --git a/framework/src/onos/etc/init/onos.conf b/framework/src/onos/etc/init/onos.conf
deleted file mode 100644
index 779df905..00000000
--- a/framework/src/onos/etc/init/onos.conf
+++ /dev/null
@@ -1,37 +0,0 @@
-description  "Open Network Operating System"
-author       "ON.Lab"
-
-start on (net-device-up
-          and local-filesystems
-          and runlevel [2345])
-stop on runlevel [016]
-
-console output
-kill timeout 60
-respawn
-
-env LANG=en_US.UTF-8
-
-pre-start script
-    [ -f /opt/onos/options ] && . /opt/onos/options
-    ONOS_USER=${ONOS_USER:-root}
-
-    # Ensure that the environment is initialized
-    [ -d /opt/onos ] && mkdir /opt/onos/var 2>/dev/null && chown $ONOS_USER.$ONOS_USER /opt/onos/var
-    [ -d /opt/onos ] && mkdir /opt/onos/config 2>/dev/null && chown $ONOS_USER.$ONOS_USER /opt/onos/config
-    # TODO make karaf version configurable
-    [ -d /opt/onos ] && [ ! -h /opt/onos/log ] \
-         && ln -s /opt/onos/apache-karaf-3.0.3/data/log /opt/onos/log || :
-end script
-
-pre-stop script
-    /opt/onos/bin/onos halt 2>>/opt/onos/var/stderr.log
-    sleep 1
-end script
-
-script
-  [ -f /opt/onos/options ] && . /opt/onos/options
-  start-stop-daemon --signal INT --start --chuid ${ONOS_USER:-root} \
-    --exec /opt/onos/bin/onos-service -- ${ONOS_OPTS:-server} \
-        >/opt/onos/var/stdout.log 2>/opt/onos/var/stderr.log
-end script
diff --git a/framework/src/onos/etc/org.ops4j.pax.url.mvn.cfg b/framework/src/onos/etc/org.ops4j.pax.url.mvn.cfg
deleted file mode 100644
index 15167a3b..00000000
--- a/framework/src/onos/etc/org.ops4j.pax.url.mvn.cfg
+++ /dev/null
@@ -1,101 +0,0 @@
-################################################################################
-#
-#    Licensed to the Apache Software Foundation (ASF) under one or more
-#    contributor license agreements.  See the NOTICE file distributed with
-#    this work for additional information regarding copyright ownership.
-#    The ASF licenses this file to You 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.
-#
-################################################################################
-
-#
-# If set to true, the following property will not allow any certificate to be used
-# when accessing Maven repositories through SSL
-#
-#org.ops4j.pax.url.mvn.certificateCheck=
-
-#
-# Path to the local Maven settings file.
-# The repositories defined in this file will be automatically added to the list
-# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property
-# below is not set.
-# The following locations are checked for the existence of the settings.xml file
-#   * 1. looks for the specified url
-#   * 2. if not found looks for ${user.home}/.m2/settings.xml
-#   * 3. if not found looks for ${maven.home}/conf/settings.xml
-#   * 4. if not found looks for ${M2_HOME}/conf/settings.xml
-#
-#org.ops4j.pax.url.mvn.settings=
-
-#
-# Path to the local Maven repository which is used to avoid downloading
-# artifacts when they already exist locally.
-# The value of this property will be extracted from the settings.xml file
-# above, or defaulted to:
-#     System.getProperty( "user.home" ) + "/.m2/repository"
-#
-#org.ops4j.pax.url.mvn.localRepository=
-
-#
-# Default this to false. It's just weird to use undocumented repos
-#
-org.ops4j.pax.url.mvn.useFallbackRepositories=false
-
-#
-# Uncomment if you don't wanna use the proxy settings
-# from the Maven conf/settings.xml file
-#
-# org.ops4j.pax.url.mvn.proxySupport=false
-
-#
-# Comma separated list of repositories scanned when resolving an artifact.
-# Those repositories will be checked before iterating through the
-#    below list of repositories and even before the local repository
-# A repository url can be appended with zero or more of the following flags:
-#    @snapshots  : the repository contains snaphots
-#    @noreleases : the repository does not contain any released artifacts
-#
-# The following property value will add the system folder as a repo.
-#
-org.ops4j.pax.url.mvn.defaultRepositories=\
-    file:${karaf.home}/${karaf.default.repository}@id=system.repository@snapshots,\
-    file:${karaf.data}/kar@id=kar.repository@multi@snapshots
-
-# Use the default local repo (e.g.~/.m2/repository) as a "remote" repo
-#org.ops4j.pax.url.mvn.defaultLocalRepoAsRemote=false
-
-#
-# Comma separated list of repositories scanned when resolving an artifact.
-# The default list includes the following repositories:
-#    http://repo1.maven.org/maven2@id=central
-#    http://repository.springsource.com/maven/bundles/release@id=spring.ebr
-#    http://repository.springsource.com/maven/bundles/external@id=spring.ebr.external
-#    http://zodiac.springsource.com/maven/bundles/release@id=gemini
-#    http://repository.apache.org/content/groups/snapshots-group@id=apache@snapshots@noreleases
-#    https://oss.sonatype.org/content/repositories/snapshots@id=sonatype.snapshots.deploy@snapshots@noreleases
-#    https://oss.sonatype.org/content/repositories/ops4j-snapshots@id=ops4j.sonatype.snapshots.deploy@snapshots@noreleases
-# To add repositories to the default ones, prepend '+' to the list of repositories
-# to add.
-# A repository url can be appended with zero or more of the following flags:
-#    @snapshots  : the repository contains snapshots
-#    @noreleases : the repository does not contain any released artifacts
-#    @id=repository.id : the id for the repository, just like in the settings.xml this is optional but recommended
-#
-org.ops4j.pax.url.mvn.repositories= \
-    ${org.ops4j.pax.url.mvn.defaultRepositories}, \
-    http://repo1.maven.org/maven2@id=central, \
-    http://repository.springsource.com/maven/bundles/release@id=spring.ebr.release, \
-    http://repository.springsource.com/maven/bundles/external@id=spring.ebr.external, \
-    http://zodiac.springsource.com/maven/bundles/release@id=gemini, \
-    http://repository.apache.org/content/groups/snapshots-group@id=apache@snapshots@noreleases, \
-    https://oss.sonatype.org/content/repositories/snapshots@id=sonatype.snapshots.deploy@snapshots@noreleases, \
-    https://oss.sonatype.org/content/repositories/ops4j-snapshots@id=ops4j.sonatype.snapshots.deploy@snapshots@noreleases
diff --git a/framework/src/onos/etc/org.ops4j.pax.web.cfg b/framework/src/onos/etc/org.ops4j.pax.web.cfg
deleted file mode 100644
index c8fb3b3d..00000000
--- a/framework/src/onos/etc/org.ops4j.pax.web.cfg
+++ /dev/null
@@ -1,12 +0,0 @@
-org.osgi.service.http.port=8181
-org.osgi.service.http.port.secure=8443
-
-org.osgi.service.http.enabled=true
-org.osgi.service.http.secure.enabled=false
-
-org.ops4j.pax.web.ssl.keystore=etc/keystore
-org.ops4j.pax.web.ssl.password=OBF:1xtn1w1u1uob1xtv1y7z1xtn1unn1w1o1xtv
-org.ops4j.pax.web.ssl.keypassword=OBF:1xtn1w1u1uob1xtv1y7z1xtn1unn1w1o1xtv
-
-org.ops4j.pax.web.session.url=none
-org.ops4j.pax.web.config.file=./etc/jetty.xml
diff --git a/framework/src/onos/etc/samples/linkGraph.cfg b/framework/src/onos/etc/samples/linkGraph.cfg
deleted file mode 100644
index 41ce5bdc..00000000
--- a/framework/src/onos/etc/samples/linkGraph.cfg
+++ /dev/null
@@ -1,27 +0,0 @@
-# NullLinkProvider topology description (config file).
-#
-# Dot-style topology graph. Each controller's topology begins with
-#
-#   graph <node ID>, followed by a list of links between braces.
-#
-# The links are either bidirectional (--) or directed (->). The directed
-# edges are used to connect together Null devices of different controllers.
-# The endpoint has the format:
-# 
-#   devID:port:NodeId
-#
-# The NodeId is only added if the destination is another node's device.
-#
-graph 192.168.56.20 {
-    0:0 -- 1:0
-    1:1 -> 0:0:192.168.56.30
-    1:2 -- 2:0
-    2:1 -> 1:0:192.168.56.30
-}
-graph 192.168.56.30 {
-    0:0 -> 1:1:192.168.56.20
-    0:1 -- 1:1
-    1:0 -> 2:1:192.168.56.20
-    1:2 -- 2:0
-}
-# Bugs: Comments cannot be appended to a line to be read.
diff --git a/framework/src/onos/etc/samples/org.onosproject.fwd.ReactiveForwarding.cfg b/framework/src/onos/etc/samples/org.onosproject.fwd.ReactiveForwarding.cfg
deleted file mode 100644
index 4befc706..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.fwd.ReactiveForwarding.cfg
+++ /dev/null
@@ -1,79 +0,0 @@
-#
-# Sample configuration for onos-app-fwd.
-#
-
-#
-# Reactive flows default matching is InPort, Src MAC, Dst MAC and EtherType fields 
-#
-
-#
-# Enable packet-out only forwarding.
-# This flag affects to both IPv4 and IPv6.
-#
-# packetOutOnly = true
-
-#
-# Enable forwarding of the first packet by using OFPP_TABLE port in the
-# PacketOut message instead of sending it directly to the switch port
-#
-# packetOutOfppTable = true
-
-#
-# Timeout of reactively installed flows (in seconds). 
-# Default is 10 sec
-#
-# flowTimeout = 10
-
-#
-# Priority of reactively installed flows
-#
-# flowPriority = 10
-
-#
-# Enable IPv6 forwarding.
-#
-# ipv6Forwarding = true
-
-#
-# Flows matching destination MAC only - as legacy L2 switches 
-# - This option overrides all other options below
-#
-# matchDstMacOnly = true
-
-#
-# Matching of VLAN ID in Ethernet header
-#
-# matchVlanId = true
-
-#
-# Matching of IPv4 addresses and Protocol field
-# - must be enabled to match IPv4 DSCP, TCP/UDP ports and ICMP type/code
-#
-# matchIpv4Address = true
-
-#
-# Matching of IPv4 DSCP and ECN fields 
-#
-# matchIpv4Dscp = true
-
-#
-# Matching of IPv6 addresses and Next-Header field
-# - must be enabled to match IPv6 Flow Label, TCP/UDP ports and ICMP type/code
-#
-# matchIpv6Address = true
-
-#
-# Matching of IPv6 Flow Label
-#
-# matchIpv6FlowLabel = true
-
-#
-# Matching of TCP/UDP ports for IPv4 and IPv6
-#
-# matchTcpUdpPorts = true
-
-#
-# Matching of ICMP Type and Code fields for IPv4 and IPv6 
-#
-# matchIcmpFields = true
-
diff --git a/framework/src/onos/etc/samples/org.onosproject.provider.host.impl.HostLocationProvider.cfg b/framework/src/onos/etc/samples/org.onosproject.provider.host.impl.HostLocationProvider.cfg
deleted file mode 100644
index 6d3a50d9..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.provider.host.impl.HostLocationProvider.cfg
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# Sample configuration for Host Location Provider
-#
-
-#
-# Enable host removal on port/device down events.
-#
-# hostRemovalEnabled = true
-
-#
-# Enable using IPv6 Neighbor Discovery by the Host Location Provider.
-#
-# ipv6NeighborDiscovery = true
diff --git a/framework/src/onos/etc/samples/org.onosproject.provider.lldp.impl.LLDPLinkProvider.cfg b/framework/src/onos/etc/samples/org.onosproject.provider.lldp.impl.LLDPLinkProvider.cfg
deleted file mode 100644
index 6eb39a11..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.provider.lldp.impl.LLDPLinkProvider.cfg
+++ /dev/null
@@ -1,21 +0,0 @@
-#
-# Sample configuration for link discovery
-#
-
-#
-# Disable Link Dicovery Permanently (Note: changing this property at runtime will have NO effect)
-# WARNING: This should only be used for special projects like bgprouter, where ONOS is controlling
-# a single switch
-#
-#disableLinkDiscovery = true
-
-#
-# Enable Broadcast Discovery Protocol (EthType=0x8942)
-#
-#useBDDP = false
-
-#
-# Disable LLDP's received from specific devices
-# Details of the devices are in the file configured below
-#
-#lldpSuppression = ../config/lldp_suppresion.json
diff --git a/framework/src/onos/etc/samples/org.onosproject.provider.netconf.device.impl.NetconfDeviceProvider.cfg b/framework/src/onos/etc/samples/org.onosproject.provider.netconf.device.impl.NetconfDeviceProvider.cfg
deleted file mode 100644
index 30ed0c26..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.provider.netconf.device.impl.NetconfDeviceProvider.cfg
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Instance-specific configurations, in this case, the number of 
-# devices per node.
-#
-devConfigs = cisco:cisco@192.168.56.20:2022:inactive,sdn:rocks@192.168.56.30:22:inactive
-
-#
-# Number of ports per device. This is global to all devices
-# on all instances.
-#
-# numPorts = 8
diff --git a/framework/src/onos/etc/samples/org.onosproject.provider.nil.device.impl.NullDeviceProvider.cfg b/framework/src/onos/etc/samples/org.onosproject.provider.nil.device.impl.NullDeviceProvider.cfg
deleted file mode 100644
index 194bf037..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.provider.nil.device.impl.NullDeviceProvider.cfg
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Instance-specific configurations, in this case, the number of 
-# devices per node.
-#
-devConfigs = 192.168.56.20:3,192.168.56.30:3
-
-#
-# Number of ports per device. This is global to all devices
-# on all instances.
-#
-# numPorts = 8
diff --git a/framework/src/onos/etc/samples/org.onosproject.provider.nil.link.impl.NullLinkProvider.cfg b/framework/src/onos/etc/samples/org.onosproject.provider.nil.link.impl.NullLinkProvider.cfg
deleted file mode 100644
index ef72b1ee..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.provider.nil.link.impl.NullLinkProvider.cfg
+++ /dev/null
@@ -1,16 +0,0 @@
-#
-# Sample configurations for the NullLinkProvider.
-#
-
-#
-# If enabled, sets the time between LinkEvent generation,
-# in microseconds.
-#
-
-#eventRate = 1000000
-
-#
-# If enabled, points to the full path to the topology file.
-#
-
-#cfgFile = /tmp/foo.cfg
diff --git a/framework/src/onos/etc/samples/org.onosproject.provider.nil.packet.impl.NullPacketProvider.cfg b/framework/src/onos/etc/samples/org.onosproject.provider.nil.packet.impl.NullPacketProvider.cfg
deleted file mode 100644
index db4342c0..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.provider.nil.packet.impl.NullPacketProvider.cfg
+++ /dev/null
@@ -1,4 +0,0 @@
-#
-# Uncomment and tweak to tune the rate of Packet events (per second)
-#
-# pktRate = 100
diff --git a/framework/src/onos/etc/samples/org.onosproject.proxyarp.ProxyArp.cfg b/framework/src/onos/etc/samples/org.onosproject.proxyarp.ProxyArp.cfg
deleted file mode 100644
index 108de136..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.proxyarp.ProxyArp.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Sample configuration for onos-app-proxyarp.
-#
-
-#
-# Enable IPv6 Neighbor Discovery.
-#
-# ipv6NeighborDiscovery = true
diff --git a/framework/src/onos/etc/samples/org.onosproject.routing.bgp.BgpSessionManager.cfg b/framework/src/onos/etc/samples/org.onosproject.routing.bgp.BgpSessionManager.cfg
deleted file mode 100644
index fbcc13f9..00000000
--- a/framework/src/onos/etc/samples/org.onosproject.routing.bgp.BgpSessionManager.cfg
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Sample configuration for onos-app-sdnip.
-#
-
-#
-# The port number that SDN-IP listens for incoming BGP connections on.
-#
-# bgpPort=2000
\ No newline at end of file
diff --git a/framework/src/onos/etc/samples/org.onosproject.xos.XOS.cfg b/framework/src/onos/etc/samples/org.onosproject.xos.XOS.cfg
deleted file mode 100644
index e69de29b..00000000
diff --git a/framework/src/onos/etc/users.properties b/framework/src/onos/etc/users.properties
deleted file mode 100644
index 9f7a2667..00000000
--- a/framework/src/onos/etc/users.properties
+++ /dev/null
@@ -1,34 +0,0 @@
-################################################################################
-#
-#    Licensed to the Apache Software Foundation (ASF) under one or more
-#    contributor license agreements.  See the NOTICE file distributed with
-#    this work for additional information regarding copyright ownership.
-#    The ASF licenses this file to You 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.
-#
-################################################################################
-
-#
-# This file contains the users, groups, and roles.
-# Each line has to be of the format:
-#
-# USER=PASSWORD,ROLE1,ROLE2,...
-# USER=PASSWORD,_g_:GROUP,...
-# _g_\:GROUP=ROLE1,ROLE2,...
-#
-# All users, grousp, and roles entered in this file are available after Karaf startup
-# and modifiable via the JAAS command group. These users reside in a JAAS domain
-# with the name "karaf".
-#
-karaf = karaf,_g_:admingroup
-onos = rocks,_g_:admingroup
-_g_\:admingroup = group,admin,manager,viewer,webconsole
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java
index 592336c2..9f2d4105 100644
--- a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java
+++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/config/basics/InterfaceConfig.java
@@ -39,7 +39,6 @@ public class InterfaceConfig extends Config<ConnectPoint> {
     public static final String MAC = "mac";
     public static final String VLAN = "vlan";
 
-    public static final String MAC_MISSING_ERROR = "Must have a MAC address for each interface";
     public static final String CONFIG_VALUE_ERROR = "Error parsing config value";
 
     /**
@@ -55,15 +54,12 @@ public class InterfaceConfig extends Config<ConnectPoint> {
             for (JsonNode intfNode : array) {
                 Set<InterfaceIpAddress> ips = getIps(intfNode);
 
-                if (intfNode.path(MAC).isMissingNode()) {
-                    throw new ConfigException(MAC_MISSING_ERROR);
-                }
-
-                MacAddress mac = MacAddress.valueOf(intfNode.path(MAC).asText());
+                String mac = intfNode.path(MAC).asText();
+                MacAddress macAddr = mac.isEmpty() ? null : MacAddress.valueOf(mac);
 
                 VlanId vlan = getVlan(intfNode);
 
-                interfaces.add(new Interface(subject, ips, mac, vlan));
+                interfaces.add(new Interface(subject, ips, macAddr, vlan));
             }
         } catch (IllegalArgumentException e) {
             throw new ConfigException(CONFIG_VALUE_ERROR, e);
@@ -79,7 +75,10 @@ public class InterfaceConfig extends Config<ConnectPoint> {
      */
     public void addInterface(Interface intf) {
         ObjectNode intfNode = array.addObject();
-        intfNode.put(MAC, intf.mac().toString());
+
+        if (intf.mac() != null) {
+            intfNode.put(MAC, intf.mac().toString());
+        }
 
         if (!intf.ipAddresses().isEmpty()) {
             intfNode.set(IPS, putIps(intf.ipAddresses()));
diff --git a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/Interface.java b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/Interface.java
index 69d14bce..b9d3eadf 100644
--- a/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/Interface.java
+++ b/framework/src/onos/incubator/api/src/main/java/org/onosproject/incubator/net/intf/Interface.java
@@ -30,7 +30,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
 
 /**
  * An Interface maps network configuration information (such as addresses and
- * vlans) to a port in the network.
+ * vlans) to a port in the network. This is considered a L2/L3 network
+ * interface.
  */
 @Beta
 public class Interface {
@@ -51,9 +52,9 @@ public class Interface {
                      Set<InterfaceIpAddress> ipAddresses,
                      MacAddress macAddress, VlanId vlan) {
         this.connectPoint = checkNotNull(connectPoint);
-        this.ipAddresses = Sets.newHashSet(checkNotNull(ipAddresses));
-        this.macAddress = checkNotNull(macAddress);
-        this.vlan = checkNotNull(vlan);
+        this.ipAddresses = ipAddresses == null ? Sets.newHashSet() : ipAddresses;
+        this.macAddress = macAddress == null ? MacAddress.NONE : macAddress;
+        this.vlan = vlan == null ? VlanId.NONE : vlan;
     }
 
     /**
diff --git a/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitch.java b/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitch.java
index 1b6810be..51a2ce42 100644
--- a/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitch.java
+++ b/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/OpenFlowSwitch.java
@@ -30,6 +30,14 @@ public interface OpenFlowSwitch {
     /**
      * Writes the message to the driver.
      *
+     * Note:
+     * Calling {@link #sendMsg(OFMessage)} does NOT guarantee the messages to be
+     * transmitted on the wire in order, especially during role transition.
+     * The messages may be reordered at the switch side.
+     *
+     * Calling {@link #sendMsg(List)} guarantee the messages inside the list
+     * to be transmitted on the wire in order.
+     *
      * @param msg the message to write
      */
     void sendMsg(OFMessage msg);
diff --git a/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java b/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
index 2f6357bd..2c19837e 100644
--- a/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
+++ b/framework/src/onos/openflow/api/src/main/java/org/onosproject/openflow/controller/driver/AbstractOpenFlowSwitch.java
@@ -79,6 +79,8 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
     protected OFFeaturesReply features;
     protected OFDescStatsReply desc;
 
+    List<OFMessage> messagesPendingMastership;
+
     @Override
     public void init(Dpid dpid, OFDescStatsReply desc, OFVersion ofv) {
         this.dpid = dpid;
@@ -96,16 +98,21 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
     }
 
     @Override
-    public void sendMsg(OFMessage m) {
-        if (role == RoleState.MASTER && channel.isConnected()) {
-            channel.write(Collections.singletonList(m));
-        }
+    public void sendMsg(OFMessage msg) {
+        this.sendMsg(Collections.singletonList(msg));
     }
 
     @Override
     public final void sendMsg(List<OFMessage> msgs) {
         if (role == RoleState.MASTER && channel.isConnected()) {
             channel.write(msgs);
+        } else if (messagesPendingMastership != null) {
+            messagesPendingMastership.addAll(msgs);
+            log.debug("Enqueue message for switch {}. queue size after is {}",
+                      dpid, messagesPendingMastership.size());
+        } else {
+            log.warn("Dropping message for switch {} (role: {}, connected: {}): {}",
+                     dpid, role, channel.isConnected(), msgs);
         }
     }
 
@@ -232,6 +239,12 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
     @Override
     public final void transitionToMasterSwitch() {
         this.agent.transitionToMasterSwitch(dpid);
+        if (messagesPendingMastership != null) {
+            this.sendMsg(messagesPendingMastership);
+            log.debug("Sending {} pending messages to switch {}",
+                     messagesPendingMastership.size(), dpid);
+            messagesPendingMastership = null;
+        }
     }
 
     @Override
@@ -278,6 +291,11 @@ public abstract class AbstractOpenFlowSwitch extends AbstractHandlerBehaviour
                 log.debug("Sending role {} to switch {}", role, getStringId());
                 if (role == RoleState.SLAVE || role == RoleState.EQUAL) {
                     this.role = role;
+                } else {
+                    if (messagesPendingMastership == null) {
+                        log.debug("Initializing new queue for switch {}", dpid);
+                        messagesPendingMastership = new ArrayList<>();
+                    }
                 }
             } else {
                 this.role = role;
diff --git a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageEncoder.java b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageEncoder.java
index df7865d3..4c1b16fe 100644
--- a/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageEncoder.java
+++ b/framework/src/onos/openflow/ctl/src/main/java/org/onosproject/openflow/controller/impl/OFMessageEncoder.java
@@ -25,7 +25,6 @@ import org.jboss.netty.channel.ChannelHandlerContext;
 import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;
 import org.projectfloodlight.openflow.protocol.OFMessage;
 
-
 /**
  * Encode an openflow message for output into a ChannelBuffer, for use in a
  * netty pipeline.
@@ -50,7 +49,9 @@ public class OFMessageEncoder extends OneToOneEncoder {
         ChannelBuffer buf = ChannelBuffers.dynamicBuffer();
 
         for (OFMessage ofm : msglist) {
-            ofm.writeTo(buf);
+            if (ofm != null) {
+                ofm.writeTo(buf);
+            }
         }
         return buf;
     }
diff --git a/framework/src/onos/opt/onos/.empty b/framework/src/onos/opt/onos/.empty
deleted file mode 100644
index e69de29b..00000000
diff --git a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java
index 2575a256..3280ad34 100644
--- a/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java
+++ b/framework/src/onos/ovsdb/api/src/main/java/org/onosproject/ovsdb/controller/driver/DefaultOvsdbClient.java
@@ -175,14 +175,19 @@ public class DefaultOvsdbClient
      *
      * @param dbName    the ovsdb database name
      * @param tableName the ovsdb table name
-     * @return ovsRowStore, empty if row store is find
+     * @return ovsRowStore, empty store if no rows exist in the table
      */
     private OvsdbRowStore getRowStore(String dbName, String tableName) {
         OvsdbTableStore tableStore = getTableStore(dbName);
         if (tableStore == null) {
             return null;
         }
-        return tableStore.getRows(tableName);
+
+        OvsdbRowStore rowStore = tableStore.getRows(tableName);
+        if (rowStore == null) {
+            rowStore = new OvsdbRowStore();
+        }
+        return rowStore;
     }
 
     /**
diff --git a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
index 329df20b..a7e334f4 100644
--- a/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
+++ b/framework/src/onos/providers/openflow/device/src/main/java/org/onosproject/provider/of/device/impl/OpenFlowDeviceProvider.java
@@ -319,6 +319,9 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
             }
             DeviceId did = deviceId(uri(dpid));
             OpenFlowSwitch sw = controller.getSwitch(dpid);
+            if (sw == null) {
+                return;
+            }
 
             ChassisId cId = new ChassisId(dpid.value());
 
@@ -339,9 +342,14 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
             providerService.updatePorts(did, buildPortDescriptions(sw));
 
             PortStatsCollector psc =
-                    new PortStatsCollector(controller.getSwitch(dpid), portStatsPollFrequency);
+                    new PortStatsCollector(sw, portStatsPollFrequency);
             psc.start();
             collectors.put(dpid, psc);
+
+            //figure out race condition for collectors.remove() and collectors.put()
+            if (controller.getSwitch(dpid) == null) {
+                switchRemoved(dpid);
+            }
         }
 
         @Override
@@ -364,6 +372,9 @@ public class OpenFlowDeviceProvider extends AbstractProvider implements DevicePr
             }
             DeviceId did = deviceId(uri(dpid));
             OpenFlowSwitch sw = controller.getSwitch(dpid);
+            if (sw == null) {
+                return;
+            }
             providerService.updatePorts(did, buildPortDescriptions(sw));
         }
 
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
index cf918605..1039d049 100644
--- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowEntryBuilder.java
@@ -55,6 +55,7 @@ import org.projectfloodlight.openflow.protocol.action.OFActionCircuit;
 import org.projectfloodlight.openflow.protocol.action.OFActionExperimenter;
 import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetQueue;
 import org.projectfloodlight.openflow.protocol.action.OFActionPopMpls;
 import org.projectfloodlight.openflow.protocol.action.OFActionSetDlDst;
 import org.projectfloodlight.openflow.protocol.action.OFActionSetDlSrc;
@@ -333,6 +334,10 @@ public class FlowEntryBuilder {
                     OFActionGroup group = (OFActionGroup) act;
                     builder.group(new DefaultGroupId(group.getGroup().getGroupNumber()));
                     break;
+                case SET_QUEUE:
+                    OFActionSetQueue setQueue = (OFActionSetQueue) act;
+                    builder.setQueue(setQueue.getQueueId());
+                    break;
                 case STRIP_VLAN:
                 case POP_VLAN:
                     builder.popVlan();
@@ -350,7 +355,6 @@ public class FlowEntryBuilder {
                 case SET_NW_ECN:
                 case SET_NW_TOS:
                 case SET_NW_TTL:
-                case SET_QUEUE:
 
                 case ENQUEUE:
                 default:
diff --git a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
index cc265758..64b4360a 100644
--- a/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
+++ b/framework/src/onos/providers/openflow/flow/src/main/java/org/onosproject/provider/of/flow/impl/FlowModBuilderVer13.java
@@ -26,6 +26,7 @@ import org.onosproject.net.flow.instructions.Instruction;
 import org.onosproject.net.flow.instructions.Instructions;
 import org.onosproject.net.flow.instructions.Instructions.GroupInstruction;
 import org.onosproject.net.flow.instructions.Instructions.OutputInstruction;
+import org.onosproject.net.flow.instructions.Instructions.SetQueueInstruction;
 import org.onosproject.net.flow.instructions.L0ModificationInstruction;
 import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModLambdaInstruction;
 import org.onosproject.net.flow.instructions.L0ModificationInstruction.ModOchSignalInstruction;
@@ -50,6 +51,7 @@ import org.projectfloodlight.openflow.protocol.OFFlowModFlags;
 import org.projectfloodlight.openflow.protocol.action.OFAction;
 import org.projectfloodlight.openflow.protocol.action.OFActionGroup;
 import org.projectfloodlight.openflow.protocol.action.OFActionOutput;
+import org.projectfloodlight.openflow.protocol.action.OFActionSetQueue;
 import org.projectfloodlight.openflow.protocol.instruction.OFInstruction;
 import org.projectfloodlight.openflow.protocol.match.Match;
 import org.projectfloodlight.openflow.protocol.oxm.OFOxm;
@@ -244,6 +246,12 @@ public class FlowModBuilderVer13 extends FlowModBuilder {
                             .setGroup(OFGroup.of(group.groupId().id()));
                     actions.add(groupBuilder.build());
                     break;
+                case QUEUE:
+                    SetQueueInstruction queue = (SetQueueInstruction) i;
+                    OFActionSetQueue.Builder queueBuilder = factory().actions().buildSetQueue()
+                            .setQueueId(queue.queueId());
+                    actions.add(queueBuilder.build());
+                    break;
                 case TABLE:
                     //FIXME: should not occur here.
                     tableFound = true;
diff --git a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
index 78650fe6..8acf08ee 100644
--- a/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
+++ b/framework/src/onos/providers/openflow/group/src/main/java/org/onosproject/provider/of/group/impl/OpenFlowGroupProvider.java
@@ -334,12 +334,20 @@ public class OpenFlowGroupProvider extends AbstractProvider implements GroupProv
         @Override
         public void switchAdded(Dpid dpid) {
             OpenFlowSwitch sw = controller.getSwitch(dpid);
+            if (sw == null) {
+                return;
+            }
             if (isGroupSupported(sw)) {
                 GroupStatsCollector gsc = new GroupStatsCollector(
                         controller.getSwitch(dpid), POLL_INTERVAL);
                 gsc.start();
                 collectors.put(dpid, gsc);
             }
+
+            //figure out race condition
+            if (controller.getSwitch(dpid) == null) {
+                switchRemoved(dpid);
+            }
         }
 
         @Override
diff --git a/framework/src/onos/tools/package/config/samples/network-cfg.json b/framework/src/onos/tools/package/config/samples/network-cfg.json
index c2af8b81..92328479 100644
--- a/framework/src/onos/tools/package/config/samples/network-cfg.json
+++ b/framework/src/onos/tools/package/config/samples/network-cfg.json
@@ -28,8 +28,8 @@
                 "routerMac" : "00:00:00:00:01:80",
                 "isEdgeRouter" : true,
                 "adjacencySids" : [
-                    { "sid" : 100, "port" : [2, 3] },
-                    { "sid" : 200, "port" : [4, 5] }
+                    { "sid" : 100, "ports" : [2, 3] },
+                    { "sid" : 200, "ports" : [4, 5] }
                 ]
             }
     	},
diff --git a/framework/src/onos/tools/package/config/samples/segmentrouting_dell.conf b/framework/src/onos/tools/package/config/samples/segmentrouting_dell.conf
deleted file mode 100644
index be489a65..00000000
--- a/framework/src/onos/tools/package/config/samples/segmentrouting_dell.conf
+++ /dev/null
@@ -1,93 +0,0 @@
-{
-  "comment": " Multilayer topology description and configuration",
-  "restrictSwitches": true,
-  "restrictLinks": true,
-
-  "switchConfig":
-             [
-               { "nodeDpid" : "of:00010001e88b9368", "name": "Dell-R1", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.1/32",
-                             "routerMac": "00:01:e8:8b:93:6b",
-                             "nodeSid": 101,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 46, "subnetIp": "10.200.1.1/24" }
-                                         ]
-                             }
-                 },
-
-               { "nodeDpid": "of:00010001e88b939b", "name": "Dell-R2", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.2/32",
-                             "routerMac": "00:01:e8:8b:93:9e",
-                             "nodeSid": 102,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 46, "subnetIp": "10.200.2.1/24" }
-                                         ]
-                             }
-                 },
-
-               { "nodeDpid": "of:00010001e88b938c", "name": "Dell-R3", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.3/32",
-                             "routerMac": "00:01:e8:8b:93:8f",
-                             "nodeSid": 103,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 46, "subnetIp": "10.200.3.1/24" }
-                                         ]
-                             }
-                 },
-
-		{ "nodeDpid": "of:00010001e88b93ad", "name": "Dell-R4", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.4/32",
-                             "routerMac": "00:01:e8:8b:93:b0",
-                             "nodeSid": 104,
-                             "isEdgeRouter" : true,
-                             "subnets": [
-                                         { "portNo": 46, "subnetIp": "10.200.4.1/24" }
-                                         ]
-                             }
-                 },
-
-		{ "nodeDpid": "of:00010001e88b93bc", "name": "Dell-R5", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.5/32",
-                             "routerMac": "00:01:e8:8b:93:bf",
-                             "nodeSid": 105,
-                             "isEdgeRouter" : false
-                             }
-                 },
-
-		{ "nodeDpid": "of:00010001e88b93c2", "name": "Dell-R6", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.6/32",
-                             "routerMac": "00:01:e8:8b:93:c5",
-                             "nodeSid": 106,
-                             "isEdgeRouter" : false
-                             }
-                 },
-
-                 { "nodeDpid": "of:00010001e88b9398", "name": "Dell-R7", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.7/32",
-                             "routerMac": "00:01:e8:8b:93:9b",
-                             "nodeSid": 107,
-                             "isEdgeRouter": false
-                             }
-                 },
-
-                 { "nodeDpid": "of:00010001e88b27e3", "name": "Dell-R8", "type": "Router_SR", "allowed": true,
-                 "latitude": 80.80, "longitude": 90.10,
-                 "params": { "routerIp": "192.168.0.8/32",
-                             "routerMac": "00:01:e8:8b:27:e6",
-                             "nodeSid": 108,
-                             "isEdgeRouter": false
-                             }
-                 }
-
-               ]
-}
diff --git a/framework/src/onos/tools/test/scenarios/sequential-example.xml b/framework/src/onos/tools/test/scenarios/sequential-example.xml
new file mode 100644
index 00000000..eee32d30
--- /dev/null
+++ b/framework/src/onos/tools/test/scenarios/sequential-example.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright 2015 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.
+  -->
+<scenario name="example" description="sequential scenario example">
+    <group name="Wrapup">
+        <!-- 'starts' is a comma-separated list of patterns that name steps starting the current iteration of the sequence -->
+        <!-- 'ends' is a comma-separated list of patterns that name steps ending the previous iteration of the sequence -->
+        <!-- In this example each Final-Check-Logs-(N) will become dependent on Fetch-Logs-(N-1), for N > 1 -->
+        <sequential var="${OC#}" starts="Final-Check-Logs-${#}" ends="Fetch-Logs-${#-1}">
+            <step name="Final-Check-Logs-${#}" exec="onos-check-logs ${OC#}"/>
+            <step name="Fetch-Logs-${#}" exec="onos-fetch-logs ${OC#}"
+                  cwd="${WORKSPACE}/tmp/stc" requires="~^"/>
+        </sequential>
+    </group>
+</scenario>
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java
index 561c9307..b3b79e2c 100644
--- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/EthType.java
@@ -45,7 +45,7 @@ public class EthType {
         private final Deserializer<?> deserializer;
 
         /**
-         * Constucts a new ethertype.
+         * Constructs a new ethertype.
          *
          * @param ethType The actual ethertype
          * @param type it's textual representation
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
index 89cddbae..cb7f2013 100644
--- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/MacAddress.java
@@ -22,6 +22,7 @@ import java.util.Arrays;
  */
 public class MacAddress {
 
+    public static final MacAddress NONE = valueOf("a4:23:05:00:00:00");
     public static final MacAddress ZERO = valueOf("00:00:00:00:00:00");
     public static final MacAddress BROADCAST = valueOf("ff:ff:ff:ff:ff:ff");
 
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PIM.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PIM.java
index d9a5e83f..e6ef0aed 100755
--- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PIM.java
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/PIM.java
@@ -43,6 +43,9 @@ public class PIM extends BasePacket {
 
     public static final int PIM_HEADER_LEN = 4;
 
+    public static final byte ADDRESS_FAMILY_IP4 = 0x1;
+    public static final byte ADDRESS_FAMILY_IP6 = 0x2;
+
     public static final Map<Byte, Deserializer<? extends IPacket>> PROTOCOL_DESERIALIZER_MAP =
             new HashMap<>();
 
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java
index be4ab19a..0db82afb 100644
--- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrGroup.java
@@ -17,9 +17,11 @@ package org.onlab.packet.pim;
 
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
-import org.onlab.packet.Ip6Address;
+import org.onlab.packet.PIM;
+
 
 import java.nio.ByteBuffer;
 
@@ -41,7 +43,7 @@ public class PIMAddrGroup {
      * PIM Encoded Group Address.
      */
     public PIMAddrGroup() {
-        this.family = 4;
+        this.family = PIM.ADDRESS_FAMILY_IP4;
         this.encType = 0;
         this.reserved = 0;
         this.bBit = false;
@@ -83,7 +85,7 @@ public class PIMAddrGroup {
     public void setAddr(IpPrefix pfx) {
         this.addr = pfx.address();
         this.masklen = (byte) pfx.prefixLength();
-        this.family = (byte) ((this.addr.isIp4()) ? 4 : 6);
+        this.family = (byte) ((this.addr.isIp4()) ? PIM.ADDRESS_FAMILY_IP4 : PIM.ADDRESS_FAMILY_IP6);
     }
 
     /**
@@ -181,9 +183,9 @@ public class PIMAddrGroup {
         checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_GROUP_IPV4_BYTE_LENGTH);
 
         this.family = bb.get();
-        if (family != 4 && family != 6) {
+        if (family != PIM.ADDRESS_FAMILY_IP4 && family != PIM.ADDRESS_FAMILY_IP6) {
             throw new DeserializationException("Illegal IP version number: " + family + "\n");
-        } else if (family == 6) {
+        } else if (family == PIM.ADDRESS_FAMILY_IP6) {
 
             // Check for one less by since we have already read the first byte of the packet.
             checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_GROUP_IPV6_BYTE_LENGTH - 1);
@@ -201,9 +203,9 @@ public class PIMAddrGroup {
         this.reserved |= 0x7d;
 
         this.masklen = bb.get();
-        if (this.family == 4) {
+        if (this.family == PIM.ADDRESS_FAMILY_IP4) {
             this.addr = IpAddress.valueOf(bb.getInt());
-        } else if (this.family == 6) {
+        } else if (this.family == PIM.ADDRESS_FAMILY_IP6) {
             this.addr = Ip6Address.valueOf(bb.array(), 2);
         }
         return this;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java
index 21526408..762dad56 100644
--- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrSource.java
@@ -17,9 +17,10 @@ package org.onlab.packet.pim;
 
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.Ip6Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
-import org.onlab.packet.Ip6Address;
+import org.onlab.packet.PIM;
 
 import java.nio.ByteBuffer;
 
@@ -66,7 +67,7 @@ public class PIMAddrSource {
     }
 
     private void init() {
-        this.family = 4;
+        this.family = PIM.ADDRESS_FAMILY_IP4;
         this.encType = 0;
         this.reserved = 0;
         this.sBit = true;
@@ -92,7 +93,7 @@ public class PIMAddrSource {
     public void setAddr(IpPrefix spfx) {
         this.addr = spfx.address();
         this.masklen = (byte) spfx.prefixLength();
-        this.family = (byte) ((this.addr.isIp4()) ? 4 : 6);
+        this.family = (byte) ((this.addr.isIp4()) ? PIM.ADDRESS_FAMILY_IP4 : PIM.ADDRESS_FAMILY_IP6);
     }
 
     /**
@@ -156,7 +157,7 @@ public class PIMAddrSource {
      */
     public int getByteSize() {
         int size = 4;
-        size += addr.isIp4() ? 4 : 16;
+        size += addr.isIp4() ? PIM.ADDRESS_FAMILY_IP4 : PIM.ADDRESS_FAMILY_IP6;
         return size;
     }
 
@@ -202,9 +203,9 @@ public class PIMAddrSource {
         checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_SOURCE_IPV4_BYTE_LENGTH);
 
         this.family = bb.get();
-        if (family != 4 && family != 6) {
+        if (family != PIM.ADDRESS_FAMILY_IP4 && family != PIM.ADDRESS_FAMILY_IP6) {
             throw new DeserializationException("Illegal IP version number: " + family + "\n");
-        } else if (family == 6) {
+        } else if (family == PIM.ADDRESS_FAMILY_IP6) {
 
             // Check for one less by since we have already read the first byte of the packet.
             checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_SOURCE_IPV6_BYTE_LENGTH - 1);
@@ -226,9 +227,9 @@ public class PIMAddrSource {
         this.reserved &= 0xf8;
 
         this.masklen = bb.get();
-        if (this.family == 4) {
+        if (this.family == PIM.ADDRESS_FAMILY_IP4) {
             this.addr = IpAddress.valueOf(bb.getInt());
-        } else if (this.family == 6) {
+        } else if (this.family == PIM.ADDRESS_FAMILY_IP6) {
             this.addr = Ip6Address.valueOf(bb.array(), 2);
         }
         return this;
diff --git a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java
index a6ba3895..3327a8e7 100644
--- a/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java
+++ b/framework/src/onos/utils/misc/src/main/java/org/onlab/packet/pim/PIMAddrUnicast.java
@@ -15,10 +15,13 @@
  */
 package org.onlab.packet.pim;
 
+
+
 import org.onlab.packet.DeserializationException;
 import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
 import org.onlab.packet.Ip6Address;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.PIM;
 
 import java.nio.ByteBuffer;
 
@@ -36,7 +39,7 @@ public class PIMAddrUnicast {
      * PIM Encoded Source Address.
      */
     public PIMAddrUnicast() {
-        this.family = 4;
+        this.family = PIM.ADDRESS_FAMILY_IP4;
         this.encType = 0;
     }
 
@@ -48,9 +51,9 @@ public class PIMAddrUnicast {
     public PIMAddrUnicast(String addr) {
         this.addr = IpAddress.valueOf(addr);
         if (this.addr.isIp4()) {
-            this.family = 4;
+            this.family = PIM.ADDRESS_FAMILY_IP4;
         } else {
-            this.family = 6;
+            this.family = PIM.ADDRESS_FAMILY_IP6;
         }
         this.encType = 0;
     }
@@ -63,9 +66,9 @@ public class PIMAddrUnicast {
     public void setAddr(IpAddress addr) {
         this.addr = addr;
         if (this.addr.isIp4()) {
-            this.family = 4;
+            this.family = PIM.ADDRESS_FAMILY_IP4;
         } else {
-            this.family = 6;
+            this.family = PIM.ADDRESS_FAMILY_IP6;
         }
     }
 
@@ -121,17 +124,17 @@ public class PIMAddrUnicast {
         this.family = bb.get();
 
         // If we have IPv6 we need to ensure we have adequate buffer space.
-        if (this.family != 4 && this.family != 6) {
+        if (this.family != PIM.ADDRESS_FAMILY_IP4 && this.family != PIM.ADDRESS_FAMILY_IP6) {
             throw new DeserializationException("Invalid address family: " + this.family);
-        } else if (this.family == 6) {
+        } else if (this.family == PIM.ADDRESS_FAMILY_IP6) {
             // Subtract -1 from ENC_UNICAST_IPv6 BYTE_LENGTH because we read one byte for family previously.
             checkInput(bb.array(), bb.position(), bb.limit() - bb.position(), ENC_UNICAST_IPV6_BYTE_LENGTH - 1);
         }
 
         this.encType = bb.get();
-        if (this.family == 4) {
+        if (this.family == PIM.ADDRESS_FAMILY_IP4) {
             this.addr = IpAddress.valueOf(bb.getInt());
-        } else if (this.family == 6) {
+        } else if (this.family == PIM.ADDRESS_FAMILY_IP6) {
             this.addr = Ip6Address.valueOf(bb.array(), 2);
         }
         return this;
diff --git a/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java b/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java
index add71eb5..919cbd5b 100644
--- a/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java
+++ b/framework/src/onos/utils/stc/src/main/java/org/onlab/stc/Compiler.java
@@ -48,6 +48,7 @@ public class Compiler {
     private static final String GROUP = "group";
     private static final String STEP = "step";
     private static final String PARALLEL = "parallel";
+    private static final String SEQUENTIAL = "sequential";
     private static final String DEPENDENCY = "dependency";
 
     private static final String LOG_DIR = "[@logDir]";
@@ -59,12 +60,16 @@ public class Compiler {
     private static final String IF = "[@if]";
     private static final String UNLESS = "[@unless]";
     private static final String VAR = "[@var]";
+    private static final String STARTS = "[@starts]";
+    private static final String ENDS = "[@ends]";
     private static final String FILE = "[@file]";
     private static final String NAMESPACE = "[@namespace]";
 
     static final String PROP_START = "${";
     static final String PROP_END = "}";
+
     private static final String HASH = "#";
+    private static final String HASH_PREV = "#-1";
 
     private final Scenario scenario;
 
@@ -72,7 +77,7 @@ public class Compiler {
     private final Map<String, Step> inactiveSteps = Maps.newHashMap();
     private final Map<String, String> requirements = Maps.newHashMap();
     private final Set<Dependency> dependencies = Sets.newHashSet();
-    private final List<Integer> parallels = Lists.newArrayList();
+    private final List<Integer> clonables = Lists.newArrayList();
 
     private ProcessFlow processFlow;
     private File logDir;
@@ -175,6 +180,10 @@ public class Compiler {
         cfg.configurationsAt(PARALLEL)
                 .forEach(c -> processParallelGroup(c, namespace, parentGroup));
 
+        // Scan all sequential groups
+        cfg.configurationsAt(SEQUENTIAL)
+                .forEach(c -> processSequentialGroup(c, namespace, parentGroup));
+
         // Scan all dependencies
         cfg.configurationsAt(DEPENDENCY)
                 .forEach(c -> processDependency(c, namespace));
@@ -309,13 +318,61 @@ public class Compiler {
 
         int i = 1;
         while (condition(var, i).length() > 0) {
-            parallels.add(0, i);
+            clonables.add(0, i);
+            compile(cfg, namespace, parentGroup);
+            clonables.remove(0);
+            i++;
+        }
+    }
+
+    /**
+     * Processes a sequential clone group directive.
+     *
+     * @param cfg         hierarchical definition
+     * @param namespace   optional namespace
+     * @param parentGroup optional parent group
+     */
+    private void processSequentialGroup(HierarchicalConfiguration cfg,
+                                        String namespace, Group parentGroup) {
+        String var = cfg.getString(VAR);
+        String starts = cfg.getString(STARTS);
+        String ends = cfg.getString(ENDS);
+        print("sequential var=%s", var);
+
+        int i = 1;
+        while (condition(var, i).length() > 0) {
+            clonables.add(0, i);
             compile(cfg, namespace, parentGroup);
-            parallels.remove(0);
+            if (i > 1) {
+                processSequentialRequirements(starts, ends, namespace);
+            }
+            clonables.remove(0);
             i++;
         }
     }
 
+    /**
+     * Hooks starts of this sequence tier to the previous tier.
+     *
+     * @param starts    comma-separated list of start steps
+     * @param ends      comma-separated list of end steps
+     * @param namespace optional namespace
+     */
+    private void processSequentialRequirements(String starts, String ends,
+                                               String namespace) {
+        for (String s : split(starts)) {
+            String start = expand(prefix(s, namespace));
+            String reqs = requirements.get(s);
+            for (String n : split(ends)) {
+                boolean isSoft = n.startsWith("~");
+                String name = n.replaceFirst("^~", "");
+                name = (isSoft ? "~" : "") + expand(prefix(name, namespace));
+                reqs = reqs == null ? name : (reqs + "," + name);
+            }
+            requirements.put(start, reqs);
+        }
+    }
+
     /**
      * Returns the elaborated repetition construct conditional.
      *
@@ -413,9 +470,11 @@ public class Compiler {
             String prop = pString.substring(start + PROP_START.length(), end);
             String value;
             if (prop.equals(HASH)) {
-                value = parallels.get(0).toString();
+                value = Integer.toString(clonables.get(0));
+            } else if (prop.equals(HASH_PREV)) {
+                value = Integer.toString(clonables.get(0) - 1);
             } else if (prop.endsWith(HASH)) {
-                pString = pString.replaceFirst("#}", parallels.get(0).toString() + "}");
+                pString = pString.replaceFirst("#}", clonables.get(0) + "}");
                 last = start;
                 continue;
             } else {
diff --git a/framework/src/onos/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java b/framework/src/onos/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java
index d70eff08..59b55307 100644
--- a/framework/src/onos/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java
+++ b/framework/src/onos/utils/stc/src/test/java/org/onlab/stc/CompilerTest.java
@@ -69,8 +69,8 @@ public class CompilerTest {
         ProcessFlow flow = compiler.processFlow();
 
         assertSame("incorrect scenario", scenario, compiler.scenario());
-        assertEquals("incorrect step count", 24, flow.getVertexes().size());
-        assertEquals("incorrect dependency count", 16, flow.getEdges().size());
+        assertEquals("incorrect step count", 33, flow.getVertexes().size());
+        assertEquals("incorrect dependency count", 26, flow.getEdges().size());
         assertEquals("incorrect logDir",
                      new File(TEST_DIR.getAbsolutePath(), "foo"), compiler.logDir());
 
diff --git a/framework/src/onos/utils/stc/src/test/resources/org/onlab/stc/scenario.xml b/framework/src/onos/utils/stc/src/test/resources/org/onlab/stc/scenario.xml
index 34e67fd5..533b11ea 100644
--- a/framework/src/onos/utils/stc/src/test/resources/org/onlab/stc/scenario.xml
+++ b/framework/src/onos/utils/stc/src/test/resources/org/onlab/stc/scenario.xml
@@ -44,4 +44,11 @@
         <step name="ding-${#}" exec="asdads" requires="ping-${#},pong-${#}"/>
         <dependency name="maybe" requires="ding-${#}"/>
     </parallel>
+
+    <sequential var="${TOC#}" requires="alpha" starts="fifi-${#},gigi-${#}" ends="gaga-${#-1}">
+        <step name="fifi-${#}" exec="asdads ${TOC#}"/>
+        <step name="gigi-${#}" exec="asdads"/>
+        <step name="gaga-${#}" exec="asdads" requires="fifi-${#},gigi-${#}"/>
+        <dependency name="maybe" requires="gaga-${#}"/>
+    </sequential>
 </scenario>
\ No newline at end of file
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js
index 28f262a1..d8dce362 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/fw/svg/glyph.js
@@ -49,6 +49,8 @@
     //          otn, roadm_otn, firewall, balancer, ips, ids,
     //          controller, virtual, fiber_switch, other
 
+    // NOTE: when adding glyphs, please also update TopoConstants.Glyphs class.
+
         glyphDataSet = {
             _viewbox: "0 0 110 110",
 
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoD3.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoD3.js
index d29748b1..1f061dd6 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoD3.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoD3.js
@@ -218,6 +218,24 @@
             .attr('transform', sus.translate(dx, dy));
     }
 
+    function updateDeviceBadge(d) {
+        // TODO: Fix this WIP
+        var node = d.el,
+            bsel;
+
+        if (d.badge) {
+            bsel = node.append('g')
+                .classed('badge', true)
+                .attr('transform', sus.translate(-14, -14));
+
+            bsel.append('circle')
+                .attr('r', 14);
+            bsel.append('text')
+                .attr('transform', sus.translate(-5, 3))
+                .text('42');
+        }
+    }
+
     function updateHostLabel(d) {
         var label = trimLabel(hostLabel(d));
         d.el.select('text').text(label);
@@ -241,6 +259,7 @@
         var node = d.el;
         node.classed('online', d.online);
         updateDeviceLabel(d);
+        updateDeviceBadge(d);
         api.posNode(d, true);
     }
 
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoForce.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoForce.js
index dbe8f9f5..f00b87fa 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoForce.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoForce.js
@@ -860,6 +860,16 @@
         });
     }
 
+    function clearNodeDeco() {
+        node.selectAll('g.badge').remove();
+    }
+
+    function removeNodeBadges() {
+        network.nodes.forEach(function (d) {
+            d.badge = null;
+        });
+    }
+
     function updateLinkLabelModel() {
         // create the backing data for showing labels..
         var data = [];
@@ -923,6 +933,8 @@
 
     function mkOverlayApi() {
         return {
+            clearNodeDeco: clearNodeDeco,
+            removeNodeBadges: removeNodeBadges,
             clearLinkTrafficStyle: clearLinkTrafficStyle,
             removeLinkLabels: removeLinkLabels,
             findLinkById: tms.findLinkById,
diff --git a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
index 74fa2f24..9a3b4358 100644
--- a/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
+++ b/framework/src/onos/web/gui/src/main/webapp/app/view/topo/topoOverlay.js
@@ -294,7 +294,8 @@
              unsupLink( key, [less] )
          */
 
-        // TODO: clear node highlighting
+        api.clearNodeDeco();
+        api.removeNodeBadges();
         api.clearLinkTrafficStyle();
         api.removeLinkLabels();
 
@@ -319,8 +320,11 @@
         });
 
         data.devices.forEach(function (device) {
-            var ddata = api.findNodeById(device.id);
+            var ddata = api.findNodeById(device.id),
+                badgeData = device.badge || null;
+
             if (ddata && !ddata.el.empty()) {
+                ddata.badge = badgeData;
                 if (!device.subdue) {
                     api.unsupNode(ddata.id, less);
                 }
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_1_addInstance.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_1_addInstance.json
new file mode 100644
index 00000000..cdb95361
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_1_addInstance.json
@@ -0,0 +1,14 @@
+{
+  "event": "addInstance",
+  "payload": {
+    "id": "ONOS",
+    "ip": "192.168.56.101",
+    "online": true,
+    "uiAttached": true,
+    "switches": 2,
+    "labels": [
+      "ONOS",
+      "192.168.56.101"
+    ]
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_2_addDevice_s1.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_2_addDevice_s1.json
new file mode 100644
index 00000000..2e9d30f2
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_2_addDevice_s1.json
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000001",
+    "type": "switch",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "switch-1",
+      "of:0000000000000001"
+    ],
+    "metaUi": {
+      "x": 200,
+      "y": 200
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_3_addDevice_s2.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_3_addDevice_s2.json
new file mode 100644
index 00000000..79c04d3a
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_3_addDevice_s2.json
@@ -0,0 +1,18 @@
+{
+  "event": "addDevice",
+  "payload": {
+    "id": "of:0000000000000002",
+    "type": "switch",
+    "online": true,
+    "master": "ONOS",
+    "labels": [
+      "",
+      "switch-2",
+      "of:0000000000000002"
+    ],
+    "metaUi": {
+      "x": 400,
+      "y": 220
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_4_addLink_1_2.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_4_addLink_1_2.json
new file mode 100644
index 00000000..fb952837
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_4_addLink_1_2.json
@@ -0,0 +1,16 @@
+{
+  "event": "addLink",
+  "payload": {
+    "id": "of:0000000000000001/5-of:0000000000000002/7",
+    "type": "direct",
+    "online": true,
+    "linkWidth": 2,
+    "src": "of:0000000000000001",
+    "srcPort": "5",
+    "dst": "of:0000000000000002",
+    "dstPort": "7",
+    "props" : {
+      "BW": "70 Gb"
+    }
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_5_showHighlights_clear.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_5_showHighlights_clear.json
new file mode 100644
index 00000000..615efd25
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_5_showHighlights_clear.json
@@ -0,0 +1,8 @@
+{
+  "event": "showHighlights",
+  "payload": {
+    "devices": [],
+    "hosts": [],
+    "links": []
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_6_showHighlights_stuff.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_6_showHighlights_stuff.json
new file mode 100644
index 00000000..74c42c5c
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_6_showHighlights_stuff.json
@@ -0,0 +1,30 @@
+{
+  "event": "showHighlights",
+  "payload": {
+    "devices": [
+      {
+        "id": "of:0000000000000001",
+        "badge": {
+          "status": "e",
+          "gid": "xMark",
+          "msg": "x marks the spot"
+        }
+      },
+      {
+        "id": "of:0000000000000002",
+        "badge": {
+          "status": "w",
+          "txt": "7"
+        }
+      }
+    ],
+    "hosts": [],
+    "links": [
+      {
+        "css": "primary",
+        "id": "of:0000000000000001/5-of:0000000000000002/7",
+        "label": "Antz!"
+      }
+    ]
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_7_showHighlights_clear.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_7_showHighlights_clear.json
new file mode 100644
index 00000000..615efd25
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/ev_7_showHighlights_clear.json
@@ -0,0 +1,8 @@
+{
+  "event": "showHighlights",
+  "payload": {
+    "devices": [],
+    "hosts": [],
+    "links": []
+  }
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/badges/scenario.json b/framework/src/onos/web/gui/src/test/_karma/ev/badges/scenario.json
new file mode 100644
index 00000000..0ca4f4f4
--- /dev/null
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/badges/scenario.json
@@ -0,0 +1,12 @@
+{
+  "comments": [
+    "Demo of adding badges to devices"
+  ],
+  "title": "Demo adding badges",
+  "params": {
+    "lastAuto": 5
+  },
+  "description": [
+    "Demonstrate the device badging feature."
+  ]
+}
diff --git a/framework/src/onos/web/gui/src/test/_karma/ev/traffic/scenario.json b/framework/src/onos/web/gui/src/test/_karma/ev/traffic/scenario.json
index 57c03733..4e7ce4f8 100644
--- a/framework/src/onos/web/gui/src/test/_karma/ev/traffic/scenario.json
+++ b/framework/src/onos/web/gui/src/test/_karma/ev/traffic/scenario.json
@@ -1,6 +1,8 @@
 {
   "comments": [
-    "Stepping through showTraffic"
+    "Stepping through showTraffic",
+    "NOTE: showTraffic event is deprecated",
+    "      This needs to be re-worked to use showHighlights"
   ],
   "title": "Show Traffic Scenario",
   "params": {
-- 
cgit