Rename bundles

app -> impl
oar -> app
in order to be more consistent with other apps

Change-Id: Ic8c12bb7267d116bd58d09647ef4cca0c53ee272
diff --git a/app/app.xml b/app/app.xml
new file mode 100644
index 0000000..2395202
--- /dev/null
+++ b/app/app.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Copyright 2015-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<app name="org.onosproject.segmentrouting" origin="Open Networking Foundation"
+     version="${project.version}"
+     category="Traffic Steering" url="http://trellisfabric.org"
+     title="Trellis Control App"
+     featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
+     features="${project.artifactId}" apps="org.onosproject.mcast,org.onosproject.route-service,org.onosproject.portloadbalancer">
+    <description>${project.description}</description>
+    <artifact>mvn:${project.groupId}/segmentrouting-app/${project.version}</artifact>
+    <artifact>mvn:${project.groupId}/segmentrouting-web/${project.version}</artifact>
+</app>
\ No newline at end of file
diff --git a/app/features.xml b/app/features.xml
new file mode 100644
index 0000000..bdfa219
--- /dev/null
+++ b/app/features.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+  ~ Copyright 2015-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.2.0" name="${project.artifactId}-${project.version}">
+    <feature name="${project.artifactId}" version="${project.version}"
+             description="${project.description}">
+        <feature>onos-api</feature>
+        <bundle>mvn:${project.groupId}/segmentrouting-app/${project.version}</bundle>
+        <bundle>mvn:${project.groupId}/segmentrouting-web/${project.version}</bundle>
+    </feature>
+</features>
\ No newline at end of file
diff --git a/app/pom.xml b/app/pom.xml
index c43447e..eaea0db 100644
--- a/app/pom.xml
+++ b/app/pom.xml
@@ -29,115 +29,7 @@
     <artifactId>segmentrouting-app</artifactId>
     <packaging>bundle</packaging>
     <url>http://trellisfabric.org</url>
-    <description>Trellis control app</description>
-
-    <dependencies>
-        <!-- ONOS core -->
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-api</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-core-net</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-core-serializers</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-
-        <!-- Other Trellis apps -->
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>segmentrouting-api</artifactId>
-            <version>${project.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-apps-portloadbalancer</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-apps-route-service-api</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-apps-mcast-api</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-apps-mcast-cli</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-
-        <!-- Tests -->
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-api</artifactId>
-            <version>${onos.version}</version>
-            <classifier>tests</classifier>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-core-common</artifactId>
-            <version>${onos.version}</version>
-            <classifier>tests</classifier>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>segmentrouting-api</artifactId>
-            <version>${project.version}</version>
-            <classifier>tests</classifier>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-apps-route-service-api</artifactId>
-            <version>${onos.version}</version>
-            <classifier>tests</classifier>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onlab-junit</artifactId>
-            <version>${onos.version}</version>
-            <scope>test</scope>
-        </dependency>
-
-        <!-- CLI -->
-        <dependency>
-            <groupId>org.apache.karaf.shell</groupId>
-            <artifactId>org.apache.karaf.shell.console</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.karaf.shell</groupId>
-            <artifactId>org.apache.karaf.shell.core</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.onosproject</groupId>
-            <artifactId>onos-cli</artifactId>
-            <version>${onos.version}</version>
-            <scope>provided</scope>
-        </dependency>
-    </dependencies>
+    <description>Trellis control OAR wrapper</description>
 
     <build>
         <plugins>
@@ -145,16 +37,6 @@
                 <groupId>org.onosproject</groupId>
                 <artifactId>onos-maven-plugin</artifactId>
             </plugin>
-            <plugin>
-                <groupId>org.apache.felix</groupId>
-                <artifactId>maven-bundle-plugin</artifactId>
-                <extensions>true</extensions>
-                <configuration>
-                    <instructions>
-                        <Karaf-Commands>org.onosproject.segmentrouting.cli</Karaf-Commands>
-                    </instructions>
-                </configuration>
-            </plugin>
         </plugins>
     </build>
 </project>
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/AppConfigHandler.java b/app/src/main/java/org/onosproject/segmentrouting/AppConfigHandler.java
deleted file mode 100644
index 38d8aac..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/AppConfigHandler.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.config.NetworkConfigEvent;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.criteria.Criteria;
-import org.onosproject.net.flowobjective.DefaultFilteringObjective;
-import org.onosproject.net.flowobjective.DefaultObjectiveContext;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Handles Segment Routing app config events.
- */
-public class AppConfigHandler {
-    private static final Logger log = LoggerFactory.getLogger(AppConfigHandler.class);
-    private final SegmentRoutingManager srManager;
-    private final DeviceService deviceService;
-
-    /**
-     * Constructs Segment Routing App Config Handler.
-     *
-     * @param srManager instance of {@link SegmentRoutingManager}
-     */
-    public AppConfigHandler(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        this.deviceService = srManager.deviceService;
-    }
-
-    /**
-     * Processes Segment Routing App Config added event.
-     *
-     * @param event network config added event
-     */
-    protected void processAppConfigAdded(NetworkConfigEvent event) {
-        log.info("Processing AppConfig CONFIG_ADDED");
-        SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
-        deviceService.getAvailableDevices().forEach(device -> {
-            populateVRouter(device.id(), getMacAddresses(config));
-            config.blackholeIPs().forEach(ipPrefix -> {
-                srManager.routingRulePopulator.populateDefaultRouteBlackhole(device.id(), ipPrefix);
-            });
-        });
-    }
-
-    /**
-     * Processes Segment Routing App Config updated event.
-     *
-     * @param event network config updated event
-     */
-    protected void processAppConfigUpdated(NetworkConfigEvent event) {
-        log.info("Processing AppConfig CONFIG_UPDATED");
-        SegmentRoutingAppConfig config = (SegmentRoutingAppConfig) event.config().get();
-        SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
-        deviceService.getAvailableDevices().forEach(device -> {
-            Set<MacAddress> macAddresses = new HashSet<>(getMacAddresses(config));
-            Set<MacAddress> prevMacAddresses = new HashSet<>(getMacAddresses(prevConfig));
-            // Avoid removing and re-adding unchanged MAC addresses since
-            // FlowObjective does not guarantee the execution order.
-            Set<MacAddress> sameMacAddresses = new HashSet<>(macAddresses);
-            sameMacAddresses.retainAll(prevMacAddresses);
-            macAddresses.removeAll(sameMacAddresses);
-            prevMacAddresses.removeAll(sameMacAddresses);
-
-            revokeVRouter(device.id(), prevMacAddresses);
-            populateVRouter(device.id(), macAddresses);
-            Set<IpPrefix> toRemove = Sets.difference(prevConfig.blackholeIPs(), config.blackholeIPs());
-            toRemove.forEach(ipPrefix -> {
-                srManager.routingRulePopulator.removeDefaultRouteBlackhole(device.id(), ipPrefix);
-            });
-            Set<IpPrefix> toAdd = Sets.difference(config.blackholeIPs(), prevConfig.blackholeIPs());
-            toAdd.forEach(ipPrefix -> {
-                srManager.routingRulePopulator.populateDefaultRouteBlackhole(device.id(), ipPrefix);
-            });
-        });
-
-    }
-
-    /**
-     * Processes Segment Routing App Config removed event.
-     *
-     * @param event network config removed event
-     */
-    protected void processAppConfigRemoved(NetworkConfigEvent event) {
-        log.info("Processing AppConfig CONFIG_REMOVED");
-        SegmentRoutingAppConfig prevConfig = (SegmentRoutingAppConfig) event.prevConfig().get();
-        deviceService.getAvailableDevices().forEach(device -> {
-            revokeVRouter(device.id(), getMacAddresses(prevConfig));
-            prevConfig.blackholeIPs().forEach(ipPrefix -> {
-                srManager.routingRulePopulator.removeDefaultRouteBlackhole(device.id(), ipPrefix);
-            });
-        });
-    }
-
-    /**
-     * Populates initial vRouter and blackhole rules.
-     *
-     * @param deviceId device ID
-     */
-    public void init(DeviceId deviceId) {
-        SegmentRoutingAppConfig config =
-                srManager.cfgService.getConfig(srManager.appId, SegmentRoutingAppConfig.class);
-        populateVRouter(deviceId, getMacAddresses(config));
-        if (config != null) {
-            config.blackholeIPs().forEach(ipPrefix -> {
-                srManager.routingRulePopulator.populateDefaultRouteBlackhole(deviceId, ipPrefix);
-            });
-        }
-    }
-
-    private void populateVRouter(DeviceId deviceId, Set<MacAddress> pendingAdd) {
-        if (!isEdge(deviceId)) {
-            return;
-        }
-        getVRouterFlowObjBuilders(pendingAdd).forEach(foBuilder -> {
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("vRouterMac filter for {} populated", pendingAdd),
-                    (objective, error) ->
-                            log.warn("Failed to populate vRouterMac filter for {}: {}", pendingAdd, error));
-            srManager.flowObjectiveService.filter(deviceId, foBuilder.add(context));
-        });
-    }
-
-    private void revokeVRouter(DeviceId deviceId, Set<MacAddress> pendingRemove) {
-        if (!isEdge(deviceId)) {
-            return;
-        }
-        getVRouterFlowObjBuilders(pendingRemove).forEach(foBuilder -> {
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("vRouterMac filter for {} revoked", pendingRemove),
-                    (objective, error) ->
-                            log.warn("Failed to revoke vRouterMac filter for {}: {}", pendingRemove, error));
-            srManager.flowObjectiveService.filter(deviceId, foBuilder.remove(context));
-        });
-    }
-
-    private Set<FilteringObjective.Builder> getVRouterFlowObjBuilders(Set<MacAddress> macAddresses) {
-        ImmutableSet.Builder<FilteringObjective.Builder> setBuilder = ImmutableSet.builder();
-        macAddresses.forEach(macAddress -> {
-            FilteringObjective.Builder fobuilder = DefaultFilteringObjective.builder();
-            fobuilder.withKey(Criteria.matchInPort(PortNumber.ANY))
-                    .addCondition(Criteria.matchEthDst(macAddress))
-                    .permit()
-                    .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
-                    .fromApp(srManager.appId);
-            setBuilder.add(fobuilder);
-        });
-        return setBuilder.build();
-    }
-
-    private Set<MacAddress> getMacAddresses(SegmentRoutingAppConfig config) {
-        if (config == null) {
-            return ImmutableSet.of();
-        }
-        return ImmutableSet.copyOf(config.vRouterMacs());
-    }
-
-    private boolean isEdge(DeviceId deviceId) {
-        try {
-            if (srManager.deviceConfiguration.isEdgeDevice(deviceId)) {
-                return true;
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn("Device configuration for {} is not present.", deviceId);
-        }
-        return false;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/ArpHandler.java b/app/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
deleted file mode 100644
index cfef0f1..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/ArpHandler.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.ARP;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.neighbour.NeighbourMessageContext;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.host.HostService;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static org.onosproject.net.neighbour.NeighbourMessageType.REQUEST;
-
-/**
- * Handler of ARP packets that responses or forwards ARP packets that
- * are sent to the controller.
- */
-public class ArpHandler extends SegmentRoutingNeighbourHandler {
-
-    private static Logger log = LoggerFactory.getLogger(ArpHandler.class);
-
-    /**
-     * Creates an ArpHandler object.
-     *
-     * @param srManager SegmentRoutingManager object
-     */
-    public ArpHandler(SegmentRoutingManager srManager) {
-        super(srManager);
-    }
-
-    /**
-     * Processes incoming ARP packets.
-     *
-     * If it is an ARP request to router itself or known hosts,
-     * then it sends ARP response.
-     * If it is an ARP request to unknown hosts in its own subnet,
-     * then it flood the ARP request to the ports.
-     * If it is an ARP response, then set a flow rule for the host
-     * and forward any IP packets to the host in the packet buffer to the host.
-     * <p>
-     * Note: We handles all ARP packet in, even for those ARP packets between
-     * hosts in the same subnet.
-     * For an ARP packet with broadcast destination MAC,
-     * some switches pipelines will send it to the controller due to table miss,
-     * other switches will flood the packets directly in the data plane without
-     * packet in.
-     * We can deal with both cases.
-     *
-     * @param pkt incoming ARP packet and context information
-     * @param hostService the host service
-     */
-    public void processPacketIn(NeighbourMessageContext pkt, HostService hostService) {
-
-        SegmentRoutingAppConfig appConfig = srManager.cfgService
-                .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
-        if (appConfig != null && appConfig.suppressSubnet().contains(pkt.inPort())) {
-            // Ignore ARP packets come from suppressed ports
-            pkt.drop();
-            return;
-        }
-
-        if (!validateArpSpa(pkt)) {
-            log.debug("Ignore ARP packet discovered on {} with unexpected src protocol address {}.",
-                    pkt.inPort(), pkt.sender().getIp4Address());
-            pkt.drop();
-            return;
-        }
-
-        if (pkt.type() == REQUEST) {
-            handleArpRequest(pkt, hostService);
-        } else {
-            handleArpReply(pkt, hostService);
-        }
-    }
-
-    private void handleArpRequest(NeighbourMessageContext pkt, HostService hostService) {
-        // ARP request for router. Send ARP reply.
-        if (isArpForRouter(pkt)) {
-            MacAddress targetMac = config.getRouterMacForAGatewayIp(pkt.target().getIp4Address());
-            if (targetMac == null) {
-                log.warn("Router MAC of {} is not configured. Cannot handle ARP request from {}",
-                        pkt.inPort().deviceId(), pkt.sender());
-                return;
-            }
-            sendResponse(pkt, targetMac, hostService, true);
-        } else {
-            // NOTE: Ignore ARP packets except those target for the router
-            //       We will reconsider enabling this when we have host learning support
-            /*
-            Set<Host> hosts = hostService.getHostsByIp(pkt.target());
-            if (hosts.size() > 1) {
-                log.warn("More than one host with the same ip {}", pkt.target());
-            }
-            Host targetHost = hosts.stream().findFirst().orElse(null);
-            // ARP request for known hosts. Send proxy ARP reply on behalf of the target.
-            if (targetHost != null) {
-                pkt.forward(targetHost.location());
-            // ARP request for unknown host in the subnet. Flood in the subnet.
-            } else {
-                flood(pkt);
-            }
-            */
-        }
-    }
-
-    private void handleArpReply(NeighbourMessageContext pkt, HostService hostService) {
-        // ARP reply for router. Process all pending IP packets.
-        if (isArpForRouter(pkt)) {
-            Ip4Address hostIpAddress = pkt.sender().getIp4Address();
-            srManager.ipHandler.forwardPackets(pkt.inPort().deviceId(), hostIpAddress);
-        } else {
-            // NOTE: Ignore ARP packets except those target for the router
-            //       We will reconsider enabling this when we have host learning support
-            /*
-            HostId targetHostId = HostId.hostId(pkt.dstMac(), pkt.vlan());
-            Host targetHost = hostService.getHost(targetHostId);
-            // ARP reply for known hosts. Forward to the host.
-            if (targetHost != null) {
-                pkt.forward(targetHost.location());
-            // ARP reply for unknown host, Flood in the subnet.
-            } else {
-                // Don't flood to non-edge ports
-                if (pkt.vlan().equals(SegmentRoutingManager.INTERNAL_VLAN)) {
-                    return;
-                }
-                flood(pkt);
-            }
-            */
-        }
-    }
-
-    /**
-     * Check if the source protocol address of an ARP packet belongs to the same
-     * subnet configured on the port it is seen.
-     *
-     * @param pkt ARP packet and context information
-     * @return true if the source protocol address belongs to the configured subnet
-     */
-    private boolean validateArpSpa(NeighbourMessageContext pkt) {
-        Ip4Address spa = pkt.sender().getIp4Address();
-        Set<IpPrefix> subnet = config.getPortSubnets(pkt.inPort().deviceId(), pkt.inPort().port())
-                .stream()
-                .filter(ipPrefix -> ipPrefix.isIp4() && ipPrefix.contains(spa))
-                .collect(Collectors.toSet());
-        return !subnet.isEmpty();
-    }
-
-
-    private boolean isArpForRouter(NeighbourMessageContext pkt) {
-        Ip4Address targetProtocolAddress = pkt.target().getIp4Address();
-        Set<IpAddress> gatewayIpAddresses = null;
-        try {
-            if (targetProtocolAddress.equals(config.getRouterIpv4(pkt.inPort().deviceId()))) {
-                return true;
-            }
-            gatewayIpAddresses = config.getPortIPs(pkt.inPort().deviceId());
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting check for router IP in processing arp");
-        }
-        if (gatewayIpAddresses != null &&
-                gatewayIpAddresses.contains(targetProtocolAddress)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Sends an APR request for the target IP address to all ports except in-port.
-     *
-     * @param deviceId Switch device ID
-     * @param targetAddress target IP address for ARP
-     * @param inPort in-port
-     */
-    public void sendArpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
-        byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
-        byte[] senderIpAddress = new byte[Ip4Address.BYTE_LENGTH];
-        /*
-         * Retrieves device info.
-         */
-        if (!getSenderInfo(senderMacAddress, senderIpAddress, deviceId, targetAddress)) {
-            log.warn("Aborting sendArpRequest, we cannot get all the information needed");
-            return;
-        }
-        /*
-         * Creates the request.
-         */
-        Ethernet arpRequest = ARP.buildArpRequest(
-                senderMacAddress,
-                senderIpAddress,
-                targetAddress.toOctets(),
-                VlanId.NO_VID
-        );
-        flood(arpRequest, inPort, targetAddress);
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java b/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
deleted file mode 100644
index 8b4b31c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/DefaultRoutingHandler.java
+++ /dev/null
@@ -1,2222 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableMap.Builder;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-
-import org.onlab.packet.EthType;
-import com.google.common.collect.Streams;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip6Address;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.util.PredictableExecutor;
-import org.onlab.util.PredictableExecutor.PickyCallable;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.mastership.MastershipEvent;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMultimap;
-import org.onosproject.store.service.Serializer;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.Executors.newScheduledThreadPool;
-import static org.onlab.util.Tools.groupedThreads;
-
-/**
- * Default routing handler that is responsible for route computing and
- * routing rule population.
- */
-public class DefaultRoutingHandler {
-    private static final int MAX_CONSTANT_RETRY_ATTEMPTS = 5;
-    private static final long RETRY_INTERVAL_MS = 250L;
-    private static final int RETRY_INTERVAL_SCALE = 1;
-    private static final long STABLITY_THRESHOLD = 10; //secs
-    private static final long MASTER_CHANGE_DELAY = 1000; // ms
-    private static final long PURGE_DELAY = 1000; // ms
-    private static Logger log = LoggerFactory.getLogger(DefaultRoutingHandler.class);
-
-    private SegmentRoutingManager srManager;
-    private RoutingRulePopulator rulePopulator;
-    private HashMap<DeviceId, EcmpShortestPathGraph> currentEcmpSpgMap;
-    private HashMap<DeviceId, EcmpShortestPathGraph> updatedEcmpSpgMap;
-    private DeviceConfiguration config;
-    private final Lock statusLock = new ReentrantLock();
-    private volatile Status populationStatus;
-    private ScheduledExecutorService executorService
-        = newScheduledThreadPool(1, groupedThreads("retryftr", "retry-%d", log));
-    private ScheduledExecutorService executorServiceMstChg
-        = newScheduledThreadPool(1, groupedThreads("masterChg", "mstch-%d", log));
-    private ScheduledExecutorService executorServiceFRR
-        = newScheduledThreadPool(1, groupedThreads("fullRR", "fullRR-%d", log));
-    // Route populators - 0 will leverage available processors
-    private static final int DEFAULT_THREADS = 0;
-    private ExecutorService routePopulators;
-
-    private Instant lastRoutingChange = Instant.EPOCH;
-    private Instant lastFullReroute = Instant.EPOCH;
-
-    // Distributed store to keep track of ONOS instance that should program the
-    // device pair. There should be only one instance (the king) that programs the same pair.
-    Map<Set<DeviceId>, NodeId> shouldProgram;
-    Map<DeviceId, Boolean> shouldProgramCache;
-
-    // Distributed routes store to keep track of the routes already seen
-    // destination device is the key and target sw is the value
-    ConsistentMultimap<DeviceId, DeviceId> seenBeforeRoutes;
-
-    // Local store to keep track of all devices that this instance was responsible
-    // for programming in the last run. Helps to determine if mastership changed
-    // during a run - only relevant for programming as a result of topo change.
-    Set<DeviceId> lastProgrammed;
-
-    /**
-     * Represents the default routing population status.
-     */
-    public enum Status {
-        // population process is not started yet.
-        IDLE,
-        // population process started.
-        STARTED,
-        // population process was aborted due to errors, mostly for groups not found.
-        ABORTED,
-        // population process was finished successfully.
-        SUCCEEDED
-    }
-
-    /**
-     * Creates a DefaultRoutingHandler object.
-     *
-     * @param srManager SegmentRoutingManager object
-     */
-    DefaultRoutingHandler(SegmentRoutingManager srManager) {
-        this.shouldProgram = srManager.storageService.<Set<DeviceId>, NodeId>consistentMapBuilder()
-                .withName("sr-should-program")
-                .withSerializer(Serializer.using(KryoNamespaces.API))
-                .withRelaxedReadConsistency()
-                .build().asJavaMap();
-        this.seenBeforeRoutes = srManager.storageService.<DeviceId, DeviceId>consistentMultimapBuilder()
-                .withName("programmed-routes")
-                .withSerializer(Serializer.using(KryoNamespaces.API))
-                .withRelaxedReadConsistency()
-                .build();
-        this.shouldProgramCache = Maps.newConcurrentMap();
-        update(srManager);
-        this.routePopulators = new PredictableExecutor(DEFAULT_THREADS,
-                                                      groupedThreads("onos/sr", "r-populator-%d", log));
-    }
-
-    /**
-     * Updates a DefaultRoutingHandler object.
-     *
-     * @param srManager SegmentRoutingManager object
-     */
-    void update(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        this.rulePopulator = checkNotNull(srManager.routingRulePopulator);
-        this.config = checkNotNull(srManager.deviceConfiguration);
-        this.populationStatus = Status.IDLE;
-        this.currentEcmpSpgMap = Maps.newHashMap();
-        this.lastProgrammed = Sets.newConcurrentHashSet();
-    }
-
-    /**
-     * Returns an immutable copy of the current ECMP shortest-path graph as
-     * computed by this controller instance.
-     *
-     * @return immutable copy of the current ECMP graph
-     */
-    public ImmutableMap<DeviceId, EcmpShortestPathGraph> getCurrentEmcpSpgMap() {
-        Builder<DeviceId, EcmpShortestPathGraph> builder = ImmutableMap.builder();
-        currentEcmpSpgMap.entrySet().forEach(entry -> {
-            if (entry.getValue() != null) {
-                builder.put(entry.getKey(), entry.getValue());
-            }
-        });
-        return builder.build();
-    }
-
-    /**
-     * Acquires the lock used when making routing changes.
-     */
-    public void acquireRoutingLock() {
-        statusLock.lock();
-    }
-
-    /**
-     * Releases the lock used when making routing changes.
-     */
-    public void releaseRoutingLock() {
-        statusLock.unlock();
-    }
-
-    /**
-    * Determines if routing in the network has been stable in the last
-    * STABILITY_THRESHOLD seconds, by comparing the current time to the last
-    * routing change timestamp.
-    *
-    * @return true if stable
-    */
-   public boolean isRoutingStable() {
-       long last = (long) (lastRoutingChange.toEpochMilli() / 1000.0);
-       long now = (long) (Instant.now().toEpochMilli() / 1000.0);
-       log.trace("Routing stable since {}s", now - last);
-       return (now - last) > STABLITY_THRESHOLD;
-   }
-
-    /**
-     * Gracefully shuts down the defaultRoutingHandler. Typically called when
-     * the app is deactivated
-     */
-    public void shutdown() {
-        executorService.shutdown();
-        executorServiceMstChg.shutdown();
-        executorServiceFRR.shutdown();
-        routePopulators.shutdown();
-    }
-
-    //////////////////////////////////////
-    //  Route path handling
-    //////////////////////////////////////
-
-    /* The following three methods represent the three major ways in which
-     * route-path handling is triggered in the network
-     *      a) due to configuration change
-     *      b) due to route-added event
-     *      c) due to change in the topology
-     */
-
-    /**
-     * Populates all routing rules to all switches. Typically triggered at
-     * startup or after a configuration event.
-     */
-    public void populateAllRoutingRules() {
-        lastRoutingChange = Instant.now();
-        statusLock.lock();
-        try {
-            if (populationStatus == Status.STARTED) {
-                log.warn("Previous rule population is not finished. Cannot"
-                        + " proceed with populateAllRoutingRules");
-                return;
-            }
-
-            populationStatus = Status.STARTED;
-            rulePopulator.resetCounter();
-            log.info("Starting to populate all routing rules");
-            log.debug("populateAllRoutingRules: populationStatus is STARTED");
-
-            // take a snapshot of the topology
-            updatedEcmpSpgMap = new HashMap<>();
-            Set<EdgePair> edgePairs = new HashSet<>();
-            Set<ArrayList<DeviceId>> routeChanges = new HashSet<>();
-            for (DeviceId dstSw : srManager.deviceConfiguration.getRouters()) {
-                EcmpShortestPathGraph ecmpSpgUpdated =
-                        new EcmpShortestPathGraph(dstSw, srManager);
-                updatedEcmpSpgMap.put(dstSw, ecmpSpgUpdated);
-                Optional<DeviceId> pairDev = srManager.getPairDeviceId(dstSw);
-                if (pairDev.isPresent()) {
-                    // pairDev may not be available yet, but we still need to add
-                    ecmpSpgUpdated = new EcmpShortestPathGraph(pairDev.get(), srManager);
-                    updatedEcmpSpgMap.put(pairDev.get(), ecmpSpgUpdated);
-                    edgePairs.add(new EdgePair(dstSw, pairDev.get()));
-                }
-
-                if (!shouldProgram(dstSw)) {
-                    lastProgrammed.remove(dstSw);
-                    continue;
-                } else {
-                    lastProgrammed.add(dstSw);
-                }
-                // To do a full reroute, assume all route-paths have changed
-                for (DeviceId dev : deviceAndItsPair(dstSw)) {
-                    for (DeviceId targetSw : srManager.deviceConfiguration.getRouters()) {
-                        if (targetSw.equals(dev)) {
-                            continue;
-                        }
-                        routeChanges.add(Lists.newArrayList(targetSw, dev));
-                    }
-                }
-            }
-
-            log.debug("seenBeforeRoutes size {}", seenBeforeRoutes.size());
-            if (!redoRouting(routeChanges, edgePairs, null)) {
-                log.debug("populateAllRoutingRules: populationStatus is ABORTED");
-                populationStatus = Status.ABORTED;
-                log.warn("Failed to repopulate all routing rules.");
-                return;
-            }
-
-            log.debug("populateAllRoutingRules: populationStatus is SUCCEEDED");
-            populationStatus = Status.SUCCEEDED;
-            log.info("Completed all routing rule population. Total # of rules pushed : {}",
-                    rulePopulator.getCounter());
-            return;
-        } catch (Exception e) {
-            log.error("populateAllRoutingRules thrown an exception: {}",
-                      e.getMessage(), e);
-            populationStatus = Status.ABORTED;
-        } finally {
-            statusLock.unlock();
-        }
-    }
-
-    /**
-     * Populate rules from all other edge devices to the connect-point(s)
-     * specified for the given subnets.
-     *
-     * @param cpts connect point(s) of the subnets being added
-     * @param subnets subnets being added
-     */
-    // XXX refactor
-    protected void populateSubnet(Set<ConnectPoint> cpts, Set<IpPrefix> subnets) {
-        if (cpts == null || cpts.size() < 1 || cpts.size() > 2) {
-            log.warn("Skipping populateSubnet due to illegal size of connect points. {}", cpts);
-            return;
-        }
-
-        lastRoutingChange = Instant.now();
-        statusLock.lock();
-        try {
-           if (populationStatus == Status.STARTED) {
-                log.warn("Previous rule population is not finished. Cannot"
-                        + " proceed with routing rules for added routes");
-                return;
-            }
-            populationStatus = Status.STARTED;
-            rulePopulator.resetCounter();
-            log.info("Starting to populate routing rules for added routes, subnets={}, cpts={}",
-                    subnets, cpts);
-            // In principle an update to a subnet/prefix should not require a
-            // new ECMPspg calculation as it is not a topology event. As a
-            // result, we use the current/existing ECMPspg in the updated map
-            // used by the redoRouting method.
-            if (updatedEcmpSpgMap == null) {
-                updatedEcmpSpgMap = new HashMap<>();
-            }
-            currentEcmpSpgMap.entrySet().forEach(entry -> {
-                updatedEcmpSpgMap.put(entry.getKey(), entry.getValue());
-                if (log.isTraceEnabled()) {
-                    log.trace("Root switch: {}", entry.getKey());
-                    log.trace("  Current/Existing SPG: {}", entry.getValue());
-                }
-            });
-            log.debug("seenBeforeRoutes size {}", seenBeforeRoutes.size());
-            Set<EdgePair> edgePairs = new HashSet<>();
-            Set<ArrayList<DeviceId>> routeChanges = new HashSet<>();
-            boolean handleRouting = false;
-
-            if (cpts.size() == 2) {
-                // ensure connect points are edge-pairs
-                Iterator<ConnectPoint> iter = cpts.iterator();
-                DeviceId dev1 = iter.next().deviceId();
-                Optional<DeviceId> pairDev = srManager.getPairDeviceId(dev1);
-                if (pairDev.isPresent() && iter.next().deviceId().equals(pairDev.get())) {
-                    edgePairs.add(new EdgePair(dev1, pairDev.get()));
-                } else {
-                    log.warn("Connectpoints {} for subnets {} not on "
-                            + "pair-devices.. aborting populateSubnet", cpts, subnets);
-                    populationStatus = Status.ABORTED;
-                    return;
-                }
-                for (ConnectPoint cp : cpts) {
-                    if (updatedEcmpSpgMap.get(cp.deviceId()) == null) {
-                        EcmpShortestPathGraph ecmpSpgUpdated =
-                            new EcmpShortestPathGraph(cp.deviceId(), srManager);
-                        updatedEcmpSpgMap.put(cp.deviceId(), ecmpSpgUpdated);
-                        log.warn("populateSubnet: no updated graph for dev:{}"
-                                + " ... creating", cp.deviceId());
-                    }
-                    if (!shouldProgram(cp.deviceId())) {
-                        continue;
-                    }
-                    handleRouting = true;
-                }
-            } else {
-                // single connect point
-                DeviceId dstSw = cpts.iterator().next().deviceId();
-                if (updatedEcmpSpgMap.get(dstSw) == null) {
-                    EcmpShortestPathGraph ecmpSpgUpdated =
-                        new EcmpShortestPathGraph(dstSw, srManager);
-                    updatedEcmpSpgMap.put(dstSw, ecmpSpgUpdated);
-                    log.warn("populateSubnet: no updated graph for dev:{}"
-                            + " ... creating", dstSw);
-                }
-                handleRouting = shouldProgram(dstSw);
-            }
-
-            if (!handleRouting) {
-                log.debug("This instance is not handling ecmp routing to the "
-                        + "connectPoint(s) {}", cpts);
-                populationStatus = Status.ABORTED;
-                return;
-            }
-
-            // if it gets here, this instance should handle routing for the
-            // connectpoint(s). Assume all route-paths have to be updated to
-            // the connectpoint(s) with the following exceptions
-            // 1. if target is non-edge no need for routing rules
-            // 2. if target is one of the connectpoints
-            for (ConnectPoint cp : cpts) {
-                DeviceId dstSw = cp.deviceId();
-                for (Device targetSw : srManager.deviceService.getDevices()) {
-                    boolean isEdge = false;
-                    try {
-                        isEdge = config.isEdgeDevice(targetSw.id());
-                    } catch (DeviceConfigNotFoundException e) {
-                        log.warn(e.getMessage() + "aborting populateSubnet on targetSw {}", targetSw.id());
-                        continue;
-                    }
-                    Optional<DeviceId> pairDev = srManager.getPairDeviceId(dstSw);
-                    if (dstSw.equals(targetSw.id()) || !isEdge ||
-                            (cpts.size() == 2 && pairDev.isPresent() && targetSw.id().equals(pairDev.get()))) {
-                        continue;
-                    }
-                    routeChanges.add(Lists.newArrayList(targetSw.id(), dstSw));
-                }
-            }
-
-            if (!redoRouting(routeChanges, edgePairs, subnets)) {
-                log.debug("populateSubnet: populationStatus is ABORTED");
-                populationStatus = Status.ABORTED;
-                log.warn("Failed to repopulate the rules for subnet.");
-                return;
-            }
-
-            log.debug("populateSubnet: populationStatus is SUCCEEDED");
-            populationStatus = Status.SUCCEEDED;
-            log.info("Completed subnet population. Total # of rules pushed : {}",
-                    rulePopulator.getCounter());
-            return;
-
-        } catch (Exception e) {
-            log.error("populateSubnet thrown an exception: {}",
-                      e.getMessage(), e);
-            populationStatus = Status.ABORTED;
-        } finally {
-            statusLock.unlock();
-        }
-    }
-
-    /**
-     * Populates the routing rules or makes hash group changes according to the
-     * route-path changes due to link failure, switch failure or link up. This
-     * method should only be called for one of these three possible event-types.
-     * Note that when a switch goes away, all of its links fail as well, but
-     * this is handled as a single switch removal event.
-     *
-     * @param linkDown the single failed link, or null for other conditions such
-     *            as link-up or a removed switch
-     * @param linkUp the single link up, or null for other conditions such as
-     *            link-down or a removed switch
-     * @param switchDown the removed switch, or null for other conditions such
-     *            as link-down or link-up
-     * @param seenBefore true if this event is for a linkUp or linkDown for a
-     *            seen link
-     */
-    // TODO This method should be refactored into three separated methods
-    public void populateRoutingRulesForLinkStatusChange(Link linkDown, Link linkUp,
-                                                        DeviceId switchDown, boolean seenBefore) {
-        if (Stream.of(linkDown, linkUp, switchDown).filter(Objects::nonNull)
-                .count() != 1) {
-            log.warn("Only one event can be handled for link status change .. aborting");
-            return;
-        }
-
-        lastRoutingChange = Instant.now();
-        statusLock.lock();
-        try {
-
-            if (populationStatus == Status.STARTED) {
-                log.warn("Previous rule population is not finished. Cannot"
-                        + " proceeed with routingRules for Topology change");
-                return;
-            }
-
-            // Take snapshots of the topology
-            updatedEcmpSpgMap = new HashMap<>();
-            Set<EdgePair> edgePairs = new HashSet<>();
-            for (Device sw : srManager.deviceService.getDevices()) {
-                EcmpShortestPathGraph ecmpSpgUpdated =
-                        new EcmpShortestPathGraph(sw.id(), srManager);
-                updatedEcmpSpgMap.put(sw.id(), ecmpSpgUpdated);
-                Optional<DeviceId> pairDev = srManager.getPairDeviceId(sw.id());
-                if (pairDev.isPresent()) {
-                    // pairDev may not be available yet, but we still need to add
-                    ecmpSpgUpdated = new EcmpShortestPathGraph(pairDev.get(), srManager);
-                    updatedEcmpSpgMap.put(pairDev.get(), ecmpSpgUpdated);
-                    edgePairs.add(new EdgePair(sw.id(), pairDev.get()));
-                }
-            }
-
-            log.info("Starting to populate routing rules from Topology change");
-
-            Set<ArrayList<DeviceId>> routeChanges;
-            log.debug("populateRoutingRulesForLinkStatusChange: "
-                    + "populationStatus is STARTED");
-            log.debug("seenBeforeRoutes size {}", seenBeforeRoutes.size());
-            populationStatus = Status.STARTED;
-            rulePopulator.resetCounter(); //XXX maybe useful to have a rehash ctr
-            boolean hashGroupsChanged = false;
-            // try optimized re-routing
-            if (linkDown == null) {
-                // either a linkUp or a switchDown - compute all route changes by
-                // comparing all routes of existing ECMP SPG to new ECMP SPG
-                routeChanges = computeRouteChange(switchDown);
-
-                // deal with linkUp
-                if (linkUp != null) {
-                    // deal with linkUp of a seen-before link
-                    if (seenBefore) {
-                        // link previously seen before
-                        // do hash-bucket changes instead of a re-route
-                        processHashGroupChangeForLinkUp(routeChanges);
-                        // clear out routesChanges so a re-route is not attempted
-                        routeChanges = ImmutableSet.of();
-                        hashGroupsChanged = true;
-                    } else {
-                        // do hash-bucket changes first, method will return changed routes;
-                        // for each route not changed it will perform a reroute
-                        Set<ArrayList<DeviceId>> changedRoutes = processHashGroupChangeForLinkUp(routeChanges);
-                        Set<ArrayList<DeviceId>> routeChangesTemp = getExpandedRoutes(routeChanges);
-                        changedRoutes.forEach(routeChangesTemp::remove);
-                        // if routesChanges is empty a re-route is not attempted
-                        routeChanges = routeChangesTemp;
-                        for (ArrayList<DeviceId> route : routeChanges) {
-                            log.debug("remaining routes Target -> Root");
-                            if (route.size() == 1) {
-                                log.debug(" : all -> {}", route.get(0));
-                            } else {
-                                log.debug(" : {} -> {}", route.get(0), route.get(1));
-                            }
-                        }
-                        // Mark hash groups as changed
-                        if (!changedRoutes.isEmpty()) {
-                            hashGroupsChanged = true;
-                        }
-                    }
-
-                }
-
-                //deal with switchDown
-                if (switchDown != null) {
-                    processHashGroupChangeForFailure(routeChanges, switchDown);
-                    // clear out routesChanges so a re-route is not attempted
-                    routeChanges = ImmutableSet.of();
-                    hashGroupsChanged = true;
-                }
-            } else {
-                // link has gone down
-                // Compare existing ECMP SPG only with the link that went down
-                routeChanges = computeDamagedRoutes(linkDown);
-                processHashGroupChangeForFailure(routeChanges, null);
-                // clear out routesChanges so a re-route is not attempted
-                routeChanges = ImmutableSet.of();
-                hashGroupsChanged = true;
-            }
-
-            if (routeChanges.isEmpty()) {
-                if (hashGroupsChanged) {
-                    log.info("Hash-groups changed for link status change");
-                } else {
-                    log.info("No re-route or re-hash attempted for the link"
-                            + " status change");
-                    updatedEcmpSpgMap.keySet().forEach(devId -> {
-                        currentEcmpSpgMap.put(devId, updatedEcmpSpgMap.get(devId));
-                        log.debug("Updating ECMPspg for remaining dev:{}", devId);
-                    });
-                }
-                log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
-                populationStatus = Status.SUCCEEDED;
-                return;
-            }
-
-            if (hashGroupsChanged) {
-                log.debug("Hash-groups changed for link status change");
-            }
-
-            // reroute of routeChanges
-            if (redoRouting(routeChanges, edgePairs, null)) {
-                log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is SUCCEEDED");
-                populationStatus = Status.SUCCEEDED;
-                log.info("Completed repopulation of rules for link-status change."
-                        + " # of rules populated : {}", rulePopulator.getCounter());
-                return;
-            } else {
-                log.debug("populateRoutingRulesForLinkStatusChange: populationStatus is ABORTED");
-                populationStatus = Status.ABORTED;
-                log.warn("Failed to repopulate the rules for link status change.");
-                return;
-            }
-        } catch (Exception e) {
-            log.error("populateRoutingRulesForLinkStatusChange thrown an exception: {}",
-                      e.getMessage(), e);
-            populationStatus = Status.ABORTED;
-        } finally {
-            statusLock.unlock();
-        }
-    }
-
-    /**
-     * Processes a set a route-path changes by reprogramming routing rules and
-     * creating new hash-groups or editing them if necessary. This method also
-     * determines the next-hops for the route-path from the src-switch (target)
-     * of the path towards the dst-switch of the path.
-     *
-     * @param routeChanges a set of route-path changes, where each route-path is
-     *                     a list with its first element the src-switch (target)
-     *                     of the path, and the second element the dst-switch of
-     *                     the path.
-     * @param edgePairs a set of edge-switches that are paired by configuration
-     * @param subnets  a set of prefixes that need to be populated in the routing
-     *                 table of the target switch in the route-path. Can be null,
-     *                 in which case all the prefixes belonging to the dst-switch
-     *                 will be populated in the target switch
-     * @return true if successful in repopulating all routes
-     */
-    private boolean redoRouting(Set<ArrayList<DeviceId>> routeChanges,
-                                Set<EdgePair> edgePairs, Set<IpPrefix> subnets) {
-        // first make every entry two-elements
-        Set<ArrayList<DeviceId>> changedRoutes = getExpandedRoutes(routeChanges);
-        // no valid routes - fail fast
-        if (changedRoutes.isEmpty()) {
-            return false;
-        }
-
-        // Temporary stores the changed routes
-        Set<ArrayList<DeviceId>> tempRoutes = ImmutableSet.copyOf(changedRoutes);
-        // now process changedRoutes according to edgePairs
-        if (!redoRoutingEdgePairs(edgePairs, subnets, changedRoutes)) {
-            return false; //abort routing and fail fast
-        }
-        // Calculate the programmed routes pointing to the pairs
-        Set<ArrayList<DeviceId>> programmedPairRoutes = Sets.difference(tempRoutes, changedRoutes);
-        log.debug("Evaluating programmed pair routes");
-        storeSeenBeforeRoutes(programmedPairRoutes);
-
-        // Temporary stores the left routes
-        tempRoutes = ImmutableSet.copyOf(changedRoutes);
-        // whatever is left in changedRoutes is now processed for individual dsts.
-        Set<DeviceId> updatedDevices = Sets.newHashSet();
-        if (!redoRoutingIndividualDests(subnets, changedRoutes,
-                                        updatedDevices)) {
-            return false; //abort routing and fail fast
-        }
-        // Calculate the individual programmed routes
-        Set<ArrayList<DeviceId>> programmedIndividualRoutes = Sets.difference(tempRoutes, changedRoutes);
-        log.debug("Evaluating individual programmed routes");
-        storeSeenBeforeRoutes(programmedIndividualRoutes);
-
-        // update ecmpSPG for all edge-pairs
-        for (EdgePair ep : edgePairs) {
-            currentEcmpSpgMap.put(ep.dev1, updatedEcmpSpgMap.get(ep.dev1));
-            currentEcmpSpgMap.put(ep.dev2, updatedEcmpSpgMap.get(ep.dev2));
-            log.debug("Updating ECMPspg for edge-pair:{}-{}", ep.dev1, ep.dev2);
-        }
-
-        // here is where we update all devices not touched by this instance
-        updatedEcmpSpgMap.keySet().stream()
-            .filter(devId -> !edgePairs.stream().anyMatch(ep -> ep.includes(devId)))
-            .filter(devId -> !updatedDevices.contains(devId))
-            .forEach(devId -> {
-                currentEcmpSpgMap.put(devId, updatedEcmpSpgMap.get(devId));
-                log.debug("Updating ECMPspg for remaining dev:{}", devId);
-            });
-        return true;
-    }
-
-    /**
-     * Stores the routes seen before. Routes are two-elements arrays.
-     * @param seenRoutes seen before routes
-     */
-    private void storeSeenBeforeRoutes(Set<ArrayList<DeviceId>> seenRoutes) {
-        Set<DeviceId> nextHops;
-        for (ArrayList<DeviceId> route : seenRoutes) {
-            log.debug("Route {} -> {} has been programmed", route.get(0), route.get(1));
-            nextHops = getNextHops(route.get(0), route.get(1));
-            // No valid next hops - cannot be considered a programmed route
-            if (nextHops.isEmpty()) {
-                log.debug("Could not find next hop from target:{} --> dst {} "
-                                  + "skipping this route", route.get(0), route.get(1));
-                continue;
-            }
-            // Already present - do not add again
-            if (seenBeforeRoutes.containsEntry(route.get(1), route.get(0))) {
-                log.debug("Route from target:{} --> dst {} " +
-                                  "already present, skipping this route", route.get(0), route.get(1));
-                continue;
-            }
-            seenBeforeRoutes.put(route.get(1), route.get(0));
-        }
-    }
-
-    /**
-     * Programs targetSw in the changedRoutes for given prefixes reachable by
-     * an edgePair. If no prefixes are given, the method will use configured
-     * subnets/prefixes. If some configured subnets belong only to a specific
-     * destination in the edgePair, then the target switch will be programmed
-     * only to that destination.
-     *
-     * @param edgePairs set of edge-pairs for which target will be programmed
-     * @param subnets a set of prefixes that need to be populated in the routing
-     *                 table of the target switch in the changedRoutes. Can be null,
-     *                 in which case all the configured prefixes belonging to the
-     *                 paired switches will be populated in the target switch
-     * @param changedRoutes a set of route-path changes, where each route-path is
-     *                     a list with its first element the src-switch (target)
-     *                     of the path, and the second element the dst-switch of
-     *                     the path.
-     * @return true if successful
-     */
-    private boolean redoRoutingEdgePairs(Set<EdgePair> edgePairs, Set<IpPrefix> subnets,
-                                         Set<ArrayList<DeviceId>> changedRoutes) {
-        for (EdgePair ep : edgePairs) {
-            // temp store for a target's changedRoutes to this edge-pair
-            Map<DeviceId, Set<ArrayList<DeviceId>>> targetRoutes = new HashMap<>();
-            Iterator<ArrayList<DeviceId>> i = changedRoutes.iterator();
-            while (i.hasNext()) {
-                ArrayList<DeviceId> route = i.next();
-                DeviceId dstSw = route.get(1);
-                if (ep.includes(dstSw)) {
-                    // routeChange for edge pair found
-                    // sort by target iff target is edge and remove from changedRoutes
-                    DeviceId targetSw = route.get(0);
-                    try {
-                        if (!srManager.deviceConfiguration.isEdgeDevice(targetSw)) {
-                            continue;
-                        }
-                    } catch (DeviceConfigNotFoundException e) {
-                        log.warn(e.getMessage() + "aborting redoRouting");
-                        return false;
-                    }
-                    // route is from another edge to this edge-pair
-                    if (targetRoutes.containsKey(targetSw)) {
-                        targetRoutes.get(targetSw).add(route);
-                    } else {
-                        Set<ArrayList<DeviceId>> temp = new HashSet<>();
-                        temp.add(route);
-                        targetRoutes.put(targetSw, temp);
-                    }
-                    i.remove();
-                }
-            }
-            // so now for this edgepair we have a per target set of routechanges
-            // process target->edgePair route
-            List<Future<Boolean>> futures = Lists.newArrayList();
-            for (Entry<DeviceId, Set<ArrayList<DeviceId>>> entry :
-                            targetRoutes.entrySet()) {
-                log.debug("* redoRoutingDstPair Target:{} -> edge-pair {}",
-                          entry.getKey(), ep);
-                futures.add(routePopulators.submit(new RedoRoutingEdgePair(entry.getKey(), entry.getValue(),
-                                                                           subnets, ep)));
-            }
-            if (!checkJobs(futures)) {
-                return false;
-            }
-            // if it gets here it has succeeded for all targets to this edge-pair
-        }
-        return true;
-    }
-
-    private final class RedoRoutingEdgePair implements PickyCallable<Boolean> {
-        private DeviceId targetSw;
-        private Set<ArrayList<DeviceId>> routes;
-        private Set<IpPrefix> subnets;
-        private EdgePair ep;
-
-        /**
-         * Builds a RedoRoutingEdgePair task which provides a result.
-         *
-         * @param targetSw the target switch
-         * @param routes the changed routes
-         * @param subnets the subnets
-         * @param ep the edge pair
-         */
-        RedoRoutingEdgePair(DeviceId targetSw, Set<ArrayList<DeviceId>> routes,
-                            Set<IpPrefix> subnets, EdgePair ep) {
-            this.targetSw = targetSw;
-            this.routes = routes;
-            this.subnets = subnets;
-            this.ep = ep;
-        }
-
-        @Override
-        public Boolean call() throws Exception {
-            return redoRoutingEdgePair();
-        }
-
-        @Override
-        public int hint() {
-            return targetSw.hashCode();
-        }
-
-        private boolean redoRoutingEdgePair() {
-            Map<DeviceId, Set<DeviceId>> perDstNextHops = new HashMap<>();
-            routes.forEach(route -> {
-                Set<DeviceId> nhops = getNextHops(route.get(0), route.get(1));
-                log.debug("route: target {} -> dst {} found with next-hops {}",
-                          route.get(0), route.get(1), nhops);
-                perDstNextHops.put(route.get(1), nhops);
-            });
-
-            List<Set<IpPrefix>> batchedSubnetDev1, batchedSubnetDev2;
-            if (subnets != null) {
-                batchedSubnetDev1 = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
-                batchedSubnetDev2 = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
-            } else {
-                batchedSubnetDev1 = config.getBatchedSubnets(ep.dev1);
-                batchedSubnetDev2 = config.getBatchedSubnets(ep.dev2);
-            }
-            List<Set<IpPrefix>> batchedSubnetBoth = Streams
-                    .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.intersection(a, b))
-                    .filter(set -> !set.isEmpty())
-                    .collect(Collectors.toList());
-            List<Set<IpPrefix>> batchedSubnetDev1Only = Streams
-                    .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.difference(a, b))
-                    .filter(set -> !set.isEmpty())
-                    .collect(Collectors.toList());
-            List<Set<IpPrefix>> batchedSubnetDev2Only = Streams
-                    .zip(batchedSubnetDev1.stream(), batchedSubnetDev2.stream(), (a, b) -> Sets.difference(b, a))
-                    .filter(set -> !set.isEmpty())
-                    .collect(Collectors.toList());
-
-            Set<DeviceId> nhDev1 = perDstNextHops.get(ep.dev1);
-            Set<DeviceId> nhDev2 = perDstNextHops.get(ep.dev2);
-
-            // handle routing to subnets common to edge-pair
-            // only if the targetSw is not part of the edge-pair and there
-            // exists a next hop to at least one of the devices in the edge-pair
-            if (!ep.includes(targetSw)
-                    && ((nhDev1 != null && !nhDev1.isEmpty()) || (nhDev2 != null && !nhDev2.isEmpty()))) {
-                log.trace("getSubnets on both {} and {}: {}", ep.dev1, ep.dev2, batchedSubnetBoth);
-                for (Set<IpPrefix> prefixes : batchedSubnetBoth) {
-                    if (!populateEcmpRoutingRulePartial(targetSw, ep.dev1, ep.dev2,
-                                                        perDstNextHops, prefixes)) {
-                        return false; // abort everything and fail fast
-                    }
-                }
-
-            }
-            // handle routing to subnets that only belong to dev1 only if
-            // a next-hop exists from the target to dev1
-            if (!batchedSubnetDev1Only.isEmpty() &&
-                    batchedSubnetDev1Only.stream().anyMatch(subnet -> !subnet.isEmpty()) &&
-                    nhDev1 != null  && !nhDev1.isEmpty()) {
-                Map<DeviceId, Set<DeviceId>> onlyDev1NextHops = new HashMap<>();
-                onlyDev1NextHops.put(ep.dev1, nhDev1);
-                log.trace("getSubnets on {} only: {}", ep.dev1, batchedSubnetDev1Only);
-                for (Set<IpPrefix> prefixes : batchedSubnetDev1Only) {
-                    if (!populateEcmpRoutingRulePartial(targetSw, ep.dev1, null,
-                                                        onlyDev1NextHops, prefixes)) {
-                        return false; // abort everything and fail fast
-                    }
-                }
-            }
-            // handle routing to subnets that only belong to dev2 only if
-            // a next-hop exists from the target to dev2
-            if (!batchedSubnetDev2Only.isEmpty() &&
-                    batchedSubnetDev2Only.stream().anyMatch(subnet -> !subnet.isEmpty()) &&
-                    nhDev2 != null && !nhDev2.isEmpty()) {
-                Map<DeviceId, Set<DeviceId>> onlyDev2NextHops = new HashMap<>();
-                onlyDev2NextHops.put(ep.dev2, nhDev2);
-                log.trace("getSubnets on {} only: {}", ep.dev2, batchedSubnetDev2Only);
-                for (Set<IpPrefix> prefixes : batchedSubnetDev2Only) {
-                    if (!populateEcmpRoutingRulePartial(targetSw, ep.dev2, null,
-                                                        onlyDev2NextHops, prefixes)) {
-                        return false; // abort everything and fail fast
-                    }
-                }
-            }
-            return true;
-        }
-    }
-
-    /**
-     * Programs targetSw in the changedRoutes for given prefixes reachable by
-     * a destination switch that is not part of an edge-pair.
-     * If no prefixes are given, the method will use configured subnets/prefixes.
-     *
-     * @param subnets a set of prefixes that need to be populated in the routing
-     *                 table of the target switch in the changedRoutes. Can be null,
-     *                 in which case all the configured prefixes belonging to the
-     *                 paired switches will be populated in the target switch
-     * @param changedRoutes a set of route-path changes, where each route-path is
-     *                     a list with its first element the src-switch (target)
-     *                     of the path, and the second element the dst-switch of
-     *                     the path.
-     * @return true if successful
-     */
-    private boolean redoRoutingIndividualDests(Set<IpPrefix> subnets, Set<ArrayList<DeviceId>> changedRoutes,
-                                               Set<DeviceId> updatedDevices) {
-        // aggregate route-path changes for each dst device
-        HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> routesBydevice =
-                new HashMap<>();
-        for (ArrayList<DeviceId> route: changedRoutes) {
-            DeviceId dstSw = route.get(1);
-            ArrayList<ArrayList<DeviceId>> deviceRoutes =
-                    routesBydevice.get(dstSw);
-            if (deviceRoutes == null) {
-                deviceRoutes = new ArrayList<>();
-                routesBydevice.put(dstSw, deviceRoutes);
-            }
-            deviceRoutes.add(route);
-        }
-        // iterate over the impacted devices
-        for (DeviceId impactedDstDevice : routesBydevice.keySet()) {
-            ArrayList<ArrayList<DeviceId>> deviceRoutes =
-                    routesBydevice.get(impactedDstDevice);
-            List<Future<Boolean>> futures = Lists.newArrayList();
-            for (ArrayList<DeviceId> route: deviceRoutes) {
-                log.debug("* redoRoutingIndiDst Target: {} -> dst: {}",
-                          route.get(0), route.get(1));
-                futures.add(routePopulators.submit(new RedoRoutingIndividualDest(subnets, route)));
-                changedRoutes.remove(route);
-            }
-            // check the execution of each job
-            if (!checkJobs(futures)) {
-                return false;
-            }
-            //Only if all the flows for all impacted routes to a
-            //specific target are pushed successfully, update the
-            //ECMP graph for that target. Or else the next event
-            //would not see any changes in the ECMP graphs.
-            //In another case, the target switch has gone away, so
-            //routes can't be installed. In that case, the current map
-            //is updated here, without any flows being pushed.
-            currentEcmpSpgMap.put(impactedDstDevice,
-                                  updatedEcmpSpgMap.get(impactedDstDevice));
-            updatedDevices.add(impactedDstDevice);
-            log.debug("Updating ECMPspg for impacted dev:{}", impactedDstDevice);
-        }
-        return true;
-    }
-
-    private final class RedoRoutingIndividualDest implements PickyCallable<Boolean> {
-        private DeviceId targetSw;
-        private ArrayList<DeviceId> route;
-        private Set<IpPrefix> subnets;
-
-        /**
-         * Builds a RedoRoutingIndividualDest task, which provides a result.
-         *
-         * @param subnets a set of prefixes
-         * @param route a route-path change
-         */
-        RedoRoutingIndividualDest(Set<IpPrefix> subnets, ArrayList<DeviceId> route) {
-            this.targetSw = route.get(0);
-            this.route = route;
-            this.subnets = subnets;
-        }
-
-        @Override
-        public Boolean call() throws Exception {
-            DeviceId dstSw = route.get(1); // same as impactedDstDevice
-            Set<DeviceId> nextHops = getNextHops(targetSw, dstSw);
-            if (nextHops.isEmpty()) {
-                log.debug("Could not find next hop from target:{} --> dst {} "
-                                  + "skipping this route", targetSw, dstSw);
-                return true;
-            }
-            Map<DeviceId, Set<DeviceId>> nhops = new HashMap<>();
-            nhops.put(dstSw, nextHops);
-            if (!populateEcmpRoutingRulePartial(targetSw, dstSw, null, nhops,
-                                                (subnets == null) ? Sets.newHashSet() : subnets)) {
-                return false; // abort routing and fail fast
-            }
-            log.debug("Populating flow rules from target: {} to dst: {}"
-                              + " is successful", targetSw, dstSw);
-            return true;
-        }
-
-        @Override
-        public int hint() {
-            return targetSw.hashCode();
-        }
-    }
-
-    /**
-     * Populate ECMP rules for subnets from target to destination via nexthops.
-     *
-     * @param targetSw Device ID of target switch in which rules will be programmed
-     * @param destSw1 Device ID of final destination switch to which the rules will forward
-     * @param destSw2 Device ID of paired destination switch to which the rules will forward
-     *                A null deviceId indicates packets should only be sent to destSw1
-     * @param nextHops Map of a set of next hops per destSw
-     * @param subnets Subnets to be populated. If empty, populate all configured subnets.
-     * @return true if it succeeds in populating rules
-     */ // refactor
-    private boolean populateEcmpRoutingRulePartial(DeviceId targetSw, DeviceId destSw1, DeviceId destSw2,
-                                                   Map<DeviceId, Set<DeviceId>> nextHops, Set<IpPrefix> subnets) {
-        boolean result;
-        // If both target switch and dest switch are edge routers, then set IP
-        // rule for both subnet and router IP.
-        boolean targetIsEdge;
-        boolean dest1IsEdge;
-        Ip4Address dest1RouterIpv4, dest2RouterIpv4 = null;
-        Ip6Address dest1RouterIpv6, dest2RouterIpv6 = null;
-
-        try {
-            targetIsEdge = config.isEdgeDevice(targetSw);
-            dest1IsEdge = config.isEdgeDevice(destSw1);
-            dest1RouterIpv4 = config.getRouterIpv4(destSw1);
-            dest1RouterIpv6 = config.getRouterIpv6(destSw1);
-            if (destSw2 != null) {
-                dest2RouterIpv4 = config.getRouterIpv4(destSw2);
-                dest2RouterIpv6 = config.getRouterIpv6(destSw2);
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting populateEcmpRoutingRulePartial.");
-            return false;
-        }
-
-        if (targetIsEdge && dest1IsEdge) {
-            List<Set<IpPrefix>> batchedSubnets;
-            if (subnets != null && !subnets.isEmpty()) {
-                batchedSubnets = Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(subnets));
-            } else {
-                batchedSubnets = config.getBatchedSubnets(destSw1);
-            }
-            // XXX - Rethink this - ignoring routerIPs in all other switches
-            // even edge to edge switches
-            /*subnets.add(dest1RouterIpv4.toIpPrefix());
-            if (dest1RouterIpv6 != null) {
-                subnets.add(dest1RouterIpv6.toIpPrefix());
-            }
-            if (destSw2 != null && dest2RouterIpv4 != null) {
-                subnets.add(dest2RouterIpv4.toIpPrefix());
-                if (dest2RouterIpv6 != null) {
-                    subnets.add(dest2RouterIpv6.toIpPrefix());
-                }
-            }*/
-            log.trace("getSubnets on {}: {}", destSw1, batchedSubnets);
-            for (Set<IpPrefix> prefixes : batchedSubnets) {
-                log.debug(". populateEcmpRoutingRulePartial in device {} towards {} {} "
-                                + "for subnets {}", targetSw, destSw1,
-                        (destSw2 != null) ? ("& " + destSw2) : "",
-                        prefixes);
-                if (!rulePopulator.populateIpRuleForSubnet(targetSw, prefixes, destSw1, destSw2, nextHops)) {
-                    return false;
-                }
-            }
-        }
-
-        if (!targetIsEdge && dest1IsEdge) {
-            // MPLS rules in all non-edge target devices. These rules are for
-            // individual destinations, even if the dsts are part of edge-pairs.
-            log.debug(". populateEcmpRoutingRulePartial in device{} towards {} for "
-                    + "all MPLS rules", targetSw, destSw1);
-            result = rulePopulator.populateMplsRule(targetSw, destSw1, nextHops.get(destSw1), dest1RouterIpv4);
-            if (!result) {
-                return false;
-            }
-            if (dest1RouterIpv6 != null) {
-                int v4sid = 0, v6sid = 0;
-                try {
-                    v4sid = config.getIPv4SegmentId(destSw1);
-                    v6sid = config.getIPv6SegmentId(destSw1);
-                } catch (DeviceConfigNotFoundException e) {
-                    log.warn(e.getMessage());
-                }
-                if (v4sid != v6sid) {
-                    result = rulePopulator.populateMplsRule(targetSw, destSw1, nextHops.get(destSw1),
-                                                            dest1RouterIpv6);
-                    if (!result) {
-                        return false;
-                    }
-                }
-            }
-        }
-
-        if (!targetIsEdge && !dest1IsEdge) {
-            // MPLS rules for inter-connected spines
-            // can be merged with above if, left it here for clarity
-            log.debug(". populateEcmpRoutingRulePartial in device{} towards {} for "
-                              + "all MPLS rules", targetSw, destSw1);
-
-            result = rulePopulator.populateMplsRule(targetSw, destSw1, nextHops.get(destSw1), dest1RouterIpv4);
-            if (!result) {
-                return false;
-            }
-
-            if (dest1RouterIpv6 != null) {
-                int v4sid = 0, v6sid = 0;
-                try {
-                    v4sid = config.getIPv4SegmentId(destSw1);
-                    v6sid = config.getIPv6SegmentId(destSw1);
-                } catch (DeviceConfigNotFoundException e) {
-                    log.warn(e.getMessage());
-                }
-                if (v4sid != v6sid) {
-                    result = rulePopulator.populateMplsRule(targetSw, destSw1, nextHops.get(destSw1),
-                                                            dest1RouterIpv6);
-                    if (!result) {
-                        return false;
-                    }
-                }
-           }
-        }
-
-        // To save on ECMP groups
-        // avoid MPLS rules in non-edge-devices to non-edge-devices
-        // avoid MPLS transit rules in edge-devices
-        // avoid loopback IP rules in edge-devices to non-edge-devices
-        return true;
-    }
-
-    /**
-     * Processes a set a route-path changes due to a switch/link failure by editing hash groups.
-     *
-     * @param routeChanges a set of route-path changes, where each route-path is
-     *                     a list with its first element the src-switch of the path
-     *                     and the second element the dst-switch of the path.
-     * @param failedSwitch the switchId if the route changes are for a failed switch,
-     *                     otherwise null
-     */
-    private void processHashGroupChangeForFailure(Set<ArrayList<DeviceId>> routeChanges,
-                                                  DeviceId failedSwitch) {
-        // first, ensure each routeChanges entry has two elements
-        Set<ArrayList<DeviceId>> changedRoutes = getAllExpandedRoutes(routeChanges);
-        boolean someFailed = false;
-        boolean success;
-        Set<DeviceId> updatedDevices = Sets.newHashSet();
-        for (ArrayList<DeviceId> route : changedRoutes) {
-            DeviceId targetSw = route.get(0);
-            DeviceId dstSw = route.get(1);
-            success = fixHashGroupsForRoute(route, true);
-            // it's possible that we cannot fix hash groups for a route
-            // if the target switch has failed. Nevertheless the ecmp graph
-            // for the impacted switch must still be updated.
-            if (!success && failedSwitch != null && targetSw.equals(failedSwitch)) {
-                currentEcmpSpgMap.put(dstSw, updatedEcmpSpgMap.get(dstSw));
-                currentEcmpSpgMap.remove(targetSw);
-                log.debug("Updating ECMPspg for dst:{} removing failed switch "
-                        + "target:{}", dstSw, targetSw);
-                updatedDevices.add(targetSw);
-                updatedDevices.add(dstSw);
-                continue;
-
-            }
-            //linkfailed - update both sides
-            if (success) {
-                currentEcmpSpgMap.put(targetSw, updatedEcmpSpgMap.get(targetSw));
-                currentEcmpSpgMap.put(dstSw, updatedEcmpSpgMap.get(dstSw));
-                log.debug("Updating ECMPspg for dst:{} and target:{} for linkdown"
-                        + " or switchdown", dstSw, targetSw);
-                updatedDevices.add(targetSw);
-                updatedDevices.add(dstSw);
-            } else {
-                someFailed = true;
-            }
-        }
-        if (!someFailed) {
-            // here is where we update all devices not touched by this instance
-            updatedEcmpSpgMap.keySet().stream()
-                .filter(devId -> !updatedDevices.contains(devId))
-                .forEach(devId -> {
-                    currentEcmpSpgMap.put(devId, updatedEcmpSpgMap.get(devId));
-                    log.debug("Updating ECMPspg for remaining dev:{}", devId);
-            });
-        }
-    }
-
-    /**
-     * Processes a set a route-path changes due to link up by editing hash groups.
-     *
-     * @param routeChanges a set of route-path changes, where each route-path is
-     *                     a list with its first element the src-switch of the path
-     *                     and the second element the dst-switch of the path.
-     * @return set of changed routes
-     */
-    private Set<ArrayList<DeviceId>> processHashGroupChangeForLinkUp(Set<ArrayList<DeviceId>> routeChanges) {
-        // Stores changed routes
-        Set<ArrayList<DeviceId>> doneRoutes = new HashSet<>();
-        // first, ensure each routeChanges entry has two elements
-        Set<ArrayList<DeviceId>> changedRoutes = getAllExpandedRoutes(routeChanges);
-        boolean someFailed = false;
-        boolean success;
-        Set<DeviceId> updatedDevices = Sets.newHashSet();
-        for (ArrayList<DeviceId> route : changedRoutes) {
-            DeviceId targetSw = route.get(0);
-            DeviceId dstSw = route.get(1);
-            // linkup - fix (if possible)
-            success = fixHashGroupsForRoute(route, false);
-            if (success) {
-                currentEcmpSpgMap.put(targetSw, updatedEcmpSpgMap.get(targetSw));
-                currentEcmpSpgMap.put(dstSw, updatedEcmpSpgMap.get(dstSw));
-                log.debug("Updating ECMPspg for target:{} and dst:{} for linkup",
-                          targetSw, dstSw);
-                updatedDevices.add(targetSw);
-                updatedDevices.add(dstSw);
-                doneRoutes.add(route);
-            } else {
-                someFailed = true;
-            }
-
-        }
-        if (!someFailed) {
-            // here is where we update all devices not touched by this instance
-            updatedEcmpSpgMap.keySet().stream()
-                    .filter(devId -> !updatedDevices.contains(devId))
-                    .forEach(devId -> {
-                        currentEcmpSpgMap.put(devId, updatedEcmpSpgMap.get(devId));
-                        log.debug("Updating ECMPspg for remaining dev:{}", devId);
-                    });
-        }
-        return doneRoutes;
-    }
-
-    /**
-     * Edits hash groups in the src-switch (targetSw) of a route-path by
-     * calling the groupHandler to either add or remove buckets in an existing
-     * hash group.
-     *
-     * @param route a single list representing a route-path where the first element
-     *                  is the src-switch (targetSw) of the route-path and the
-     *                  second element is the dst-switch
-     * @param revoke true if buckets in the hash-groups need to be removed;
-     *              false if buckets in the hash-groups need to be added
-     * @return true if the hash group editing is successful
-     */
-    private boolean fixHashGroupsForRoute(ArrayList<DeviceId> route,
-                                          boolean revoke) {
-        DeviceId targetSw = route.get(0);
-        if (route.size() < 2) {
-            log.warn("Cannot fixHashGroupsForRoute - no dstSw in route {}", route);
-            return false;
-        }
-        DeviceId destSw = route.get(1);
-        if (!seenBeforeRoutes.containsEntry(destSw, targetSw)) {
-            log.warn("Cannot fixHashGroupsForRoute {} -> {} has not been programmed before",
-                     targetSw, destSw);
-            return false;
-        }
-        log.debug("* processing fixHashGroupsForRoute: Target {} -> Dest {}",
-                  targetSw, destSw);
-        // figure out the new next hops at the targetSw towards the destSw
-        Set<DeviceId> nextHops = getNextHops(targetSw, destSw);
-        // call group handler to change hash group at targetSw
-        DefaultGroupHandler grpHandler = srManager.getGroupHandler(targetSw);
-        if (grpHandler == null) {
-            log.warn("Cannot find grouphandler for dev:{} .. aborting"
-                    + " {} hash group buckets for route:{} ", targetSw,
-                    (revoke) ? "revoke" : "repopulate", route);
-            return false;
-        }
-        log.debug("{} hash-groups buckets For Route {} -> {} to new next-hops {}",
-                  (revoke) ? "revoke" : "repopulating",
-                  targetSw, destSw, nextHops);
-        return (revoke) ? grpHandler.fixHashGroups(targetSw, nextHops,
-                                                       destSw, true)
-                            : grpHandler.fixHashGroups(targetSw, nextHops,
-                                                       destSw, false);
-    }
-
-    /**
-     * Start the flow rule population process if it was never started. The
-     * process finishes successfully when all flow rules are set and stops with
-     * ABORTED status when any groups required for flows is not set yet.
-     */
-    public void startPopulationProcess() {
-        statusLock.lock();
-        try {
-            if (populationStatus == Status.IDLE
-                    || populationStatus == Status.SUCCEEDED
-                    || populationStatus == Status.ABORTED) {
-                populateAllRoutingRules();
-            } else {
-                log.warn("Not initiating startPopulationProcess as populationStatus is {}",
-                         populationStatus);
-            }
-        } finally {
-            statusLock.unlock();
-        }
-    }
-
-    /**
-     * Revoke rules of given subnet in all edge switches.
-     *
-     * @param subnets subnet being removed
-     * @return true if succeed
-     */
-    protected boolean revokeSubnet(Set<IpPrefix> subnets) {
-        DeviceId targetSw;
-        List<Future<Boolean>> futures = Lists.newArrayList();
-        for (Device sw : srManager.deviceService.getAvailableDevices()) {
-            targetSw = sw.id();
-            if (shouldProgram(targetSw)) {
-                futures.add(routePopulators.submit(new RevokeSubnet(targetSw, subnets)));
-            } else {
-                futures.add(CompletableFuture.completedFuture(true));
-            }
-        }
-        // check the execution of each job
-        return checkJobs(futures);
-    }
-
-    /**
-     * Revoke rules of given subnets in the given switches.
-     *
-     * @param targetSwitches switched from which subnets to be removed
-     * @param subnets subnet bring removed
-     * @return true if succeed
-     */
-    protected boolean revokeSubnet(Set<DeviceId> targetSwitches, Set<IpPrefix> subnets) {
-        List<Future<Boolean>> futures = Lists.newArrayList();
-        for (DeviceId targetSw : targetSwitches) {
-            if (shouldProgram(targetSw)) {
-                futures.add(routePopulators.submit(new RevokeSubnet(targetSw, subnets)));
-            } else {
-                futures.add(CompletableFuture.completedFuture(true));
-            }
-        }
-        // check the execution of each job
-        return checkJobs(futures);
-    }
-
-    private final class RevokeSubnet implements PickyCallable<Boolean> {
-        private DeviceId targetSw;
-        private Set<IpPrefix> subnets;
-
-        /**
-         * Builds a RevokeSubnet task, which provides a result.
-         *
-         * @param subnets a set of prefixes
-         * @param targetSw target switch
-         */
-        RevokeSubnet(DeviceId targetSw, Set<IpPrefix> subnets) {
-            this.targetSw = targetSw;
-            this.subnets = subnets;
-        }
-
-        @Override
-        public Boolean call() throws Exception {
-            return srManager.routingRulePopulator.revokeIpRuleForSubnet(targetSw, subnets);
-        }
-
-        @Override
-        public int hint() {
-            return targetSw.hashCode();
-        }
-    }
-
-    /**
-     * Populates IP rules for a route that has direct connection to the switch
-     * if the current instance is the master of the switch.
-     *
-     * @param deviceId device ID of the device that next hop attaches to
-     * @param prefix IP prefix of the route
-     * @param hostMac MAC address of the next hop
-     * @param hostVlanId Vlan ID of the nexthop
-     * @param outPort port where the next hop attaches to
-     * @param directHost host is of type direct or indirect
-     * @return future that includes the flow objective if succeeded, null if otherwise
-     */
-    CompletableFuture<Objective> populateRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac,
-                                               VlanId hostVlanId, PortNumber outPort, boolean directHost) {
-        if (shouldProgram(deviceId)) {
-            return srManager.routingRulePopulator.populateRoute(deviceId, prefix,
-                    hostMac, hostVlanId, outPort, directHost);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-    /**
-     * Removes IP rules for a route when the next hop is gone.
-     * if the current instance is the master of the switch.
-     *
-     * @param deviceId device ID of the device that next hop attaches to
-     * @param prefix IP prefix of the route
-     * @param hostMac MAC address of the next hop
-     * @param hostVlanId Vlan ID of the nexthop
-     * @param outPort port that next hop attaches to
-     * @param directHost host is of type direct or indirect
-     * @return future that carries the flow objective if succeeded, null if otherwise
-     */
-    CompletableFuture<Objective> revokeRoute(DeviceId deviceId, IpPrefix prefix,
-                     MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
-        if (shouldProgram(deviceId)) {
-            return srManager.routingRulePopulator.revokeRoute(deviceId, prefix, hostMac, hostVlanId,
-                    outPort, directHost);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-    CompletableFuture<Objective> populateBridging(DeviceId deviceId, PortNumber port, MacAddress mac, VlanId vlanId) {
-        if (shouldProgram(deviceId)) {
-            return srManager.routingRulePopulator.populateBridging(deviceId, port, mac, vlanId);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-    CompletableFuture<Objective> revokeBridging(DeviceId deviceId, PortNumber port, MacAddress mac, VlanId vlanId) {
-        if (shouldProgram(deviceId)) {
-            return srManager.routingRulePopulator.revokeBridging(deviceId, port, mac, vlanId);
-        }
-        return CompletableFuture.completedFuture(null);
-    }
-
-    void updateBridging(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
-                        VlanId vlanId, boolean popVlan, boolean install) {
-        if (shouldProgram(deviceId)) {
-            srManager.routingRulePopulator.updateBridging(deviceId, portNum, hostMac, vlanId, popVlan, install);
-        }
-    }
-
-    void updateFwdObj(DeviceId deviceId, PortNumber portNumber, IpPrefix prefix, MacAddress hostMac,
-                      VlanId vlanId, boolean popVlan, boolean install) {
-        if (shouldProgram(deviceId)) {
-            srManager.routingRulePopulator.updateFwdObj(deviceId, portNumber, prefix, hostMac,
-                    vlanId, popVlan, install);
-        }
-    }
-
-    /**
-     * Populates IP rules for a route when the next hop is double-tagged.
-     *
-     * @param deviceId  device ID that next hop attaches to
-     * @param prefix    IP prefix of the route
-     * @param hostMac   MAC address of the next hop
-     * @param innerVlan Inner Vlan ID of the next hop
-     * @param outerVlan Outer Vlan ID of the next hop
-     * @param outerTpid Outer TPID of the next hop
-     * @param outPort   port that the next hop attaches to
-     */
-    void populateDoubleTaggedRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac, VlanId innerVlan,
-                                   VlanId outerVlan, EthType outerTpid, PortNumber outPort) {
-        if (srManager.mastershipService.isLocalMaster(deviceId)) {
-            srManager.routingRulePopulator.populateDoubleTaggedRoute(
-                    deviceId, prefix, hostMac, innerVlan, outerVlan, outerTpid, outPort);
-            srManager.routingRulePopulator.processDoubleTaggedFilter(
-                    deviceId, outPort, outerVlan, innerVlan, true);
-        }
-    }
-
-    /**
-     * Revokes IP rules for a route when the next hop is double-tagged.
-     *
-     * @param deviceId  device ID that next hop attaches to
-     * @param prefix    IP prefix of the route
-     * @param hostMac   MAC address of the next hop
-     * @param innerVlan Inner Vlan ID of the next hop
-     * @param outerVlan Outer Vlan ID of the next hop
-     * @param outerTpid Outer TPID of the next hop
-     * @param outPort   port that the next hop attaches to
-     */
-    void revokeDoubleTaggedRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac, VlanId innerVlan,
-                                 VlanId outerVlan, EthType outerTpid, PortNumber outPort) {
-        // Revoke route either if this node have the mastership (when device is available) or
-        // if this node is the leader (even when device is unavailable)
-        if (!srManager.mastershipService.isLocalMaster(deviceId)) {
-            if (srManager.deviceService.isAvailable(deviceId)) {
-                // Master node will revoke specified rule.
-                log.debug("This node is not a master for {}, stop revoking route.", deviceId);
-                return;
-            }
-
-            // isLocalMaster will return false when the device is unavailable.
-            // Verify if this node is the leader in that case.
-            NodeId leader = srManager.leadershipService.runForLeadership(
-                    deviceId.toString()).leaderNodeId();
-            if (!srManager.clusterService.getLocalNode().id().equals(leader)) {
-                // Leader node will revoke specified rule.
-                log.debug("This node is not a master for {}, stop revoking route.", deviceId);
-                return;
-            }
-        }
-
-        srManager.routingRulePopulator.revokeDoubleTaggedRoute(deviceId, prefix, hostMac,
-                innerVlan, outerVlan, outerTpid, outPort);
-        srManager.routingRulePopulator.processDoubleTaggedFilter(deviceId, outPort, outerVlan, innerVlan, false);
-    }
-
-    /**
-     * Purges seen before routes for a given device.
-     * @param deviceId the device id
-     */
-    void purgeSeenBeforeRoutes(DeviceId deviceId) {
-        log.debug("Purging seen before routes having as target {}", deviceId);
-        Set<Entry<DeviceId, DeviceId>> routesToPurge = seenBeforeRoutes.stream()
-                .filter(entry -> entry.getValue().equals(deviceId))
-                .collect(Collectors.toSet());
-        routesToPurge.forEach(entry -> seenBeforeRoutes.remove(entry.getKey(), entry.getValue()));
-    }
-
-    /**
-     * Remove ECMP graph entry for the given device. Typically called when
-     * device is no longer available.
-     *
-     * @param deviceId the device for which graphs need to be purged
-     */
-    void purgeEcmpGraph(DeviceId deviceId) {
-        statusLock.lock();
-        try {
-            if (populationStatus == Status.STARTED) {
-                log.warn("Previous rule population is not finished. Cannot"
-                        + " proceeed with purgeEcmpGraph for {}", deviceId);
-                return;
-            }
-            log.debug("Updating ECMPspg for unavailable dev:{}", deviceId);
-            currentEcmpSpgMap.remove(deviceId);
-            if (updatedEcmpSpgMap != null) {
-                updatedEcmpSpgMap.remove(deviceId);
-            }
-        } finally {
-            statusLock.unlock();
-        }
-    }
-
-    /**
-     * Attempts a full reroute of route-paths if topology has changed relatively
-     * close to a mastership change event. Does not do a reroute if mastership
-     * change is due to reasons other than a ONOS cluster event - for example a
-     * call to balance-masters, or a switch up/down event.
-     *
-     * @param devId the device identifier for which mastership has changed
-     * @param me the mastership event
-     */
-    void checkFullRerouteForMasterChange(DeviceId devId, MastershipEvent me) {
-        // give small delay to absorb mastership events that are caused by
-        // device that has disconnected from cluster
-        executorServiceMstChg.schedule(new MasterChange(devId, me),
-                                       MASTER_CHANGE_DELAY, TimeUnit.MILLISECONDS);
-    }
-
-    protected final class MasterChange implements Runnable {
-        private DeviceId devId;
-        private MastershipEvent me;
-        private static final long CLUSTER_EVENT_THRESHOLD = 4500; // ms
-        private static final long DEVICE_EVENT_THRESHOLD = 2000; // ms
-        private static final long EDGE_PORT_EVENT_THRESHOLD = 10000; //ms
-        private static final long FULL_REROUTE_THRESHOLD = 10000; // ms
-
-        MasterChange(DeviceId devId, MastershipEvent me) {
-            this.devId = devId;
-            this.me = me;
-        }
-
-        @Override
-        public void run() {
-            long lce = srManager.clusterListener.timeSinceLastClusterEvent();
-            boolean clusterEvent = lce < CLUSTER_EVENT_THRESHOLD;
-
-            // ignore event for lost switch if cluster event hasn't happened -
-            // device down event will handle it
-            if ((me.roleInfo().master() == null
-                    || !srManager.deviceService.isAvailable(devId))
-                    && !clusterEvent) {
-                log.debug("Full reroute not required for lost device: {}/{} "
-                        + "clusterEvent/timeSince: {}/{}",
-                          devId, me.roleInfo(), clusterEvent, lce);
-                return;
-            }
-
-            long update = srManager.deviceService.getLastUpdatedInstant(devId);
-            long lde = Instant.now().toEpochMilli() - update;
-            boolean deviceEvent = lde < DEVICE_EVENT_THRESHOLD;
-
-            // ignore event for recently connected switch if cluster event hasn't
-            // happened - link up events will handle it
-            if (srManager.deviceService.isAvailable(devId) && deviceEvent
-                    && !clusterEvent) {
-                log.debug("Full reroute not required for recently available"
-                        + " device: {}/{} deviceEvent/timeSince: {}/{} "
-                        + "clusterEvent/timeSince: {}/{}",
-                        devId, me.roleInfo(), deviceEvent, lde, clusterEvent, lce);
-                return;
-            }
-
-            long lepe = Instant.now().toEpochMilli()
-                    - srManager.lastEdgePortEvent.toEpochMilli();
-            boolean edgePortEvent = lepe < EDGE_PORT_EVENT_THRESHOLD;
-
-            // if it gets here, then mastership change is likely due to onos
-            // instance failure, or network partition in onos cluster
-            // normally a mastership change like this does not require re-programming
-            // but if topology changes happen at the same time then we may miss events
-            if (!isRoutingStable() && clusterEvent) {
-                log.warn("Mastership changed for dev: {}/{} while programming route-paths "
-                        + "due to clusterEvent {} ms ago .. attempting full reroute",
-                         devId, me.roleInfo(), lce);
-                if (srManager.mastershipService.isLocalMaster(devId)) {
-                    // old master could have died when populating filters
-                    populatePortAddressingRules(devId);
-                }
-                // old master could have died when creating groups
-                // XXX right now we have no fine-grained way to only make changes
-                // for the route paths affected by this device. Thus we do a
-                // full reroute after purging all hash groups. We also try to do
-                // it only once, irrespective of the number of devices
-                // that changed mastership when their master instance died.
-                long lfrr = Instant.now().toEpochMilli() - lastFullReroute.toEpochMilli();
-                boolean doFullReroute = lfrr > FULL_REROUTE_THRESHOLD;
-                if (doFullReroute) {
-                    lastFullReroute = Instant.now();
-                    for (Device dev : srManager.deviceService.getDevices()) {
-                        if (shouldProgram(dev.id())) {
-                            srManager.purgeHashedNextObjectiveStore(dev.id());
-                            seenBeforeRoutes.removeAll(dev.id());
-                        }
-                    }
-                    // give small delay to ensure entire store is purged
-                    executorServiceFRR.schedule(new FullRerouteAfterPurge(),
-                                                PURGE_DELAY,
-                                                TimeUnit.MILLISECONDS);
-                } else {
-                    log.warn("Full reroute attempted {} ms ago .. skipping", lfrr);
-                }
-
-            } else if (edgePortEvent && clusterEvent) {
-                log.warn("Mastership changed for dev: {}/{} due to clusterEvent {} ms ago "
-                        + "while edge-port event happened {} ms ago "
-                        + " .. reprogramming all edge-ports",
-                         devId, me.roleInfo(), lce, lepe);
-                if (shouldProgram(devId)) {
-                    srManager.deviceService.getPorts(devId).stream()
-                        .filter(p -> srManager.interfaceService
-                                .isConfigured(new ConnectPoint(devId, p.number())))
-                        .forEach(p -> srManager.processPortUpdated(devId, p));
-                }
-
-            } else {
-                log.debug("Stable route-paths .. full reroute not attempted for "
-                        + "mastership change {}/{} deviceEvent/timeSince: {}/{} "
-                        + "clusterEvent/timeSince: {}/{}", devId, me.roleInfo(),
-                        deviceEvent, lde, clusterEvent, lce);
-            }
-        }
-    }
-
-    /**
-     * Performs a full reroute of routing rules in all the switches. Assumes
-     * caller has purged hash groups from the nextObjective store, otherwise
-     * re-uses ones available in the store.
-     */
-    protected final class FullRerouteAfterPurge implements Runnable {
-        @Override
-        public void run() {
-            populateAllRoutingRules();
-        }
-    }
-
-
-    //////////////////////////////////////
-    //  Routing helper methods and classes
-    //////////////////////////////////////
-
-    /**
-     * Computes set of affected routes due to failed link. Assumes previous ecmp
-     * shortest-path graph exists for a switch in order to compute affected
-     * routes. If such a graph does not exist, the method returns null.
-     *
-     * @param linkFail the failed link
-     * @return the set of affected routes which may be empty if no routes were
-     *         affected
-     */
-    private Set<ArrayList<DeviceId>> computeDamagedRoutes(Link linkFail) {
-        Set<ArrayList<DeviceId>> routes = new HashSet<>();
-
-        for (Device sw : srManager.deviceService.getDevices()) {
-            log.debug("Computing the impacted routes for device {} due to link fail",
-                      sw.id());
-            if (!shouldProgram(sw.id())) {
-                lastProgrammed.remove(sw.id());
-                continue;
-            }
-            for (DeviceId rootSw : deviceAndItsPair(sw.id())) {
-                // check for mastership change since last run
-                if (!lastProgrammed.contains(sw.id())) {
-                    log.warn("New responsibility for this node to program dev:{}"
-                            + " ... nuking current ECMPspg", sw.id());
-                    currentEcmpSpgMap.remove(sw.id());
-                }
-                lastProgrammed.add(sw.id());
-
-                EcmpShortestPathGraph ecmpSpg = currentEcmpSpgMap.get(rootSw);
-                if (ecmpSpg == null) {
-                    log.warn("No existing ECMP graph for switch {}. Assuming "
-                            + "all route-paths have changed towards it.", rootSw);
-                    for (DeviceId targetSw : srManager.deviceConfiguration.getRouters()) {
-                        if (targetSw.equals(rootSw)) {
-                            continue;
-                        }
-                        routes.add(Lists.newArrayList(targetSw, rootSw));
-                        log.debug("Impacted route:{}->{}", targetSw, rootSw);
-                    }
-                    continue;
-                }
-
-                if (log.isDebugEnabled()) {
-                    log.debug("Root switch: {}", rootSw);
-                    log.debug("  Current/Existing SPG: {}", ecmpSpg);
-                    log.debug("       New/Updated SPG: {}", updatedEcmpSpgMap.get(rootSw));
-                }
-                HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>>
-                    switchVia = ecmpSpg.getAllLearnedSwitchesAndVia();
-                // figure out if the broken link affected any route-paths in this graph
-                for (Integer itrIdx : switchVia.keySet()) {
-                    log.trace("Current/Exiting SPG Iterindex# {}", itrIdx);
-                    HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
-                            switchVia.get(itrIdx);
-                    for (DeviceId targetSw : swViaMap.keySet()) {
-                        log.trace("TargetSwitch {} --> RootSwitch {}",
-                                  targetSw, rootSw);
-                        for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
-                            log.trace(" Via:");
-                            via.forEach(e -> log.trace("  {}", e));
-                        }
-                        Set<ArrayList<DeviceId>> subLinks =
-                                computeLinks(targetSw, rootSw, swViaMap);
-                        for (ArrayList<DeviceId> alink: subLinks) {
-                            if ((alink.get(0).equals(linkFail.src().deviceId()) &&
-                                    alink.get(1).equals(linkFail.dst().deviceId()))
-                                    ||
-                                    (alink.get(0).equals(linkFail.dst().deviceId()) &&
-                                         alink.get(1).equals(linkFail.src().deviceId()))) {
-                                log.debug("Impacted route:{}->{}", targetSw, rootSw);
-                                ArrayList<DeviceId> aRoute = new ArrayList<>();
-                                aRoute.add(targetSw); // switch with rules to populate
-                                aRoute.add(rootSw); // towards this destination
-                                routes.add(aRoute);
-                                break;
-                            }
-                        }
-                    }
-                }
-
-            }
-
-        }
-        return routes;
-    }
-
-    /**
-     * Computes set of affected routes due to new links or failed switches.
-     *
-     * @param failedSwitch deviceId of failed switch if any
-     * @return the set of affected routes which may be empty if no routes were
-     *         affected
-     */
-    private Set<ArrayList<DeviceId>> computeRouteChange(DeviceId failedSwitch) {
-        ImmutableSet.Builder<ArrayList<DeviceId>> changedRtBldr =
-                ImmutableSet.builder();
-
-        for (Device sw : srManager.deviceService.getDevices()) {
-            log.debug("Computing the impacted routes for device {}", sw.id());
-            if (!shouldProgram(sw.id())) {
-                lastProgrammed.remove(sw.id());
-                continue;
-            }
-            for (DeviceId rootSw : deviceAndItsPair(sw.id())) {
-                if (log.isTraceEnabled()) {
-                    log.trace("Device links for dev: {}", rootSw);
-                    for (Link link: srManager.linkService.getDeviceLinks(rootSw)) {
-                        log.trace("{} -> {} ", link.src().deviceId(),
-                                  link.dst().deviceId());
-                    }
-                }
-                // check for mastership change since last run
-                if (!lastProgrammed.contains(sw.id())) {
-                    log.warn("New responsibility for this node to program dev:{}"
-                            + " ... nuking current ECMPspg", sw.id());
-                    currentEcmpSpgMap.remove(sw.id());
-                }
-                lastProgrammed.add(sw.id());
-                EcmpShortestPathGraph currEcmpSpg = currentEcmpSpgMap.get(rootSw);
-                if (currEcmpSpg == null) {
-                    log.debug("No existing ECMP graph for device {}.. adding self as "
-                            + "changed route", rootSw);
-                    changedRtBldr.add(Lists.newArrayList(rootSw));
-                    continue;
-                }
-                EcmpShortestPathGraph newEcmpSpg = updatedEcmpSpgMap.get(rootSw);
-                if (newEcmpSpg == null) {
-                    log.warn("Cannot find updated ECMP graph for dev:{}", rootSw);
-                    continue;
-                }
-                if (log.isDebugEnabled()) {
-                    log.debug("Root switch: {}", rootSw);
-                    log.debug("  Current/Existing SPG: {}", currEcmpSpg);
-                    log.debug("       New/Updated SPG: {}", newEcmpSpg);
-                }
-                // first use the updated/new map to compare to current/existing map
-                // as new links may have come up
-                changedRtBldr.addAll(compareGraphs(newEcmpSpg, currEcmpSpg, rootSw));
-                // then use the current/existing map to compare to updated/new map
-                // as switch may have been removed
-                changedRtBldr.addAll(compareGraphs(currEcmpSpg, newEcmpSpg, rootSw));
-            }
-        }
-
-        // handle clearing state for a failed switch in case the switch does
-        // not have a pair, or the pair is not available
-        if (failedSwitch != null) {
-            Optional<DeviceId> pairDev = srManager.getPairDeviceId(failedSwitch);
-            if (!pairDev.isPresent() || !srManager.deviceService.isAvailable(pairDev.get())) {
-                log.debug("Proxy Route changes to downed Sw:{}", failedSwitch);
-                srManager.deviceService.getDevices().forEach(dev -> {
-                    if (!dev.id().equals(failedSwitch) &&
-                            srManager.mastershipService.isLocalMaster(dev.id())) {
-                        log.debug(" : {}", dev.id());
-                        changedRtBldr.add(Lists.newArrayList(dev.id(), failedSwitch));
-                    }
-                });
-            }
-        }
-
-        Set<ArrayList<DeviceId>> changedRoutes = changedRtBldr.build();
-        for (ArrayList<DeviceId> route: changedRoutes) {
-            log.debug("Route changes Target -> Root");
-            if (route.size() == 1) {
-                log.debug(" : all -> {}", route.get(0));
-            } else {
-                log.debug(" : {} -> {}", route.get(0), route.get(1));
-            }
-        }
-        return changedRoutes;
-    }
-
-    // Utility method to expands the route changes in two elements array using
-    // the ECMP graph. Caller represents all to dst switch routes with an
-    // array containing only the dst switch.
-    private Set<ArrayList<DeviceId>> getExpandedRoutes(Set<ArrayList<DeviceId>> routeChanges) {
-        Set<ArrayList<DeviceId>> changedRoutes = new HashSet<>();
-        // Ensure each routeChanges entry has two elements
-        for (ArrayList<DeviceId> route : routeChanges) {
-            if (route.size() == 1) {
-                DeviceId dstSw = route.get(0);
-                EcmpShortestPathGraph ec = updatedEcmpSpgMap.get(dstSw);
-                if (ec == null) {
-                    log.warn("No graph found for {} .. aborting redoRouting", dstSw);
-                    return Collections.emptySet();
-                }
-                ec.getAllLearnedSwitchesAndVia().keySet().forEach(key -> {
-                    ec.getAllLearnedSwitchesAndVia().get(key).keySet().forEach(target -> {
-                        changedRoutes.add(Lists.newArrayList(target, dstSw));
-                    });
-                });
-            } else {
-                DeviceId targetSw = route.get(0);
-                DeviceId dstSw = route.get(1);
-                changedRoutes.add(Lists.newArrayList(targetSw, dstSw));
-            }
-        }
-        return changedRoutes;
-    }
-
-    // Utility method to expands the route changes in two elements array using
-    // the available devices. Caller represents all to dst switch routes with an
-    // array containing only the dst switch.
-    private Set<ArrayList<DeviceId>> getAllExpandedRoutes(Set<ArrayList<DeviceId>> routeChanges) {
-        Set<ArrayList<DeviceId>> changedRoutes = new HashSet<>();
-        // Ensure each routeChanges entry has two elements
-        for (ArrayList<DeviceId> route : routeChanges) {
-            if (route.size() == 1) {
-                // route-path changes are from everyone else to this switch
-                DeviceId dstSw = route.get(0);
-                srManager.deviceService.getAvailableDevices().forEach(sw -> {
-                    if (!sw.id().equals(dstSw)) {
-                        changedRoutes.add(Lists.newArrayList(sw.id(), dstSw));
-                    }
-                });
-            } else {
-                changedRoutes.add(route);
-            }
-        }
-        return changedRoutes;
-    }
-
-    /**
-     * For the root switch, searches all the target nodes reachable in the base
-     * graph, and compares paths to the ones in the comp graph.
-     *
-     * @param base the graph that is indexed for all reachable target nodes
-     *             from the root node
-     * @param comp the graph that the base graph is compared to
-     * @param rootSw  both ecmp graphs are calculated for the root node
-     * @return all the routes that have changed in the base graph
-     */
-    private Set<ArrayList<DeviceId>> compareGraphs(EcmpShortestPathGraph base,
-                                                   EcmpShortestPathGraph comp,
-                                                   DeviceId rootSw) {
-        ImmutableSet.Builder<ArrayList<DeviceId>> changedRoutesBuilder =
-                ImmutableSet.builder();
-        HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> baseMap =
-                base.getAllLearnedSwitchesAndVia();
-        HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> compMap =
-                comp.getAllLearnedSwitchesAndVia();
-        for (Integer itrIdx : baseMap.keySet()) {
-            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> baseViaMap =
-                    baseMap.get(itrIdx);
-            for (DeviceId targetSw : baseViaMap.keySet()) {
-                ArrayList<ArrayList<DeviceId>> basePath = baseViaMap.get(targetSw);
-                ArrayList<ArrayList<DeviceId>> compPath = getVia(compMap, targetSw);
-                if ((compPath == null) || !basePath.equals(compPath)) {
-                    log.trace("Impacted route:{} -> {}", targetSw, rootSw);
-                    ArrayList<DeviceId> route = new ArrayList<>();
-                    route.add(targetSw); // switch with rules to populate
-                    route.add(rootSw); // towards this destination
-                    changedRoutesBuilder.add(route);
-                }
-            }
-        }
-        return changedRoutesBuilder.build();
-    }
-
-    /**
-     * Returns the ECMP paths traversed to reach the target switch.
-     *
-     * @param switchVia a per-iteration view of the ECMP graph for a root switch
-     * @param targetSw the switch to reach from the root switch
-     * @return the nodes traversed on ECMP paths to the target switch
-     */
-    private ArrayList<ArrayList<DeviceId>> getVia(HashMap<Integer, HashMap<DeviceId,
-            ArrayList<ArrayList<DeviceId>>>> switchVia, DeviceId targetSw) {
-        for (Integer itrIdx : switchVia.keySet()) {
-            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
-                    switchVia.get(itrIdx);
-            if (swViaMap.get(targetSw) == null) {
-                continue;
-            } else {
-                return swViaMap.get(targetSw);
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Utility method to break down a path from src to dst device into a collection
-     * of links.
-     *
-     * @param src src device of the path
-     * @param dst dst device of the path
-     * @param viaMap path taken from src to dst device
-     * @return collection of links in the path
-     */
-    private Set<ArrayList<DeviceId>> computeLinks(DeviceId src,
-                                                  DeviceId dst,
-                       HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> viaMap) {
-        Set<ArrayList<DeviceId>> subLinks = Sets.newHashSet();
-        for (ArrayList<DeviceId> via : viaMap.get(src)) {
-            DeviceId linkSrc = src;
-            DeviceId linkDst = dst;
-            for (DeviceId viaDevice: via) {
-                ArrayList<DeviceId> link = new ArrayList<>();
-                linkDst = viaDevice;
-                link.add(linkSrc);
-                link.add(linkDst);
-                subLinks.add(link);
-                linkSrc = viaDevice;
-            }
-            ArrayList<DeviceId> link = new ArrayList<>();
-            link.add(linkSrc);
-            link.add(dst);
-            subLinks.add(link);
-        }
-
-        return subLinks;
-    }
-
-    /**
-     * Determines whether this controller instance should program the
-     * given {@code deviceId}, based on mastership and pairDeviceId if one exists.
-     * <p>
-     * Once an instance is elected, it will be the only instance responsible for programming
-     * both devices in the pair until it goes down.
-     *
-     * @param deviceId device identifier to consider for routing
-     * @return true if current instance should handle the routing for given device
-     */
-    boolean shouldProgram(DeviceId deviceId) {
-        Boolean cached = shouldProgramCache.get(deviceId);
-        if (cached != null) {
-            log.debug("shouldProgram dev:{} cached:{}", deviceId, cached);
-            return cached;
-        }
-
-        Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(deviceId);
-
-        NodeId currentNodeId = srManager.clusterService.getLocalNode().id();
-        NodeId masterNodeId = srManager.mastershipService.getMasterFor(deviceId);
-        Optional<NodeId> pairMasterNodeId = pairDeviceId.map(srManager.mastershipService::getMasterFor);
-        log.debug("Evaluate shouldProgram {}/pair={}. currentNodeId={}, master={}, pairMaster={}",
-                deviceId, pairDeviceId, currentNodeId, masterNodeId, pairMasterNodeId);
-
-        // No pair device configured. Only handle when current instance is the master of the device
-        if (!pairDeviceId.isPresent()) {
-            log.debug("No pair device. currentNodeId={}, master={}", currentNodeId, masterNodeId);
-            return currentNodeId.equals(masterNodeId);
-        }
-
-        // Should not handle if current instance is not the master of either switch
-        if (!currentNodeId.equals(masterNodeId) &&
-                !(pairMasterNodeId.isPresent() && currentNodeId.equals(pairMasterNodeId.get()))) {
-            log.debug("Current nodeId {} is neither the master of target device {} nor pair device {}",
-                    currentNodeId, deviceId, pairDeviceId);
-            return false;
-        }
-
-        Set<DeviceId> key = Sets.newHashSet(deviceId, pairDeviceId.get());
-
-        NodeId king = shouldProgram.compute(key, ((k, v) -> {
-            if (v == null) {
-                // There is no value in the map. Elect a node
-                return elect(Lists.newArrayList(masterNodeId, pairMasterNodeId.orElse(null)));
-            } else {
-                if (v.equals(masterNodeId) || v.equals(pairMasterNodeId.orElse(null))) {
-                    // Use the node in the map if it is still alive and is a master of any of the two switches
-                    return v;
-                } else {
-                    // Previously elected node is no longer the master of either switch. Re-elect a node.
-                    return elect(Lists.newArrayList(masterNodeId, pairMasterNodeId.orElse(null)));
-                }
-            }
-        }));
-
-        if (king != null) {
-            log.debug("{} is king, should handle routing for {}/pair={}", king, deviceId, pairDeviceId);
-            shouldProgramCache.put(deviceId, king.equals(currentNodeId));
-            return king.equals(currentNodeId);
-        } else {
-            log.error("Fail to elect a king for {}/pair={}. Abort.", deviceId, pairDeviceId);
-            shouldProgramCache.remove(deviceId);
-            return false;
-        }
-    }
-
-    /**
-     * Elects a node who should take responsibility of programming devices.
-     * @param nodeIds list of candidate node ID
-     *
-     * @return NodeId of the node that gets elected, or null if none of the node can be elected
-     */
-    private NodeId elect(List<NodeId> nodeIds) {
-        // Remove all null elements. This could happen when some device has no master
-        nodeIds.removeAll(Collections.singleton(null));
-        nodeIds.sort(null);
-        return nodeIds.size() == 0 ? null : nodeIds.get(0);
-    }
-
-    void invalidateShouldProgramCache(DeviceId deviceId) {
-        shouldProgramCache.remove(deviceId);
-    }
-
-    /**
-     * Returns a set of device ID, containing given device and its pair device if exist.
-     *
-     * @param deviceId Device ID
-     * @return a set of device ID, containing given device and its pair device if exist.
-     */
-    private Set<DeviceId> deviceAndItsPair(DeviceId deviceId) {
-        Set<DeviceId> ret = Sets.newHashSet(deviceId);
-        srManager.getPairDeviceId(deviceId).ifPresent(ret::add);
-        return ret;
-    }
-
-    /**
-     * Returns the set of deviceIds which are the next hops from the targetSw
-     * to the dstSw according to the latest ECMP spg.
-     *
-     * @param targetSw the switch for which the next-hops are desired
-     * @param dstSw the switch to which the next-hops lead to from the targetSw
-     * @return set of next hop deviceIds, could be empty if no next hops are found
-     */
-    private Set<DeviceId> getNextHops(DeviceId targetSw, DeviceId dstSw) {
-        boolean targetIsEdge = false;
-        try {
-            targetIsEdge = srManager.deviceConfiguration.isEdgeDevice(targetSw);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + "Cannot determine if targetIsEdge {}.. "
-                    + "continuing to getNextHops", targetSw);
-        }
-
-        EcmpShortestPathGraph ecmpSpg = updatedEcmpSpgMap.get(dstSw);
-        if (ecmpSpg == null) {
-            log.debug("No ecmpSpg found for dstSw: {}", dstSw);
-            return ImmutableSet.of();
-        }
-        HashMap<Integer,
-            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> switchVia =
-                ecmpSpg.getAllLearnedSwitchesAndVia();
-        for (Integer itrIdx : switchVia.keySet()) {
-            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swViaMap =
-                    switchVia.get(itrIdx);
-            for (DeviceId target : swViaMap.keySet()) {
-                if (!target.equals(targetSw)) {
-                    continue;
-                }
-                // optimization for spines to not use leaves to get
-                // to a spine or other leaves. Also leaves should not use other
-                // leaves to get to the destination
-                if ((!targetIsEdge && itrIdx > 1) || targetIsEdge) {
-                    boolean pathdevIsEdge = false;
-                    for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
-                        log.debug("Evaluating next-hop in path: {}", via);
-                        for (DeviceId pathdev : via) {
-                            try {
-                                pathdevIsEdge = srManager.deviceConfiguration
-                                        .isEdgeDevice(pathdev);
-                            } catch (DeviceConfigNotFoundException e) {
-                                log.warn(e.getMessage());
-                            }
-                            if (pathdevIsEdge) {
-                                log.debug("Avoiding {} hop path for targetSw:{}"
-                                        + " --> dstSw:{} which goes through an edge"
-                                        + " device {} in path {}", itrIdx,
-                                          targetSw, dstSw, pathdev, via);
-                                return ImmutableSet.of();
-                            }
-                        }
-                    }
-                }
-                Set<DeviceId> nextHops = new HashSet<>();
-                for (ArrayList<DeviceId> via : swViaMap.get(targetSw)) {
-                    if (via.isEmpty()) {
-                        // the dstSw is the next-hop from the targetSw
-                        nextHops.add(dstSw);
-                    } else {
-                        // first elem is next-hop in each ECMP path
-                        nextHops.add(via.get(0));
-                    }
-                }
-                log.debug("target {} --> dst: {} has next-hops:{}", targetSw,
-                          dstSw, nextHops);
-                return nextHops;
-            }
-        }
-        log.debug("No next hops found for target:{} --> dst: {}", targetSw, dstSw);
-        return ImmutableSet.of(); //no next-hops found
-    }
-
-    //////////////////////////////////////
-    //  Filtering rule creation
-    //////////////////////////////////////
-
-    /**
-     * Populates filtering rules for port, and punting rules
-     * for gateway IPs, loopback IPs and arp/ndp traffic.
-     * Should only be called by the master instance for this device/port.
-     *
-     * @param deviceId Switch ID to set the rules
-     */
-    void populatePortAddressingRules(DeviceId deviceId) {
-        // Although device is added, sometimes device store does not have the
-        // ports for this device yet. It results in missing filtering rules in the
-        // switch. We will attempt it a few times. If it still does not work,
-        // user can manually repopulate using CLI command sr-reroute-network
-        PortFilterInfo firstRun = rulePopulator.populateVlanMacFilters(deviceId);
-        if (firstRun == null) {
-            firstRun = new PortFilterInfo(0, 0, 0);
-        }
-        executorService.schedule(new RetryFilters(deviceId, firstRun),
-                                 RETRY_INTERVAL_MS, TimeUnit.MILLISECONDS);
-    }
-
-    /**
-     * RetryFilters populates filtering objectives for a device and keeps retrying
-     * till the number of ports filtered are constant for a predefined number
-     * of attempts.
-     */
-    protected final class RetryFilters implements Runnable {
-        int constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS;
-        DeviceId devId;
-        int counter;
-        PortFilterInfo prevRun;
-
-        private RetryFilters(DeviceId deviceId, PortFilterInfo previousRun) {
-            devId = deviceId;
-            prevRun = previousRun;
-            counter = 0;
-        }
-
-        @Override
-        public void run() {
-            log.debug("RETRY FILTER ATTEMPT {} ** dev:{}", ++counter, devId);
-            PortFilterInfo thisRun = rulePopulator.populateVlanMacFilters(devId);
-            boolean sameResult = prevRun.equals(thisRun);
-            log.debug("dev:{} prevRun:{} thisRun:{} sameResult:{}", devId, prevRun,
-                      thisRun, sameResult);
-            if (thisRun == null || !sameResult || (--constantAttempts > 0)) {
-                // exponentially increasing intervals for retries
-                executorService.schedule(this,
-                    RETRY_INTERVAL_MS * (int) Math.pow(counter, RETRY_INTERVAL_SCALE),
-                    TimeUnit.MILLISECONDS);
-                if (!sameResult) {
-                    constantAttempts = MAX_CONSTANT_RETRY_ATTEMPTS; //reset
-                }
-            }
-            prevRun = (thisRun == null) ? prevRun : thisRun;
-        }
-    }
-
-    // Check jobs completion. It returns false if one of the job fails
-    // and cancel the remaining
-    private boolean checkJobs(List<Future<Boolean>> futures) {
-        boolean completed = true;
-        for (Future<Boolean> future : futures) {
-            try {
-                if (completed) {
-                    if (!future.get()) {
-                        completed = false;
-                    }
-                } else {
-                    future.cancel(true);
-                }
-            } catch (InterruptedException | ExecutionException e) {
-                completed = false;
-            }
-        }
-        return completed;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java b/app/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
deleted file mode 100644
index 0fd7c07..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/DefaultTunnel.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import java.util.List;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Default Tunnel class.
- */
-public class DefaultTunnel implements Tunnel {
-
-    private final String id;
-    private final List<Integer> labelIds;
-
-    private int groupId;
-    private boolean allowedToRemoveGroup;
-
-    /**
-     * Creates a Tunnel reference.
-     *
-     * @param tid  Tunnel ID
-     * @param labelIds Label stack of the tunnel
-     */
-    public DefaultTunnel(String tid, List<Integer> labelIds) {
-        this.id = checkNotNull(tid);
-        this.labelIds = labelIds;
-        //TODO: need to register the class in Kryo for this
-        //this.labelIds = Collections.unmodifiableList(labelIds);
-        this.groupId = -1;
-    }
-
-    /**
-     * Creates a new DefaultTunnel reference using the tunnel reference.
-     *
-     * @param tunnel DefaultTunnel reference
-     */
-    public DefaultTunnel(DefaultTunnel tunnel) {
-        this.id = tunnel.id;
-        this.labelIds = tunnel.labelIds;
-        this.groupId = tunnel.groupId;
-    }
-
-    @Override
-    public String id() {
-        return this.id;
-    }
-
-    @Override
-    public List<Integer> labelIds() {
-        return this.labelIds;
-    }
-
-    @Override
-    public int groupId() {
-        return this.groupId;
-    }
-
-    @Override
-    public void setGroupId(int id) {
-        this.groupId = id;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (o instanceof DefaultTunnel) {
-            DefaultTunnel tunnel = (DefaultTunnel) o;
-            // We compare only the tunnel paths.
-            if (tunnel.labelIds.equals(this.labelIds)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return labelIds.hashCode();
-    }
-
-    @Override
-    public boolean isAllowedToRemoveGroup() {
-        return this.allowedToRemoveGroup;
-    }
-
-    @Override
-    public void allowToRemoveGroup(boolean b) {
-        this.allowedToRemoveGroup = b;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/EcmpShortestPathGraph.java b/app/src/main/java/org/onosproject/segmentrouting/EcmpShortestPathGraph.java
deleted file mode 100644
index 036044c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/EcmpShortestPathGraph.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.graph.ScalarWeight;
-import org.onosproject.net.DefaultLink;
-import org.onosproject.net.DefaultPath;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.Path;
-import org.onosproject.net.provider.ProviderId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Sets;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Set;
-
-/**
- * This class creates breadth-first-search (BFS) tree for a given root device
- * and returns paths from the root Device to leaf Devices (target devices).
- * The paths are snapshot paths at the point of the class instantiation.
- */
-public class EcmpShortestPathGraph {
-    LinkedList<DeviceId> deviceQueue = new LinkedList<>();
-    LinkedList<Integer> distanceQueue = new LinkedList<>();
-    HashMap<DeviceId, Integer> deviceSearched = new HashMap<>();
-    HashMap<DeviceId, ArrayList<Link>> upstreamLinks = new HashMap<>();
-    HashMap<DeviceId, ArrayList<Path>> paths = new HashMap<>();
-    HashMap<Integer, ArrayList<DeviceId>> distanceDeviceMap = new HashMap<>();
-    DeviceId rootDevice;
-    private SegmentRoutingManager srManager;
-    private static final Logger log = LoggerFactory.getLogger(EcmpShortestPathGraph.class);
-
-    /**
-     * Constructor.
-     *
-     * @param rootDevice root of the BFS tree
-     * @param srManager SegmentRoutingManager object
-     */
-    public EcmpShortestPathGraph(DeviceId rootDevice, SegmentRoutingManager srManager) {
-        this.rootDevice = rootDevice;
-        this.srManager = srManager;
-        calcECMPShortestPathGraph();
-    }
-
-    /**
-     * Calculates the BFS tree.
-     */
-   private void calcECMPShortestPathGraph() {
-        deviceQueue.add(rootDevice);
-        int currDistance = 0;
-        distanceQueue.add(currDistance);
-        deviceSearched.put(rootDevice, currDistance);
-        while (!deviceQueue.isEmpty()) {
-            DeviceId sw = deviceQueue.poll();
-            Set<DeviceId> prevSw = Sets.newHashSet();
-            currDistance = distanceQueue.poll();
-
-            for (Link link : srManager.linkHandler.getDeviceEgressLinks(sw)) {
-                if (srManager.linkHandler.avoidLink(link)) {
-                    continue;
-                }
-                DeviceId reachedDevice = link.dst().deviceId();
-                if (prevSw.contains(reachedDevice)) {
-                    // Ignore LAG links between the same set of Devices
-                    continue;
-                } else  {
-                    prevSw.add(reachedDevice);
-                }
-
-                Integer distance = deviceSearched.get(reachedDevice);
-                if ((distance != null) && (distance < (currDistance + 1))) {
-                    continue;
-                }
-                if (distance == null) {
-                    // First time visiting this Device node
-                    deviceQueue.add(reachedDevice);
-                    distanceQueue.add(currDistance + 1);
-                    deviceSearched.put(reachedDevice, currDistance + 1);
-
-                    ArrayList<DeviceId> distanceSwArray = distanceDeviceMap
-                            .get(currDistance + 1);
-                    if (distanceSwArray == null) {
-                        distanceSwArray = new ArrayList<>();
-                        distanceSwArray.add(reachedDevice);
-                        distanceDeviceMap.put(currDistance + 1, distanceSwArray);
-                    } else {
-                        distanceSwArray.add(reachedDevice);
-                    }
-                }
-
-                ArrayList<Link> upstreamLinkArray =
-                        upstreamLinks.get(reachedDevice);
-                if (upstreamLinkArray == null) {
-                    upstreamLinkArray = new ArrayList<>();
-                    upstreamLinkArray.add(copyDefaultLink(link));
-                    //upstreamLinkArray.add(link);
-                    upstreamLinks.put(reachedDevice, upstreamLinkArray);
-                } else {
-                    // ECMP links
-                    upstreamLinkArray.add(copyDefaultLink(link));
-                }
-            }
-        }
-    }
-
-    private void getDFSPaths(DeviceId dstDeviceDeviceId, Path path, ArrayList<Path> paths) {
-        DeviceId rootDeviceDeviceId = rootDevice;
-        for (Link upstreamLink : upstreamLinks.get(dstDeviceDeviceId)) {
-            /* Deep clone the path object */
-            Path sofarPath;
-            ArrayList<Link> sofarLinks = new ArrayList<>();
-            if (path != null && !path.links().isEmpty()) {
-                sofarLinks.addAll(path.links());
-            }
-            sofarLinks.add(upstreamLink);
-            sofarPath = new DefaultPath(ProviderId.NONE, sofarLinks, ScalarWeight.toWeight(0));
-            if (upstreamLink.src().deviceId().equals(rootDeviceDeviceId)) {
-                paths.add(sofarPath);
-                return;
-            } else {
-                getDFSPaths(upstreamLink.src().deviceId(), sofarPath, paths);
-            }
-        }
-    }
-
-    /**
-     * Return root Device for the graph.
-     *
-     * @return root Device
-     */
-    public DeviceId getRootDevice() {
-        return rootDevice;
-    }
-
-    /**
-     * Return the computed ECMP paths from the root Device to a given Device in
-     * the network.
-     *
-     * @param targetDevice the target Device
-     * @return the list of ECMP Paths from the root Device to the target Device
-     */
-    public ArrayList<Path> getECMPPaths(DeviceId targetDevice) {
-        ArrayList<Path> pathArray = paths.get(targetDevice);
-        if (pathArray == null && deviceSearched.containsKey(
-                targetDevice)) {
-            pathArray = new ArrayList<>();
-            DeviceId sw = targetDevice;
-            getDFSPaths(sw, null, pathArray);
-            paths.put(targetDevice, pathArray);
-        }
-        return pathArray;
-    }
-
-    /**
-     * 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 Devices learned in multiple Dijkstra
-     *         iterations and corresponding ECMP paths to it from the root
-     *         Device
-     */
-    public HashMap<Integer, HashMap<DeviceId,
-            ArrayList<Path>>> getCompleteLearnedDeviceesAndPaths() {
-
-        HashMap<Integer, HashMap<DeviceId, ArrayList<Path>>> pathGraph = new HashMap<>();
-
-        for (Integer itrIndx : distanceDeviceMap.keySet()) {
-            HashMap<DeviceId, ArrayList<Path>> swMap = new HashMap<>();
-            for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
-                swMap.put(sw, getECMPPaths(sw));
-            }
-            pathGraph.put(itrIndx, swMap);
-        }
-
-        return pathGraph;
-    }
-
-    /**
-     * Returns the complete info of the computed ECMP paths for each target device
-     * learned in multiple iterations from the root Device. The computed info
-     * returned is per iteration (Integer key of outer HashMap). In each
-     * iteration, for the target devices reached (DeviceId key of inner HashMap),
-     * the ECMP paths are detailed (2D array).
-     *
-     * @return the hash table of target Devices learned in multiple Dijkstra
-     *         iterations and corresponding ECMP paths in terms of Devices to
-     *         be traversed (via) from the root Device to the target Device
-     */
-    public HashMap<Integer, HashMap<DeviceId,
-            ArrayList<ArrayList<DeviceId>>>> getAllLearnedSwitchesAndVia() {
-
-        HashMap<Integer, HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>>> deviceViaMap = new HashMap<>();
-
-        for (Integer itrIndx : distanceDeviceMap.keySet()) {
-            HashMap<DeviceId, ArrayList<ArrayList<DeviceId>>> swMap = new HashMap<>();
-
-            for (DeviceId sw : distanceDeviceMap.get(itrIndx)) {
-                ArrayList<ArrayList<DeviceId>> swViaArray = new ArrayList<>();
-                for (Path path : getECMPPaths(sw)) {
-                    ArrayList<DeviceId> swVia = new ArrayList<>();
-                    for (Link link : path.links()) {
-                        if (link.src().deviceId().equals(rootDevice)) {
-                            /* No need to add the root Device again in
-                             * the Via list
-                             */
-                            continue;
-                        }
-                        swVia.add(link.src().deviceId());
-                    }
-                    swViaArray.add(swVia);
-                }
-                swMap.put(sw, swViaArray);
-            }
-            deviceViaMap.put(itrIndx, swMap);
-        }
-        return deviceViaMap;
-    }
-
-
-    private Link copyDefaultLink(Link link) {
-        DefaultLink src = (DefaultLink) link;
-        DefaultLink defaultLink = DefaultLink.builder()
-                .providerId(src.providerId())
-                .src(src.src())
-                .dst(src.dst())
-                .type(src.type())
-                .annotations(src.annotations())
-                .build();
-
-        return defaultLink;
-    }
-
-    @Override
-    public String toString() {
-        StringBuilder sBuilder = new StringBuilder();
-        for (Device device: srManager.deviceService.getDevices()) {
-            if (!device.id().equals(rootDevice)) {
-                sBuilder.append("\r\n  Paths from " + rootDevice + " to "
-                                + device.id());
-                ArrayList<Path> paths = getECMPPaths(device.id());
-                if (paths != null) {
-                    for (Path path : paths) {
-                        sBuilder.append("\r\n       == "); // equal cost paths delimiter
-                        for (int i = path.links().size() - 1; i >= 0; i--) {
-                            Link link = path.links().get(i);
-                            sBuilder.append(" : " + link.src() + " -> " + link.dst());
-                        }
-                    }
-                } else {
-                    sBuilder.append("\r\n       == no paths");
-                }
-            }
-        }
-        return sBuilder.toString();
-    }
-}
-
diff --git a/app/src/main/java/org/onosproject/segmentrouting/EdgePair.java b/app/src/main/java/org/onosproject/segmentrouting/EdgePair.java
deleted file mode 100644
index 571e87f..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/EdgePair.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Represents two devices that are paired by configuration. An EdgePair for
- * (dev1, dev2) is the same as as EdgePair for (dev2, dev1)
- */
-public final class EdgePair {
-    DeviceId dev1;
-    DeviceId dev2;
-
-    EdgePair(DeviceId dev1, DeviceId dev2) {
-        this.dev1 = dev1;
-        this.dev2 = dev2;
-    }
-
-    boolean includes(DeviceId dev) {
-        return dev1.equals(dev) || dev2.equals(dev);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof EdgePair)) {
-            return false;
-        }
-        EdgePair that = (EdgePair) o;
-        return ((this.dev1.equals(that.dev1) && this.dev2.equals(that.dev2)) ||
-                (this.dev1.equals(that.dev2) && this.dev2.equals(that.dev1)));
-    }
-
-    @Override
-    public int hashCode() {
-        if (dev1.toString().compareTo(dev2.toString()) <= 0) {
-            return Objects.hash(dev1, dev2);
-        } else {
-            return Objects.hash(dev2, dev1);
-        }
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(this)
-                .add("Dev1", dev1)
-                .add("Dev2", dev2)
-                .toString();
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
deleted file mode 100644
index 46231b0..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ /dev/null
@@ -1,872 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Lists;
-import org.onlab.packet.EthType;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.util.PredictableExecutor;
-import org.onlab.util.Tools;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.ProbeMode;
-import org.onosproject.segmentrouting.phasedrecovery.api.Phase;
-import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.Sets;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.onlab.util.Tools.groupedThreads;
-
-/**
- * Handles host-related events.
- */
-public class HostHandler {
-    private static final Logger log = LoggerFactory.getLogger(HostHandler.class);
-
-    protected final SegmentRoutingManager srManager;
-    private HostService hostService;
-    // Host workers - 0 will leverage available processors
-    private static final int DEFAULT_THREADS = 0;
-    protected PredictableExecutor hostWorkers;
-
-    /**
-     * Constructs the HostHandler.
-     *
-     * @param srManager Segment Routing manager
-     */
-    HostHandler(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        hostService = srManager.hostService;
-        this.hostWorkers = new PredictableExecutor(DEFAULT_THREADS,
-                                                   groupedThreads("onos/sr", "h-worker-%d", log));
-    }
-
-    /**
-     * Shutdowns the workers.
-     */
-    void terminate() {
-        hostWorkers.shutdown();
-    }
-
-    protected void init(DeviceId devId) {
-        log.info("Initializing hosts on {}", devId);
-        List<CompletableFuture<Void>> hostFutures = Lists.newArrayList();
-
-        // Init hosts in parallel using hostWorkers executor
-        hostService.getHosts().forEach(host -> {
-            hostFutures.add(hostWorkers.submit(() -> initHost(host, devId), host.id().hashCode()));
-        });
-
-        log.debug("{} hostFutures for {}", hostFutures.size(), devId);
-        CompletableFuture<Void> allHostFuture = CompletableFuture.allOf(hostFutures.toArray(new CompletableFuture[0]));
-        CompletableFuture<Void> timeoutFuture =
-                Tools.completeAfter(PhasedRecoveryService.PAIR_TIMEOUT, TimeUnit.SECONDS);
-
-        allHostFuture.runAfterEitherAsync(timeoutFuture, () -> {
-            if (allHostFuture.isDone()) {
-                log.info("{} hosts initialized. Move {} to the next phase", hostFutures.size(), devId);
-            } else {
-                log.info("Timeout reached. Move {} to the next phase", devId);
-            }
-            srManager.phasedRecoveryService.setPhase(devId, Phase.INFRA);
-        });
-    }
-
-    private void initHost(Host host, DeviceId deviceId) {
-        List<CompletableFuture<Objective>> locationFutures = Lists.newArrayList();
-
-        effectiveLocations(host).forEach(location -> {
-            if (location.deviceId().equals(deviceId) ||
-                    location.deviceId().equals(srManager.getPairDeviceId(deviceId).orElse(null))) {
-                locationFutures.addAll(processHostAddedAtLocation(host, location));
-            }
-        });
-
-        log.debug("{} locationFutures for {}", locationFutures.size(), host);
-
-        // Waiting for all locationFutures to be completed.
-        // This is a blocking operation but it is fine since this is run in a separate thread
-        try {
-            CompletableFuture.allOf(locationFutures.toArray(new CompletableFuture[0]))
-                    .thenApply(objectives -> locationFutures.stream()
-                            .map(CompletableFuture::join)
-                            .collect(Collectors.toList())
-                    )
-                    .get();
-        } catch (InterruptedException | ExecutionException e) {
-            log.warn("Exception caught when executing locationFutures");
-            locationFutures.forEach(future -> future.cancel(false));
-        }
-    }
-
-    void processHostAddedEvent(HostEvent event) {
-        Host host = event.subject();
-        hostWorkers.execute(() -> processHostAdded(host), host.id().hashCode());
-    }
-
-    private void processHostAdded(Host host) {
-        effectiveLocations(host).forEach(location -> processHostAddedAtLocation(host, location));
-        // ensure dual-homed host locations have viable uplinks
-        if (effectiveLocations(host).size() > 1 || srManager.singleHomedDown) {
-            effectiveLocations(host).forEach(loc -> {
-                if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
-                    srManager.linkHandler.checkUplinksForHost(loc);
-                }
-            });
-        }
-    }
-
-    List<CompletableFuture<Objective>> processHostAddedAtLocation(Host host, HostLocation location) {
-        checkArgument(effectiveLocations(host).contains(location), "{} is not a location of {}", location, host);
-
-        MacAddress hostMac = host.mac();
-        VlanId hostVlanId = host.vlan();
-        Set<HostLocation> locations = effectiveLocations(host);
-        Set<IpAddress> ips = host.ipAddresses();
-        log.info("Host {}/{} is added at {}", hostMac, hostVlanId, locations);
-
-        List<CompletableFuture<Objective>> objectiveFutures = Lists.newArrayList();
-
-        // TODO Phased recovery does not trace double tagged hosts
-        if (isDoubleTaggedHost(host)) {
-            ips.forEach(ip ->
-                processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
-                                               host.innerVlan(), hostVlanId, host.tpid(), ip, false)
-            );
-        } else {
-            objectiveFutures.add(
-                    processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, false)
-            );
-            ips.forEach(ip ->
-                objectiveFutures.add(
-                        processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, false)
-                )
-            );
-        }
-
-        // Use the pair link temporarily before the second location of a dual-homed host shows up.
-        // This do not affect single-homed hosts since the flow will be blocked in
-        // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
-        srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
-            if (effectiveLocations(host).stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
-                srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
-                    // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
-                    //       when the host is untagged
-                    VlanId vlanId = vlanForPairPort(hostVlanId, location);
-                    if (vlanId == null) {
-                        return;
-                    }
-
-                    objectiveFutures.add(
-                            processBridgingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, false)
-                    );
-                    ips.forEach(ip ->
-                            objectiveFutures.add(
-                                    processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
-                            )
-                    );
-
-                    if (srManager.activeProbing) {
-                        probe(host, location, pairDeviceId, pairRemotePort);
-                    }
-                });
-            }
-        });
-
-        int nextId = srManager.getMacVlanNextObjectiveId(location.deviceId(), hostMac, hostVlanId, null, false);
-        if (nextId != -1) {
-            VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(hostVlanId);
-            log.debug(" Updating next objective for device {}, host {}/{}, port {}, nextid {}",
-                                location.deviceId(), hostMac, vlanId, location.port(), nextId);
-            srManager.updateMacVlanTreatment(location.deviceId(), hostMac, vlanId,
-                                location.port(), nextId);
-        }
-
-        log.debug("{} objectiveFutures for {}", objectiveFutures.size(), location);
-        return objectiveFutures;
-    }
-
-    void processHostRemovedEvent(HostEvent event) {
-        Host host = event.subject();
-        hostWorkers.execute(() -> processHostRemoved(host), host.id().hashCode());
-    }
-
-    private void processHostRemoved(Host host) {
-        MacAddress hostMac = host.mac();
-        VlanId hostVlanId = host.vlan();
-        Set<HostLocation> locations = effectiveLocations(host);
-        Set<IpAddress> ips = host.ipAddresses();
-        log.info("Host {}/{} is removed from {}", hostMac, hostVlanId, locations);
-
-        locations.forEach(location -> {
-            if (isDoubleTaggedHost(host)) {
-                ips.forEach(ip ->
-                    processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
-                                                   host.innerVlan(), hostVlanId, host.tpid(), ip, true)
-                );
-            } else {
-                processBridgingRule(location.deviceId(), location.port(), hostMac, hostVlanId, true);
-                ips.forEach(ip ->
-                    processRoutingRule(location.deviceId(), location.port(), hostMac, hostVlanId, ip, true)
-                );
-            }
-
-            // Also remove redirection flows on the pair device if exists.
-            Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
-            Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
-            if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
-                // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
-                //       when the host is untagged
-                VlanId vlanId = vlanForPairPort(hostVlanId, location);
-                if (vlanId == null) {
-                    return;
-                }
-
-                processBridgingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId, true);
-                ips.forEach(ip ->
-                        processRoutingRule(pairDeviceId.get(), pairLocalPort.get(), hostMac, vlanId,
-                                ip, true));
-            }
-
-            // Delete prefix from sr-device-subnet when the next hop host is removed
-            srManager.routeService.getRouteTables().forEach(tableId -> {
-                srManager.routeService.getRoutes(tableId).forEach(routeInfo -> {
-                    if (routeInfo.allRoutes().stream().anyMatch(rr -> ips.contains(rr.nextHop()))) {
-                        log.debug("HostRemoved. removeSubnet {}, {}", location, routeInfo.prefix());
-                        srManager.deviceConfiguration.removeSubnet(location, routeInfo.prefix());
-                    }
-                });
-            });
-
-        });
-    }
-
-    void processHostMovedEvent(HostEvent event) {
-        Host host = event.subject();
-        hostWorkers.execute(() -> processHostMovedEventInternal(event), host.id().hashCode());
-    }
-
-    private void processHostMovedEventInternal(HostEvent event) {
-        // This method will be called when one of the following value has changed:
-        // (1) locations (2) auxLocations or (3) both locations and auxLocations.
-        // We only need to proceed when effectiveLocation has changed.
-        Set<HostLocation> newLocations = effectiveLocations(event.subject());
-        Set<HostLocation> prevLocations = effectiveLocations(event.prevSubject());
-
-        if (newLocations.equals(prevLocations)) {
-            log.info("effectiveLocations of {} has not changed. Skipping {}", event.subject().id(), event);
-            return;
-        }
-
-        Host host = event.subject();
-        Host prevHost = event.prevSubject();
-        MacAddress hostMac = host.mac();
-        VlanId hostVlanId = host.vlan();
-        Set<IpAddress> prevIps = prevHost.ipAddresses();
-        Set<IpAddress> newIps = host.ipAddresses();
-        EthType hostTpid = host.tpid();
-        boolean doubleTaggedHost = isDoubleTaggedHost(host);
-
-        log.info("Host {}/{} is moved from {} to {}", hostMac, hostVlanId, prevLocations, newLocations);
-        Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
-                .collect(Collectors.toSet());
-
-        // For each old location
-        Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
-            // Remove routing rules for old IPs
-            Sets.difference(prevIps, newIps).forEach(ip -> {
-                if (doubleTaggedHost) {
-                    processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
-                                                   hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
-                } else {
-                    processRoutingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId,
-                                       ip, true);
-                }
-            });
-
-            // Redirect the flows to pair link if configured
-            // Note: Do not continue removing any rule
-            Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId());
-            Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(prevLocation.deviceId());
-            if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
-                    newLocations.stream().anyMatch(location -> location.deviceId().equals(pairDeviceId.get())) &&
-                    newLocations.stream().noneMatch(location -> location.deviceId().equals(prevLocation.deviceId()))) {
-                // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
-                //       when the host is untagged
-                VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(prevLocation)).orElse(hostVlanId);
-
-                processBridgingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId, false);
-                newIps.forEach(ip ->
-                        processRoutingRule(prevLocation.deviceId(), pairLocalPort.get(), hostMac, vlanId,
-                            ip, false));
-                return;
-            }
-
-            // Remove flows for unchanged IPs only when the host moves from a switch to another.
-            // Otherwise, do not remove and let the adding part update the old flow
-            if (!newDeviceIds.contains(prevLocation.deviceId())) {
-                processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
-                Sets.intersection(prevIps, newIps).forEach(ip -> {
-                    if (doubleTaggedHost) {
-                        processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
-                                                       hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
-                    } else {
-                        processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
-                                           hostMac, hostVlanId, ip, true);
-                    }
-                });
-            }
-
-            // Remove bridging rules if new interface vlan is different from old interface vlan
-            // Otherwise, do not remove and let the adding part update the old flow
-            if (newLocations.stream().noneMatch(newLocation -> {
-                VlanId oldAssignedVlan = srManager.getInternalVlanId(prevLocation);
-                VlanId newAssignedVlan = srManager.getInternalVlanId(newLocation);
-                // Host is tagged and the new location has the host vlan in vlan-tagged
-                return srManager.interfaceService.getTaggedVlanId(newLocation).contains(hostVlanId) ||
-                        (oldAssignedVlan != null && newAssignedVlan != null &&
-                        // Host is untagged and the new location has the same assigned vlan
-                        oldAssignedVlan.equals(newAssignedVlan));
-            })) {
-                processBridgingRule(prevLocation.deviceId(), prevLocation.port(), hostMac, hostVlanId, true);
-            }
-
-            // Remove routing rules for unchanged IPs if none of the subnet of new location contains
-            // the IP. Otherwise, do not remove and let the adding part update the old flow
-            Sets.intersection(prevIps, newIps).forEach(ip -> {
-                if (newLocations.stream().noneMatch(newLocation ->
-                        srManager.deviceConfiguration.inSameSubnet(newLocation, ip))) {
-                    if (doubleTaggedHost) {
-                        processDoubleTaggedRoutingRule(prevLocation.deviceId(), prevLocation.port(),
-                                                       hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
-                    } else {
-                        processRoutingRule(prevLocation.deviceId(), prevLocation.port(),
-                                           hostMac, hostVlanId, ip, true);
-                    }
-                }
-            });
-        });
-
-        // For each new location, add all new IPs.
-        Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
-            processBridgingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId, false);
-            newIps.forEach(ip -> {
-                if (doubleTaggedHost) {
-                    processDoubleTaggedRoutingRule(newLocation.deviceId(), newLocation.port(),
-                                                   hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
-                } else {
-                    processRoutingRule(newLocation.deviceId(), newLocation.port(), hostMac, hostVlanId,
-                                       ip, false);
-                }
-            });
-
-            // Probe on pair device when host move
-            // Majorly for the 2nd step of [1A/x, 1B/x] -> [1A/x, 1B/y] -> [1A/y, 1B/y]
-            // But will also cover [1A/x] -> [1A/y] -> [1A/y, 1B/y]
-            if (srManager.activeProbing) {
-
-                srManager.getPairDeviceId(newLocation.deviceId()).ifPresent(pairDeviceId ->
-                        srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort ->
-                                probe(host, newLocation, pairDeviceId, pairRemotePort)
-                        )
-                );
-            }
-        });
-
-        // For each unchanged location, add new IPs and remove old IPs.
-        Sets.intersection(newLocations, prevLocations).forEach(unchangedLocation -> {
-            Sets.difference(prevIps, newIps).forEach(ip -> {
-                 if (doubleTaggedHost) {
-                     processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
-                                                    hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, true);
-                 } else {
-                     processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
-                                        hostMac, hostVlanId, ip, true);
-                 }
-            });
-
-            Sets.difference(newIps, prevIps).forEach(ip -> {
-                if (doubleTaggedHost) {
-                    processDoubleTaggedRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
-                                                   hostMac, host.innerVlan(), hostVlanId, hostTpid, ip, false);
-                } else {
-                    processRoutingRule(unchangedLocation.deviceId(), unchangedLocation.port(),
-                                       hostMac, hostVlanId, ip, false);
-                }
-            });
-
-            // Verify existing location and see if it is still valid
-            srManager.probingService.probeHost(host, unchangedLocation, ProbeMode.VERIFY);
-        });
-
-        // ensure dual-homed host locations have viable uplinks
-        if (newLocations.size() > prevLocations.size() || srManager.singleHomedDown) {
-            newLocations.forEach(loc -> {
-                if (srManager.mastershipService.isLocalMaster(loc.deviceId())) {
-                    srManager.linkHandler.checkUplinksForHost(loc);
-                }
-            });
-        }
-    }
-
-    void processHostUpdatedEvent(HostEvent event) {
-        Host host = event.subject();
-        hostWorkers.execute(() -> processHostUpdatedEventInternal(event), host.id().hashCode());
-    }
-
-    private void processHostUpdatedEventInternal(HostEvent event) {
-        Host host = event.subject();
-        MacAddress hostMac = host.mac();
-        VlanId hostVlanId = host.vlan();
-        EthType hostTpid = host.tpid();
-        Set<HostLocation> locations = effectiveLocations(host);
-        Set<IpAddress> prevIps = event.prevSubject().ipAddresses();
-        Set<IpAddress> newIps = host.ipAddresses();
-        log.info("Host {}/{} is updated", hostMac, hostVlanId);
-
-        locations.forEach(location -> {
-            Sets.difference(prevIps, newIps).forEach(ip -> {
-                if (isDoubleTaggedHost(host)) {
-                    processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
-                                                   host.innerVlan(), hostVlanId, hostTpid, ip, true);
-                } else {
-                    processRoutingRule(location.deviceId(), location.port(), hostMac,
-                                       hostVlanId, ip, true);
-                }
-            });
-            Sets.difference(newIps, prevIps).forEach(ip -> {
-                if (isDoubleTaggedHost(host)) {
-                    processDoubleTaggedRoutingRule(location.deviceId(), location.port(), hostMac,
-                                                   host.innerVlan(), hostVlanId, hostTpid, ip, false);
-                } else {
-                    processRoutingRule(location.deviceId(), location.port(), hostMac,
-                                       hostVlanId, ip, false);
-                }
-            });
-        });
-
-        // Use the pair link temporarily before the second location of a dual-homed host shows up.
-        // This do not affect single-homed hosts since the flow will be blocked in
-        // processBridgingRule or processRoutingRule due to VLAN or IP mismatch respectively
-        locations.forEach(location ->
-            srManager.getPairDeviceId(location.deviceId()).ifPresent(pairDeviceId -> {
-                if (locations.stream().noneMatch(l -> l.deviceId().equals(pairDeviceId))) {
-                    Set<IpAddress> ipsToAdd = Sets.difference(newIps, prevIps);
-                    Set<IpAddress> ipsToRemove = Sets.difference(prevIps, newIps);
-
-                    srManager.getPairLocalPort(pairDeviceId).ifPresent(pairRemotePort -> {
-                        // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
-                        //       when the host is untagged
-                        VlanId vlanId = vlanForPairPort(hostVlanId, location);
-                        if (vlanId == null) {
-                            return;
-                        }
-
-                        ipsToRemove.forEach(ip ->
-                                processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, true)
-                        );
-                        ipsToAdd.forEach(ip ->
-                                processRoutingRule(pairDeviceId, pairRemotePort, hostMac, vlanId, ip, false)
-                        );
-
-                        if (srManager.activeProbing) {
-                            probe(host, location, pairDeviceId, pairRemotePort);
-                        }
-                    });
-                }
-            })
-        );
-    }
-
-    /**
-     * When a non-pair port comes up, probe each host on the pair device if
-     * (1) the host is tagged and the tagged vlan of current port contains host vlan; or
-     * (2) the host is untagged and the internal vlan is the same on the host port and current port.
-     *
-     * @param cp connect point
-     */
-    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
-    void processPortUp(ConnectPoint cp) {
-        if (cp.port().equals(srManager.getPairLocalPort(cp.deviceId()).orElse(null))) {
-            return;
-        }
-        if (srManager.activeProbing) {
-            srManager.getPairDeviceId(cp.deviceId())
-                    .ifPresent(pairDeviceId -> srManager.hostService.getConnectedHosts(pairDeviceId).forEach(
-                            host -> hostWorkers.execute(() -> probingIfNecessary(host, pairDeviceId, cp),
-                                                        host.id().hashCode())));
-        }
-    }
-
-    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
-    private void probingIfNecessary(Host host, DeviceId pairDeviceId, ConnectPoint cp) {
-        if (isHostInVlanOfPort(host, pairDeviceId, cp)) {
-            srManager.probingService.probeHost(host, cp, ProbeMode.DISCOVER);
-        }
-    }
-
-    /**
-     * Checks if given host located on given device id matches VLAN config of current port.
-     *
-     * @param host host to check
-     * @param deviceId device id to check
-     * @param cp current connect point
-     * @return true if the host located at deviceId matches the VLAN config on cp
-     */
-    private boolean isHostInVlanOfPort(Host host, DeviceId deviceId, ConnectPoint cp) {
-        VlanId internalVlan = srManager.getInternalVlanId(cp);
-        Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(cp);
-
-        return taggedVlan.contains(host.vlan()) ||
-                (internalVlan != null && effectiveLocations(host).stream()
-                        .filter(l -> l.deviceId().equals(deviceId))
-                        .map(srManager::getInternalVlanId)
-                        .anyMatch(internalVlan::equals));
-    }
-
-    /**
-     * Send a probe on all locations with the same VLAN on pair device, excluding pair port.
-     *
-     * @param host host to probe
-     * @param location newly discovered host location
-     * @param pairDeviceId pair device id
-     * @param pairRemotePort pair remote port
-     */
-    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
-    private void probe(Host host, ConnectPoint location, DeviceId pairDeviceId, PortNumber pairRemotePort) {
-        //Check if the host still exists in the host store
-        if (hostService.getHost(host.id()) == null) {
-            log.debug("Host entry for host {} no more present. Aborting hostprobe discover for this host", host.id());
-            return;
-        }
-
-        VlanId vlanToProbe = host.vlan().equals(VlanId.NONE) ?
-                srManager.getInternalVlanId(location) : host.vlan();
-        if (srManager.symmetricProbing) {
-            srManager.interfaceService.getInterfaces().stream()
-                    .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
-                            i.vlanUntagged().equals(vlanToProbe) ||
-                            i.vlanNative().equals(vlanToProbe))
-                    .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
-                    .filter(i -> i.connectPoint().port().equals(location.port()))
-                    .forEach(i -> {
-                        log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
-                        srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
-                    });
-        } else {
-            srManager.interfaceService.getInterfaces().stream()
-                    .filter(i -> i.vlanTagged().contains(vlanToProbe) ||
-                            i.vlanUntagged().equals(vlanToProbe) ||
-                            i.vlanNative().equals(vlanToProbe))
-                    .filter(i -> i.connectPoint().deviceId().equals(pairDeviceId))
-                    .filter(i -> !i.connectPoint().port().equals(pairRemotePort))
-                    .forEach(i -> {
-                        log.debug("Probing host {} on pair device {}", host.id(), i.connectPoint());
-                        srManager.probingService.probeHost(host, i.connectPoint(), ProbeMode.DISCOVER);
-                    });
-        }
-    }
-
-    /**
-     * Populates or revokes a bridging rule on given deviceId that matches given mac,
-     * given vlan and output to given port.
-     *
-     * @param deviceId device ID
-     * @param port port
-     * @param mac mac address
-     * @param vlanId VLAN ID
-     * @param revoke true to revoke the rule; false to populate
-     * @return future that includes the flow objective if succeeded, null if otherwise
-     */
-    private CompletableFuture<Objective> processBridgingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
-                                     VlanId vlanId, boolean revoke) {
-        log.info("{} bridging entry for host {}/{} at {}:{}", revoke ? "Revoking" : "Populating",
-                mac, vlanId, deviceId, port);
-
-        if (!revoke) {
-            return srManager.defaultRoutingHandler.populateBridging(deviceId, port, mac, vlanId);
-        } else {
-            return srManager.defaultRoutingHandler.revokeBridging(deviceId, port, mac, vlanId);
-        }
-    }
-
-    /**
-     * Populate or revoke a routing rule on given deviceId that matches given ip,
-     * set destination mac to given mac, set vlan to given vlan and output to given port.
-     *
-     * @param deviceId device ID
-     * @param port port
-     * @param mac mac address
-     * @param vlanId VLAN ID
-     * @param ip IP address
-     * @param revoke true to revoke the rule; false to populate
-     * @return future that includes the flow objective if succeeded, null if otherwise
-     */
-    private CompletableFuture<Objective> processRoutingRule(DeviceId deviceId, PortNumber port, MacAddress mac,
-                                    VlanId vlanId, IpAddress ip, boolean revoke) {
-        ConnectPoint location = new ConnectPoint(deviceId, port);
-        if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
-            log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
-            return CompletableFuture.completedFuture(null);
-        }
-
-        log.info("{} routing rule for {} at {}", revoke ? "Revoking" : "Populating", ip, location);
-        if (revoke) {
-            return srManager.defaultRoutingHandler.revokeRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
-        } else {
-            return srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port, true);
-        }
-    }
-
-    /**
-     * Populate or revoke a routing rule and egress rules on given deviceId that matches given IP,
-     * to set destination mac to given mac, set inner vlan and outer vlan to given vlans,
-     * set outer TPID, and output to given port.
-     *
-     * @param deviceId  device ID
-     * @param port      port
-     * @param mac       mac address
-     * @param innerVlan inner VLAN ID
-     * @param outerVlan outer VLAN ID
-     * @param outerTpid outer TPID
-     * @param ip        IP address
-     * @param revoke    true to revoke the rule; false to populate
-     */
-    private void processDoubleTaggedRoutingRule(DeviceId deviceId, PortNumber port,
-                                                MacAddress mac, VlanId innerVlan,
-                                                VlanId outerVlan, EthType outerTpid,
-                                                IpAddress ip, boolean revoke) {
-        if (!srManager.routeDoubleTaggedHosts) {
-            log.debug("Routing for double tagged host is disabled. Ignore {}/{}/{}", mac, outerVlan, innerVlan);
-            return;
-        }
-
-        ConnectPoint location = new ConnectPoint(deviceId, port);
-        if (!srManager.deviceConfiguration.inSameSubnet(location, ip)) {
-            log.info("{} is not included in the subnet config of {}/{}. Ignored.", ip, deviceId, port);
-            return;
-        }
-        log.info("{} routing rule for double-tagged host {} at {}",
-                 revoke ? "Revoking" : "Populating", ip, location);
-        if (revoke) {
-            srManager.defaultRoutingHandler.revokeDoubleTaggedRoute(
-                    deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
-        } else {
-            srManager.defaultRoutingHandler.populateDoubleTaggedRoute(
-                    deviceId, ip.toIpPrefix(), mac, innerVlan, outerVlan, outerTpid, port);
-        }
-    }
-
-    void populateAllDoubleTaggedHost() {
-        log.info("Enabling routing for all double tagged hosts");
-        Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
-                .forEach(h -> effectiveLocations(h).forEach(l ->
-                    h.ipAddresses().forEach(i ->
-                        processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
-                                h.vlan(), h.tpid(), i, false)
-                    )
-            )
-        );
-    }
-
-    void revokeAllDoubleTaggedHost() {
-        log.info("Disabling routing for all double tagged hosts");
-        Sets.newHashSet(srManager.hostService.getHosts()).stream().filter(this::isDoubleTaggedHost)
-                .forEach(h -> effectiveLocations(h).forEach(l ->
-                    h.ipAddresses().forEach(i ->
-                        processDoubleTaggedRoutingRule(l.deviceId(), l.port(), h.mac(), h.innerVlan(),
-                            h.vlan(), h.tpid(), i, true)
-                    )
-            )
-        );
-    }
-
-    /**
-     * Returns VLAN ID to be used to program redirection flow on pair port.
-     *
-     * @param hostVlanId host VLAN ID
-     * @param location host location
-     * @return VLAN ID to be used; Or null if host VLAN does not match the interface config
-     */
-    VlanId vlanForPairPort(VlanId hostVlanId, ConnectPoint location) {
-        VlanId internalVlan = srManager.getInternalVlanId(location);
-        Set<VlanId> taggedVlan = srManager.interfaceService.getTaggedVlanId(location);
-
-        if (!hostVlanId.equals(VlanId.NONE) && taggedVlan.contains(hostVlanId)) {
-            return hostVlanId;
-        } else if (hostVlanId.equals(VlanId.NONE) && internalVlan != null) {
-            return internalVlan;
-        } else {
-            log.warn("VLAN mismatch. hostVlan={}, location={}, internalVlan={}, taggedVlan={}",
-                    hostVlanId, location, internalVlan, taggedVlan);
-            return null;
-        }
-    }
-
-    /**
-     * Update forwarding objective for unicast bridging and unicast routing.
-     * Also check the validity of updated interface configuration on VLAN.
-     *
-     * @param deviceId device ID that host attaches to
-     * @param portNum port number that host attaches to
-     * @param vlanId Vlan ID configured on the switch port
-     * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
-     * @param install true to populate the objective, false to revoke
-     */
-    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
-    void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
-                                     boolean popVlan, boolean install) {
-        ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
-        Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
-
-        if (hosts == null || hosts.size() == 0) {
-            log.debug("processIntfVlanUpdatedEvent: No hosts connected to {}", connectPoint);
-            return;
-        }
-
-        hosts.forEach(host -> hostWorkers.execute(() -> processIntfVlanUpdatedEventInternal(
-                host, deviceId, portNum, vlanId, popVlan, install), host.id().hashCode()));
-    }
-
-    private void processIntfVlanUpdatedEventInternal(Host host, DeviceId deviceId, PortNumber portNum,
-                                                     VlanId vlanId, boolean popVlan, boolean install) {
-        MacAddress mac = host.mac();
-        VlanId hostVlanId = host.vlan();
-
-        if (!install) {
-            // Do not check the host validity. Just remove all rules corresponding to the vlan id
-            // Revoke forwarding objective for bridging to the host
-            srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, false);
-
-            // Revoke forwarding objective and corresponding simple Next objective
-            // for each Host and IP address connected to given port
-            host.ipAddresses().forEach(ipAddress ->
-                srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
-                                                            mac, vlanId, popVlan, false)
-            );
-        } else {
-            // Check whether the host vlan is valid for new interface configuration
-            if ((!popVlan && hostVlanId.equals(vlanId)) ||
-                    (popVlan && hostVlanId.equals(VlanId.NONE))) {
-                srManager.defaultRoutingHandler.updateBridging(deviceId, portNum, mac, vlanId, popVlan, true);
-                // Update Forwarding objective and corresponding simple Next objective
-                // for each Host and IP address connected to given port
-                host.ipAddresses().forEach(ipAddress ->
-                    srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
-                                                                mac, vlanId, popVlan, true)
-                );
-            }
-        }
-    }
-
-    /**
-     * Populate or revoke routing rule for each host, according to the updated
-     * subnet configuration on the interface.
-     * @param cp connect point of the updated interface
-     * @param ipPrefixSet IP Prefixes added or removed
-     * @param install true if IP Prefixes added, false otherwise
-     */
-    // TODO Current implementation does not handle dual-homed hosts with auxiliary locations.
-    void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
-        Set<Host> hosts = hostService.getConnectedHosts(cp);
-
-        if (hosts == null || hosts.size() == 0) {
-            log.debug("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
-            return;
-        }
-
-        // Check whether the host IP address is in the interface's subnet
-        hosts.forEach(host -> hostWorkers.execute(() -> processIntfIpUpdatedEventInternal(
-                host, cp, ipPrefixSet, install)));
-    }
-
-    private void processIntfIpUpdatedEventInternal(Host host, ConnectPoint cp, Set<IpPrefix> ipPrefixSet,
-                                                   boolean install) {
-        host.ipAddresses().forEach(hostIpAddress -> {
-            ipPrefixSet.forEach(ipPrefix -> {
-                if (install && ipPrefix.contains(hostIpAddress)) {
-                    srManager.defaultRoutingHandler.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
-                                                                  host.mac(), host.vlan(), cp.port(), true);
-                } else if (!install && ipPrefix.contains(hostIpAddress)) {
-                    srManager.defaultRoutingHandler.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
-                                                                host.mac(), host.vlan(), cp.port(), true);
-                }
-            });
-        });
-    }
-
-    /**
-     * Returns the set of portnumbers on the given device that are part of the
-     * locations for dual-homed hosts.
-     *
-     * @param deviceId the given deviceId
-     * @return set of port numbers on given device that are dual-homed host
-     *         locations. May be empty if no dual homed hosts are connected to
-     *         the given device
-     */
-    Set<PortNumber> getDualHomedHostPorts(DeviceId deviceId) {
-        Set<PortNumber> dualHomedLocations = new HashSet<>();
-        srManager.hostService.getConnectedHosts(deviceId).stream()
-            .filter(host -> effectiveLocations(host).size() == 2)
-            .forEach(host -> effectiveLocations(host).stream()
-                     .filter(loc -> loc.deviceId().equals(deviceId))
-                        .forEach(loc -> dualHomedLocations.add(loc.port())));
-        return dualHomedLocations;
-    }
-
-    /**
-     * Checks if the given host is double-tagged or not.
-     *
-     * @param host host to check
-     * @return true if it is double-tagged, false otherwise
-     */
-    private boolean isDoubleTaggedHost(Host host) {
-        return !host.innerVlan().equals(VlanId.NONE);
-    }
-
-    /**
-     * Returns effective locations of given host.
-     *
-     * @param host host to check
-     * @return auxLocations of the host if exists, or locations of the host otherwise.
-     */
-    Set<HostLocation> effectiveLocations(Host host) {
-        return (host.auxLocations() != null) ? host.auxLocations() : host.locations();
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/app/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
deleted file mode 100644
index fc3dd17..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ /dev/null
@@ -1,558 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.ICMP;
-import org.onlab.packet.ICMP6;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.IPv6;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip6Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MPLS;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.packet.ndp.NeighborSolicitation;
-import org.onosproject.net.neighbour.NeighbourMessageContext;
-import org.onosproject.net.neighbour.NeighbourMessageType;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.net.packet.OutboundPacket;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Handler of ICMP packets that responses or forwards ICMP packets that
- * are sent to the controller.
- */
-public class IcmpHandler extends SegmentRoutingNeighbourHandler {
-
-    private static Logger log = LoggerFactory.getLogger(IcmpHandler.class);
-
-    /**
-     * Creates an IcmpHandler object.
-     *
-     * @param srManager SegmentRoutingManager object
-     */
-    public IcmpHandler(SegmentRoutingManager srManager) {
-        super(srManager);
-    }
-
-    /**
-     * Utility function to send packet out.
-     *
-     * @param outport the output port
-     * @param payload the packet to send
-     * @param destSid the segment id of the dest device
-     * @param destIpAddress the destination ip address
-     * @param allowedHops the hop limit/ttl
-     */
-    private void sendPacketOut(ConnectPoint outport,
-                               Ethernet payload,
-                               int destSid,
-                               IpAddress destIpAddress,
-                               byte allowedHops) {
-        int origSid;
-        try {
-            if (destIpAddress.isIp4()) {
-                origSid = config.getIPv4SegmentId(outport.deviceId());
-            } else {
-                origSid = config.getIPv6SegmentId(outport.deviceId());
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting sendPacketOut");
-            return;
-        }
-
-        if (destSid == -1 || origSid == destSid ||
-                srManager.interfaceService.isConfigured(outport)) {
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder().
-                    setOutput(outport.port()).build();
-            OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
-                                                              treatment, ByteBuffer.wrap(payload.serialize()));
-            log.trace("Sending packet {} to {}", payload, outport);
-            srManager.packetService.emit(packet);
-        } else {
-            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                    .setOutput(outport.port())
-                    .build();
-
-            payload.setEtherType(Ethernet.MPLS_UNICAST);
-            MPLS mplsPkt = new MPLS();
-            mplsPkt.setLabel(destSid);
-            mplsPkt.setTtl(allowedHops);
-            mplsPkt.setPayload(payload.getPayload());
-            payload.setPayload(mplsPkt);
-            OutboundPacket packet = new DefaultOutboundPacket(outport.deviceId(),
-                                                              treatment, ByteBuffer.wrap(payload.serialize()));
-            log.trace("Sending packet {} to {}", payload, outport);
-            srManager.packetService.emit(packet);
-        }
-    }
-
-    private IpAddress selectRouterIpAddress(IpAddress destIpAddress, ConnectPoint outPort,
-                                            Set<ConnectPoint> connectPoints) {
-        IpAddress routerIpAddress;
-        // Let's get first the online connect points
-        Set<ConnectPoint> onlineCps =  connectPoints.stream()
-                .filter(connectPoint -> srManager.deviceService.isAvailable(connectPoint.deviceId()))
-                .collect(Collectors.toSet());
-        // Check if ping is local
-        if (onlineCps.contains(outPort)) {
-            routerIpAddress = config.getRouterIpAddress(destIpAddress, outPort.deviceId());
-            log.trace("Local ping received from {} - send to {}", destIpAddress, routerIpAddress);
-            return routerIpAddress;
-        }
-        // Check if it comes from a remote device. Loopbacks are sorted comparing byte by byte
-        // FIXME if we lose both links from the chosen leaf to spine - ping will fail
-        routerIpAddress = onlineCps.stream()
-                .filter(onlineCp -> !onlineCp.deviceId().equals(outPort.deviceId()))
-                .map(selectedCp -> config.getRouterIpAddress(destIpAddress, selectedCp.deviceId()))
-                .filter(Objects::nonNull)
-                .sorted()
-                .findFirst().orElse(null);
-        if (routerIpAddress != null) {
-            log.trace("Remote ping received from {} - send to {}", destIpAddress, routerIpAddress);
-        } else {
-            log.warn("Not found a valid loopback for ping coming from {} - {}", destIpAddress, outPort);
-        }
-        return routerIpAddress;
-    }
-
-    private Ip4Address selectRouterIp4Address(IpAddress destIpAddress, ConnectPoint outPort,
-                                              Set<ConnectPoint> connectPoints) {
-        IpAddress routerIpAddress = selectRouterIpAddress(destIpAddress, outPort, connectPoints);
-        return routerIpAddress != null ? routerIpAddress.getIp4Address() : null;
-    }
-
-    private Ip6Address selectRouterIp6Address(IpAddress destIpAddress, ConnectPoint outPort,
-                                              Set<ConnectPoint> connectPoints) {
-        IpAddress routerIpAddress = selectRouterIpAddress(destIpAddress, outPort, connectPoints);
-        return routerIpAddress != null ? routerIpAddress.getIp6Address() : null;
-    }
-
-    //////////////////////////////////////
-    //     ICMP Echo/Reply Protocol     //
-    //////////////////////////////////////
-
-    /**
-     * Process incoming ICMP packet.
-     * If it is an ICMP request to router, then sends an ICMP response.
-     * Otherwise ignore the packet.
-     *
-     * @param eth inbound ICMP packet
-     * @param inPort the input port
-     */
-    public void processIcmp(Ethernet eth, ConnectPoint inPort) {
-        DeviceId deviceId = inPort.deviceId();
-        IPv4 ipv4Packet = (IPv4) eth.getPayload();
-        ICMP icmp = (ICMP) ipv4Packet.getPayload();
-        Ip4Address destinationAddress = Ip4Address.valueOf(ipv4Packet.getDestinationAddress());
-        Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
-        IpAddress routerIp;
-
-        // Only proceed with echo request
-        if (icmp.getIcmpType() != ICMP.TYPE_ECHO_REQUEST) {
-            return;
-        }
-
-        try {
-            routerIp = config.getRouterIpv4(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting processPacketIn.");
-            return;
-        }
-
-        // Get pair ip - if it exists
-        IpAddress pairRouterIp;
-        try {
-            DeviceId pairDeviceId = config.getPairDeviceId(deviceId);
-            pairRouterIp = pairDeviceId != null ? config.getRouterIpv4(pairDeviceId) : null;
-        } catch (DeviceConfigNotFoundException e) {
-            pairRouterIp = null;
-        }
-
-        // ICMP to the router IP or gateway IP
-        if (destinationAddress.equals(routerIp.getIp4Address()) ||
-                (pairRouterIp != null && destinationAddress.equals(pairRouterIp.getIp4Address())) ||
-                gatewayIpAddresses.contains(destinationAddress)) {
-            sendIcmpResponse(eth, inPort);
-        } else {
-            log.trace("Ignore ICMP that targets for {}", destinationAddress);
-        }
-    }
-
-    /**
-     * Sends an ICMP reply message.
-     *
-     * @param icmpRequest the original ICMP request
-     * @param outport the output port where the ICMP reply should be sent to
-     */
-    private void sendIcmpResponse(Ethernet icmpRequest, ConnectPoint outport) {
-        Ethernet icmpReplyEth = ICMP.buildIcmpReply(icmpRequest);
-        IPv4 icmpRequestIpv4 = (IPv4) icmpRequest.getPayload();
-        IPv4 icmpReplyIpv4 = (IPv4) icmpReplyEth.getPayload();
-        Ip4Address destIpAddress = Ip4Address.valueOf(icmpRequestIpv4.getSourceAddress());
-
-        // Get the available connect points
-        Set<ConnectPoint> destConnectPoints = config.getConnectPointsForASubnetHost(destIpAddress);
-        // Select a router
-        Ip4Address destRouterAddress = selectRouterIp4Address(destIpAddress, outport, destConnectPoints);
-
-        // Note: Source IP of the ICMP request doesn't belong to any configured subnet.
-        //       The source might be an indirectly attached host (e.g. behind a router)
-        //       Lookup the route store for the nexthop instead.
-        if (destRouterAddress == null) {
-            Optional<DeviceId> deviceId = srManager.routeService
-                    .longestPrefixLookup(destIpAddress).map(srManager::nextHopLocations)
-                    .flatMap(locations -> locations.stream().findFirst())
-                    .map(ConnectPoint::deviceId);
-            if (deviceId.isPresent()) {
-                try {
-                    destRouterAddress = config.getRouterIpv4(deviceId.get());
-                } catch (DeviceConfigNotFoundException e) {
-                    log.warn("Device config for {} not found. Abort ICMP processing", deviceId);
-                    return;
-                }
-            }
-        }
-
-        int destSid = config.getIPv4SegmentId(destRouterAddress);
-        if (destSid < 0) {
-            log.warn("Failed to lookup SID of the switch that {} attaches to. " +
-                    "Unable to process ICMP request.", destIpAddress);
-            return;
-        }
-        sendPacketOut(outport, icmpReplyEth, destSid, destIpAddress, icmpReplyIpv4.getTtl());
-    }
-
-    ///////////////////////////////////////////
-    //      ICMPv6 Echo/Reply Protocol       //
-    ///////////////////////////////////////////
-
-    /**
-     * Process incoming ICMPv6 packet.
-     * If it is an ICMPv6 request to router, then sends an ICMPv6 response.
-     * Otherwise ignore the packet.
-     *
-     * @param eth the incoming ICMPv6 packet
-     * @param inPort the input port
-     */
-    public void processIcmpv6(Ethernet eth, ConnectPoint inPort) {
-        DeviceId deviceId = inPort.deviceId();
-        IPv6 ipv6Packet = (IPv6) eth.getPayload();
-        ICMP6 icmp6 = (ICMP6) ipv6Packet.getPayload();
-        Ip6Address destinationAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
-        Set<IpAddress> gatewayIpAddresses = config.getPortIPs(deviceId);
-        IpAddress routerIp;
-
-        // Only proceed with echo request
-        if (icmp6.getIcmpType() != ICMP6.ECHO_REQUEST) {
-            return;
-        }
-
-        try {
-            routerIp = config.getRouterIpv6(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting processPacketIn.");
-            return;
-        }
-
-        // Get pair ip - if it exists
-        IpAddress pairRouterIp;
-        try {
-            DeviceId pairDeviceId = config.getPairDeviceId(deviceId);
-            pairRouterIp = pairDeviceId != null ? config.getRouterIpv6(pairDeviceId) : null;
-        } catch (DeviceConfigNotFoundException e) {
-            pairRouterIp = null;
-        }
-
-        Optional<Ip6Address> linkLocalIp = getLinkLocalIp(inPort);
-        // Ensure ICMP to the router IP, EUI-64 link-local IP, or gateway IP
-        if (destinationAddress.equals(routerIp.getIp6Address()) ||
-                (linkLocalIp.isPresent() && destinationAddress.equals(linkLocalIp.get())) ||
-                (pairRouterIp != null && destinationAddress.equals(pairRouterIp.getIp6Address())) ||
-                gatewayIpAddresses.contains(destinationAddress)) {
-            sendIcmpv6Response(eth, inPort);
-        } else {
-            log.trace("Ignore ICMPv6 that targets for {}", destinationAddress);
-        }
-    }
-
-    /**
-     * Sends an ICMPv6 reply message.
-     *
-     * @param ethRequest the original ICMP request
-     * @param outport the output port where the ICMP reply should be sent to
-     */
-    private void sendIcmpv6Response(Ethernet ethRequest, ConnectPoint outport) {
-        int destSid = -1;
-        Ethernet ethReply = ICMP6.buildIcmp6Reply(ethRequest);
-        IPv6 icmpRequestIpv6 = (IPv6) ethRequest.getPayload();
-        IPv6 icmpReplyIpv6 = (IPv6) ethRequest.getPayload();
-        // Source IP of the echo "reply"
-        Ip6Address srcIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getDestinationAddress());
-        // Destination IP of the echo "reply"
-        Ip6Address destIpAddress = Ip6Address.valueOf(icmpRequestIpv6.getSourceAddress());
-        Optional<Ip6Address> linkLocalIp = getLinkLocalIp(outport);
-
-        // Fast path if the echo request targets the link-local address of switch interface
-        if (linkLocalIp.isPresent() && srcIpAddress.equals(linkLocalIp.get())) {
-            sendPacketOut(outport, ethReply, destSid, destIpAddress, icmpReplyIpv6.getHopLimit());
-            return;
-        }
-
-        // Get the available connect points
-        Set<ConnectPoint> destConnectPoints = config.getConnectPointsForASubnetHost(destIpAddress);
-        // Select a router
-        Ip6Address destRouterAddress = selectRouterIp6Address(destIpAddress, outport, destConnectPoints);
-
-        // Note: Source IP of the ICMP request doesn't belong to any configured subnet.
-        //       The source might be an indirect host behind a router.
-        //       Lookup the route store for the nexthop instead.
-        if (destRouterAddress == null) {
-            Optional<DeviceId> deviceId = srManager.routeService
-                    .longestPrefixLookup(destIpAddress).map(srManager::nextHopLocations)
-                    .flatMap(locations -> locations.stream().findFirst())
-                    .map(ConnectPoint::deviceId);
-            if (deviceId.isPresent()) {
-                try {
-                    destRouterAddress = config.getRouterIpv6(deviceId.get());
-                } catch (DeviceConfigNotFoundException e) {
-                    log.warn("Device config for {} not found. Abort ICMPv6 processing", deviceId);
-                    return;
-                }
-            }
-        }
-
-        destSid = config.getIPv6SegmentId(destRouterAddress);
-        if (destSid < 0) {
-            log.warn("Failed to lookup SID of the switch that {} attaches to. " +
-                    "Unable to process ICMPv6 request.", destIpAddress);
-            return;
-        }
-        sendPacketOut(outport, ethReply, destSid, destIpAddress, icmpReplyIpv6.getHopLimit());
-    }
-
-    ///////////////////////////////////////////
-    //  ICMPv6 Neighbour Discovery Protocol  //
-    ///////////////////////////////////////////
-
-    /**
-     * Process incoming NDP packet.
-     *
-     * If it is an NDP request for the router or for the gateway, then sends a NDP reply.
-     * If it is an NDP request to unknown host flood in the subnet.
-     * If it is an NDP packet to known host forward the packet to the host.
-     *
-     * FIXME If the NDP packets use link local addresses we fail.
-     *
-     * @param pkt inbound packet
-     * @param hostService the host service
-     */
-    public void processPacketIn(NeighbourMessageContext pkt, HostService hostService) {
-        // First we validate the ndp packet
-        SegmentRoutingAppConfig appConfig = srManager.cfgService
-                .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
-        if (appConfig != null && appConfig.suppressSubnet().contains(pkt.inPort())) {
-            // Ignore NDP packets come from suppressed ports
-            pkt.drop();
-            return;
-        }
-
-        if (pkt.type() == NeighbourMessageType.REQUEST) {
-            handleNdpRequest(pkt, hostService);
-        } else {
-            handleNdpReply(pkt, hostService);
-        }
-
-    }
-
-    /**
-     * Helper method to handle the ndp requests.
-     * @param pkt the ndp packet request and context information
-     * @param hostService the host service
-     */
-    private void handleNdpRequest(NeighbourMessageContext pkt, HostService hostService) {
-        // ND request for the gateway. We have to reply on behalf of the gateway.
-        if (isNdpForGateway(pkt)) {
-            log.trace("Sending NDP reply on behalf of gateway IP for pkt: {}", pkt.target());
-            MacAddress routerMac = config.getRouterMacForAGatewayIp(pkt.target());
-            if (routerMac == null) {
-                log.warn("Router MAC of {} is not configured. Cannot handle NDP request from {}",
-                        pkt.inPort().deviceId(), pkt.sender());
-                return;
-            }
-            sendResponse(pkt, routerMac, hostService, true);
-        } else {
-
-            // Process NDP targets towards EUI-64 address.
-            try {
-                DeviceId deviceId = pkt.inPort().deviceId();
-
-                Optional<Ip6Address> linkLocalIp = getLinkLocalIp(pkt.inPort());
-                if (linkLocalIp.isPresent() && pkt.target().equals(linkLocalIp.get())) {
-                    MacAddress routerMac = config.getDeviceMac(deviceId);
-                    sendResponse(pkt, routerMac, hostService, true);
-                }
-            } catch (DeviceConfigNotFoundException e) {
-                log.warn(e.getMessage() + " Unable to handle NDP packet to {}. Aborting.", pkt.target());
-                return;
-            }
-
-            // NOTE: Ignore NDP packets except those target for the router
-            //       We will reconsider enabling this when we have host learning support
-            /*
-            // ND request for an host. We do a search by Ip.
-            Set<Host> hosts = hostService.getHostsByIp(pkt.target());
-            // Possible misconfiguration ? In future this case
-            // should be handled we can have same hosts in different VLANs.
-            if (hosts.size() > 1) {
-                log.warn("More than one host with IP {}", pkt.target());
-            }
-            Host targetHost = hosts.stream().findFirst().orElse(null);
-            // If we know the host forward to its attachment point.
-            if (targetHost != null) {
-                log.debug("Forward NDP request to the target host");
-                pkt.forward(targetHost.location());
-            } else {
-                // Flood otherwise.
-                log.debug("Flood NDP request to the target subnet");
-                flood(pkt);
-            }
-            */
-        }
-    }
-
-    /**
-     * Helper method to handle the ndp replies.
-     *
-     * @param pkt the ndp packet reply and context information
-     * @param hostService the host service
-     */
-    private void handleNdpReply(NeighbourMessageContext pkt, HostService hostService) {
-        if (isNdpForGateway(pkt)) {
-            log.debug("Forwarding all the ip packets we stored");
-            Ip6Address hostIpAddress = pkt.sender().getIp6Address();
-            srManager.ipHandler.forwardPackets(pkt.inPort().deviceId(), hostIpAddress);
-        } else {
-            // NOTE: Ignore NDP packets except those target for the router
-            //       We will reconsider enabling this when we have host learning support
-            /*
-            HostId hostId = HostId.hostId(pkt.dstMac(), pkt.vlan());
-            Host targetHost = hostService.getHost(hostId);
-            if (targetHost != null) {
-                log.debug("Forwarding the reply to the host");
-                pkt.forward(targetHost.location());
-            } else {
-                // We don't have to flood towards spine facing ports.
-                if (pkt.vlan().equals(SegmentRoutingManager.INTERNAL_VLAN)) {
-                    return;
-                }
-                log.debug("Flooding the reply to the subnet");
-                flood(pkt);
-            }
-            */
-        }
-    }
-
-    /**
-     * Utility to verify if the ND are for the gateway.
-     *
-     * @param pkt the ndp packet
-     * @return true if the ndp is for the gateway. False otherwise
-     */
-    private boolean isNdpForGateway(NeighbourMessageContext pkt) {
-        DeviceId deviceId = pkt.inPort().deviceId();
-        Set<IpAddress> gatewayIpAddresses = null;
-
-        try {
-            if (pkt.target().equals(config.getRouterIpv6(deviceId))) {
-                return true;
-            }
-            gatewayIpAddresses = config.getPortIPs(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting check for router IP in processing ndp");
-            return false;
-        }
-        return gatewayIpAddresses != null && gatewayIpAddresses.stream()
-                .filter(IpAddress::isIp6)
-                .anyMatch(gatewayIp -> gatewayIp.equals(pkt.target()) ||
-                        Arrays.equals(IPv6.getSolicitNodeAddress(gatewayIp.toOctets()),
-                                pkt.target().toOctets()));
-    }
-
-    /**
-     * Sends a NDP request for the target IP address to all ports except in-port.
-     *
-     * @param deviceId Switch device ID
-     * @param targetAddress target IP address for ARP
-     * @param inPort in-port
-     */
-    public void sendNdpRequest(DeviceId deviceId, IpAddress targetAddress, ConnectPoint inPort) {
-        byte[] senderMacAddress = new byte[MacAddress.MAC_ADDRESS_LENGTH];
-        byte[] senderIpAddress = new byte[Ip6Address.BYTE_LENGTH];
-        // Retrieves device info.
-        if (!getSenderInfo(senderMacAddress, senderIpAddress, deviceId, targetAddress)) {
-            log.warn("Aborting sendNdpRequest, we cannot get all the information needed");
-            return;
-        }
-        // We have to compute the dst mac address and dst ip address.
-        byte[] dstIp = IPv6.getSolicitNodeAddress(targetAddress.toOctets());
-        byte[] dstMac = IPv6.getMCastMacAddress(dstIp);
-        // Creates the request.
-        Ethernet ndpRequest = NeighborSolicitation.buildNdpSolicit(
-                targetAddress.getIp6Address(),
-                Ip6Address.valueOf(senderIpAddress),
-                Ip6Address.valueOf(dstIp),
-                MacAddress.valueOf(senderMacAddress),
-                MacAddress.valueOf(dstMac),
-                VlanId.NONE
-        );
-        flood(ndpRequest, inPort, targetAddress);
-    }
-
-    /**
-     * Returns link-local IP of given connect point.
-     *
-     * @param cp connect point
-     * @return optional link-local IP
-     */
-    private Optional<Ip6Address> getLinkLocalIp(ConnectPoint cp) {
-        return srManager.interfaceService.getInterfacesByPort(cp)
-                .stream()
-                .map(Interface::mac)
-                .map(MacAddress::toBytes)
-                .map(IPv6::getLinkLocalAddress)
-                .map(Ip6Address::valueOf)
-                .findFirst();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/IpHandler.java b/app/src/main/java/org/onosproject/segmentrouting/IpHandler.java
deleted file mode 100644
index c445ac4..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/IpHandler.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IP;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.IPv6;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip6Address;
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.net.packet.OutboundPacket;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onlab.packet.IpAddress.Version.INET6;
-
-/**
- * Handler of IP packets that forwards IP packets that are sent to the controller,
- * except the ICMP packets which are processed by @link{IcmpHandler}.
- */
-public class IpHandler {
-
-    private static Logger log = LoggerFactory.getLogger(IpHandler.class);
-    private SegmentRoutingManager srManager;
-    private DeviceConfiguration config;
-    private ConcurrentHashMap<IpAddress, ConcurrentLinkedQueue<IP>> ipPacketQueue;
-
-    /**
-     * Creates an IpHandler object.
-     *
-     * @param srManager SegmentRoutingManager object
-     */
-    public IpHandler(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        this.config = checkNotNull(srManager.deviceConfiguration);
-        ipPacketQueue = new ConcurrentHashMap<>();
-    }
-
-    /**
-     * Enqueues the packet using the destination address as key.
-     *
-     * @param ipPacket the ip packet to store
-     * @param destinationAddress the destination address
-     */
-    private void enqueuePacket(IP ipPacket, IpAddress destinationAddress) {
-
-        ipPacketQueue
-            .computeIfAbsent(destinationAddress, a -> new ConcurrentLinkedQueue<>())
-            .add(ipPacket);
-
-    }
-
-    /**
-     * Dequeues the packet using the destination address as key.
-     *
-     * @param ipPacket the ip packet to remove
-     * @param destinationAddress the destination address
-     */
-    public void dequeuePacket(IP ipPacket, IpAddress destinationAddress) {
-
-        if (ipPacketQueue.get(destinationAddress) == null) {
-            return;
-        }
-        ipPacketQueue.get(destinationAddress).remove(ipPacket);
-    }
-
-    /**
-     * Forwards the packet to a given host and deque the packet.
-     *
-     * @param deviceId the target device
-     * @param eth the packet to send
-     * @param dest the target host
-     */
-    private void forwardToHost(DeviceId deviceId, Ethernet eth, Host dest) {
-        TrafficTreatment treatment = DefaultTrafficTreatment.builder().
-                setOutput(dest.location().port()).build();
-        OutboundPacket packet = new DefaultOutboundPacket(deviceId,
-                                                          treatment, ByteBuffer.wrap(eth.serialize()));
-        srManager.packetService.emit(packet);
-    }
-
-    //////////////////////
-    //  IPv4 Handling  //
-    ////////////////////
-
-    /**
-     * Processes incoming IP packets.
-     *
-     * If it is an IP packet for known host, then forward it to the host.
-     * If it is an IP packet for unknown host in subnet, then send an ARP request
-     * to the subnet.
-     *
-     * @param pkt incoming packet
-     * @param connectPoint the target device
-     */
-    public void processPacketIn(IPv4 pkt, ConnectPoint connectPoint) {
-
-        DeviceId deviceId = connectPoint.deviceId();
-        Ip4Address destinationAddress = Ip4Address.valueOf(pkt.getDestinationAddress());
-
-        // IP packet for know hosts
-        if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
-            forwardPackets(deviceId, destinationAddress);
-
-        // IP packet for unknown host in one of the configured subnets of the router
-        } else if (config.inSameSubnet(deviceId, destinationAddress)) {
-            srManager.arpHandler.sendArpRequest(deviceId, destinationAddress, connectPoint);
-
-        // IP packets for unknown host
-        } else {
-            log.debug("IPv4 packet for unknown host {} which is not in the subnet",
-                    destinationAddress);
-            // Do nothing
-        }
-    }
-
-    /**
-     * Adds the IP packet to a buffer.
-     * The packets are forwarded to corresponding destination when the destination
-     * MAC address is known via ARP response.
-     *
-     * @param ipPacket IP packet to add to the buffer
-     */
-    public void addToPacketBuffer(IPv4 ipPacket) {
-
-        // Better not buffer TCP packets due to out-of-order packet transfer
-        if (ipPacket.getProtocol() == IPv4.PROTOCOL_TCP) {
-            return;
-        }
-        IpAddress destIpAddress = IpAddress.valueOf(ipPacket.getDestinationAddress());
-        enqueuePacket(ipPacket, destIpAddress);
-    }
-
-    /**
-     * Forwards IP packets in the buffer to the destination IP address.
-     * It is called when the controller finds the destination MAC address
-     * via ARP responses.
-     *
-     * @param deviceId switch device ID
-     * @param destIpAddress destination IP address
-     */
-    public void forwardPackets(DeviceId deviceId, Ip4Address destIpAddress) {
-        if (ipPacketQueue.get(destIpAddress) == null) {
-            return;
-        }
-        for (IP ipPacket : ipPacketQueue.get(destIpAddress)) {
-            if (ipPacket.getVersion() == ((byte) 4)) {
-                IPv4 ipv4Packet = (IPv4) ipPacket;
-                Ip4Address destAddress = Ip4Address.valueOf(ipv4Packet.getDestinationAddress());
-                if (config.inSameSubnet(deviceId, destAddress)) {
-                    ipv4Packet.setTtl((byte) (ipv4Packet.getTtl() - 1));
-                    ipv4Packet.setChecksum((short) 0);
-                    for (Host dest : srManager.hostService.getHostsByIp(destIpAddress)) {
-                        Ethernet eth = new Ethernet();
-                        eth.setDestinationMACAddress(dest.mac());
-                        try {
-                            eth.setSourceMACAddress(config.getDeviceMac(deviceId));
-                        } catch (DeviceConfigNotFoundException e) {
-                            log.warn(e.getMessage()
-                                             + " Skipping forwardPackets for this destination.");
-                            continue;
-                        }
-                        eth.setEtherType(Ethernet.TYPE_IPV4);
-                        eth.setPayload(ipv4Packet);
-                        forwardToHost(deviceId, eth, dest);
-                        ipPacketQueue.get(destIpAddress).remove(ipPacket);
-                    }
-                    ipPacketQueue.get(destIpAddress).remove(ipPacket);
-                }
-            }
-        }
-    }
-
-    //////////////////////
-    //  IPv6 Handling  //
-    ////////////////////
-
-    /**
-     * Processes incoming IPv6 packets.
-     *
-     * If it is an IPv6 packet for known host, then forward it to the host.
-     * If it is an IPv6 packet for unknown host in subnet, then send an NDP request
-     * to the subnet.
-     *
-     * @param pkt incoming packet
-     * @param connectPoint the target device
-     */
-    public void processPacketIn(IPv6 pkt, ConnectPoint connectPoint) {
-
-        DeviceId deviceId = connectPoint.deviceId();
-        Ip6Address destinationAddress = Ip6Address.valueOf(pkt.getDestinationAddress());
-
-        // IPv6 packet for know hosts
-        if (!srManager.hostService.getHostsByIp(destinationAddress).isEmpty()) {
-            forwardPackets(deviceId, destinationAddress);
-
-            // IPv6 packet for unknown host in one of the configured subnets of the router
-        } else if (config.inSameSubnet(deviceId, destinationAddress)) {
-            srManager.icmpHandler.sendNdpRequest(deviceId, destinationAddress, connectPoint);
-
-            // IPv6 packets for unknown host
-        } else {
-            log.debug("IPv6 packet for unknown host {} which is not in the subnet",
-                      destinationAddress);
-        }
-    }
-
-    /**
-     * Adds the IPv6 packet to a buffer.
-     * The packets are forwarded to corresponding destination when the destination
-     * MAC address is known via NDP response.
-     *
-     * @param ipPacket IP packet to add to the buffer
-     */
-    public void addToPacketBuffer(IPv6 ipPacket) {
-
-        // Better not buffer TCP packets due to out-of-order packet transfer
-        if (ipPacket.getNextHeader() == IPv6.PROTOCOL_TCP) {
-            return;
-        }
-        IpAddress destIpAddress = IpAddress.valueOf(INET6, ipPacket.getDestinationAddress());
-        enqueuePacket(ipPacket, destIpAddress);
-    }
-
-    /**
-     * Forwards IP packets in the buffer to the destination IP address.
-     * It is called when the controller finds the destination MAC address
-     * via NDP replies.
-     *
-     * @param deviceId the target device
-     * @param destIpAddress the destination ip address
-     */
-    public void forwardPackets(DeviceId deviceId, Ip6Address destIpAddress) {
-        if (ipPacketQueue.get(destIpAddress) == null) {
-            return;
-        }
-        for (IP ipPacket : ipPacketQueue.get(destIpAddress)) {
-            if (ipPacket.getVersion() == ((byte) 6)) {
-                IPv6 ipv6Packet = (IPv6) ipPacket;
-                Ip6Address destAddress = Ip6Address.valueOf(ipv6Packet.getDestinationAddress());
-                if (config.inSameSubnet(deviceId, destAddress)) {
-                    ipv6Packet.setHopLimit((byte) (ipv6Packet.getHopLimit() - 1));
-                    for (Host dest : srManager.hostService.getHostsByIp(destIpAddress)) {
-                        Ethernet eth = new Ethernet();
-                        eth.setDestinationMACAddress(dest.mac());
-                        try {
-                            eth.setSourceMACAddress(config.getDeviceMac(deviceId));
-                        } catch (DeviceConfigNotFoundException e) {
-                            log.warn(e.getMessage()
-                                             + " Skipping forwardPackets for this destination.");
-                            continue;
-                        }
-                        eth.setEtherType(Ethernet.TYPE_IPV6);
-                        eth.setPayload(ipv6Packet);
-                        forwardToHost(deviceId, eth, dest);
-                        ipPacketQueue.get(destIpAddress).remove(ipPacket);
-                    }
-                    ipPacketQueue.get(destIpAddress).remove(ipPacket);
-                }
-            }
-            ipPacketQueue.get(destIpAddress).remove(ipPacket);
-        }
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/LinkHandler.java b/app/src/main/java/org/onosproject/segmentrouting/LinkHandler.java
deleted file mode 100644
index 2193c95..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/LinkHandler.java
+++ /dev/null
@@ -1,736 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.Link;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.link.LinkService;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
-import org.onosproject.store.service.EventuallyConsistentMap;
-import org.onosproject.store.service.EventuallyConsistentMapBuilder;
-import org.onosproject.store.service.WallClockTimestamp;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Sets;
-
-
-public class LinkHandler {
-    private static final Logger log = LoggerFactory.getLogger(LinkHandler.class);
-    protected final SegmentRoutingManager srManager;
-
-    // Local store for all links seen and their present status, used for
-    // optimized routing. The existence of the link in the keys is enough to know
-    // if the link has been "seen-before" by this instance of the controller.
-    // The boolean value indicates if the link is currently up or not.
-    // Currently the optimized routing logic depends on "forgetting" a link
-    // when a switch goes down, but "remembering" it when only the link goes down.
-    private Map<Link, Boolean> seenLinks = new ConcurrentHashMap<>();
-    private Set<Link> seenBefore = Sets.newConcurrentHashSet();
-    private EventuallyConsistentMap<DeviceId, Set<PortNumber>> downedPortStore = null;
-
-    /**
-     * Constructs the LinkHandler.
-     *
-     * @param srManager Segment Routing manager
-     */
-    LinkHandler(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        log.debug("Creating EC map downedportstore");
-        EventuallyConsistentMapBuilder<DeviceId, Set<PortNumber>> downedPortsMapBuilder
-                = srManager.storageService.eventuallyConsistentMapBuilder();
-        downedPortStore = downedPortsMapBuilder.withName("downedportstore")
-                .withSerializer(srManager.createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-        log.trace("Current size {}", downedPortStore.size());
-    }
-
-    /**
-     * Constructs the LinkHandler for unit-testing.
-     *
-     * @param srManager SegmentRoutingManager
-     * @param linkService LinkService
-     */
-    LinkHandler(SegmentRoutingManager srManager, LinkService linkService) {
-        this.srManager = srManager;
-    }
-
-    /**
-     * Initialize LinkHandler.
-     */
-    void init() {
-        log.info("Loading stored links");
-        srManager.linkService.getActiveLinks().forEach(this::processLinkAdded);
-    }
-
-    /**
-     * Preprocessing of added link before being sent for route-path handling.
-     * Also performs post processing of link.
-     *
-     * @param link the link to be processed
-     */
-    void processLinkAdded(Link link) {
-        log.info("** LINK ADDED {}", link.toString());
-        if (!isLinkValid(link)) {
-            return;
-        }
-        // Irrespective of whether the local is a MASTER or not for this device,
-        // create group handler instance and push default TTP flow rules if needed,
-        // as in a multi-instance setup, instances can initiate groups for any
-        // device. Also update local groupHandler stores.
-        DefaultGroupHandler groupHandler = srManager.groupHandlerMap
-                                                .get(link.src().deviceId());
-        if (groupHandler == null) {
-            Device device = srManager.deviceService.getDevice(link.src().deviceId());
-            if (device != null) {
-                log.warn("processLinkAdded: Link Added notification without "
-                        + "Device Added event, still handling it");
-                srManager.processDeviceAdded(device);
-            }
-        }
-
-        if (isSeenLink(link)) {
-            // temp store retains previous state, just before the state is updated in
-            // seenLinks; previous state is necessary when processing the
-            // linkupdate in defaultRoutingHandler
-            seenBefore.add(link);
-        }
-        updateSeenLink(link, true);
-
-        if (srManager.deviceConfiguration == null ||
-                !srManager.deviceConfiguration.isConfigured(link.src().deviceId())) {
-            log.warn("Source device of this link is not configured.. "
-                    + "not processing further");
-            return;
-        }
-
-        // process link only if it is bidirectional
-        if (!isBidirectionalLinkUp(link)) {
-            log.debug("Link not bidirectional.. waiting for other direction " +
-                    "src {} --> dst {} ", link.dst(), link.src());
-            return;
-        }
-
-        log.info("processing bidi links {} <--> {} UP", link.src(), link.dst());
-        // update groupHandler internal port state for both directions
-        List<Link> ulinks = getBidiComponentLinks(link);
-        for (Link ulink : ulinks) {
-            DefaultGroupHandler gh = srManager.groupHandlerMap
-                                         .get(ulink.src().deviceId());
-            if (gh != null) {
-                gh.portUpForLink(ulink);
-            }
-        }
-
-        for (Link ulink : ulinks) {
-            log.info("-- Starting optimized route-path processing for component "
-                    + "unidirectional link {} --> {} UP", ulink.src(), ulink.dst());
-            srManager.defaultRoutingHandler
-                    .populateRoutingRulesForLinkStatusChange(null, ulink, null,
-                                                             seenBefore.contains(ulink));
-
-            if (srManager.mastershipService.isLocalMaster(ulink.src().deviceId())) {
-                // handle edge-ports for dual-homed hosts
-                updateHostPorts(ulink, true);
-
-                // It's possible that linkUp causes no route-path change as ECMP graph does
-                // not change if the link is a parallel link (same src-dst as
-                // another link). However we still need to update ECMP hash
-                // groups to include new buckets for the link that has come up.
-                DefaultGroupHandler gh = srManager.groupHandlerMap
-                                            .get(ulink.src().deviceId());
-                if (gh != null) {
-                    if (!seenBefore.contains(ulink) && isParallelLink(ulink)) {
-                        // if link seen first time, we need to ensure hash-groups have
-                        // all ports
-                        log.debug("Attempting retryHash for paralled first-time link {}",
-                                  ulink);
-                        gh.retryHash(ulink, false, true);
-                    } else {
-                        // seen before-link
-                        if (isParallelLink(ulink)) {
-                            log.debug("Attempting retryHash for paralled seen-before "
-                                    + "link {}", ulink);
-                            gh.retryHash(ulink, false, false);
-                        }
-                    }
-                }
-            }
-            //clean up temp state
-            seenBefore.remove(ulink);
-        }
-
-    }
-
-    /**
-     * Preprocessing of removed link before being sent for route-path handling.
-     * Also performs post processing of link.
-     *
-     * @param link the link to be processed
-     */
-    void processLinkRemoved(Link link) {
-        log.info("** LINK REMOVED {}", link.toString());
-        if (!isLinkValid(link)) {
-            return;
-        }
-        // when removing links, update seen links first, before doing route-path
-        // changes
-        updateSeenLink(link, false);
-        // handle edge-ports for dual-homed hosts
-        if (srManager.mastershipService.isLocalMaster(link.src().deviceId())) {
-            updateHostPorts(link, false);
-        }
-
-        // device availability check helps to ensure that multiple link-removed
-        // events are actually treated as a single switch removed event.
-        // purgeSeenLink is necessary so we do rerouting (instead of rehashing)
-        // when switch comes back.
-        if (link.src().elementId() instanceof DeviceId
-                && !srManager.deviceService.isAvailable(link.src().deviceId())) {
-            purgeSeenLink(link);
-            return;
-        }
-        if (link.dst().elementId() instanceof DeviceId
-                && !srManager.deviceService.isAvailable(link.dst().deviceId())) {
-            purgeSeenLink(link);
-            return;
-        }
-
-        // process link only if it is bidirectional
-        if (!isBidirectionalLinkDown(link)) {
-            log.debug("Link not bidirectional.. waiting for other direction " +
-                    "src {} --> dst {} ", link.dst(), link.src());
-            return;
-        }
-        log.info("processing bidi links {} <--> {} DOWN", link.src(), link.dst());
-
-        for (Link ulink : getBidiComponentLinks(link)) {
-            log.info("-- Starting optimized route-path processing for component "
-                    + "unidirectional link {} --> {} DOWN", ulink.src(), ulink.dst());
-            srManager.defaultRoutingHandler
-                .populateRoutingRulesForLinkStatusChange(ulink, null, null, true);
-
-            // attempt rehashing for parallel links
-            DefaultGroupHandler groupHandler = srManager.groupHandlerMap
-                    .get(ulink.src().deviceId());
-            if (groupHandler != null) {
-                if (srManager.mastershipService.isLocalMaster(ulink.src().deviceId())
-                        && isParallelLink(ulink)) {
-                    log.debug("* retrying hash for parallel link removed:{}", ulink);
-                    groupHandler.retryHash(ulink, true, false);
-                } else {
-                    log.debug("Not attempting retry-hash for link removed: {} .. {}",
-                              ulink,
-                              (srManager.mastershipService.isLocalMaster(ulink
-                                      .src().deviceId())) ? "not parallel"
-                                                          : "not master");
-                }
-                // ensure local stores are updated after all rerouting or rehashing
-                groupHandler.portDownForLink(ulink);
-            } else {
-                log.warn("group handler not found for dev:{} when removing link: {}",
-                         ulink.src().deviceId(), ulink);
-            }
-        }
-    }
-
-    /**
-     * Checks validity of link. Examples of invalid links include
-     * indirect-links, links between ports on the same switch, and more.
-     *
-     * @param link the link to be processed
-     * @return true if valid link
-     */
-     boolean isLinkValid(Link link) {
-        if (link.type() != Link.Type.DIRECT) {
-            // NOTE: A DIRECT link might be transiently marked as INDIRECT
-            // if BDDP is received before LLDP. We can safely ignore that
-            // until the LLDP is received and the link is marked as DIRECT.
-            log.info("Ignore link {}->{}. Link type is {} instead of DIRECT.",
-                     link.src(), link.dst(), link.type());
-            return false;
-        }
-        DeviceId srcId = link.src().deviceId();
-        DeviceId dstId = link.dst().deviceId();
-        if (srcId.equals(dstId)) {
-            log.warn("Links between ports on the same switch are not "
-                    + "allowed .. ignoring link {}", link);
-            return false;
-        }
-        DeviceConfiguration devConfig = srManager.deviceConfiguration;
-        if (devConfig == null) {
-            log.warn("Cannot check validity of link without device config");
-            return true;
-        }
-        try {
-            /*if (!devConfig.isEdgeDevice(srcId)
-                    && !devConfig.isEdgeDevice(dstId)) {
-                // ignore links between spines
-                // XXX revisit when handling multi-stage fabrics
-                log.warn("Links between spines not allowed...ignoring "
-                        + "link {}", link);
-                return false;
-            }*/
-            if (devConfig.isEdgeDevice(srcId)
-                    && devConfig.isEdgeDevice(dstId)) {
-                // ignore links between leaves if they are not pair-links
-                // XXX revisit if removing pair-link config or allowing more than
-                // one pair-link
-                if (devConfig.getPairDeviceId(srcId).equals(dstId)
-                        && devConfig.getPairLocalPort(srcId)
-                                .equals(link.src().port())
-                        && devConfig.getPairLocalPort(dstId)
-                                .equals(link.dst().port())) {
-                    // found pair link - allow it
-                    return true;
-                } else {
-                    log.warn("Links between leaves other than pair-links are "
-                            + "not allowed...ignoring link {}", link);
-                    return false;
-                }
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            // We still want to count the links in seenLinks even though there
-            // is no config. So we let it return true
-            log.warn("Could not check validity of link {} as subtending devices "
-                    + "are not yet configured", link);
-        }
-        return true;
-    }
-
-    /**
-     * Administratively enables or disables edge ports if the link that was
-     * added or removed was the only uplink port from an edge device. Edge ports
-     * that belong to dual-homed hosts are always processed. In addition,
-     * single-homed host ports are optionally processed depending on the
-     * singleHomedDown property.
-     *
-     * @param link the link to be processed
-     * @param added true if link was added, false if link was removed
-     */
-    private void updateHostPorts(Link link, boolean added) {
-        DeviceConfiguration devConfig = srManager.deviceConfiguration;
-        if (added) {
-            try {
-                if (!devConfig.isEdgeDevice(link.src().deviceId())
-                        || devConfig.isEdgeDevice(link.dst().deviceId())) {
-                    return;
-                }
-            } catch (DeviceConfigNotFoundException e) {
-                log.warn("Unable to determine if link is a valid uplink"
-                        + e.getMessage());
-            }
-            // re-enable previously disabled ports on this edge-device if any
-            Set<PortNumber> p = downedPortStore.remove(link.src().deviceId());
-            if (p != null) {
-                log.warn("Link src {} --> dst {} added is an edge-device uplink, "
-                        + "enabling dual homed ports if any: {}", link.src().deviceId(),
-                        link.dst().deviceId(), (p.isEmpty()) ? "no ports" : p);
-                p.forEach(pnum -> srManager.deviceAdminService
-                        .changePortState(link.src().deviceId(), pnum, true));
-            }
-        } else {
-            // If the device does not have a pair device - skip
-            DeviceId dev = link.src().deviceId();
-            if (getPairDeviceIdOrNull(dev) == null) {
-                log.info("Device {} does not have pair device " +
-                                 "not disabling access port", dev);
-                return;
-            }
-            // Verify if last uplink
-            if (!lastUplink(link)) {
-                return;
-            }
-            // find dual homed hosts on this dev to disable
-            Set<PortNumber> dp = srManager.hostHandler
-                    .getDualHomedHostPorts(dev);
-            log.warn("Link src {} --> dst {} removed was the last uplink, "
-                    + "disabling  dual homed ports:  {}", dev,
-                     link.dst().deviceId(), (dp.isEmpty()) ? "no ports" : dp);
-            dp.forEach(pnum -> srManager.deviceAdminService
-                        .changePortState(dev, pnum, false));
-            if (srManager.singleHomedDown) {
-                // get all configured ports and down them if they haven't already
-                // been downed
-                srManager.deviceService.getPorts(dev).stream()
-                    .filter(p -> p.isEnabled() && !dp.contains(p.number()))
-                    .filter(p -> srManager.interfaceService
-                            .isConfigured(new ConnectPoint(dev, p.number())))
-                    .filter(p -> !srManager.deviceConfiguration
-                            .isPairLocalPort(dev, p.number()))
-                    .forEach(p -> {
-                        log.warn("Last uplink gone src {} -> dst {} .. removing "
-                                + "configured port {}", p.number());
-                        srManager.deviceAdminService
-                            .changePortState(dev, p.number(), false);
-                        dp.add(p.number());
-                    });
-            }
-            if (!dp.isEmpty()) {
-                // update global store
-                Set<PortNumber> p = downedPortStore.get(dev);
-                if (p == null) {
-                    p = dp;
-                } else {
-                    p.addAll(dp);
-                }
-                downedPortStore.put(link.src().deviceId(), p);
-            }
-        }
-    }
-
-    /**
-     * Returns true if given link was the last active uplink from src-device of
-     * link. An uplink is defined as a unidirectional link with src as
-     * edgeRouter and dst as non-edgeRouter.
-     *
-     * @param link
-     * @return true if given link was the last uplink from the src device
-     */
-    private boolean lastUplink(Link link) {
-        DeviceConfiguration devConfig = srManager.deviceConfiguration;
-        try {
-            if (!devConfig.isEdgeDevice(link.src().deviceId())
-                    || devConfig.isEdgeDevice(link.dst().deviceId())) {
-                return false;
-            }
-            // note that using linkservice here would cause race conditions as
-            // more links can show up while the app is still processing the first one
-            Set<Link> devLinks = seenLinks.entrySet().stream()
-                    .filter(entry -> entry.getKey().src().deviceId()
-                            .equals(link.src().deviceId()))
-                    .filter(entry -> entry.getValue())
-                    .filter(entry -> !entry.getKey().equals(link))
-                    .map(entry -> entry.getKey())
-                    .collect(Collectors.toSet());
-
-            for (Link l : devLinks) {
-                if (devConfig.isEdgeDevice(l.dst().deviceId())) {
-                    continue;
-                }
-                log.debug("Found another active uplink {}", l);
-                return false;
-            }
-            log.debug("No active uplink found");
-            return true;
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn("Unable to determine if link was the last uplink"
-                    + e.getMessage());
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if this controller instance has seen this link before. The
-     * link may not be currently up, but as long as the link had been seen
-     * before this method will return true. The one exception is when the link
-     * was indeed seen before, but this controller instance was forced to forget
-     * it by a call to purgeSeenLink method.
-     *
-     * @param link the infrastructure link being queried
-     * @return true if this controller instance has seen this link before
-     */
-    boolean isSeenLink(Link link) {
-        return seenLinks.containsKey(link);
-    }
-
-    /**
-     * Updates the seen link store. Updates can be for links that are currently
-     * available or not.
-     *
-     * @param link the link to update in the seen-link local store
-     * @param up the status of the link, true if up, false if down
-     */
-    void updateSeenLink(Link link, boolean up) {
-        seenLinks.put(link, up);
-    }
-
-    /**
-     * Returns the status of a seen-link (up or down). If the link has not been
-     * seen-before, a null object is returned.
-     *
-     * @param link the infrastructure link being queried
-     * @return null if the link was not seen-before; true if the seen-link is
-     *         up; false if the seen-link is down
-     */
-    private Boolean isSeenLinkUp(Link link) {
-        return seenLinks.get(link);
-    }
-
-    /**
-     * Makes this controller instance forget a previously seen before link.
-     *
-     * @param link the infrastructure link to purge
-     */
-    private void purgeSeenLink(Link link) {
-        seenLinks.remove(link);
-        seenBefore.remove(link);
-    }
-
-    /**
-     * Returns the status of a link as parallel link. A parallel link is defined
-     * as a link which has common src and dst switches as another seen-link that
-     * is currently enabled. It is not necessary for the link being queried to
-     * be a seen-link.
-     *
-     * @param link the infrastructure link being queried
-     * @return true if a seen-link exists that is up, and shares the same src
-     *         and dst switches as the link being queried
-     */
-    private boolean isParallelLink(Link link) {
-        for (Entry<Link, Boolean> seen : seenLinks.entrySet()) {
-            Link seenLink = seen.getKey();
-            if (seenLink.equals(link)) {
-                continue;
-            }
-            if (seenLink.src().deviceId().equals(link.src().deviceId())
-                    && seenLink.dst().deviceId().equals(link.dst().deviceId())
-                    && seen.getValue()) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the link being queried is a bidirectional link that is
-     * up. A bidi-link is defined as a component unidirectional link, whose
-     * reverse link - ie. the component unidirectional link in the reverse
-     * direction - has been seen-before and is up. It is NOT necessary for the
-     * link being queried to be a previously seen-link.
-     *
-     * @param link the infrastructure (unidirectional) link being queried
-     * @return true if another unidirectional link exists in the reverse
-     *         direction, has been seen-before and is up
-     */
-    boolean isBidirectionalLinkUp(Link link) {
-        // cannot call linkService as link may be gone
-        Link reverseLink = getReverseLink(link);
-        if (reverseLink == null) {
-            return false;
-        }
-        Boolean result = isSeenLinkUp(reverseLink);
-        if (result == null) {
-            return false;
-        }
-        return result.booleanValue();
-    }
-
-    /**
-     * Returns true if the link being queried is a bidirectional link that is
-     * down. A bidi-link is defined as a component unidirectional link, whose
-     * reverse link - i.e the component unidirectional link in the reverse
-     * direction - has been seen before and is down. It is necessary for the
-     * reverse-link to have been previously seen.
-     *
-     * @param link the infrastructure (unidirectional) link being queried
-     * @return true if another unidirectional link exists in the reverse
-     *         direction, has been seen-before and is down
-     */
-    boolean isBidirectionalLinkDown(Link link) {
-        // cannot call linkService as link may be gone
-        Link reverseLink = getReverseLink(link);
-        if (reverseLink == null) {
-            log.warn("Query for bidi-link down but reverse-link not found "
-                    + "for link {}", link);
-            return false;
-        }
-        Boolean result = seenLinks.get(reverseLink);
-        if (result == null) {
-            return false;
-        }
-        // if reverse link is seen UP (true), then its not bidi yet
-        return !result.booleanValue();
-    }
-
-    /**
-     * Returns the link in the reverse direction from the given link, by
-     * consulting the seen-links store.
-     *
-     * @param link the given link
-     * @return the reverse link or null
-     */
-    Link getReverseLink(Link link) {
-        return seenLinks.keySet().stream()
-                .filter(l -> l.src().equals(link.dst()) && l.dst().equals(link.src()))
-                .findAny()
-                .orElse(null);
-    }
-
-    /**
-     * Returns the component unidirectional links of a declared bidirectional
-     * link, by consulting the seen-links store. Caller is responsible for
-     * previously verifying bidirectionality. Returned list may be empty if
-     * errors are encountered.
-     *
-     * @param link the declared bidirectional link
-     * @return list of component unidirectional links
-     */
-    List<Link> getBidiComponentLinks(Link link) {
-        Link reverseLink = getReverseLink(link);
-        List<Link> componentLinks;
-        if (reverseLink == null) { // really should not happen if link is bidi
-            log.error("cannot find reverse link for given link: {} ... is it "
-                    + "bi-directional?", link);
-            componentLinks = ImmutableList.of();
-        } else {
-            componentLinks = ImmutableList.of(reverseLink, link);
-        }
-        return componentLinks;
-    }
-
-    /**
-     * Determines if the given link should be avoided in routing calculations by
-     * policy or design.
-     *
-     * @param link the infrastructure link being queried
-     * @return true if link should be avoided
-     */
-    boolean avoidLink(Link link) {
-        // XXX currently only avoids all pair-links. In the future can be
-        // extended to avoid any generic link
-        DeviceId src = link.src().deviceId();
-        PortNumber srcPort = link.src().port();
-        DeviceConfiguration devConfig = srManager.deviceConfiguration;
-        if (devConfig == null || !devConfig.isConfigured(src)) {
-            log.warn("Device {} not configured..cannot avoid link {}", src,
-                     link);
-            return false;
-        }
-        DeviceId pairDev;
-        PortNumber pairLocalPort, pairRemotePort = null;
-        try {
-            pairDev = devConfig.getPairDeviceId(src);
-            pairLocalPort = devConfig.getPairLocalPort(src);
-            if (pairDev != null) {
-                pairRemotePort = devConfig
-                        .getPairLocalPort(pairDev);
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn("Pair dev for dev {} not configured..cannot avoid link {}",
-                     src, link);
-            return false;
-        }
-
-        return srcPort.equals(pairLocalPort)
-                && link.dst().deviceId().equals(pairDev)
-                && link.dst().port().equals(pairRemotePort);
-    }
-
-    /**
-     * Cleans up internal LinkHandler stores.
-     *
-     * @param device the device that has been removed
-     */
-    void processDeviceRemoved(Device device) {
-        seenLinks.keySet()
-                .removeIf(key -> key.src().deviceId().equals(device.id())
-                        || key.dst().deviceId().equals(device.id()));
-    }
-
-    /**
-     * Administratively disables the host location switchport if the edge device
-     * has no viable uplinks. The caller needs to determine if such behavior is
-     * desired for the single or dual-homed host.
-     *
-     * @param loc the host location
-     */
-    void checkUplinksForHost(HostLocation loc) {
-        // If the device does not have a pair device - return
-        if (getPairDeviceIdOrNull(loc.deviceId()) == null) {
-            log.info("Device {} does not have pair device " +
-                             "not disabling access port", loc.deviceId());
-            return;
-        }
-        // Verify link validity
-        try {
-            for (Link l : srManager.linkService.getDeviceLinks(loc.deviceId())) {
-                if (srManager.deviceConfiguration.isEdgeDevice(l.dst().deviceId())
-                        || l.state() == Link.State.INACTIVE) {
-                    continue;
-                }
-                // found valid uplink - so, nothing to do
-                return;
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn("Could not check for valid uplinks due to missing device"
-                    + "config " + e.getMessage());
-            return;
-        }
-        log.warn("Host location {} has no valid uplinks disabling port", loc);
-        srManager.deviceAdminService.changePortState(loc.deviceId(), loc.port(),
-                                                     false);
-        Set<PortNumber> p = downedPortStore.get(loc.deviceId());
-        if (p == null) {
-            p = Sets.newHashSet(loc.port());
-        } else {
-            p.add(loc.port());
-        }
-        downedPortStore.put(loc.deviceId(), p);
-    }
-
-    private DeviceId getPairDeviceIdOrNull(DeviceId deviceId) {
-        DeviceId pairDev;
-        try {
-            pairDev = srManager.deviceConfiguration.getPairDeviceId(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            pairDev = null;
-        }
-        return pairDev;
-    }
-
-    ImmutableMap<Link, Boolean> getSeenLinks() {
-        return ImmutableMap.copyOf(seenLinks);
-    }
-
-    ImmutableMap<DeviceId, Set<PortNumber>> getDownedPorts() {
-        return ImmutableMap.copyOf(downedPortStore.entrySet());
-    }
-
-    /**
-     * Returns all links that egress from given device that are UP in the
-     * seenLinks store. The returned links are also confirmed to be
-     * bidirectional.
-     *
-     * @param deviceId the device identifier
-     * @return set of egress links from the device
-     */
-    Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
-        return seenLinks.keySet().stream()
-                .filter(link -> link.src().deviceId().equals(deviceId))
-                .filter(link -> seenLinks.get(link))
-                .filter(link -> isBidirectionalLinkUp(link))
-                .collect(Collectors.toSet());
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/OsgiPropertyConstants.java b/app/src/main/java/org/onosproject/segmentrouting/OsgiPropertyConstants.java
deleted file mode 100644
index ac63f73..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/OsgiPropertyConstants.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-/**
- * Constants for default values of configurable properties.
- */
-public final class OsgiPropertyConstants {
-
-    private OsgiPropertyConstants() {}
-
-    public static final String PROP_ACTIVE_PROBING = "activeProbing";
-    public static final boolean ACTIVE_PROBING_DEFAULT = true;
-
-    public static final String PROP_SINGLE_HOMED_DOWN = "singleHomedDown";
-    public static final boolean SINGLE_HOMED_DOWN_DEFAULT = false;
-
-    public static final String PROP_RESPOND_TO_UNKNOWN_HOSTS = "respondToUnknownHosts";
-    public static final boolean RESPOND_TO_UNKNOWN_HOSTS_DEFAULT = true;
-
-    public static final String PROP_ROUTE_DOUBLE_TAGGED_HOSTS = "routeDoubleTaggedHosts";
-    public static final boolean ROUTE_DOUBLE_TAGGED_HOSTS_DEFAULT = false;
-
-    public static final String PROP_DEFAULT_INTERNAL_VLAN = "defaultInternalVlan";
-    public static final int DEFAULT_INTERNAL_VLAN_DEFAULT = 4094;
-
-    public static final String PROP_PW_TRANSPORT_VLAN = "pwTransportVlan";
-    public static final int PW_TRANSPORT_VLAN_DEFAULT = 4090;
-
-    static final String PROP_SYMMETRIC_PROBING = "symmetricProbing";
-    static final boolean SYMMETRIC_PROBING_DEFAULT = false;
-
-    public static final String PROP_ROUTE_SIMPLIFICATION = "routeSimplification";
-    public static final boolean ROUTE_SIMPLIFICATION_DEFAULT = false;
-
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/Policy.java b/app/src/main/java/org/onosproject/segmentrouting/Policy.java
deleted file mode 100644
index c9baf93..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/Policy.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-/**
- * Interface for Segment Routing Policy.
- */
-public interface Policy {
-    /**
-     * Enums for policy type.
-     */
-    enum Type {
-        /**
-         * Tunnel flow policy type.
-         */
-        TUNNEL_FLOW,
-
-        /**
-         * Load balancing policy type.
-         */
-        LOADBALANCE,
-
-        /**
-         * policy to avoid specific routers or links.
-         */
-        AVOID,
-
-        /**
-         * Access Control policy type.
-         */
-        DENY
-    }
-
-    /**
-     * Returns the policy ID.
-     *
-     * @return policy ID
-     */
-    String id();
-
-    /**
-     * Returns the priority of the policy.
-     *
-     * @return priority
-     */
-    int priority();
-
-    /**
-     * Returns the policy type.
-     *
-     * @return policy type
-     */
-    Type type();
-
-    /**
-     * Returns the source IP address of the policy.
-     *
-     * @return source IP address
-     */
-    String srcIp();
-
-    /**
-     * Returns the destination IP address of the policy.
-     *
-     * @return destination IP address
-     */
-    String dstIp();
-
-    /**
-     * Returns the IP protocol of the policy.
-     *
-     * @return IP protocol
-     */
-    String ipProto();
-
-    /**
-     * Returns the source port of the policy.
-     *
-     * @return source port
-     */
-    short srcPort();
-
-    /**
-     * Returns the destination of the policy.
-     *
-     * @return destination port
-     */
-    short dstPort();
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java b/app/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
deleted file mode 100644
index a341bb1..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/PolicyHandler.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.TpPort;
-import org.onosproject.cli.net.IpProtocol;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.flow.DefaultTrafficSelector;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.store.service.EventuallyConsistentMap;
-import org.slf4j.Logger;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Segment Routing Policy Handler.
- */
-public class PolicyHandler {
-
-    protected final Logger log = getLogger(getClass());
-
-    private ApplicationId appId;
-    private DeviceConfiguration deviceConfiguration;
-    private FlowObjectiveService flowObjectiveService;
-    private TunnelHandler tunnelHandler;
-    private final EventuallyConsistentMap<String, Policy> policyStore;
-    /**
-     * Result of policy creation.
-     */
-    public enum Result {
-        /**
-         * Success.
-         */
-        SUCCESS,
-
-        /**
-         * The same policy exists already.
-         */
-        POLICY_EXISTS,
-
-        /**
-         * The policy ID exists already.
-         */
-        ID_EXISTS,
-
-        /**
-         * Cannot find associated tunnel.
-         */
-        TUNNEL_NOT_FOUND,
-
-        /**
-         * Policy was not found.
-         */
-        POLICY_NOT_FOUND,
-
-        /**
-         * Policy type {} is not supported yet.
-         */
-        UNSUPPORTED_TYPE
-    }
-
-    /**
-     * Constructs policy handler.
-     *
-     * @param appId                segment routing application ID
-     * @param deviceConfiguration  DeviceConfiguration reference
-     * @param flowObjectiveService FlowObjectiveService reference
-     * @param tunnelHandler        tunnel handler reference
-     * @param policyStore          policy store
-     */
-    public PolicyHandler(ApplicationId appId,
-                         DeviceConfiguration deviceConfiguration,
-                         FlowObjectiveService flowObjectiveService,
-                         TunnelHandler tunnelHandler,
-                         EventuallyConsistentMap<String, Policy> policyStore) {
-        this.appId = appId;
-        this.deviceConfiguration = deviceConfiguration;
-        this.flowObjectiveService = flowObjectiveService;
-        this.tunnelHandler = tunnelHandler;
-        this.policyStore = policyStore;
-    }
-
-    /**
-     * Returns the policies.
-     *
-     * @return policy list
-     */
-    public List<Policy> getPolicies() {
-        return policyStore.values()
-                .stream()
-                .filter(policy -> policy instanceof TunnelPolicy)
-                .map(policy -> new TunnelPolicy((TunnelPolicy) policy))
-                .collect(Collectors.toList());
-    }
-
-    /**
-     * Creates a policy using the policy information given.
-     *  @param policy policy reference to create
-     *  @return ID_EXISTS if the same policy ID exists,
-     *  POLICY_EXISTS if the same policy exists, TUNNEL_NOT_FOUND if the tunnel
-     *  does not exists, UNSUPPORTED_TYPE if the policy type is not supported,
-     *  SUCCESS if the policy is created successfully
-     */
-    public Result createPolicy(Policy policy) {
-
-        if (policyStore.containsKey(policy.id())) {
-            log.warn("The policy id {} exists already", policy.id());
-            return Result.ID_EXISTS;
-        }
-
-        if (policyStore.containsValue(policy)) {
-            log.warn("The same policy exists already");
-            return Result.POLICY_EXISTS;
-        }
-
-        if (policy.type() == Policy.Type.TUNNEL_FLOW) {
-
-            TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
-            Tunnel tunnel = tunnelHandler.getTunnel(tunnelPolicy.tunnelId());
-            if (tunnel == null) {
-                return Result.TUNNEL_NOT_FOUND;
-            }
-
-            ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                    .builder()
-                    .fromApp(appId)
-                    .makePermanent()
-                    .nextStep(tunnel.groupId())
-                    .withPriority(tunnelPolicy.priority())
-                    .withSelector(buildSelector(policy))
-                    .withFlag(ForwardingObjective.Flag.VERSATILE);
-
-            DeviceId source = deviceConfiguration.getDeviceId(tunnel.labelIds().get(0));
-            flowObjectiveService.forward(source, fwdBuilder.add());
-
-        } else {
-            log.warn("Policy type {} is not supported yet.", policy.type());
-            return Result.UNSUPPORTED_TYPE;
-        }
-
-        policyStore.put(policy.id(), policy);
-
-        return Result.SUCCESS;
-    }
-
-    /**
-     * Removes the policy given.
-     *
-     * @param policyInfo policy information to remove
-     * @return POLICY_NOT_FOUND if the policy to remove does not exists,
-     * SUCCESS if it is removed successfully
-     */
-    public Result removePolicy(Policy policyInfo) {
-
-        if (policyStore.get(policyInfo.id()) != null) {
-            Policy policy = policyStore.get(policyInfo.id());
-            if (policy.type() == Policy.Type.TUNNEL_FLOW) {
-                TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
-                Tunnel tunnel = tunnelHandler.getTunnel(tunnelPolicy.tunnelId());
-
-                ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                        .builder()
-                        .fromApp(appId)
-                        .makePermanent()
-                        .withSelector(buildSelector(policy))
-                        .withPriority(tunnelPolicy.priority())
-                        .nextStep(tunnel.groupId())
-                        .withFlag(ForwardingObjective.Flag.VERSATILE);
-
-                DeviceId source = deviceConfiguration.getDeviceId(tunnel.labelIds().get(0));
-                flowObjectiveService.forward(source, fwdBuilder.remove());
-
-                policyStore.remove(policyInfo.id());
-            }
-        } else {
-            log.warn("Policy {} was not found", policyInfo.id());
-            return Result.POLICY_NOT_FOUND;
-        }
-
-        return Result.SUCCESS;
-    }
-
-
-    private TrafficSelector buildSelector(Policy policy) {
-
-        TrafficSelector.Builder tsb = DefaultTrafficSelector.builder();
-        tsb.matchEthType(Ethernet.TYPE_IPV4);
-        if (policy.dstIp() != null && !policy.dstIp().isEmpty()) {
-            tsb.matchIPDst(IpPrefix.valueOf(policy.dstIp()));
-        }
-        if (policy.srcIp() != null && !policy.srcIp().isEmpty()) {
-            tsb.matchIPSrc(IpPrefix.valueOf(policy.srcIp()));
-        }
-        if (policy.ipProto() != null && !policy.ipProto().isEmpty()) {
-            Short ipProto = IpProtocol.valueOf(policy.ipProto()).value();
-            tsb.matchIPProtocol(ipProto.byteValue());
-            if (IpProtocol.valueOf(policy.ipProto()).equals(IpProtocol.TCP)) {
-                if (policy.srcPort() != 0) {
-                    tsb.matchTcpSrc(TpPort.tpPort(policy.srcPort()));
-                }
-                if (policy.dstPort() != 0) {
-                    tsb.matchTcpDst(TpPort.tpPort(policy.dstPort()));
-                }
-            } else if (IpProtocol.valueOf(policy.ipProto()).equals(IpProtocol.UDP)) {
-                if (policy.srcPort() != 0) {
-                    tsb.matchUdpSrc(TpPort.tpPort(policy.srcPort()));
-                }
-                if (policy.dstPort() != 0) {
-                    tsb.matchUdpDst(TpPort.tpPort(policy.dstPort()));
-                }
-            }
-        }
-
-        return tsb.build();
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/PortAuthTracker.java b/app/src/main/java/org/onosproject/segmentrouting/PortAuthTracker.java
deleted file mode 100644
index 2127019..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/PortAuthTracker.java
+++ /dev/null
@@ -1,357 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-
-import com.google.common.base.Objects;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.segmentrouting.config.BlockedPortsConfig;
-import org.onosproject.utils.Comparators;
-import org.slf4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import static org.onosproject.net.DeviceId.deviceId;
-import static org.onosproject.net.PortNumber.portNumber;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Keeps track of ports that have been configured for blocking,
- * and their current authentication state.
- */
-public class PortAuthTracker {
-
-    private static final Logger log = getLogger(PortAuthTracker.class);
-
-    private Map<DeviceId, Map<PortNumber, BlockState>> blockedPorts = new HashMap<>();
-    private Map<DeviceId, Map<PortNumber, BlockState>> oldMap;
-
-    @Override
-    public String toString() {
-        return "PortAuthTracker{entries = " + blockedPorts.size() + "}";
-    }
-
-    /**
-     * Changes the state of the given device id / port number pair to the
-     * specified state.
-     *
-     * @param d        device identifier
-     * @param p        port number
-     * @param newState the updated state
-     * @return true, if the state changed from what was previously mapped
-     */
-    private boolean changeStateTo(DeviceId d, PortNumber p, BlockState newState) {
-        Map<PortNumber, BlockState> portMap =
-                blockedPorts.computeIfAbsent(d, k -> new HashMap<>());
-        BlockState oldState =
-                portMap.computeIfAbsent(p, k -> BlockState.UNCHECKED);
-        portMap.put(p, newState);
-        return (oldState != newState);
-    }
-
-    /**
-     * Radius has authorized the supplicant at this connect point. If
-     * we are tracking this port, clear the blocking flow and mark the
-     * port as authorized.
-     *
-     * @param connectPoint supplicant connect point
-     */
-    void radiusAuthorize(ConnectPoint connectPoint) {
-        DeviceId d = connectPoint.deviceId();
-        PortNumber p = connectPoint.port();
-        if (configured(d, p)) {
-            clearBlockingFlow(d, p);
-            markAsAuthenticated(d, p);
-        }
-    }
-
-    /**
-     * Supplicant at specified connect point has logged off Radius. If
-     * we are tracking this port, install a blocking flow and mark the
-     * port as blocked.
-     *
-     * @param connectPoint supplicant connect point
-     */
-    void radiusLogoff(ConnectPoint connectPoint) {
-        DeviceId d = connectPoint.deviceId();
-        PortNumber p = connectPoint.port();
-        if (configured(d, p)) {
-            installBlockingFlow(d, p);
-            markAsBlocked(d, p);
-        }
-    }
-
-    /**
-     * Marks the specified device/port as blocked.
-     *
-     * @param d device id
-     * @param p port number
-     * @return true if the state changed (was not already blocked)
-     */
-    private boolean markAsBlocked(DeviceId d, PortNumber p) {
-        return changeStateTo(d, p, BlockState.BLOCKED);
-    }
-
-    /**
-     * Marks the specified device/port as authenticated.
-     *
-     * @param d device id
-     * @param p port number
-     * @return true if the state changed (was not already authenticated)
-     */
-    private boolean markAsAuthenticated(DeviceId d, PortNumber p) {
-        return changeStateTo(d, p, BlockState.AUTHENTICATED);
-    }
-
-    /**
-     * Returns true if the given device/port are configured for blocking.
-     *
-     * @param d device id
-     * @param p port number
-     * @return true if this device/port configured for blocking
-     */
-    private boolean configured(DeviceId d, PortNumber p) {
-        Map<PortNumber, BlockState> portMap = blockedPorts.get(d);
-        return portMap != null && portMap.get(p) != null;
-    }
-
-    private BlockState whatState(DeviceId d, PortNumber p,
-                                 Map<DeviceId, Map<PortNumber, BlockState>> m) {
-        Map<PortNumber, BlockState> portMap = m.get(d);
-        if (portMap == null) {
-            return BlockState.UNCHECKED;
-        }
-        BlockState state = portMap.get(p);
-        if (state == null) {
-            return BlockState.UNCHECKED;
-        }
-        return state;
-    }
-
-    /**
-     * Returns the current state of the given device/port.
-     *
-     * @param d device id
-     * @param p port number
-     * @return current block-state
-     */
-    BlockState currentState(DeviceId d, PortNumber p) {
-        return whatState(d, p, blockedPorts);
-    }
-
-    /**
-     * Returns the current state of the given connect point.
-     *
-     * @param cp connect point
-     * @return current block-state
-     */
-
-    BlockState currentState(ConnectPoint cp) {
-        return whatState(cp.deviceId(), cp.port(), blockedPorts);
-    }
-
-    /**
-     * Returns the number of entries being tracked.
-     *
-     * @return the number of tracked entries
-     */
-    int entryCount() {
-        int count = 0;
-        for (Map<PortNumber, BlockState> m : blockedPorts.values()) {
-            count += m.size();
-        }
-        return count;
-    }
-
-    /**
-     * Returns the previously recorded state of the given device/port.
-     *
-     * @param d device id
-     * @param p port number
-     * @return previous block-state
-     */
-    private BlockState oldState(DeviceId d, PortNumber p) {
-        return whatState(d, p, oldMap);
-    }
-
-    private void configurePort(DeviceId d, PortNumber p) {
-        boolean alreadyAuthenticated =
-                oldState(d, p) == BlockState.AUTHENTICATED;
-
-        if (alreadyAuthenticated) {
-            clearBlockingFlow(d, p);
-            markAsAuthenticated(d, p);
-        } else {
-            installBlockingFlow(d, p);
-            markAsBlocked(d, p);
-        }
-        log.info("Configuring port {}/{} as {}", d, p,
-                 alreadyAuthenticated ? "AUTHENTICATED" : "BLOCKED");
-    }
-
-    private boolean notInMap(DeviceId deviceId, PortNumber portNumber) {
-        Map<PortNumber, BlockState> m = blockedPorts.get(deviceId);
-        return m == null || m.get(portNumber) == null;
-    }
-
-    private void logPortsNoLongerBlocked() {
-        for (Map.Entry<DeviceId, Map<PortNumber, BlockState>> entry :
-                oldMap.entrySet()) {
-            DeviceId d = entry.getKey();
-            Map<PortNumber, BlockState> portMap = entry.getValue();
-
-            for (PortNumber p : portMap.keySet()) {
-                if (notInMap(d, p)) {
-                    clearBlockingFlow(d, p);
-                    log.info("De-configuring port {}/{} (UNCHECKED)", d, p);
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Reconfigures the port tracker using the supplied configuration.
-     *
-     * @param cfg the new configuration
-     */
-    void configurePortBlocking(BlockedPortsConfig cfg) {
-        // remember the old map; prepare a new map
-        oldMap = blockedPorts;
-        blockedPorts = new HashMap<>();
-
-        // for each configured device, add configured ports to map
-        for (String devId : cfg.deviceIds()) {
-            cfg.portIterator(devId)
-                    .forEachRemaining(p -> configurePort(deviceId(devId),
-                                                         portNumber(p)));
-        }
-
-        // have we de-configured any ports?
-        logPortsNoLongerBlocked();
-
-        // allow old map to be garbage collected
-        oldMap = null;
-    }
-
-    private List<PortAuthState> reportPortsAuthState() {
-        List<PortAuthState> result = new ArrayList<>();
-
-        for (Map.Entry<DeviceId, Map<PortNumber, BlockState>> entry :
-                blockedPorts.entrySet()) {
-            DeviceId d = entry.getKey();
-            Map<PortNumber, BlockState> portMap = entry.getValue();
-
-            for (PortNumber p : portMap.keySet()) {
-                result.add(new PortAuthState(d, p, portMap.get(p)));
-            }
-        }
-        Collections.sort(result);
-        return result;
-    }
-
-    /**
-     * Installs a "blocking" flow for device/port specified.
-     *
-     * @param d device id
-     * @param p port number
-     */
-    void installBlockingFlow(DeviceId d, PortNumber p) {
-        log.debug("Installing Blocking Flow at {}/{}", d, p);
-        // TODO: invoke SegmentRoutingService.block(...) appropriately
-        log.info("TODO >> Installing Blocking Flow at {}/{}", d, p);
-    }
-
-    /**
-     * Removes the "blocking" flow from device/port specified.
-     *
-     * @param d device id
-     * @param p port number
-     */
-    void clearBlockingFlow(DeviceId d, PortNumber p) {
-        log.debug("Clearing Blocking Flow from {}/{}", d, p);
-        // TODO: invoke SegmentRoutingService.block(...) appropriately
-        log.info("TODO >> Clearing Blocking Flow from {}/{}", d, p);
-    }
-
-
-    /**
-     * Designates the state of a given port. One of:
-     * <ul>
-     * <li> UNCHECKED: not configured for blocking </li>
-     * <li> BLOCKED: configured for blocking, and not yet authenticated </li>
-     * <li> AUTHENTICATED: configured for blocking, but authenticated </li>
-     * </ul>
-     */
-    public enum BlockState {
-        UNCHECKED,
-        BLOCKED,
-        AUTHENTICATED
-    }
-
-    /**
-     * A simple DTO binding of device identifier, port number, and block state.
-     */
-    public static final class PortAuthState implements Comparable<PortAuthState> {
-        private final DeviceId d;
-        private final PortNumber p;
-        private final BlockState s;
-
-        private PortAuthState(DeviceId d, PortNumber p, BlockState s) {
-            this.d = d;
-            this.p = p;
-            this.s = s;
-        }
-
-        @Override
-        public String toString() {
-            return String.valueOf(d) + "/" + p + " -- " + s;
-        }
-
-        @Override
-        public int compareTo(PortAuthState o) {
-            // NOTE: only compare against "deviceid/port"
-            int result = Comparators.ELEMENT_ID_COMPARATOR.compare(d, o.d);
-            return (result != 0) ? result : Long.signum(p.toLong() - o.p.toLong());
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == null) {
-                return false;
-            }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            final PortAuthTracker.PortAuthState that = (PortAuthTracker.PortAuthState) obj;
-
-            return Comparators.ELEMENT_ID_COMPARATOR.compare(this.d, that.d) == 0 &&
-                    p.toLong() == that.p.toLong();
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hashCode(this.d, this.p);
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/PortFilterInfo.java b/app/src/main/java/org/onosproject/segmentrouting/PortFilterInfo.java
deleted file mode 100644
index 75c12d6..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/PortFilterInfo.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.base.MoreObjects;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Utility class used to temporarily store information about the ports on a
- * device processed for filtering objectives.
- */
-public final class PortFilterInfo {
-    int disabledPorts = 0, errorPorts = 0, filteredPorts = 0;
-
-    public PortFilterInfo(int disabledPorts, int errorPorts,
-                          int filteredPorts) {
-        this.disabledPorts = disabledPorts;
-        this.filteredPorts = filteredPorts;
-        this.errorPorts = errorPorts;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(disabledPorts, filteredPorts, errorPorts);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if ((obj == null) || (!(obj instanceof PortFilterInfo))) {
-            return false;
-        }
-        PortFilterInfo other = (PortFilterInfo) obj;
-        return ((disabledPorts == other.disabledPorts) &&
-                (filteredPorts == other.filteredPorts) &&
-                (errorPorts == other.errorPorts));
-    }
-
-    @Override
-    public String toString() {
-        MoreObjects.ToStringHelper helper = toStringHelper(this)
-                .add("disabledPorts", disabledPorts)
-                .add("errorPorts", errorPorts)
-                .add("filteredPorts", filteredPorts);
-        return helper.toString();
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/RouteHandler.java b/app/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
deleted file mode 100644
index d9372fe..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Sets;
-
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.RouteEvent;
-import org.onosproject.net.DeviceId;
-import org.onosproject.routeservice.RouteInfo;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.Collection;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Handles RouteEvent and manages routing entries.
- */
-public class RouteHandler {
-    private static final Logger log = LoggerFactory.getLogger(RouteHandler.class);
-    private final SegmentRoutingManager srManager;
-
-    RouteHandler(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-    }
-
-    protected void init(DeviceId deviceId) {
-        srManager.routeService.getRouteTables().stream()
-                .map(srManager.routeService::getRoutes)
-                .flatMap(Collection::stream)
-                .map(RouteInfo::allRoutes)
-                .filter(allRoutes -> allRoutes.stream().anyMatch(resolvedRoute ->
-                        srManager.nextHopLocations(resolvedRoute).stream()
-                                .anyMatch(cp -> deviceId.equals(cp.deviceId()))))
-                .forEach(rr -> processRouteAddedInternal(rr, true));
-    }
-
-    void processRouteAdded(RouteEvent event) {
-        processRouteAddedInternal(event.alternatives(), false);
-    }
-
-    /**
-     * Internal logic that handles route addition.
-     *
-     * @param routes collection of routes to be processed
-     * @param populateRouteOnly true if we only want to populateRoute but not populateSubnet.
-     *                          Set it to true when initializing a device coming up.
-     *                          populateSubnet will be done when link comes up later so it is redundant.
-     *                          populateRoute still needs to be done for statically configured next hop hosts.
-     */
-    private void processRouteAddedInternal(Collection<ResolvedRoute> routes, boolean populateRouteOnly) {
-        if (!isReady()) {
-            log.info("System is not ready. Skip adding route for {}", routes);
-            return;
-        }
-
-        log.info("processRouteAddedInternal. routes={}", routes);
-
-        if (routes.size() > 2) {
-            log.info("Route {} has more than two next hops. Do not process route change", routes);
-            return;
-        }
-
-        ResolvedRoute rr = routes.stream().findFirst().orElse(null);
-        if (rr == null) {
-            log.warn("No resolved route found. Abort processRouteAddedInternal");
-        }
-
-        Set<ConnectPoint> allLocations = Sets.newHashSet();
-        Set<IpPrefix> allPrefixes = Sets.newHashSet();
-        routes.forEach(route -> {
-            allLocations.addAll(srManager.nextHopLocations(route));
-            allPrefixes.add(route.prefix());
-        });
-        log.debug("RouteAdded. populateSubnet {}, {}", allLocations, allPrefixes);
-        srManager.defaultRoutingHandler.populateSubnet(allLocations, allPrefixes);
-
-
-        routes.forEach(route -> {
-            IpPrefix prefix = route.prefix();
-            MacAddress nextHopMac = route.nextHopMac();
-            VlanId nextHopVlan = route.nextHopVlan();
-            Set<ConnectPoint> locations = srManager.nextHopLocations(route);
-
-            locations.forEach(location -> {
-                log.debug("RouteAdded. addSubnet {}, {}", location, prefix);
-                srManager.deviceConfiguration.addSubnet(location, prefix);
-                log.debug("RouteAdded populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
-                srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
-                        nextHopMac, nextHopVlan, location.port(), false);
-            });
-        });
-    }
-
-    void processRouteUpdated(RouteEvent event) {
-        processRouteUpdatedInternal(Sets.newHashSet(event.alternatives()),
-                Sets.newHashSet(event.prevAlternatives()));
-    }
-
-    void processAlternativeRoutesChanged(RouteEvent event) {
-        processRouteUpdatedInternal(Sets.newHashSet(event.alternatives()),
-                Sets.newHashSet(event.prevAlternatives()));
-    }
-
-    private void processRouteUpdatedInternal(Set<ResolvedRoute> routes, Set<ResolvedRoute> oldRoutes) {
-        if (!isReady()) {
-            log.info("System is not ready. Skip updating route for {} -> {}", oldRoutes, routes);
-            return;
-        }
-
-        log.info("processRouteUpdatedInternal. routes={}, oldRoutes={}", routes, oldRoutes);
-
-        if (routes.size() > 2) {
-            log.info("Route {} has more than two next hops. Do not process route change", routes);
-            return;
-        }
-
-        Set<ConnectPoint> allLocations = Sets.newHashSet();
-        Set<IpPrefix> allPrefixes = Sets.newHashSet();
-        routes.forEach(route -> {
-            allLocations.addAll(srManager.nextHopLocations(route));
-            allPrefixes.add(route.prefix());
-        });
-
-        // Just come back from an invalid next hop count
-        // Revoke subnet from all locations and reset oldRoutes such that system will be reprogrammed from scratch
-        if (oldRoutes.size() > 2) {
-            log.info("Revoke subnet {} and reset oldRoutes");
-            srManager.defaultRoutingHandler.revokeSubnet(allPrefixes);
-            oldRoutes = Sets.newHashSet();
-        }
-
-        log.debug("RouteUpdated. populateSubnet {}, {}", allLocations, allPrefixes);
-        srManager.defaultRoutingHandler.populateSubnet(allLocations, allPrefixes);
-
-        Set<ResolvedRoute> toBeRemoved = Sets.difference(oldRoutes, routes).immutableCopy();
-        Set<ResolvedRoute> toBeAdded = Sets.difference(routes, oldRoutes).immutableCopy();
-
-        toBeRemoved.forEach(route -> {
-            srManager.nextHopLocations(route).forEach(oldLocation -> {
-                if (toBeAdded.stream().map(srManager::nextHopLocations)
-                        .flatMap(Set::stream).map(ConnectPoint::deviceId)
-                        .noneMatch(deviceId -> deviceId.equals(oldLocation.deviceId()))) {
-                    IpPrefix prefix = route.prefix();
-                    log.debug("RouteUpdated. removeSubnet {}, {}", oldLocation, prefix);
-                    srManager.deviceConfiguration.removeSubnet(oldLocation, prefix);
-                    // We don't remove the flow on the old location in occasion of two next hops becoming one
-                    // since the populateSubnet will point the old location to the new location via spine.
-                }
-            });
-        });
-
-        toBeAdded.forEach(route -> {
-            IpPrefix prefix = route.prefix();
-            MacAddress nextHopMac = route.nextHopMac();
-            VlanId nextHopVlan = route.nextHopVlan();
-            Set<ConnectPoint> locations = srManager.nextHopLocations(route);
-
-            locations.forEach(location -> {
-                log.debug("RouteUpdated. addSubnet {}, {}", location, prefix);
-                srManager.deviceConfiguration.addSubnet(location, prefix);
-                log.debug("RouteUpdated. populateRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
-                srManager.defaultRoutingHandler.populateRoute(location.deviceId(), prefix,
-                        nextHopMac, nextHopVlan, location.port(), false);
-            });
-        });
-    }
-
-    void processRouteRemoved(RouteEvent event) {
-        processRouteRemovedInternal(event.alternatives());
-    }
-
-    private void processRouteRemovedInternal(Collection<ResolvedRoute> routes) {
-        if (!isReady()) {
-            log.info("System is not ready. Skip removing route for {}", routes);
-            return;
-        }
-
-        log.info("processRouteRemovedInternal. routes={}", routes);
-
-        Set<IpPrefix> allPrefixes = Sets.newHashSet();
-        routes.forEach(route -> {
-            allPrefixes.add(route.prefix());
-        });
-        log.debug("RouteRemoved. revokeSubnet {}", allPrefixes);
-        srManager.defaultRoutingHandler.revokeSubnet(allPrefixes);
-
-        routes.forEach(route -> {
-            IpPrefix prefix = route.prefix();
-            MacAddress nextHopMac = route.nextHopMac();
-            VlanId nextHopVlan = route.nextHopVlan();
-            Set<ConnectPoint> locations = srManager.nextHopLocations(route);
-
-            locations.forEach(location -> {
-                log.debug("RouteRemoved. removeSubnet {}, {}", location, prefix);
-                srManager.deviceConfiguration.removeSubnet(location, prefix);
-                // We don't need to call revokeRoute again since revokeSubnet will remove the prefix
-                // from all devices, including the ones that next hop attaches to.
-
-                // Also remove redirection flows on the pair device if exists.
-                Optional<DeviceId> pairDeviceId = srManager.getPairDeviceId(location.deviceId());
-                Optional<PortNumber> pairLocalPort = srManager.getPairLocalPort(location.deviceId());
-                if (pairDeviceId.isPresent() && pairLocalPort.isPresent()) {
-                    // NOTE: Since the pairLocalPort is trunk port, use assigned vlan of original port
-                    //       when the host is untagged
-                    VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(location)).orElse(nextHopVlan);
-
-                    log.debug("RouteRemoved. revokeRoute {}, {}, {}, {}", location, prefix, nextHopMac, nextHopVlan);
-                    srManager.defaultRoutingHandler.revokeRoute(pairDeviceId.get(), prefix,
-                            nextHopMac, vlanId, pairLocalPort.get(), false);
-                }
-            });
-        });
-    }
-
-    void processHostMovedEvent(HostEvent event) {
-        log.info("processHostMovedEvent {}", event);
-        MacAddress hostMac = event.subject().mac();
-        VlanId hostVlanId = event.subject().vlan();
-
-        Set<HostLocation> prevLocations = event.prevSubject().locations();
-        Set<HostLocation> newLocations = event.subject().locations();
-        Set<ConnectPoint> connectPoints = newLocations.stream()
-                .map(l -> (ConnectPoint) l).collect(Collectors.toSet());
-        List<Set<IpPrefix>> batchedSubnets =
-                srManager.deviceConfiguration.getBatchedSubnets(event.subject().id());
-        Set<DeviceId> newDeviceIds = newLocations.stream().map(HostLocation::deviceId)
-                .collect(Collectors.toSet());
-
-        // Set of deviceIDs of the previous locations where the host was connected
-        // Used to determine if host moved to different connect points
-        // on same device or moved to a different device altogether
-        Set<DeviceId> oldDeviceIds = prevLocations.stream().map(HostLocation::deviceId)
-                .collect(Collectors.toSet());
-
-        // L3 Ucast bucket needs to be updated only once per host
-        // and only when the no. of routes with the host as next-hop is not zero
-        if (!batchedSubnets.isEmpty()) {
-           // For each new location, if NextObj exists for the host, update with new location ..
-           Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
-                  int nextId = srManager.getMacVlanNextObjectiveId(newLocation.deviceId(),
-                                                                   hostMac, hostVlanId, null, false);
-                  VlanId vlanId = Optional.ofNullable(srManager.getInternalVlanId(newLocation)).orElse(hostVlanId);
-
-                  if (nextId != -1) {
-                      //Update the nextId group bucket
-                      log.debug("HostMoved. NextId exists, update L3 Ucast Group Bucket {}, {}, {} --> {}",
-                                             newLocation, hostMac, vlanId, nextId);
-                      srManager.updateMacVlanTreatment(newLocation.deviceId(), hostMac, vlanId,
-                                                       newLocation.port(), nextId);
-                  } else {
-                      log.debug("HostMoved. NextId does not exist for this location {}, host {}/{}",
-                                              newLocation, hostMac, vlanId);
-                  }
-           });
-        }
-
-        batchedSubnets.forEach(subnets -> {
-            log.debug("HostMoved. populateSubnet {}, {}", newLocations, subnets);
-            srManager.defaultRoutingHandler.populateSubnet(connectPoints, subnets);
-
-            subnets.forEach(prefix -> {
-                // For each old location
-                Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
-
-                    // Remove flows for unchanged IPs only when the host moves from a switch to another.
-                    // Otherwise, do not remove and let the adding part update the old flow
-                    if (newDeviceIds.contains(prevLocation.deviceId())) {
-                        return;
-                    }
-
-                    log.debug("HostMoved. removeSubnet {}, {}", prevLocation, prefix);
-                    srManager.deviceConfiguration.removeSubnet(prevLocation, prefix);
-
-                    // Do not remove flow from a device if the route is still reachable via its pair device.
-                    // populateSubnet will update the flow to point to its pair device via spine.
-                    DeviceId pairDeviceId = srManager.getPairDeviceId(prevLocation.deviceId()).orElse(null);
-                    if (newLocations.stream().anyMatch(n -> n.deviceId().equals(pairDeviceId))) {
-                        return;
-                    }
-
-                    log.debug("HostMoved. revokeRoute {}, {}, {}, {}", prevLocation, prefix, hostMac, hostVlanId);
-                    srManager.defaultRoutingHandler.revokeRoute(prevLocation.deviceId(), prefix,
-                            hostMac, hostVlanId, prevLocation.port(), false);
-                });
-
-                // For each new location, add all new IPs.
-                Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
-                    log.debug("HostMoved. addSubnet {}, {}", newLocation, prefix);
-                    srManager.deviceConfiguration.addSubnet(newLocation, prefix);
-
-                    //its a new connect point, not a move from an existing device, populateRoute
-                    if (!oldDeviceIds.contains(newLocation.deviceId())) {
-                       log.debug("HostMoved. populateRoute {}, {}, {}, {}", newLocation, prefix, hostMac, hostVlanId);
-                       srManager.defaultRoutingHandler.populateRoute(newLocation.deviceId(), prefix,
-                              hostMac, hostVlanId, newLocation.port(), false);
-                    }
-                });
-            });
-        });
-
-    }
-
-    private boolean isReady() {
-        return Objects.nonNull(srManager.deviceConfiguration) &&
-                Objects.nonNull(srManager.defaultRoutingHandler);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/RouteSimplifierUtils.java b/app/src/main/java/org/onosproject/segmentrouting/RouteSimplifierUtils.java
deleted file mode 100644
index 401007c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/RouteSimplifierUtils.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.ImmutableList;
-import org.onlab.packet.IpPrefix;
-import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.Route;
-
-/**
- * Utility class for route simplification.
- */
-final class RouteSimplifierUtils {
-
-    /*
-     * When route with source type listed in leafExclusionRouteTypes,
-     * it will programme only on the leaf pair the nexthop attaches to. Other leaves will be ignored.
-     */
-    private static final ImmutableList<Route.Source> LEAF_EXCLUSION_ROUTE_TYPES =
-            ImmutableList.of(Route.Source.DHCP, Route.Source.RIP, Route.Source.DHCPLQ);
-
-    private SegmentRoutingManager srManager;
-
-    RouteSimplifierUtils(SegmentRoutingManager srManager) {
-
-        this.srManager = srManager;
-    }
-
-    /**
-     * Checking whether the leafExclusionRouteTypes contains the given source type.
-     *
-     * @param s source type
-     * @return boolean if it containsd the source type.
-     *
-     */
-    public boolean hasLeafExclusionEnabledForType(Route.Source s) {
-        return LEAF_EXCLUSION_ROUTE_TYPES.contains(s);
-    }
-
-    /**
-     * When route with any source of given prefix is  listed in leafExclusionRouteTypes,
-     * it will programme only on the leaf pair the nexthop attaches to. Other leaves will be ignored.
-     *
-     * @param ipPrefix  ip prefix of the route.
-     * @return boolean if contains the prefix of the mentioned source type.
-     */
-    public boolean hasLeafExclusionEnabledForPrefix(IpPrefix ipPrefix) {
-        for (ResolvedRoute route : srManager.routeService.getAllResolvedRoutes(ipPrefix)) {
-            if (hasLeafExclusionEnabledForType(route.route().source())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
deleted file mode 100644
index a6f6e32..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ /dev/null
@@ -1,1942 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.onlab.packet.EthType;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IPv6;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip6Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.MplsLabel;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Device;
-import org.onosproject.net.Host;
-import org.onosproject.net.flowobjective.DefaultObjectiveContext;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.packet.PacketPriority;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
-import org.onosproject.segmentrouting.grouphandler.DestinationSet;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-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.flow.criteria.Criteria;
-import org.onosproject.net.flowobjective.DefaultFilteringObjective;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.ForwardingObjective.Builder;
-import org.onosproject.net.flowobjective.ForwardingObjective.Flag;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onlab.packet.Ethernet.TYPE_ARP;
-import static org.onlab.packet.Ethernet.TYPE_IPV6;
-import static org.onlab.packet.ICMP6.NEIGHBOR_ADVERTISEMENT;
-import static org.onlab.packet.ICMP6.NEIGHBOR_SOLICITATION;
-import static org.onlab.packet.ICMP6.ROUTER_ADVERTISEMENT;
-import static org.onlab.packet.ICMP6.ROUTER_SOLICITATION;
-import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
-import static org.onosproject.segmentrouting.SegmentRoutingService.DEFAULT_PRIORITY;
-
-/**
- * Populator of segment routing flow rules.
- */
-public class RoutingRulePopulator {
-    private static final Logger log = LoggerFactory.getLogger(RoutingRulePopulator.class);
-
-    private static final int ARP_NDP_PRIORITY = 30000;
-
-    private AtomicLong rulePopulationCounter;
-    private SegmentRoutingManager srManager;
-    private DeviceConfiguration config;
-    private RouteSimplifierUtils routeSimplifierUtils;
-
-    // used for signalling the driver to remove vlan table and tmac entry also
-    private static final long CLEANUP_DOUBLE_TAGGED_HOST_ENTRIES = 1;
-    private static final long DOUBLE_TAGGED_METADATA_MASK = 0xffffffffffffffffL;
-
-    /**
-     * Creates a RoutingRulePopulator object.
-     *
-     * @param srManager segment routing manager reference
-     */
-    RoutingRulePopulator(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        this.config = checkNotNull(srManager.deviceConfiguration);
-        this.rulePopulationCounter = new AtomicLong(0);
-        this.routeSimplifierUtils = new RouteSimplifierUtils(srManager);
-    }
-
-    /**
-     * Resets the population counter.
-     */
-    void resetCounter() {
-        rulePopulationCounter.set(0);
-    }
-
-    /**
-     * Returns the number of rules populated.
-     *
-     * @return number of rules
-     */
-    long getCounter() {
-        return rulePopulationCounter.get();
-    }
-
-    /**
-     * Populate a bridging rule on given deviceId that matches given mac, given vlan and
-     * output to given port.
-     *
-     * @param deviceId device ID
-     * @param port port
-     * @param mac mac address
-     * @param vlanId VLAN ID
-     * @return future that carries the flow objective if succeeded, null if otherwise
-     */
-    CompletableFuture<Objective> populateBridging(DeviceId deviceId, PortNumber port, MacAddress mac, VlanId vlanId) {
-        ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, false);
-        if (fob == null) {
-            log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
-            return CompletableFuture.completedFuture(null);
-        }
-
-        CompletableFuture<Objective> future = new CompletableFuture<>();
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> {
-                    log.debug("Brigding rule for {}/{} populated", mac, vlanId);
-                    future.complete(objective);
-                },
-                (objective, error) -> {
-                    log.warn("Failed to populate bridging rule for {}/{}: {}", mac, vlanId, error);
-                    future.complete(null);
-                });
-        srManager.flowObjectiveService.forward(deviceId, fob.add(context));
-        return future;
-    }
-
-    /**
-     * Revoke a bridging rule on given deviceId that matches given mac, given vlan and
-     * output to given port.
-     *
-     * @param deviceId device ID
-     * @param port port
-     * @param mac mac address
-     * @param vlanId VLAN ID
-     * @return future that carries the flow objective if succeeded, null if otherwise
-     */
-    CompletableFuture<Objective> revokeBridging(DeviceId deviceId, PortNumber port, MacAddress mac, VlanId vlanId) {
-        ForwardingObjective.Builder fob = bridgingFwdObjBuilder(deviceId, mac, vlanId, port, true);
-        if (fob == null) {
-            log.warn("Fail to build fwd obj for host {}/{}. Abort.", mac, vlanId);
-            return CompletableFuture.completedFuture(null);
-        }
-
-        CompletableFuture<Objective> future = new CompletableFuture<>();
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> {
-                    log.debug("Brigding rule for {}/{} revoked", mac, vlanId);
-                    future.complete(objective);
-                },
-                (objective, error) -> {
-                    log.warn("Failed to revoke bridging rule for {}/{}: {}", mac, vlanId, error);
-                    future.complete(null);
-                });
-        srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
-        return future;
-    }
-
-    /**
-     * Generates a forwarding objective builder for bridging rules.
-     * <p>
-     * The forwarding objective bridges packets destined to a given MAC to
-     * given port on given device.
-     *
-     * @param deviceId Device that host attaches to
-     * @param mac MAC address of the host
-     * @param hostVlanId VLAN ID of the host
-     * @param outport Port that host attaches to
-     * @param revoke true if forwarding objective is meant to revoke forwarding rule
-     * @return Forwarding objective builder
-     */
-    private ForwardingObjective.Builder bridgingFwdObjBuilder(
-            DeviceId deviceId, MacAddress mac, VlanId hostVlanId, PortNumber outport, boolean revoke) {
-        ConnectPoint connectPoint = new ConnectPoint(deviceId, outport);
-        VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
-        Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
-        VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
-
-        // Create host selector
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        sbuilder.matchEthDst(mac);
-
-        // Create host treatment
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.immediate().setOutput(outport);
-
-        // Create host meta
-        TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
-
-        // Adjust the selector, treatment and meta according to VLAN configuration
-        if (taggedVlans.contains(hostVlanId)) {
-            sbuilder.matchVlanId(hostVlanId);
-            mbuilder.matchVlanId(hostVlanId);
-        } else if (hostVlanId.equals(VlanId.NONE)) {
-            if (untaggedVlan != null) {
-                sbuilder.matchVlanId(untaggedVlan);
-                mbuilder.matchVlanId(untaggedVlan);
-                tbuilder.immediate().popVlan();
-            } else if (nativeVlan != null) {
-                sbuilder.matchVlanId(nativeVlan);
-                mbuilder.matchVlanId(nativeVlan);
-                tbuilder.immediate().popVlan();
-            } else {
-                log.warn("Untagged host {}/{} is not allowed on {} without untagged or native" +
-                        "vlan config", mac, hostVlanId, connectPoint);
-                return null;
-            }
-        } else {
-            log.warn("Tagged host {}/{} is not allowed on {} without VLAN listed in tagged vlan",
-                    mac, hostVlanId, connectPoint);
-            return null;
-        }
-
-        // All forwarding is via Groups. Drivers can re-purpose to flow-actions if needed.
-        // If the objective is to revoke an existing rule, and for some reason
-        // the next-objective does not exist, then a new one should not be created
-        int portNextObjId = srManager.getPortNextObjectiveId(deviceId, outport,
-                tbuilder.build(), mbuilder.build(), !revoke);
-        if (portNextObjId == -1) {
-            // Warning log will come from getPortNextObjective method
-            return null;
-        }
-
-        return DefaultForwardingObjective.builder()
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .withSelector(sbuilder.build())
-                .nextStep(portNextObjId)
-                .withPriority(100)
-                .fromApp(srManager.appId)
-                .makePermanent();
-    }
-
-    /**
-     * Populate or revoke a bridging rule on given deviceId that matches given vlanId,
-     * and hostMAC connected to given port, and output to given port only when
-     * vlan information is valid.
-     *
-     * @param deviceId device ID that host attaches to
-     * @param portNum port number that host attaches to
-     * @param hostMac mac address of the host connected to the switch port
-     * @param vlanId Vlan ID configured on the switch port
-     * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
-     * @param install true to populate the objective, false to revoke
-     */
-    // TODO Refactor. There are a lot of duplications between this method, populateBridging,
-    //      revokeBridging and bridgingFwdObjBuilder.
-    void updateBridging(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
-                        VlanId vlanId, boolean popVlan, boolean install) {
-        // Create host selector
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        sbuilder.matchEthDst(hostMac);
-
-        // Create host meta
-        TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
-
-        sbuilder.matchVlanId(vlanId);
-        mbuilder.matchVlanId(vlanId);
-
-        // Create host treatment
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.immediate().setOutput(portNum);
-
-        if (popVlan) {
-            tbuilder.immediate().popVlan();
-        }
-
-        int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
-                tbuilder.build(), mbuilder.build(), install);
-        if (portNextObjId != -1) {
-            ForwardingObjective.Builder fob = DefaultForwardingObjective.builder()
-                    .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                    .withSelector(sbuilder.build())
-                    .nextStep(portNextObjId)
-                    .withPriority(100)
-                    .fromApp(srManager.appId)
-                    .makePermanent();
-
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("Brigding rule for {}/{} {}", hostMac, vlanId,
-                            install ? "populated" : "revoked"),
-                    (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
-                            install ? "populate" : "revoke", hostMac, vlanId, error));
-            srManager.flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
-        } else {
-            log.warn("Failed to retrieve next objective for {}/{}", hostMac, vlanId);
-        }
-    }
-
-    /**
-     * Populates IP rules for a route that has direct connection to the switch.
-     * This method should not be invoked directly without going through DefaultRoutingHandler.
-     *
-     * @param deviceId device ID of the device that next hop attaches to
-     * @param prefix IP prefix of the route
-     * @param hostMac MAC address of the next hop
-     * @param hostVlanId Vlan ID of the nexthop
-     * @param outPort port where the next hop attaches to
-     * @param directHost host is of type direct or indirect
-     * @return future that carries the flow objective if succeeded, null if otherwise
-     */
-    CompletableFuture<Objective> populateRoute(DeviceId deviceId, IpPrefix prefix,
-                              MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
-        log.debug("Populate direct routing entry for route {} at {}:{}",
-                prefix, deviceId, outPort);
-        ForwardingObjective.Builder fwdBuilder;
-        try {
-            fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac,
-                                              hostVlanId, outPort, null, null, directHost, false);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting direct populateRoute");
-            return CompletableFuture.completedFuture(null);
-        }
-        if (fwdBuilder == null) {
-            log.warn("Aborting host routing table entry due "
-                    + "to error for dev:{} route:{}", deviceId, prefix);
-            return CompletableFuture.completedFuture(null);
-        }
-
-        int nextId = fwdBuilder.add().nextId();
-        CompletableFuture<Objective> future = new CompletableFuture<>();
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> {
-                    log.debug("Direct routing rule for route {} populated. nextId={}", prefix, nextId);
-                    future.complete(objective);
-                },
-                (objective, error) -> {
-                    log.warn("Failed to populate direct routing rule for route {}: {}", prefix, error);
-                    future.complete(null);
-                });
-        srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
-        rulePopulationCounter.incrementAndGet();
-        return future;
-    }
-
-    /**
-     * Removes IP rules for a route when the next hop is gone.
-     * This method should not be invoked directly without going through DefaultRoutingHandler.
-     *
-     * @param deviceId device ID of the device that next hop attaches to
-     * @param prefix IP prefix of the route
-     * @param hostMac MAC address of the next hop
-     * @param hostVlanId Vlan ID of the nexthop
-     * @param outPort port that next hop attaches to
-     * @param directHost host is of type direct or indirect
-     * @return future that carries the flow objective if succeeded, null if otherwise
-     */
-    CompletableFuture<Objective> revokeRoute(DeviceId deviceId, IpPrefix prefix,
-            MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
-        log.debug("Revoke IP table entry for route {} at {}:{}",
-                prefix, deviceId, outPort);
-        ForwardingObjective.Builder fwdBuilder;
-        try {
-            fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac,
-                                              hostVlanId, outPort, null, null, directHost, true);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting revokeIpRuleForHost.");
-            return CompletableFuture.completedFuture(null);
-        }
-        if (fwdBuilder == null) {
-            log.warn("Aborting host routing table entries due "
-                    + "to error for dev:{} route:{}", deviceId, prefix);
-            return CompletableFuture.completedFuture(null);
-        }
-
-        CompletableFuture<Objective> future = new CompletableFuture<>();
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> {
-                    log.debug("IP rule for route {} revoked", prefix);
-                    future.complete(objective);
-                },
-                (objective, error) -> {
-                    log.warn("Failed to revoke IP rule for route {}: {}", prefix, error);
-                    future.complete(null);
-                });
-        srManager.flowObjectiveService.forward(deviceId, fwdBuilder.remove(context));
-        return future;
-    }
-
-    /**
-     * Returns a forwarding objective builder for routing rules.
-     * <p>
-     * The forwarding objective routes packets destined to a given prefix to
-     * given port on given device with given destination MAC.
-     *
-     * @param deviceId device ID
-     * @param prefix prefix that need to be routed
-     * @param hostMac MAC address of the nexthop
-     * @param hostVlanId Vlan ID of the nexthop
-     * @param outPort port where the nexthop attaches to
-     * @param revoke true if forwarding objective is meant to revoke forwarding rule
-     * @param directHost host is direct or indirect
-     * @return forwarding objective builder
-     * @throws DeviceConfigNotFoundException if given device is not configured
-     */
-
-    private ForwardingObjective.Builder routingFwdObjBuilder(
-            DeviceId deviceId, IpPrefix prefix,
-            MacAddress hostMac, VlanId hostVlanId, PortNumber outPort,
-            VlanId innerVlan, EthType outerTpid,
-            boolean directHost, boolean revoke)
-            throws DeviceConfigNotFoundException {
-        int nextObjId;
-        if (directHost) {
-            // if the objective is to revoke an existing rule, and for some reason
-            // the next-objective does not exist, then a new one should not be created
-            ImmutablePair<TrafficTreatment, TrafficSelector> treatmentAndMeta =
-                    getTreatmentAndMeta(deviceId, hostMac, hostVlanId, outPort, innerVlan, outerTpid);
-            if (treatmentAndMeta == null) {
-                // Warning log will come from getTreatmentAndMeta method
-                return null;
-            }
-            nextObjId = srManager.getPortNextObjectiveId(deviceId, outPort,
-                    treatmentAndMeta.getLeft(), treatmentAndMeta.getRight(), !revoke);
-        } else {
-          // if the objective is to revoke an existing rule, and for some reason
-          // the next-objective does not exist, then a new one should not be created
-          nextObjId = srManager.getMacVlanNextObjectiveId(deviceId, hostMac, hostVlanId,
-                                          outPort, !revoke);
-        }
-        if (nextObjId == -1) {
-            // Warning log will come from getMacVlanNextObjective method
-            return null;
-        }
-
-        return DefaultForwardingObjective.builder()
-                .withSelector(buildIpSelectorFromIpPrefix(prefix).build())
-                .nextStep(nextObjId)
-                .fromApp(srManager.appId).makePermanent()
-                .withPriority(getPriorityFromPrefix(prefix))
-                .withFlag(ForwardingObjective.Flag.SPECIFIC);
-    }
-
-    private ImmutablePair<TrafficTreatment, TrafficSelector> getTreatmentAndMeta(
-            DeviceId deviceId, MacAddress hostMac, VlanId hostVlanId, PortNumber outPort,
-            VlanId innerVlan, EthType outerTpid)
-            throws DeviceConfigNotFoundException {
-        MacAddress routerMac;
-        routerMac = config.getDeviceMac(deviceId);
-
-        ConnectPoint connectPoint = new ConnectPoint(deviceId, outPort);
-        VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
-        Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
-        VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
-
-        // Create route treatment
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.deferred()
-                .setEthDst(hostMac)
-                .setEthSrc(routerMac)
-                .setOutput(outPort);
-
-        // Create route meta
-        TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
-
-        // Adjust treatment and meta according to VLAN configuration
-        if (taggedVlans.contains(hostVlanId)) {
-            tbuilder.setVlanId(hostVlanId);
-        } else if (hostVlanId.equals(VlanId.NONE)) {
-            if (untaggedVlan != null) {
-                mbuilder.matchVlanId(untaggedVlan);
-            } else if (nativeVlan != null) {
-                mbuilder.matchVlanId(nativeVlan);
-            } else {
-                log.warn("Untagged nexthop {}/{} is not allowed on {} without untagged or native vlan",
-                        hostMac, hostVlanId, connectPoint);
-                return null;
-            }
-        } else {
-            // Double tagged hosts
-            if (innerVlan == null || outerTpid == null) {
-                log.warn("Failed to construct NextObj for double tagged hosts {}/{}. {} {}",
-                        hostMac, hostVlanId,
-                        (innerVlan == null) ? "innerVlan = null." : "",
-                        (outerTpid == null) ? "outerTpid = null." : "");
-                return null;
-            }
-            tbuilder.setVlanId(innerVlan);
-            tbuilder.pushVlan(outerTpid);
-            tbuilder.setVlanId(hostVlanId);
-            mbuilder.matchVlanId(VlanId.ANY);
-        }
-
-        return ImmutablePair.of(tbuilder.build(), mbuilder.build());
-    }
-
-    /**
-     * Populates IP flow rules for all the given prefixes reachable from the
-     * destination switch(es).
-     *
-     * @param targetSw switch where rules are to be programmed
-     * @param subnets subnets/prefixes being added
-     * @param destSw1 destination switch where the prefixes are reachable
-     * @param destSw2 paired destination switch if one exists for the subnets/prefixes.
-     *                Should be null if there is no paired destination switch (by config)
-     *                or if the given prefixes are reachable only via destSw1
-     * @param nextHops a map containing a set of next-hops for each destination switch.
-     *                 If destSw2 is not null, then this map must contain an
-     *                 entry for destSw2 with its next-hops from the targetSw
-     *                 (although the next-hop set may be empty in certain scenarios).
-     *                 If destSw2 is null, there should not be an entry in this
-     *                 map for destSw2.
-     * @return true if all rules are set successfully, false otherwise
-     */
-    boolean populateIpRuleForSubnet(DeviceId targetSw, Set<IpPrefix> subnets,
-            DeviceId destSw1, DeviceId destSw2, Map<DeviceId, Set<DeviceId>> nextHops) {
-        // Get pair device of the target switch
-        Optional<DeviceId> pairDev = srManager.getPairDeviceId(targetSw);
-        // Route simplification will be off in case of the nexthop location at target switch is down
-        // (routing through spine case)
-        boolean routeSimplOff = pairDev.isPresent() && pairDev.get().equals(destSw1) && destSw2 == null;
-        // Iterates over the routes. Checking:
-        // If route simplification is enabled
-        // If the target device is another leaf in the network
-        if (srManager.routeSimplification && !routeSimplOff) {
-            Set<IpPrefix> subnetsToBePopulated = Sets.newHashSet();
-            for (IpPrefix subnet : subnets) {
-                // Skip route programming on the target device
-                // If route simplification applies
-                if (routeSimplifierUtils.hasLeafExclusionEnabledForPrefix(subnet)) {
-                    // XXX route simplification assumes that source of the traffic
-                    // towards the nexthops are co-located with the nexthops. In different
-                    // scenarios will not work properly.
-                    continue;
-                }
-                // populate the route in the remaning scenarios
-                subnetsToBePopulated.add(subnet);
-            }
-            subnets = subnetsToBePopulated;
-        }
-        // populate the remaining routes in the target switch
-        return populateIpRulesForRouter(targetSw, subnets, destSw1, destSw2, nextHops);
-    }
-
-    /**
-     * Revokes IP flow rules for the subnets from given device.
-     *
-     * @param targetSw target switch from which the subnets need to be removed
-     * @param subnets subnet being removed
-     * @return true if all rules are removed successfully, false otherwise
-     */
-    boolean revokeIpRuleForSubnet(DeviceId targetSw, Set<IpPrefix> subnets) {
-        for (IpPrefix subnet : subnets) {
-            if (!revokeIpRuleForRouter(targetSw, subnet)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Populates IP flow rules for a set of IP prefix in the target device.
-     * The prefix are reachable via destination device(s).
-     *
-     * @param targetSw target device ID to set the rules
-     * @param subnets the set of IP prefix
-     * @param destSw1 destination switch where the prefixes are reachable
-     * @param destSw2 paired destination switch if one exists for the subnets/prefixes.
-     *                Should be null if there is no paired destination switch (by config)
-     *                or if the given prefixes are reachable only via destSw1
-     * @param nextHops map of destination switches and their next-hops.
-     *                  Should only contain destination switches that are
-     *                  actually meant to be routed to. If destSw2 is null, there
-     *                  should not be an entry for destSw2 in this map.
-     * @return true if all rules are set successfully, false otherwise
-     */
-    private boolean populateIpRulesForRouter(DeviceId targetSw,
-                                             Set<IpPrefix> subnets,
-                                             DeviceId destSw1, DeviceId destSw2,
-                                             Map<DeviceId, Set<DeviceId>> nextHops) {
-        // pre-compute the needed information
-        int segmentIdIPv41, segmentIdIPv42 = -1;
-        int segmentIdIPv61, segmentIdIPv62 = -1;
-        TrafficTreatment treatment = null;
-        DestinationSet dsIPv4, dsIPv6;
-        TrafficSelector metaIpv4Selector, metaIpv6Selector = null;
-        int nextIdIPv4, nextIdIPv6, nextId;
-        TrafficSelector selector;
-        // start with MPLS SIDs
-        try {
-            segmentIdIPv41 = config.getIPv4SegmentId(destSw1);
-            segmentIdIPv61 = config.getIPv6SegmentId(destSw1);
-            if (destSw2 != null) {
-                segmentIdIPv42 = config.getIPv4SegmentId(destSw2);
-                segmentIdIPv62 = config.getIPv6SegmentId(destSw2);
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting populateIpRuleForRouter.");
-            return false;
-        }
-        // build the IPv4 and IPv6 destination set
-        if (destSw2 == null) {
-            // single dst - create destination set based on next-hop
-            // If the next hop is the same as the final destination, then MPLS
-            // label is not set.
-            Set<DeviceId> nhd1 = nextHops.get(destSw1);
-            if (nhd1.size() == 1 && nhd1.iterator().next().equals(destSw1)) {
-                dsIPv4 = DestinationSet.createTypePushNone(destSw1);
-                dsIPv6 = DestinationSet.createTypePushNone(destSw1);
-                treatment = DefaultTrafficTreatment.builder()
-                        .immediate()
-                        .decNwTtl()
-                        .build();
-            } else {
-                dsIPv4 = DestinationSet.createTypePushBos(segmentIdIPv41, destSw1);
-                dsIPv6 = DestinationSet.createTypePushBos(segmentIdIPv61, destSw1);
-            }
-        } else {
-            // dst pair - IP rules for dst-pairs are always from other edge nodes
-            // the destination set needs to have both destinations, even if there
-            // are no next hops to one of them
-            dsIPv4 = DestinationSet.createTypePushBos(segmentIdIPv41, destSw1, segmentIdIPv42, destSw2);
-            dsIPv6 = DestinationSet.createTypePushBos(segmentIdIPv61, destSw1, segmentIdIPv62, destSw2);
-        }
-
-        // setup metadata to pass to nextObjective - indicate the vlan on egress
-        // if needed by the switch pipeline. Since neighbor sets are always to
-        // other neighboring routers, there is no subnet assigned on those ports.
-        metaIpv4Selector = buildIpv4Selector()
-                .matchVlanId(srManager.getDefaultInternalVlan())
-                .build();
-        metaIpv6Selector = buildIpv6Selector()
-                .matchVlanId(srManager.getDefaultInternalVlan())
-                .build();
-        // get the group handler of the target switch
-        DefaultGroupHandler grpHandler = srManager.getGroupHandler(targetSw);
-        if (grpHandler == null) {
-            log.warn("populateIPRuleForRouter: groupHandler for device {} "
-                             + "not found", targetSw);
-            return false;
-        }
-        // get next id
-        nextIdIPv4 = grpHandler.getNextObjectiveId(dsIPv4, nextHops, metaIpv4Selector, false);
-        if (nextIdIPv4 <= 0) {
-            log.warn("No next objective in {} for ds: {}", targetSw, dsIPv4);
-            return false;
-        }
-        nextIdIPv6 = grpHandler.getNextObjectiveId(dsIPv6, nextHops, metaIpv6Selector, false);
-        if (nextIdIPv6 <= 0) {
-            log.warn("No next objective in {} for ds: {}", targetSw, dsIPv6);
-            return false;
-        }
-        // build all the flow rules and send to the device
-        for (IpPrefix subnet : subnets) {
-            selector = buildIpSelectorFromIpPrefix(subnet).build();
-            if (subnet.isIp4()) {
-                nextId = nextIdIPv4;
-            } else {
-                nextId = nextIdIPv6;
-            }
-            ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                    .builder()
-                    .fromApp(srManager.appId)
-                    .makePermanent()
-                    .nextStep(nextId)
-                    .withSelector(selector)
-                    .withPriority(getPriorityFromPrefix(subnet))
-                    .withFlag(ForwardingObjective.Flag.SPECIFIC);
-            if (treatment != null) {
-                fwdBuilder.withTreatment(treatment);
-            }
-            log.debug("Installing {} forwarding objective for router IP/subnet {} "
-                              + "in switch {} with nextId: {}", subnet.isIp4() ? "IPv4" : "IPv6",
-                      subnet, targetSw, nextId);
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("IP rule for router {} populated in dev:{}",
-                                             subnet, targetSw),
-                    (objective, error) -> log.warn("Failed to populate IP rule for router {}: {} in dev:{}",
-                                                   subnet, error, targetSw));
-            srManager.flowObjectiveService.forward(targetSw, fwdBuilder.add(context));
-        }
-        rulePopulationCounter.addAndGet(subnets.size());
-        return true;
-    }
-
-    /**
-     * Revokes IP flow rules for the router IP address from given device.
-     *
-     * @param targetSw target switch from which the ipPrefix need to be removed
-     * @param ipPrefix the IP address of the destination router
-     * @return true if all rules are removed successfully, false otherwise
-     */
-    private boolean revokeIpRuleForRouter(DeviceId targetSw, IpPrefix ipPrefix) {
-        TrafficSelector.Builder sbuilder = buildIpSelectorFromIpPrefix(ipPrefix);
-        TrafficSelector selector = sbuilder.build();
-        TrafficTreatment dummyTreatment = DefaultTrafficTreatment.builder().build();
-
-        ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                .builder()
-                .fromApp(srManager.appId)
-                .makePermanent()
-                .withSelector(selector)
-                .withTreatment(dummyTreatment)
-                .withPriority(getPriorityFromPrefix(ipPrefix))
-                .withFlag(ForwardingObjective.Flag.SPECIFIC);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("IP rule for router {} revoked from {}", ipPrefix, targetSw),
-                (objective, error) -> log.warn("Failed to revoke IP rule for router {} from {}: {}",
-                        ipPrefix, targetSw, error));
-        srManager.flowObjectiveService.forward(targetSw, fwdBuilder.remove(context));
-
-        return true;
-    }
-
-    /**
-     * Populates MPLS flow rules in the target device to point towards the
-     * destination device.
-     *
-     * @param targetSwId target device ID of the switch to set the rules
-     * @param destSwId destination switch device ID
-     * @param nextHops next hops switch ID list
-     * @param routerIp the router ip of the destination switch
-     * @return true if all rules are set successfully, false otherwise
-     */
-    boolean populateMplsRule(DeviceId targetSwId, DeviceId destSwId,
-                             Set<DeviceId> nextHops, IpAddress routerIp) {
-        int segmentId;
-        try {
-            if (routerIp.isIp4()) {
-                segmentId = config.getIPv4SegmentId(destSwId);
-            } else {
-                segmentId = config.getIPv6SegmentId(destSwId);
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting populateMplsRule.");
-            return false;
-        }
-
-        List<ForwardingObjective> fwdObjs = new ArrayList<>();
-        Collection<ForwardingObjective> fwdObjsMpls;
-        // Generates the transit rules used by the standard "routing".
-        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, segmentId,
-                                 routerIp, true);
-        if (fwdObjsMpls.isEmpty()) {
-            return false;
-        }
-        fwdObjs.addAll(fwdObjsMpls);
-
-        // Generates the transit rules used by the MPLS Pwaas.
-        int pwSrLabel;
-        try {
-            pwSrLabel = config.getPWRoutingLabel(destSwId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage()
-                    + " Aborting populateMplsRule. No label for PseudoWire traffic.");
-            return false;
-        }
-        fwdObjsMpls = handleMpls(targetSwId, destSwId, nextHops, pwSrLabel,
-                                 routerIp, false);
-        if (fwdObjsMpls.isEmpty()) {
-            return false;
-        }
-        fwdObjs.addAll(fwdObjsMpls);
-
-        for (ForwardingObjective fwdObj : fwdObjs) {
-            log.debug("Sending MPLS fwd obj {} for SID {}-> next {} in sw: {}",
-                      fwdObj.id(), segmentId, fwdObj.nextId(), targetSwId);
-            srManager.flowObjectiveService.forward(targetSwId, fwdObj);
-            rulePopulationCounter.incrementAndGet();
-        }
-
-        return true;
-    }
-
-    /**
-     * Differentiates between popping and swapping labels when building an MPLS
-     * forwarding objective.
-     *
-     * @param targetSwId the target sw
-     * @param destSwId the destination sw
-     * @param nextHops the set of next hops
-     * @param segmentId the segmentId to match representing the destination
-     *            switch
-     * @param routerIp the router ip representing the destination switch
-     * @return a collection of fwdobjective
-     */
-    private Collection<ForwardingObjective> handleMpls(
-                                        DeviceId targetSwId,
-                                        DeviceId destSwId,
-                                        Set<DeviceId> nextHops,
-                                        int segmentId,
-                                        IpAddress routerIp,
-                                        boolean isMplsBos) {
-
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        List<ForwardingObjective.Builder> fwdObjBuilders = Lists.newArrayList();
-        // For the transport of Pwaas we can use two or three MPLS label
-        sbuilder.matchEthType(Ethernet.MPLS_UNICAST);
-        sbuilder.matchMplsLabel(MplsLabel.mplsLabel(segmentId));
-        sbuilder.matchMplsBos(isMplsBos);
-        TrafficSelector selector = sbuilder.build();
-
-        // setup metadata to pass to nextObjective - indicate the vlan on egress
-        // if needed by the switch pipeline. Since mpls next-hops are always to
-        // other neighboring routers, there is no subnet assigned on those ports.
-        TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
-        metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
-
-        if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
-            // If the next hop is the destination router for the segment, do pop
-            log.debug("populateMplsRule: Installing MPLS forwarding objective for "
-                    + "label {} in switch {} with pop to next-hops {}",
-                    segmentId, targetSwId, nextHops);
-            ForwardingObjective.Builder fwdObjNoBosBuilder =
-                    getMplsForwardingObjective(targetSwId,
-                                               nextHops,
-                                               true,
-                                               isMplsBos,
-                                               metabuilder.build(),
-                                               routerIp,
-                                               segmentId,
-                                               destSwId);
-            // Error case, we cannot handle, exit.
-            if (fwdObjNoBosBuilder == null) {
-                return Collections.emptyList();
-            }
-            fwdObjBuilders.add(fwdObjNoBosBuilder);
-
-        } else {
-            // next hop is not destination, irrespective of the number of next
-            // hops (1 or more) -- SR CONTINUE case (swap with self)
-            log.debug("Installing MPLS forwarding objective for "
-                    + "label {} in switch {} without pop to next-hops {}",
-                    segmentId, targetSwId, nextHops);
-            ForwardingObjective.Builder fwdObjNoBosBuilder =
-                    getMplsForwardingObjective(targetSwId,
-                                               nextHops,
-                                               false,
-                                               isMplsBos,
-                                               metabuilder.build(),
-                                               routerIp,
-                                               segmentId,
-                                               destSwId);
-            // Error case, we cannot handle, exit.
-            if (fwdObjNoBosBuilder == null) {
-                return Collections.emptyList();
-            }
-            fwdObjBuilders.add(fwdObjNoBosBuilder);
-
-        }
-
-        List<ForwardingObjective> fwdObjs = Lists.newArrayList();
-        // We add the final property to the fwdObjs.
-        for (ForwardingObjective.Builder fwdObjBuilder : fwdObjBuilders) {
-            ((Builder) ((Builder) fwdObjBuilder
-                    .fromApp(srManager.appId)
-                    .makePermanent())
-                    .withSelector(selector)
-                    .withPriority(SegmentRoutingService.DEFAULT_PRIORITY))
-                    .withFlag(ForwardingObjective.Flag.SPECIFIC);
-
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) ->
-                            log.debug("MPLS rule {} for SID {} populated in dev:{} ",
-                                      objective.id(), segmentId, targetSwId),
-                    (objective, error) ->
-                            log.warn("Failed to populate MPLS rule {} for SID {}: {} in dev:{}",
-                                     objective.id(), segmentId, error, targetSwId));
-
-            ForwardingObjective fob = fwdObjBuilder.add(context);
-            fwdObjs.add(fob);
-        }
-
-        return fwdObjs;
-    }
-
-    /**
-     * Returns a Forwarding Objective builder for the MPLS rule that references
-     * the desired Next Objective. Creates a DestinationSet that allows the
-     * groupHandler to create or find the required next objective.
-     *
-     * @param targetSw the target sw
-     * @param nextHops the set of next hops
-     * @param phpRequired true if penultimate-hop-popping is required
-     * @param isBos true if matched label is bottom-of-stack
-     * @param meta metadata for creating next objective
-     * @param routerIp the router ip representing the destination switch
-     * @param destSw the destination sw
-     * @return the mpls forwarding objective builder
-     */
-    private ForwardingObjective.Builder getMplsForwardingObjective(
-                                             DeviceId targetSw,
-                                             Set<DeviceId> nextHops,
-                                             boolean phpRequired,
-                                             boolean isBos,
-                                             TrafficSelector meta,
-                                             IpAddress routerIp,
-                                             int segmentId,
-                                             DeviceId destSw) {
-
-        ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective
-                .builder().withFlag(ForwardingObjective.Flag.SPECIFIC);
-
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        DestinationSet ds = null;
-        DestinationSet.DestinationSetType dstType = null;
-        boolean simple = false;
-        if (phpRequired) {
-            // php case - pop should always be flow-action
-            log.debug("getMplsForwardingObjective: php required");
-            tbuilder.deferred().copyTtlIn();
-            if (isBos) {
-                if (routerIp.isIp4()) {
-                    tbuilder.deferred().popMpls(EthType.EtherType.IPV4.ethType());
-                } else {
-                    tbuilder.deferred().popMpls(EthType.EtherType.IPV6.ethType());
-                }
-                tbuilder.decNwTtl();
-                // standard case -> BoS == True; pop results in IP packet and forwarding
-                // is via an ECMP group
-                ds = DestinationSet.createTypePopBos(destSw);
-            } else {
-                tbuilder.deferred().popMpls(EthType.EtherType.MPLS_UNICAST.ethType())
-                    .decMplsTtl();
-                // double-label case -> BoS == False, pop results in MPLS packet
-                // depending on configuration we can ECMP this packet or choose one output
-                ds = DestinationSet.createTypePopNotBos(destSw);
-                if (!srManager.getMplsEcmp()) {
-                   simple = true;
-                }
-            }
-        } else {
-            // swap with self case - SR CONTINUE
-            log.debug("getMplsForwardingObjective: swap with self");
-            tbuilder.deferred().decMplsTtl();
-            // swap results in MPLS packet with same BoS bit regardless of bit value
-            // depending on configuration we can ECMP this packet or choose one output
-            // differentiate here between swap with not bos or swap with bos
-            ds = isBos ? DestinationSet.createTypeSwapBos(segmentId, destSw) :
-                    DestinationSet.createTypeSwapNotBos(segmentId, destSw);
-            if (!srManager.getMplsEcmp()) {
-                simple = true;
-            }
-        }
-
-        fwdBuilder.withTreatment(tbuilder.build());
-        log.debug("Trying to get a nextObjId for mpls rule on device:{} to ds:{}",
-                  targetSw, ds);
-        DefaultGroupHandler gh = srManager.getGroupHandler(targetSw);
-        if (gh == null) {
-            log.warn("getNextObjectiveId query - groupHandler for device {} "
-                    + "not found", targetSw);
-            return null;
-        }
-
-        Map<DeviceId, Set<DeviceId>> dstNextHops = new HashMap<>();
-        dstNextHops.put(destSw, nextHops);
-        int nextId = gh.getNextObjectiveId(ds, dstNextHops, meta, simple);
-        if (nextId <= 0) {
-            log.warn("No next objective in {} for ds: {}", targetSw, ds);
-            return null;
-        } else {
-            log.debug("nextObjId found:{} for mpls rule on device:{} to ds:{}",
-                      nextId, targetSw, ds);
-        }
-
-        fwdBuilder.nextStep(nextId);
-        return fwdBuilder;
-    }
-
-    /**
-     * Creates a filtering objective to permit all untagged packets with a
-     * dstMac corresponding to the router's MAC address. For those pipelines
-     * that need to internally assign vlans to untagged packets, this method
-     * provides per-subnet vlan-ids as metadata.
-     * <p>
-     * Note that the vlan assignment and filter programming should only be done by
-     * the master for a switch. This method is typically called at deviceAdd and
-     * programs filters only for the enabled ports of the device. For port-updates,
-     * that enable/disable ports after device add, singlePortFilter methods should
-     * be called.
-     *
-     * @param deviceId  the switch dpid for the router
-     * @return PortFilterInfo information about the processed ports
-     */
-    PortFilterInfo populateVlanMacFilters(DeviceId deviceId) {
-        log.debug("Installing per-port filtering objective for untagged "
-                + "packets in device {}", deviceId);
-
-        List<Port> devPorts = srManager.deviceService.getPorts(deviceId);
-        if (devPorts == null || devPorts.isEmpty()) {
-            log.warn("Device {} ports not available. Unable to add MacVlan filters",
-                     deviceId);
-            return null;
-        }
-        int disabledPorts = 0, errorPorts = 0, filteredPorts = 0;
-        for (Port port : devPorts) {
-            if (!port.isEnabled()) {
-                disabledPorts++;
-                continue;
-            }
-            if (processSinglePortFilters(deviceId, port.number(), true)) {
-                filteredPorts++;
-            } else {
-                errorPorts++;
-            }
-        }
-        log.debug("Filtering on dev:{}, disabledPorts:{}, errorPorts:{}, filteredPorts:{}",
-                  deviceId, disabledPorts, errorPorts, filteredPorts);
-        return new PortFilterInfo(disabledPorts, errorPorts, filteredPorts);
-    }
-
-    /**
-     * Creates or removes filtering objectives for a single port. Should only be
-     * called by the master for a switch.
-     *
-     * @param deviceId device identifier
-     * @param portnum  port identifier for port to be filtered
-     * @param install true to install the filtering objective, false to remove
-     * @return true if no errors occurred during the build of the filtering objective
-     */
-    boolean processSinglePortFilters(DeviceId deviceId, PortNumber portnum, boolean install) {
-        ConnectPoint connectPoint = new ConnectPoint(deviceId, portnum);
-        VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
-        Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
-        VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
-
-        // Do not configure filter for edge ports where double-tagged hosts are connected.
-        if (taggedVlans.size() != 0) {
-            // Filter for tagged vlans
-            if (!srManager.interfaceService.getTaggedVlanId(connectPoint).stream().allMatch(taggedVlanId ->
-                    processSinglePortFiltersInternal(deviceId, portnum, false, taggedVlanId, install))) {
-                return false;
-            }
-            if (nativeVlan != null) {
-                // Filter for native vlan
-                if (!processSinglePortFiltersInternal(deviceId, portnum, true, nativeVlan, install)) {
-                    return false;
-                }
-            }
-        } else if (untaggedVlan != null) {
-            // Filter for untagged vlan
-            if (!processSinglePortFiltersInternal(deviceId, portnum, true, untaggedVlan, install)) {
-                return false;
-            }
-        } else if (!hasIPConfiguration(connectPoint)) {
-            // Filter for unconfigured upstream port, using INTERNAL_VLAN
-            if (!processSinglePortFiltersInternal(deviceId, portnum, true,
-                                                  srManager.getDefaultInternalVlan(),
-                                                  install)) {
-                return false;
-            }
-            // Filter for receiveing pseudowire traffic
-            if (!processSinglePortFiltersInternal(deviceId, portnum, false,
-                                                  srManager.getPwTransportVlan(),
-                                                  install)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Updates filtering objectives for a single port. Should only be called by
-     * the master for a switch
-     * @param deviceId device identifier
-     * @param portNum  port identifier for port to be filtered
-     * @param pushVlan true to push vlan, false otherwise
-     * @param vlanId vlan identifier
-     * @param install true to install the filtering objective, false to remove
-     */
-    void updateSinglePortFilters(DeviceId deviceId, PortNumber portNum,
-                                 boolean pushVlan, VlanId vlanId, boolean install) {
-        if (!processSinglePortFiltersInternal(deviceId, portNum, pushVlan, vlanId, install)) {
-            log.warn("Failed to update FilteringObjective for {}/{} with vlan {}",
-                     deviceId, portNum, vlanId);
-        }
-    }
-
-    private boolean processSinglePortFiltersInternal(DeviceId deviceId, PortNumber portnum,
-                                                      boolean pushVlan, VlanId vlanId, boolean install) {
-        boolean doTMAC = true;
-
-        if (!pushVlan) {
-            // Skip the tagged vlans belonging to an interface without an IP address
-            Set<Interface> ifaces = srManager.interfaceService
-                    .getInterfacesByPort(new ConnectPoint(deviceId, portnum))
-                    .stream()
-                    .filter(intf -> intf.vlanTagged().contains(vlanId) && intf.ipAddressesList().isEmpty())
-                    .collect(Collectors.toSet());
-            if (!ifaces.isEmpty()) {
-                log.debug("processSinglePortFiltersInternal: skipping TMAC for vlan {} at {}/{} - no IP",
-                          vlanId, deviceId, portnum);
-                doTMAC = false;
-            }
-        }
-
-        FilteringObjective.Builder fob = buildFilteringObjective(deviceId, portnum, pushVlan, vlanId, doTMAC);
-        if (fob == null) {
-            // error encountered during build
-            return false;
-        }
-        log.debug("{} filtering objectives for dev/port: {}/{}",
-                 install ? "Installing" : "Removing", deviceId, portnum);
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Filter for {}/{} {}", deviceId, portnum,
-                        install ? "installed" : "removed"),
-                (objective, error) -> log.warn("Failed to {} filter for {}/{}: {}",
-                        install ? "install" : "remove", deviceId, portnum, error));
-        if (install) {
-            srManager.flowObjectiveService.filter(deviceId, fob.add(context));
-        } else {
-            srManager.flowObjectiveService.filter(deviceId, fob.remove(context));
-        }
-        return true;
-    }
-
-    private FilteringObjective.Builder buildFilteringObjective(DeviceId deviceId, PortNumber portnum,
-                                                               boolean pushVlan, VlanId vlanId, boolean doTMAC) {
-        MacAddress deviceMac;
-        try {
-            deviceMac = config.getDeviceMac(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Processing SinglePortFilters aborted");
-            return null;
-        }
-        FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
-
-        if (doTMAC) {
-            fob.withKey(Criteria.matchInPort(portnum))
-                    .addCondition(Criteria.matchEthDst(deviceMac))
-                    .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
-        } else {
-            fob.withKey(Criteria.matchInPort(portnum))
-                    .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
-        }
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        if (pushVlan) {
-            fob.addCondition(Criteria.matchVlanId(VlanId.NONE));
-            tBuilder.pushVlan().setVlanId(vlanId);
-        } else {
-            fob.addCondition(Criteria.matchVlanId(vlanId));
-        }
-
-        // NOTE: Some switch hardware share the same filtering flow among different ports.
-        //       We use this metadata to let the driver know that there is no more enabled port
-        //       within the same VLAN on this device.
-        if (noMoreEnabledPort(deviceId, vlanId)) {
-            tBuilder.wipeDeferred();
-        }
-
-        fob.withMeta(tBuilder.build());
-
-        fob.permit().fromApp(srManager.appId);
-        return fob;
-    }
-
-    /**
-     * Creates or removes filtering objectives for a double-tagged host on a port.
-     *
-     * @param deviceId device identifier
-     * @param portNum  port identifier for port to be filtered
-     * @param outerVlan outer VLAN ID
-     * @param innerVlan inner VLAN ID
-     * @param install true to install the filtering objective, false to remove
-     */
-    void processDoubleTaggedFilter(DeviceId deviceId, PortNumber portNum, VlanId outerVlan,
-                                   VlanId innerVlan, boolean install) {
-        // We should trigger the removal of double tagged rules only when removing
-        // the filtering objective and no other hosts are connected to the same device port.
-        boolean cleanupDoubleTaggedRules = !anyDoubleTaggedHost(deviceId, portNum) && !install;
-        FilteringObjective.Builder fob = buildDoubleTaggedFilteringObj(deviceId, portNum,
-                                                                       outerVlan, innerVlan,
-                                                                       cleanupDoubleTaggedRules);
-        if (fob == null) {
-            // error encountered during build
-            return;
-        }
-        log.debug("{} double-tagged filtering objectives for dev/port: {}/{}",
-                  install ? "Installing" : "Removing", deviceId, portNum);
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Filter for {}/{} {}", deviceId, portNum,
-                                         install ? "installed" : "removed"),
-                (objective, error) -> log.warn("Failed to {} filter for {}/{}: {}",
-                                               install ? "install" : "remove", deviceId, portNum, error));
-        if (install) {
-            srManager.flowObjectiveService.filter(deviceId, fob.add(context));
-        } else {
-            srManager.flowObjectiveService.filter(deviceId, fob.remove(context));
-        }
-    }
-
-    /**
-     * Checks if there is any double tagged host attached to given location.
-     * This method will match on the effective location of a host.
-     * That is, it will match on auxLocations when auxLocations is not null. Otherwise, it will match on locations.
-     *
-     * @param deviceId device ID
-     * @param portNum port number
-     * @return true if there is any host attached to given location.
-     */
-    private boolean anyDoubleTaggedHost(DeviceId deviceId, PortNumber portNum) {
-        ConnectPoint cp = new ConnectPoint(deviceId, portNum);
-        Set<Host> connectedHosts = srManager.hostService.getConnectedHosts(cp, false);
-        Set<Host> auxConnectedHosts = srManager.hostService.getConnectedHosts(cp, true);
-        return !auxConnectedHosts.isEmpty() ||
-                connectedHosts.stream().anyMatch(host -> host.auxLocations() == null);
-    }
-
-    private FilteringObjective.Builder buildDoubleTaggedFilteringObj(DeviceId deviceId, PortNumber portNum,
-                                                                     VlanId outerVlan, VlanId innerVlan,
-                                                                     boolean cleanupDoubleTaggedRules) {
-        MacAddress deviceMac;
-        try {
-            deviceMac = config.getDeviceMac(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Processing DoubleTaggedFilters aborted");
-            return null;
-        }
-        FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
-        // Outer vlan id match should be appeared before inner vlan id match.
-        fob.withKey(Criteria.matchInPort(portNum))
-                .addCondition(Criteria.matchEthDst(deviceMac))
-                .addCondition(Criteria.matchVlanId(outerVlan))
-                .addCondition(Criteria.matchInnerVlanId(innerVlan))
-                .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        // Pop outer vlan
-        tBuilder.popVlan();
-
-        // special metadata for driver
-        if (cleanupDoubleTaggedRules) {
-            tBuilder.writeMetadata(CLEANUP_DOUBLE_TAGGED_HOST_ENTRIES, DOUBLE_TAGGED_METADATA_MASK);
-        } else {
-            tBuilder.writeMetadata(0, DOUBLE_TAGGED_METADATA_MASK);
-        }
-
-        // NOTE: Some switch hardware share the same filtering flow among different ports.
-        //       We use this metadata to let the driver know that there is no more enabled port
-        //       within the same VLAN on this device.
-        if (noMoreEnabledPort(deviceId, outerVlan)) {
-            tBuilder.wipeDeferred();
-        }
-
-        fob.withMeta(tBuilder.build());
-
-        fob.permit().fromApp(srManager.appId);
-        return fob;
-    }
-
-    /**
-     * Creates a forwarding objective to punt all IP packets, destined to the
-     * router's port IP addresses, to the controller. Note that the input
-     * port should not be matched on, as these packets can come from any input.
-     * Furthermore, these are applied only by the master instance.
-     *
-     * @param deviceId the switch dpid for the router
-     */
-    void populateIpPunts(DeviceId deviceId) {
-        Ip4Address routerIpv4, pairRouterIpv4 = null;
-        Ip6Address routerIpv6, routerLinkLocalIpv6, pairRouterIpv6 = null;
-        try {
-            routerIpv4 = config.getRouterIpv4(deviceId);
-            routerIpv6 = config.getRouterIpv6(deviceId);
-            routerLinkLocalIpv6 = Ip6Address.valueOf(
-                    IPv6.getLinkLocalAddress(config.getDeviceMac(deviceId).toBytes()));
-
-            if (config.isPairedEdge(deviceId)) {
-                pairRouterIpv4 = config.getRouterIpv4(config.getPairDeviceId(deviceId));
-                pairRouterIpv6 = config.getRouterIpv6(config.getPairDeviceId(deviceId));
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting populateIpPunts.");
-            return;
-        }
-
-        if (!srManager.mastershipService.isLocalMaster(deviceId)) {
-            log.debug("Not installing port-IP punts - not the master for dev:{} ",
-                      deviceId);
-            return;
-        }
-        Set<IpAddress> allIps = new HashSet<>(config.getPortIPs(deviceId));
-        allIps.add(routerIpv4);
-        if (routerIpv6 != null) {
-            allIps.add(routerIpv6);
-            allIps.add(routerLinkLocalIpv6);
-        }
-        if (pairRouterIpv4 != null) {
-            allIps.add(pairRouterIpv4);
-        }
-        if (pairRouterIpv6 != null) {
-            allIps.add(pairRouterIpv6);
-        }
-        for (IpAddress ipaddr : allIps) {
-            populateSingleIpPunts(deviceId, ipaddr);
-        }
-    }
-
-    /**
-     * Creates a forwarding objective to punt all IP packets, destined to the
-     * specified IP address, which should be router's port IP address.
-     *
-     * @param deviceId the switch dpid for the router
-     * @param ipAddress the IP address of the router's port
-     */
-    void populateSingleIpPunts(DeviceId deviceId, IpAddress ipAddress) {
-        TrafficSelector.Builder sbuilder = buildIpSelectorFromIpAddress(ipAddress);
-        Optional<DeviceId> optDeviceId = Optional.of(deviceId);
-
-        srManager.packetService.requestPackets(sbuilder.build(),
-                                               PacketPriority.CONTROL, srManager.appId, optDeviceId);
-    }
-
-    /**
-     * Removes a forwarding objective to punt all IP packets, destined to the
-     * specified IP address, which should be router's port IP address.
-     *
-     * @param deviceId the switch dpid for the router
-     * @param ipAddress the IP address of the router's port
-     */
-    void revokeSingleIpPunts(DeviceId deviceId, IpAddress ipAddress) {
-        TrafficSelector.Builder sbuilder = buildIpSelectorFromIpAddress(ipAddress);
-        Optional<DeviceId> optDeviceId = Optional.of(deviceId);
-
-        try {
-            if (!ipAddress.equals(config.getRouterIpv4(deviceId)) &&
-                    !ipAddress.equals(config.getRouterIpv6(deviceId))) {
-                srManager.packetService.cancelPackets(sbuilder.build(),
-                                                      PacketPriority.CONTROL, srManager.appId, optDeviceId);
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting revokeSingleIpPunts");
-        }
-    }
-
-    // Method for building an IPv4 selector
-    private TrafficSelector.Builder buildIpv4Selector() {
-        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
-        return selectorBuilder;
-    }
-
-    // Method for building an IPv6 selector
-    private TrafficSelector.Builder buildIpv6Selector() {
-        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
-        return selectorBuilder;
-    }
-
-    // Method for building an IPv4 or IPv6 selector from an IP address
-    private TrafficSelector.Builder buildIpSelectorFromIpAddress(IpAddress addressToMatch) {
-        return buildIpSelectorFromIpPrefix(addressToMatch.toIpPrefix());
-    }
-
-    // Method for building an IPv4 or IPv6 selector from an IP prefix
-    private TrafficSelector.Builder buildIpSelectorFromIpPrefix(IpPrefix prefixToMatch) {
-        TrafficSelector.Builder selectorBuilder = DefaultTrafficSelector.builder();
-        // If the prefix is IPv4
-        if (prefixToMatch.isIp4()) {
-            selectorBuilder.matchEthType(Ethernet.TYPE_IPV4);
-            selectorBuilder.matchIPDst(prefixToMatch.getIp4Prefix());
-            return selectorBuilder;
-        }
-        // If the prefix is IPv6
-        selectorBuilder.matchEthType(Ethernet.TYPE_IPV6);
-        selectorBuilder.matchIPv6Dst(prefixToMatch.getIp6Prefix());
-        return selectorBuilder;
-    }
-
-    /**
-     * Creates forwarding objectives to punt ARP and NDP packets, to the controller.
-     * Furthermore, these are applied only by the master instance. Deferred actions
-     * are not cleared such that packets can be flooded in the cross connect use case
-     *
-     * @param deviceId the switch dpid for the router
-     */
-    void populateArpNdpPunts(DeviceId deviceId) {
-        // We are not the master just skip.
-        if (!srManager.mastershipService.isLocalMaster(deviceId)) {
-            log.debug("Not installing ARP/NDP punts - not the master for dev:{} ",
-                      deviceId);
-            return;
-        }
-
-        ForwardingObjective fwdObj;
-        // We punt all ARP packets towards the controller.
-        fwdObj = arpFwdObjective(null, true, ARP_NDP_PRIORITY)
-                .add(new ObjectiveContext() {
-                    @Override
-                    public void onError(Objective objective, ObjectiveError error) {
-                        log.warn("Failed to install forwarding objective to punt ARP to {}: {}",
-                                 deviceId, error);
-                    }
-                });
-        srManager.flowObjectiveService.forward(deviceId, fwdObj);
-
-        if (isIpv6Configured(deviceId)) {
-            // We punt all NDP packets towards the controller.
-            ndpFwdObjective(null, true, ARP_NDP_PRIORITY).forEach(builder -> {
-                 ForwardingObjective obj = builder.add(new ObjectiveContext() {
-                    @Override
-                    public void onError(Objective objective, ObjectiveError error) {
-                        log.warn("Failed to install forwarding objective to punt NDP to {}: {}",
-                                deviceId, error);
-                    }
-                });
-                srManager.flowObjectiveService.forward(deviceId, obj);
-            });
-        }
-
-        srManager.getPairLocalPort(deviceId).ifPresent(port -> {
-            ForwardingObjective pairFwdObj;
-            // Do not punt ARP packets from pair port
-            pairFwdObj = arpFwdObjective(port, false, PacketPriority.CONTROL.priorityValue() + 1)
-                    .add(new ObjectiveContext() {
-                        @Override
-                        public void onError(Objective objective, ObjectiveError error) {
-                            log.warn("Failed to install forwarding objective to ignore ARP to {}: {}",
-                                    deviceId, error);
-                        }
-                    });
-            srManager.flowObjectiveService.forward(deviceId, pairFwdObj);
-
-            if (isIpv6Configured(deviceId)) {
-                // Do not punt NDP packets from pair port
-                ndpFwdObjective(port, false, PacketPriority.CONTROL.priorityValue() + 1).forEach(builder -> {
-                    ForwardingObjective obj = builder.add(new ObjectiveContext() {
-                        @Override
-                        public void onError(Objective objective, ObjectiveError error) {
-                            log.warn("Failed to install forwarding objective to ignore ARP to {}: {}",
-                                    deviceId, error);
-                        }
-                    });
-                    srManager.flowObjectiveService.forward(deviceId, obj);
-                });
-
-                // Do not forward DAD packets from pair port
-                pairFwdObj = dad6FwdObjective(port, PacketPriority.CONTROL.priorityValue() + 2)
-                        .add(new ObjectiveContext() {
-                            @Override
-                            public void onError(Objective objective, ObjectiveError error) {
-                                log.warn("Failed to install forwarding objective to drop DAD to {}: {}",
-                                        deviceId, error);
-                            }
-                        });
-                srManager.flowObjectiveService.forward(deviceId, pairFwdObj);
-            }
-        });
-    }
-
-    private ForwardingObjective.Builder fwdObjBuilder(TrafficSelector selector,
-                                                      TrafficTreatment treatment, int priority) {
-        return DefaultForwardingObjective.builder()
-                .withPriority(priority)
-                .withSelector(selector)
-                .fromApp(srManager.appId)
-                .withFlag(ForwardingObjective.Flag.VERSATILE)
-                .withTreatment(treatment)
-                .makePermanent();
-    }
-
-    private ForwardingObjective.Builder arpFwdObjective(PortNumber port, boolean punt, int priority) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        sBuilder.matchEthType(TYPE_ARP);
-        if (port != null) {
-            sBuilder.matchInPort(port);
-        }
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        if (punt) {
-            tBuilder.punt();
-        }
-        return fwdObjBuilder(sBuilder.build(), tBuilder.build(), priority);
-    }
-
-    private Set<ForwardingObjective.Builder> ndpFwdObjective(PortNumber port, boolean punt, int priority) {
-        Set<ForwardingObjective.Builder> result = Sets.newHashSet();
-
-        Lists.newArrayList(NEIGHBOR_SOLICITATION, NEIGHBOR_ADVERTISEMENT, ROUTER_SOLICITATION, ROUTER_ADVERTISEMENT)
-                .forEach(type -> {
-                    TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-                    sBuilder.matchEthType(TYPE_IPV6)
-                            .matchIPProtocol(PROTOCOL_ICMP6)
-                            .matchIcmpv6Type(type);
-                    if (port != null) {
-                        sBuilder.matchInPort(port);
-                    }
-
-                    TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-                    if (punt) {
-                        tBuilder.punt();
-                    }
-
-                    result.add(fwdObjBuilder(sBuilder.build(), tBuilder.build(), priority));
-                });
-
-        return result;
-    }
-
-    private ForwardingObjective.Builder dad6FwdObjective(PortNumber port, int priority) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        sBuilder.matchEthType(TYPE_IPV6)
-                .matchIPv6Src(Ip6Address.ZERO.toIpPrefix());
-                // TODO CORD-1672 Fix this when OFDPA can distinguish ::/0 and ::/128 correctly
-                // .matchIPProtocol(PROTOCOL_ICMP6)
-                // .matchIcmpv6Type(NEIGHBOR_SOLICITATION);
-        if (port != null) {
-            sBuilder.matchInPort(port);
-        }
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        tBuilder.wipeDeferred();
-        return fwdObjBuilder(sBuilder.build(), tBuilder.build(), priority);
-    }
-
-    /**
-     * Block given prefix in routing table.
-     *
-     * @param address the address to block
-     * @param deviceId switch ID to set the rules
-     */
-    void populateDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address) {
-        updateDefaultRouteBlackhole(deviceId, address, true);
-    }
-
-    /**
-     * Unblock given prefix in routing table.
-     *
-     * @param address the address to block
-     * @param deviceId switch ID to set the rules
-     */
-    void removeDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address) {
-        updateDefaultRouteBlackhole(deviceId, address, false);
-    }
-
-    private void updateDefaultRouteBlackhole(DeviceId deviceId, IpPrefix address, boolean install) {
-        try {
-            if (srManager.deviceConfiguration.isEdgeDevice(deviceId)) {
-
-                TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-                if (address.isIp4()) {
-                    sbuilder.matchIPDst(address);
-                    sbuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
-                } else {
-                    sbuilder.matchIPv6Dst(address);
-                    sbuilder.matchEthType(EthType.EtherType.IPV6.ethType().toShort());
-                }
-
-                TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-                tBuilder.wipeDeferred();
-
-                ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
-                fob.withFlag(Flag.SPECIFIC)
-                        .withSelector(sbuilder.build())
-                        .withTreatment(tBuilder.build())
-                        .withPriority(getPriorityFromPrefix(address))
-                        .fromApp(srManager.appId)
-                        .makePermanent();
-
-                log.debug("{} blackhole forwarding objectives for dev: {}",
-                        install ? "Installing" : "Removing", deviceId);
-                ObjectiveContext context = new DefaultObjectiveContext(
-                        (objective) -> log.debug("Forward for {} {}", deviceId,
-                                install ? "installed" : "removed"),
-                        (objective, error) -> log.warn("Failed to {} forward for {}: {}",
-                                install ? "install" : "remove", deviceId, error));
-                if (install) {
-                    srManager.flowObjectiveService.forward(deviceId, fob.add(context));
-                } else {
-                    srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
-                }
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.info("Not populating blackhole for un-configured device {}", deviceId);
-        }
-
-    }
-
-    /**
-     * Populates a forwarding objective to send packets that miss other high
-     * priority Bridging Table entries to a group that contains all ports of
-     * its subnet.
-     *
-     * @param deviceId switch ID to set the rules
-     */
-    void populateSubnetBroadcastRule(DeviceId deviceId) {
-        srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
-            updateSubnetBroadcastRule(deviceId, vlanId, true);
-        });
-    }
-
-    /**
-     * Creates or removes a forwarding objective to broadcast packets to its subnet.
-     * @param deviceId switch ID to set the rule
-     * @param vlanId vlan ID to specify the subnet
-     * @param install true to install the rule, false to revoke the rule
-     */
-    void updateSubnetBroadcastRule(DeviceId deviceId, VlanId vlanId, boolean install) {
-        int nextId = srManager.getVlanNextObjectiveId(deviceId, vlanId);
-
-        if (nextId < 0) {
-            log.error("Cannot install vlan {} broadcast rule in dev:{} due"
-                              + " to vlanId:{} or nextId:{}", vlanId, deviceId, vlanId, nextId);
-            return;
-        }
-
-        // Driver should treat objective with MacAddress.NONE as the
-        // subnet broadcast rule
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        sbuilder.matchVlanId(vlanId);
-        sbuilder.matchEthDst(MacAddress.NONE);
-
-        ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
-        fob.withFlag(Flag.SPECIFIC)
-                .withSelector(sbuilder.build())
-                .nextStep(nextId)
-                .withPriority(SegmentRoutingService.FLOOD_PRIORITY)
-                .fromApp(srManager.appId)
-                .makePermanent();
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Vlan broadcast rule for {} populated", vlanId),
-                (objective, error) ->
-                        log.warn("Failed to populate vlan broadcast rule for {}: {}", vlanId, error));
-
-        if (install) {
-            srManager.flowObjectiveService.forward(deviceId, fob.add(context));
-        } else {
-            srManager.flowObjectiveService.forward(deviceId, fob.remove(context));
-        }
-    }
-
-    private int getPriorityFromPrefix(IpPrefix prefix) {
-        return (prefix.isIp4()) ?
-                2000 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY :
-                500 * prefix.prefixLength() + SegmentRoutingService.MIN_IP_PRIORITY;
-    }
-
-    /**
-     * Update Forwarding objective for each host and IP address connected to given port.
-     * And create corresponding Simple Next objective if it does not exist.
-     * Applied only when populating Forwarding objective
-     * @param deviceId switch ID to set the rule
-     * @param portNumber port number
-     * @param prefix IP prefix of the route
-     * @param hostMac MAC address of the next hop
-     * @param vlanId Vlan ID of the port
-     * @param popVlan true to pop vlan tag in TrafficTreatment
-     * @param install true to populate the forwarding objective, false to revoke
-     */
-    void updateFwdObj(DeviceId deviceId, PortNumber portNumber, IpPrefix prefix, MacAddress hostMac,
-                      VlanId vlanId, boolean popVlan, boolean install) {
-        ForwardingObjective.Builder fob;
-        TrafficSelector.Builder sbuilder = buildIpSelectorFromIpPrefix(prefix);
-        MacAddress deviceMac;
-        try {
-            deviceMac = config.getDeviceMac(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting updateFwdObj.");
-            return;
-        }
-
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.deferred()
-                .setEthDst(hostMac)
-                .setEthSrc(deviceMac)
-                .setOutput(portNumber);
-
-        TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
-
-        if (!popVlan) {
-            tbuilder.setVlanId(vlanId);
-        } else {
-            mbuilder.matchVlanId(vlanId);
-        }
-
-        // if the objective is to revoke an existing rule, and for some reason
-        // the next-objective does not exist, then a new one should not be created
-        int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNumber,
-                                                             tbuilder.build(), mbuilder.build(), install);
-        if (portNextObjId == -1) {
-            // Warning log will come from getPortNextObjective method
-            return;
-        }
-
-        fob = DefaultForwardingObjective.builder().withSelector(sbuilder.build())
-                .nextStep(portNextObjId).fromApp(srManager.appId).makePermanent()
-                .withPriority(getPriorityFromPrefix(prefix)).withFlag(ForwardingObjective.Flag.SPECIFIC);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("IP rule for route {} {}", prefix, install ? "installed" : "revoked"),
-                (objective, error) ->
-                        log.warn("Failed to {} IP rule for route {}: {}",
-                                 install ? "install" : "revoke", prefix, error));
-        srManager.flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
-
-        if (!install) {
-            if (!srManager.getVlanPortMap(deviceId).containsKey(vlanId) ||
-                    !srManager.getVlanPortMap(deviceId).get(vlanId).contains(portNumber)) {
-                DefaultGroupHandler grpHandler = srManager.getGroupHandler(deviceId);
-                if (grpHandler == null) {
-                    log.warn("updateFwdObj: groupHandler for device {} not found", deviceId);
-                } else {
-                    // Remove L3UG for the given port and host
-                    grpHandler.removeGroupFromPort(portNumber, tbuilder.build(), mbuilder.build());
-                }
-            }
-        }
-    }
-
-    /**
-     * Checks if there is other enabled port within the given VLAN on the given device.
-     *
-     * @param deviceId device ID
-     * @param vlanId VLAN ID
-     * @return true if there is no more port enabled within the given VLAN on the given device
-     */
-    boolean noMoreEnabledPort(DeviceId deviceId, VlanId vlanId) {
-        Set<ConnectPoint> enabledPorts = srManager.deviceService.getPorts(deviceId).stream()
-                .filter(Port::isEnabled)
-                .map(port -> new ConnectPoint(port.element().id(), port.number()))
-                .collect(Collectors.toSet());
-
-        return enabledPorts.stream().noneMatch(cp ->
-            // Given vlanId is included in the vlan-tagged configuration
-            srManager.interfaceService.getTaggedVlanId(cp).contains(vlanId) ||
-            // Given vlanId is INTERNAL_VLAN or PSEUDOWIRE_VLAN and the interface is not configured
-            (srManager.interfaceService.getTaggedVlanId(cp).isEmpty() && srManager.getInternalVlanId(cp) == null &&
-                (vlanId.equals(srManager.getDefaultInternalVlan()) || vlanId.equals(srManager.getPwTransportVlan()))) ||
-            // interface is configured and either vlan-untagged or vlan-native matches given vlanId
-            (srManager.getInternalVlanId(cp) != null && srManager.getInternalVlanId(cp).equals(vlanId))
-        );
-    }
-
-    /**
-     * Returns a forwarding objective builder for egress forwarding rules.
-     * <p>
-     * The forwarding objective installs flow rules to egress pipeline to push
-     * two vlan headers with given inner, outer vlan ids and outer tpid.
-     *
-     * @param portNumber port where the next hop attaches to
-     * @param dummyVlanId vlan ID of the packet to match
-     * @param innerVlan inner vlan ID of the next hop
-     * @param outerVlan outer vlan ID of the next hop
-     * @param outerTpid outer TPID of the next hop
-     * @return forwarding objective builder
-     */
-    private ForwardingObjective.Builder egressFwdObjBuilder(PortNumber portNumber, VlanId dummyVlanId,
-                                                            VlanId innerVlan, VlanId outerVlan, EthType outerTpid) {
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        sbuilder.matchVlanId(dummyVlanId);
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.setOutput(portNumber).setVlanId(innerVlan);
-
-        if (outerTpid.equals(EthType.EtherType.QINQ.ethType())) {
-            tbuilder.pushVlan(outerTpid);
-        } else {
-            tbuilder.pushVlan();
-        }
-
-        tbuilder.setVlanId(outerVlan);
-        return DefaultForwardingObjective.builder()
-                .withSelector(sbuilder.build())
-                .withTreatment(tbuilder.build())
-                .fromApp(srManager.appId)
-                .makePermanent()
-                .withPriority(DEFAULT_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.EGRESS);
-    }
-
-    /**
-     * Populates IP rules for a route that has double-tagged next hop.
-     *
-     * @param deviceId device ID of the device that next hop attaches to
-     * @param prefix IP prefix of the route
-     * @param hostMac MAC address of the next hop
-     * @param innerVlan inner Vlan ID of the next hop
-     * @param outerVlan outer Vlan ID of the next hop
-     * @param outerTpid outer TPID of the next hop
-     * @param outPort port where the next hop attaches to
-     */
-    void populateDoubleTaggedRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac,
-                                   VlanId innerVlan, VlanId outerVlan, EthType outerTpid, PortNumber outPort) {
-        ForwardingObjective.Builder fwdBuilder;
-        log.debug("Populate direct routing entry for double-tagged host route {} at {}:{}",
-                  prefix, deviceId, outPort);
-
-        try {
-            fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac, outerVlan, outPort, innerVlan, outerTpid,
-                    true, false);
-        } catch (DeviceConfigNotFoundException e) {
-            log.error(e.getMessage() + " Aborting populateDoubleTaggedRoute");
-            return;
-        }
-        if (fwdBuilder == null) {
-            log.error("Aborting double-tagged host routing table entry due to error for dev:{} route:{}",
-                     deviceId, prefix);
-            return;
-        }
-
-        int nextId = fwdBuilder.add().nextId();
-        DefaultObjectiveContext context = new DefaultObjectiveContext(objective -> {
-            log.debug("Direct routing rule for double-tagged host route {} populated. nextId={}", prefix, nextId);
-        }, (objective, error) ->
-            log.warn("Failed to populate direct routing rule for double-tagged host route {}: {}", prefix, error)
-        );
-        srManager.flowObjectiveService.forward(deviceId, fwdBuilder.add(context));
-        rulePopulationCounter.incrementAndGet();
-    }
-
-    /**
-     * Revokes IP rules for a route that has double-tagged next hop.
-     *
-     * @param deviceId device ID of the device that next hop attaches to
-     * @param prefix IP prefix of the route
-     * @param hostMac MAC address of the next hop
-     * @param innerVlan inner Vlan ID of the next hop
-     * @param outerVlan outer Vlan ID of the next hop
-     * @param outerTpid outer TPID of the next hop
-     * @param outPort port where the next hop attaches to
-     */
-    void revokeDoubleTaggedRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac,
-                                 VlanId innerVlan, VlanId outerVlan, EthType outerTpid, PortNumber outPort) {
-        ForwardingObjective.Builder fwdBuilder;
-        log.debug("Revoking direct routing entry for double-tagged host route {} at {}:{}",
-                prefix, deviceId, outPort);
-
-        try {
-            fwdBuilder = routingFwdObjBuilder(deviceId, prefix, hostMac, outerVlan, outPort, innerVlan, outerTpid,
-                    true, true);
-        } catch (DeviceConfigNotFoundException e) {
-            log.error(e.getMessage() + " Aborting revokeDoubleTaggedRoute");
-            return;
-        }
-        if (fwdBuilder == null) {
-            log.error("Aborting double-tagged host routing table entry due to error for dev:{} route:{}",
-                    deviceId, prefix);
-            return;
-        }
-
-        int nextId = fwdBuilder.remove().nextId();
-        DefaultObjectiveContext context = new DefaultObjectiveContext(objective -> {
-            log.debug("Direct routing rule for double-tagged host route {} revoked. nextId={}", prefix, nextId);
-
-            // Try to remove next objective as well
-            ImmutablePair<TrafficTreatment, TrafficSelector> treatmentAndMeta;
-            try {
-                treatmentAndMeta = getTreatmentAndMeta(deviceId, hostMac, outerVlan, outPort, innerVlan, outerTpid);
-            } catch (DeviceConfigNotFoundException e) {
-                log.error(e.getMessage() + " Aborting revokeDoubleTaggedRoute");
-                return;
-            }
-
-            if (treatmentAndMeta == null) {
-                // Warning log will come from getTreatmentAndMeta method
-                return;
-            }
-
-            DefaultGroupHandler groupHandler = srManager.getGroupHandler(deviceId);
-            if (groupHandler == null) {
-                log.warn("Failed to revoke direct routing rule for double-tagged host route {}: " +
-                        "group handler not found for {}", prefix, deviceId);
-                return;
-            }
-            groupHandler.removeGroupFromPort(outPort, treatmentAndMeta.getLeft(), treatmentAndMeta.getRight());
-
-        }, (objective, error) ->
-                log.warn("Failed to revoke direct routing rule for double-tagged host route {}: {}", prefix, error)
-        );
-        srManager.flowObjectiveService.forward(deviceId, fwdBuilder.remove(context));
-    }
-
-    /**
-     * Checks whether the specified port has IP configuration or not.
-     *
-     * @param cp ConnectPoint to check the existance of IP configuration
-     * @return true if the port has IP configuration; false otherwise.
-     */
-    private boolean hasIPConfiguration(ConnectPoint cp) {
-        Set<Interface> interfaces = srManager.interfaceService.getInterfacesByPort(cp);
-        return interfaces.stream().anyMatch(intf -> intf.ipAddressesList().size() > 0);
-    }
-
-    /**
-     * Updates filtering rules for unconfigured ports on all devices for which
-     * this controller instance is master.
-     *
-     * @param pushVlan true if the filtering rule requires a push vlan action
-     * @param oldVlanId the vlanId to be removed
-     * @param newVlanId the vlanId to be added
-     */
-    void updateSpecialVlanFilteringRules(boolean pushVlan, VlanId oldVlanId,
-                                         VlanId newVlanId) {
-        for (Device dev : srManager.deviceService.getAvailableDevices()) {
-            if (srManager.mastershipService.isLocalMaster(dev.id())) {
-                for (Port p : srManager.deviceService.getPorts(dev.id())) {
-                    if (!hasIPConfiguration(new ConnectPoint(dev.id(), p.number()))
-                            && p.isEnabled()) {
-                        updateSinglePortFilters(dev.id(), p.number(), pushVlan,
-                                                oldVlanId, false);
-                        updateSinglePortFilters(dev.id(), p.number(), pushVlan,
-                                                newVlanId, true);
-                    }
-                }
-            }
-        }
-    }
-
-    private boolean isIpv6Configured(DeviceId deviceId) {
-        boolean isIpv6Configured;
-        try {
-            isIpv6Configured = (config.getRouterIpv6(deviceId) != null);
-        } catch (DeviceConfigNotFoundException e) {
-            isIpv6Configured = false;
-        }
-        return isIpv6Configured;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/SRLinkWeigher.java b/app/src/main/java/org/onosproject/segmentrouting/SRLinkWeigher.java
deleted file mode 100644
index 01e07a7..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/SRLinkWeigher.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.graph.DefaultEdgeWeigher;
-import org.onlab.graph.ScalarWeight;
-import org.onlab.graph.Weight;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.topology.LinkWeigher;
-import org.onosproject.net.topology.TopologyEdge;
-import org.onosproject.net.topology.TopologyVertex;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-
-import java.util.Set;
-
-/**
- * Link weigher for multicast related path computations.
- */
-public final class SRLinkWeigher
-        extends DefaultEdgeWeigher<TopologyVertex, TopologyEdge>
-        implements LinkWeigher {
-
-    private final SegmentRoutingManager srManager;
-    private final DeviceId srcPath;
-    private final Set<Link> linksToEnforce;
-
-    // Weight for the link to avoids. The high level idea is to build
-    // a constrained shortest path computation. 100 should provide a good
-    // threshold
-    public static final ScalarWeight LINK_TO_AVOID_WEIGHT = new ScalarWeight(HOP_WEIGHT_VALUE + 100);
-
-    /**
-     * Creates a SRLinkWeigher object.
-     *
-     * @param srManager SegmentRoutingManager object
-     * @param srcPath the source of the paths
-     * @param linksToEnforce links to be enforced by the path computation
-     */
-    public SRLinkWeigher(SegmentRoutingManager srManager, DeviceId srcPath,
-                         Set<Link> linksToEnforce) {
-        this.srManager = srManager;
-        this.srcPath = srcPath;
-        this.linksToEnforce = linksToEnforce;
-    }
-
-    @Override
-    public Weight weight(TopologyEdge edge) {
-        // 1) We need to avoid some particular paths like leaf-spine-leaf-*
-        // 2) Properly handle the pair links
-
-        // If the link is a pair link just return infinite value
-        if (isPairLink(edge.link())) {
-            return ScalarWeight.NON_VIABLE_WEIGHT;
-        }
-
-        // To avoid that the paths go through other leaves we need to influence
-        // the path computation to return infinite value for all other links having
-        // as a src a leaf different from the source we are passing to the weigher
-        DeviceId srcDeviceLink = edge.link().src().deviceId();
-        // Identify the link as leaf-spine link
-        boolean isLeafSpine;
-        try {
-            isLeafSpine = srManager.deviceConfiguration().isEdgeDevice(srcDeviceLink);
-        } catch (DeviceConfigNotFoundException e) {
-            isLeafSpine = false;
-        }
-        // If it is not the source just return infinite value
-        if (isLeafSpine && !srcDeviceLink.equals(srcPath)) {
-            return ScalarWeight.NON_VIABLE_WEIGHT;
-        }
-
-        // If the links are not in the list of the links to be enforce
-        if (!linksToEnforce.isEmpty() && !linksToEnforce.contains(edge.link())) {
-            // 100 should be a good confidence threshold
-            return LINK_TO_AVOID_WEIGHT;
-        }
-
-        // All other cases we return
-        return new ScalarWeight(HOP_WEIGHT_VALUE);
-    }
-
-    // Utility method to verify is a link is a pair-link
-    private boolean isPairLink(Link link) {
-        // Take src id, src port, dst id and dst port
-        final DeviceId srcId = link.src().deviceId();
-        final PortNumber srcPort = link.src().port();
-        final DeviceId dstId = link.dst().deviceId();
-        final PortNumber dstPort = link.dst().port();
-        // init as true
-        boolean isPairLink = true;
-        try {
-            // If one of this condition is not true; it is not a pair link
-            if (!(srManager.deviceConfiguration().isEdgeDevice(srcId) &&
-                    srManager.deviceConfiguration().isEdgeDevice(dstId) &&
-                    srManager.deviceConfiguration().getPairDeviceId(srcId).equals(dstId) &&
-                    srManager.deviceConfiguration().getPairLocalPort(srcId).equals(srcPort) &&
-                    srManager.deviceConfiguration().getPairLocalPort(dstId).equals(dstPort))) {
-                isPairLink = false;
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            // Configuration not provided
-            isPairLink = false;
-        }
-        return isPairLink;
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
deleted file mode 100644
index 673cfc9..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ /dev/null
@@ -1,2398 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.ICMP6;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.IPv6;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.cluster.ClusterEvent;
-import org.onosproject.cluster.ClusterEventListener;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.LeadershipService;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.event.Event;
-import org.onosproject.mastership.MastershipEvent;
-import org.onosproject.mastership.MastershipListener;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.mcast.api.McastEvent;
-import org.onosproject.mcast.api.McastListener;
-import org.onosproject.mcast.api.MulticastRouteService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.Link;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.config.ConfigException;
-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.InterfaceConfig;
-import org.onosproject.net.config.basics.McastConfig;
-import org.onosproject.net.config.basics.SubjectFactories;
-import org.onosproject.net.device.DeviceAdminService;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostProbingService;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intent.WorkPartitionService;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.intf.InterfaceService;
-import org.onosproject.net.link.LinkEvent;
-import org.onosproject.net.link.LinkListener;
-import org.onosproject.net.link.LinkService;
-import org.onosproject.net.neighbour.NeighbourResolutionService;
-import org.onosproject.net.packet.InboundPacket;
-import org.onosproject.net.packet.PacketContext;
-import org.onosproject.net.packet.PacketProcessor;
-import org.onosproject.net.packet.PacketService;
-import org.onosproject.net.topology.TopologyEvent;
-import org.onosproject.net.topology.TopologyListener;
-import org.onosproject.net.topology.TopologyService;
-import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.RouteEvent;
-import org.onosproject.routeservice.RouteListener;
-import org.onosproject.routeservice.RouteService;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
-import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
-import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
-import org.onosproject.segmentrouting.grouphandler.DestinationSet;
-import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
-import org.onosproject.segmentrouting.mcast.McastFilteringObjStoreKey;
-import org.onosproject.segmentrouting.mcast.McastHandler;
-import org.onosproject.segmentrouting.mcast.McastRole;
-import org.onosproject.segmentrouting.mcast.McastRoleStoreKey;
-import org.onosproject.segmentrouting.mcast.McastStoreKey;
-import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
-import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelHandler;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
-import org.onosproject.segmentrouting.pwaas.L2Tunnel;
-import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
-import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
-import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
-import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.XConnectStoreKey;
-import org.onosproject.segmentrouting.xconnect.api.XconnectService;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.EventuallyConsistentMap;
-import org.onosproject.store.service.EventuallyConsistentMapBuilder;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.WallClockTimestamp;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Modified;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
-import org.osgi.service.component.annotations.ReferencePolicy;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.time.Instant;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkState;
-import static org.onlab.packet.Ethernet.TYPE_ARP;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_REGISTERED;
-import static org.onosproject.net.config.NetworkConfigEvent.Type.CONFIG_UNREGISTERED;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.ACTIVE_PROBING_DEFAULT;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.DEFAULT_INTERNAL_VLAN_DEFAULT;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_ACTIVE_PROBING;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_DEFAULT_INTERNAL_VLAN;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_PW_TRANSPORT_VLAN;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_RESPOND_TO_UNKNOWN_HOSTS;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_ROUTE_DOUBLE_TAGGED_HOSTS;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_ROUTE_SIMPLIFICATION;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_SINGLE_HOMED_DOWN;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PROP_SYMMETRIC_PROBING;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.PW_TRANSPORT_VLAN_DEFAULT;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.RESPOND_TO_UNKNOWN_HOSTS_DEFAULT;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.ROUTE_DOUBLE_TAGGED_HOSTS_DEFAULT;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.ROUTE_SIMPLIFICATION_DEFAULT;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.SINGLE_HOMED_DOWN_DEFAULT;
-import static org.onosproject.segmentrouting.OsgiPropertyConstants.SYMMETRIC_PROBING_DEFAULT;
-
-/**
- * Segment routing manager.
- */
-@Component(
-    immediate = true,
-    service = SegmentRoutingService.class,
-    property = {
-        PROP_ACTIVE_PROBING + ":Boolean=" + ACTIVE_PROBING_DEFAULT,
-        PROP_SINGLE_HOMED_DOWN + ":Boolean=" + SINGLE_HOMED_DOWN_DEFAULT,
-        PROP_RESPOND_TO_UNKNOWN_HOSTS + ":Boolean=" + RESPOND_TO_UNKNOWN_HOSTS_DEFAULT,
-        PROP_ROUTE_DOUBLE_TAGGED_HOSTS + ":Boolean=" + ROUTE_DOUBLE_TAGGED_HOSTS_DEFAULT,
-        PROP_DEFAULT_INTERNAL_VLAN + ":Integer=" + DEFAULT_INTERNAL_VLAN_DEFAULT,
-        PROP_PW_TRANSPORT_VLAN + ":Integer=" + PW_TRANSPORT_VLAN_DEFAULT,
-        PROP_SYMMETRIC_PROBING + ":Boolean=" + SYMMETRIC_PROBING_DEFAULT,
-        PROP_ROUTE_SIMPLIFICATION + ":Boolean=" + ROUTE_SIMPLIFICATION_DEFAULT
-    }
-)
-public class SegmentRoutingManager implements SegmentRoutingService {
-
-    private static Logger log = LoggerFactory.getLogger(SegmentRoutingManager.class);
-    private static final String NOT_MASTER = "Current instance is not the master of {}. Ignore.";
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private ComponentConfigService compCfgService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public NeighbourResolutionService neighbourResolutionService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    PacketService packetService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    HostProbingService probingService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    DeviceAdminService deviceAdminService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public LinkService linkService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public StorageService storageService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public MulticastRouteService multicastRouteService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public TopologyService topologyService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public RouteService routeService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public NetworkConfigRegistry cfgService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public InterfaceService interfaceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public ClusterService clusterService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public WorkPartitionService workPartitionService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public LeadershipService leadershipService;
-
-    @Reference(cardinality = ReferenceCardinality.OPTIONAL,
-            policy = ReferencePolicy.DYNAMIC)
-    public volatile XconnectService xconnectService;
-
-    @Reference(cardinality = ReferenceCardinality.OPTIONAL,
-            policy = ReferencePolicy.DYNAMIC)
-    volatile PhasedRecoveryService phasedRecoveryService;
-
-    /** Enable active probing to discover dual-homed hosts. */
-    boolean activeProbing = ACTIVE_PROBING_DEFAULT;
-
-    /** Enable only send probe on the same port number of the pair device. */
-    boolean symmetricProbing = SYMMETRIC_PROBING_DEFAULT;
-
-    /** Enable administratively taking down single-homed hosts. */
-    boolean singleHomedDown = SINGLE_HOMED_DOWN_DEFAULT;
-
-    /** Enable this to respond to ARP/NDP requests from unknown hosts. */
-    boolean respondToUnknownHosts = RESPOND_TO_UNKNOWN_HOSTS_DEFAULT;
-
-    /** Program flows and groups to pop and route double tagged hosts. */
-    boolean routeDoubleTaggedHosts = ROUTE_DOUBLE_TAGGED_HOSTS_DEFAULT;
-
-    /** internal vlan assigned by default to unconfigured ports. */
-    private int defaultInternalVlan = DEFAULT_INTERNAL_VLAN_DEFAULT;
-
-    /** vlan used for transport of pseudowires between switches. */
-    private int pwTransportVlan = PW_TRANSPORT_VLAN_DEFAULT;
-
-    /** Enabling route simplification. */
-    boolean  routeSimplification = ROUTE_SIMPLIFICATION_DEFAULT;
-
-    ArpHandler arpHandler = null;
-    IcmpHandler icmpHandler = null;
-    IpHandler ipHandler = null;
-    RoutingRulePopulator routingRulePopulator = null;
-    ApplicationId appId;
-    DeviceConfiguration deviceConfiguration = null;
-
-    DefaultRoutingHandler defaultRoutingHandler = null;
-    private TunnelHandler tunnelHandler = null;
-    private PolicyHandler policyHandler = null;
-    private InternalPacketProcessor processor = null;
-    private InternalLinkListener linkListener = null;
-    private InternalDeviceListener deviceListener = null;
-    private AppConfigHandler appCfgHandler = null;
-    McastHandler mcastHandler = null;
-    HostHandler hostHandler = null;
-    private RouteHandler routeHandler = null;
-    LinkHandler linkHandler = null;
-    private SegmentRoutingNeighbourDispatcher neighbourHandler = null;
-    private DefaultL2TunnelHandler l2TunnelHandler = null;
-    private TopologyHandler topologyHandler = null;
-    private final InternalHostListener hostListener = new InternalHostListener();
-    private final InternalConfigListener cfgListener = new InternalConfigListener(this);
-    private final InternalMcastListener mcastListener = new InternalMcastListener();
-    private final InternalRouteEventListener routeListener = new InternalRouteEventListener();
-    private final InternalTopologyListener topologyListener = new InternalTopologyListener();
-    private final InternalMastershipListener mastershipListener = new InternalMastershipListener();
-    final InternalClusterListener clusterListener = new InternalClusterListener();
-    //Completable future for network configuration process to buffer config events handling during activation
-    private CompletableFuture<Boolean> networkConfigCompletion = null;
-    private final Object networkConfigCompletionLock = new Object();
-    private List<Event> queuedEvents = new CopyOnWriteArrayList<>();
-
-    // Handles device, link, topology and network config events
-    private ScheduledExecutorService mainEventExecutor;
-
-    // Handles host, route and mcast events respectively
-    private ScheduledExecutorService hostEventExecutor;
-    private ScheduledExecutorService routeEventExecutor;
-    private ScheduledExecutorService mcastEventExecutor;
-    private ExecutorService packetExecutor;
-    ExecutorService neighborExecutor;
-
-    Map<DeviceId, DefaultGroupHandler> groupHandlerMap = new ConcurrentHashMap<>();
-    /**
-     * Per device next objective ID store with (device id + destination set) as key.
-     * Used to keep track on MPLS group information.
-     */
-    private EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
-            dsNextObjStore = null;
-    /**
-     * Per device next objective ID store with (device id + vlanid) as key.
-     * Used to keep track on L2 flood group information.
-     */
-    private EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
-            vlanNextObjStore = null;
-    /**
-     * Per device next objective ID store with (device id + port + treatment + meta) as key.
-     * Used to keep track on L2 interface group and L3 unicast group information for direct hosts.
-     */
-    private EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
-            portNextObjStore = null;
-
-    /**
-     * Per device next objective ID store with (device id + MAC address + vlan) as key.
-     * Used to keep track of L3 unicast group for indirect hosts.
-     */
-    private EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
-            macVlanNextObjStore = null;
-
-    private EventuallyConsistentMap<String, Tunnel> tunnelStore = null;
-    private EventuallyConsistentMap<String, Policy> policyStore = null;
-
-    private AtomicBoolean programmingScheduled = new AtomicBoolean();
-
-    private final ConfigFactory<DeviceId, SegmentRoutingDeviceConfig> deviceConfigFactory =
-            new ConfigFactory<DeviceId, SegmentRoutingDeviceConfig>(
-                    SubjectFactories.DEVICE_SUBJECT_FACTORY,
-                    SegmentRoutingDeviceConfig.class, "segmentrouting") {
-                @Override
-                public SegmentRoutingDeviceConfig createConfig() {
-                    return new SegmentRoutingDeviceConfig();
-                }
-            };
-
-    private final ConfigFactory<ApplicationId, SegmentRoutingAppConfig> appConfigFactory =
-            new ConfigFactory<ApplicationId, SegmentRoutingAppConfig>(
-                    SubjectFactories.APP_SUBJECT_FACTORY,
-                    SegmentRoutingAppConfig.class, "segmentrouting") {
-                @Override
-                public SegmentRoutingAppConfig createConfig() {
-                    return new SegmentRoutingAppConfig();
-                }
-            };
-
-    private ConfigFactory<ApplicationId, McastConfig> mcastConfigFactory =
-            new ConfigFactory<ApplicationId, McastConfig>(
-                    SubjectFactories.APP_SUBJECT_FACTORY,
-                    McastConfig.class, "multicast") {
-                @Override
-                public McastConfig createConfig() {
-                    return new McastConfig();
-                }
-            };
-
-    /**
-     * Segment Routing App ID.
-     */
-    public static final String APP_NAME = "org.onosproject.segmentrouting";
-
-    /**
-     * Minumum and maximum value of dummy VLAN ID to be allocated.
-     */
-    public static final int MIN_DUMMY_VLAN_ID = 2;
-    public static final int MAX_DUMMY_VLAN_ID = 4093;
-
-    private static final int DEFAULT_POOL_SIZE = 32;
-
-    Instant lastEdgePortEvent = Instant.EPOCH;
-
-    @Activate
-    protected void activate(ComponentContext context) {
-        appId = coreService.registerApplication(APP_NAME);
-
-        mainEventExecutor = Executors.newSingleThreadScheduledExecutor(
-                groupedThreads("onos/sr", "event-main-%d", log));
-        hostEventExecutor = Executors.newSingleThreadScheduledExecutor(
-                groupedThreads("onos/sr", "event-host-%d", log));
-        routeEventExecutor = Executors.newSingleThreadScheduledExecutor(
-                groupedThreads("onos/sr", "event-route-%d", log));
-        mcastEventExecutor = Executors.newSingleThreadScheduledExecutor(
-                groupedThreads("onos/sr", "event-mcast-%d", log));
-        packetExecutor = Executors.newSingleThreadExecutor(groupedThreads("onos/sr", "packet-%d", log));
-        neighborExecutor = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE,
-                groupedThreads("onos/sr", "neighbor-%d", log));
-
-        log.debug("Creating EC map nsnextobjectivestore");
-        EventuallyConsistentMapBuilder<DestinationSetNextObjectiveStoreKey, NextNeighbors>
-                nsNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
-        dsNextObjStore = nsNextObjMapBuilder
-                .withName("nsnextobjectivestore")
-                .withSerializer(createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-        log.trace("Current size {}", dsNextObjStore.size());
-
-        log.debug("Creating EC map vlannextobjectivestore");
-        EventuallyConsistentMapBuilder<VlanNextObjectiveStoreKey, Integer>
-                vlanNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
-        vlanNextObjStore = vlanNextObjMapBuilder
-                .withName("vlannextobjectivestore")
-                .withSerializer(createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-
-        log.debug("Creating EC map macvlannextobjectivestore");
-        EventuallyConsistentMapBuilder<MacVlanNextObjectiveStoreKey, Integer>
-                macVlanNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
-        macVlanNextObjStore = macVlanNextObjMapBuilder
-                .withName("macvlannextobjectivestore")
-                .withSerializer(createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-
-        log.debug("Creating EC map subnetnextobjectivestore");
-        EventuallyConsistentMapBuilder<PortNextObjectiveStoreKey, Integer>
-                portNextObjMapBuilder = storageService.eventuallyConsistentMapBuilder();
-        portNextObjStore = portNextObjMapBuilder
-                .withName("portnextobjectivestore")
-                .withSerializer(createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-
-        EventuallyConsistentMapBuilder<String, Tunnel> tunnelMapBuilder =
-                storageService.eventuallyConsistentMapBuilder();
-        tunnelStore = tunnelMapBuilder
-                .withName("tunnelstore")
-                .withSerializer(createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-
-        EventuallyConsistentMapBuilder<String, Policy> policyMapBuilder =
-                storageService.eventuallyConsistentMapBuilder();
-        policyStore = policyMapBuilder
-                .withName("policystore")
-                .withSerializer(createSerializer())
-                .withTimestampProvider((k, v) -> new WallClockTimestamp())
-                .build();
-
-        processor = new InternalPacketProcessor();
-        linkListener = new InternalLinkListener();
-        deviceListener = new InternalDeviceListener();
-        appCfgHandler = new AppConfigHandler(this);
-        mcastHandler = new McastHandler(this);
-        hostHandler = new HostHandler(this);
-        linkHandler = new LinkHandler(this);
-        routeHandler = new RouteHandler(this);
-        neighbourHandler = new SegmentRoutingNeighbourDispatcher(this);
-        l2TunnelHandler = new DefaultL2TunnelHandler(this);
-        topologyHandler = new TopologyHandler(this);
-
-        compCfgService.preSetProperty("org.onosproject.provider.host.impl.HostLocationProvider",
-                                      "requestInterceptsEnabled", "false", false);
-        compCfgService.preSetProperty("org.onosproject.net.neighbour.impl.NeighbourResolutionManager",
-                                      "requestInterceptsEnabled", "false", false);
-        compCfgService.preSetProperty("org.onosproject.dhcprelay.DhcpRelayManager",
-                                      "arpEnabled", "false", false);
-        compCfgService.preSetProperty("org.onosproject.net.host.impl.HostManager",
-                                      "greedyLearningIpv6", "true", false);
-        compCfgService.preSetProperty("org.onosproject.routing.cpr.ControlPlaneRedirectManager",
-                                      "forceUnprovision", "true", false);
-        compCfgService.preSetProperty("org.onosproject.routeservice.store.RouteStoreImpl",
-                                      "distributed", "true", false);
-        compCfgService.preSetProperty("org.onosproject.provider.host.impl.HostLocationProvider",
-                                      "multihomingEnabled", "true", false);
-        compCfgService.preSetProperty("org.onosproject.provider.lldp.impl.LldpLinkProvider",
-                                      "staleLinkAge", "15000", false);
-        compCfgService.preSetProperty("org.onosproject.net.host.impl.HostManager",
-                                      "allowDuplicateIps", "false", false);
-        // For P4 switches
-        compCfgService.preSetProperty("org.onosproject.net.flow.impl.FlowRuleManager",
-                                      "fallbackFlowPollFrequency", "4", false);
-        compCfgService.preSetProperty("org.onosproject.net.group.impl.GroupManager",
-                                      "fallbackGroupPollFrequency", "3", false);
-        compCfgService.registerProperties(getClass());
-        modified(context);
-
-        cfgService.addListener(cfgListener);
-        cfgService.registerConfigFactory(deviceConfigFactory);
-        cfgService.registerConfigFactory(appConfigFactory);
-        cfgService.registerConfigFactory(mcastConfigFactory);
-        log.info("Configuring network before adding listeners");
-
-        cfgListener.configureNetwork();
-
-        hostService.addListener(hostListener);
-        packetService.addProcessor(processor, PacketProcessor.director(2));
-        linkService.addListener(linkListener);
-        deviceService.addListener(deviceListener);
-        multicastRouteService.addListener(mcastListener);
-        routeService.addListener(routeListener);
-        topologyService.addListener(topologyListener);
-        mastershipService.addListener(mastershipListener);
-        clusterService.addListener(clusterListener);
-
-        linkHandler.init();
-        l2TunnelHandler.init();
-
-        synchronized (networkConfigCompletionLock) {
-            networkConfigCompletion.whenComplete((value, ex) -> {
-                //setting to null for easier fall through
-                networkConfigCompletion = null;
-                //process all queued events
-                queuedEvents.forEach(event -> {
-                    mainEventExecutor.execute(new InternalEventHandler(event));
-                });
-            });
-        }
-
-        log.info("Started");
-    }
-
-    KryoNamespace.Builder createSerializer() {
-        return new KryoNamespace.Builder()
-                .register(KryoNamespaces.API)
-                .register(DestinationSetNextObjectiveStoreKey.class,
-                          VlanNextObjectiveStoreKey.class,
-                          DestinationSet.class,
-                          DestinationSet.DestinationSetType.class,
-                          NextNeighbors.class,
-                          Tunnel.class,
-                          DefaultTunnel.class,
-                          Policy.class,
-                          TunnelPolicy.class,
-                          Policy.Type.class,
-                          PortNextObjectiveStoreKey.class,
-                          XConnectStoreKey.class,
-                          L2Tunnel.class,
-                          L2TunnelPolicy.class,
-                          DefaultL2Tunnel.class,
-                          DefaultL2TunnelPolicy.class,
-                          MacVlanNextObjectiveStoreKey.class
-                );
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        mainEventExecutor.shutdown();
-        hostEventExecutor.shutdown();
-        routeEventExecutor.shutdown();
-        mcastEventExecutor.shutdown();
-        packetExecutor.shutdown();
-        neighborExecutor.shutdown();
-
-        mainEventExecutor = null;
-        hostEventExecutor = null;
-        routeEventExecutor = null;
-        mcastEventExecutor = null;
-        packetExecutor = null;
-        neighborExecutor = null;
-
-        cfgService.removeListener(cfgListener);
-        cfgService.unregisterConfigFactory(deviceConfigFactory);
-        cfgService.unregisterConfigFactory(appConfigFactory);
-        cfgService.unregisterConfigFactory(mcastConfigFactory);
-        compCfgService.unregisterProperties(getClass(), false);
-
-        hostService.removeListener(hostListener);
-        packetService.removeProcessor(processor);
-        linkService.removeListener(linkListener);
-        deviceService.removeListener(deviceListener);
-        multicastRouteService.removeListener(mcastListener);
-        routeService.removeListener(routeListener);
-        topologyService.removeListener(topologyListener);
-        mastershipService.removeListener(mastershipListener);
-        clusterService.removeListener(clusterListener);
-
-        neighbourResolutionService.unregisterNeighbourHandlers(appId);
-
-        processor = null;
-        linkListener = null;
-        deviceListener = null;
-        groupHandlerMap.forEach((k, v) -> v.shutdown());
-        groupHandlerMap.clear();
-        defaultRoutingHandler.shutdown();
-
-        dsNextObjStore.destroy();
-        vlanNextObjStore.destroy();
-        macVlanNextObjStore.destroy();
-        portNextObjStore.destroy();
-        tunnelStore.destroy();
-        policyStore.destroy();
-
-        mcastHandler.terminate();
-        hostHandler.terminate();
-        log.info("Stopped");
-    }
-
-    @Modified
-    private void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        if (properties == null) {
-            return;
-        }
-
-        String strActiveProbing = Tools.get(properties, PROP_ACTIVE_PROBING);
-        boolean expectActiveProbing = Boolean.parseBoolean(strActiveProbing);
-        if (expectActiveProbing != activeProbing) {
-            activeProbing = expectActiveProbing;
-            log.info("{} active probing", activeProbing ? "Enabling" : "Disabling");
-        }
-
-
-        String strSymmetricProbing = Tools.get(properties, PROP_SYMMETRIC_PROBING);
-        boolean expectSymmetricProbing = Boolean.parseBoolean(strSymmetricProbing);
-        if (expectSymmetricProbing != symmetricProbing) {
-            symmetricProbing = expectSymmetricProbing;
-            log.info("{} symmetric probing", symmetricProbing ? "Enabling" : "Disabling");
-        }
-
-        String strSingleHomedDown = Tools.get(properties, PROP_SINGLE_HOMED_DOWN);
-        boolean expectSingleHomedDown = Boolean.parseBoolean(strSingleHomedDown);
-        if (expectSingleHomedDown != singleHomedDown) {
-            singleHomedDown = expectSingleHomedDown;
-            log.info("{} downing of single homed hosts for lost uplinks",
-                     singleHomedDown ? "Enabling" : "Disabling");
-            if (singleHomedDown && linkHandler != null) {
-                hostService.getHosts().forEach(host -> host.locations()
-                        .forEach(loc -> {
-                            if (interfaceService.isConfigured(loc)) {
-                                linkHandler.checkUplinksForHost(loc);
-                            }
-                        }));
-            } else {
-                log.warn("Disabling singleHomedDown does not re-enable already "
-                        + "downed ports for single-homed hosts");
-            }
-        }
-
-        String strRespondToUnknownHosts = Tools.get(properties, PROP_RESPOND_TO_UNKNOWN_HOSTS);
-        boolean expectRespondToUnknownHosts = Boolean.parseBoolean(strRespondToUnknownHosts);
-        if (expectRespondToUnknownHosts != respondToUnknownHosts) {
-            respondToUnknownHosts = expectRespondToUnknownHosts;
-            log.info("{} responding to ARPs/NDPs from unknown hosts", respondToUnknownHosts ? "Enabling" : "Disabling");
-        }
-
-        String strRouteDoubleTaggedHosts = Tools.get(properties, PROP_ROUTE_DOUBLE_TAGGED_HOSTS);
-        boolean expectRouteDoubleTaggedHosts = Boolean.parseBoolean(strRouteDoubleTaggedHosts);
-        if (expectRouteDoubleTaggedHosts != routeDoubleTaggedHosts) {
-            routeDoubleTaggedHosts = expectRouteDoubleTaggedHosts;
-            log.info("{} routing for double tagged hosts", routeDoubleTaggedHosts ? "Enabling" : "Disabling");
-
-            if (routeDoubleTaggedHosts) {
-                hostHandler.populateAllDoubleTaggedHost();
-            } else {
-                hostHandler.revokeAllDoubleTaggedHost();
-            }
-        }
-
-        String strDefaultInternalVlan = Tools.get(properties, PROP_DEFAULT_INTERNAL_VLAN);
-        int defIntVlan = Integer.parseInt(strDefaultInternalVlan);
-        if (defIntVlan != defaultInternalVlan) {
-            if (canUseVlanId(defIntVlan)) {
-                log.warn("Default internal vlan value changed from {} to {}.. "
-                        + "re-programming filtering rules, but NOT any groups already "
-                        + "created with the former value", defaultInternalVlan, defIntVlan);
-                VlanId oldDefIntVlan = VlanId.vlanId((short) defaultInternalVlan);
-                defaultInternalVlan = defIntVlan;
-                routingRulePopulator
-                .updateSpecialVlanFilteringRules(true, oldDefIntVlan,
-                                                 VlanId.vlanId((short) defIntVlan));
-            } else {
-                log.warn("Cannot change default internal vlan to unusable "
-                        + "value {}", defIntVlan);
-            }
-        }
-
-        String strPwTxpVlan = Tools.get(properties, PROP_PW_TRANSPORT_VLAN);
-        int pwTxpVlan = Integer.parseInt(strPwTxpVlan);
-        if (pwTxpVlan != pwTransportVlan) {
-            if (canUseVlanId(pwTxpVlan)) {
-                log.warn("Pseudowire transport vlan value changed from {} to {}.. "
-                        + "re-programming filtering rules, but NOT any groups already "
-                        + "created with the former value", pwTransportVlan,
-                        pwTxpVlan);
-                VlanId oldPwTxpVlan = VlanId.vlanId((short) pwTransportVlan);
-                pwTransportVlan = pwTxpVlan;
-                routingRulePopulator
-                .updateSpecialVlanFilteringRules(false, oldPwTxpVlan,
-                                                 VlanId.vlanId((short) pwTxpVlan));
-            } else {
-                log.warn("Cannot change pseudowire transport vlan to unusable "
-                        + "value {}", pwTxpVlan);
-            }
-        }
-
-        String strRouteSimplification = Tools.get(properties, PROP_ROUTE_SIMPLIFICATION);
-        boolean expectRouteSimplification = Boolean.parseBoolean(strRouteSimplification);
-        if (expectRouteSimplification != routeSimplification) {
-            routeSimplification = expectRouteSimplification;
-            log.info("{} route simplification", routeSimplification ? "Enabling" : "Disabling");
-        }
-
-    }
-
-    /**
-     * Returns true if given vlan id is not being used in the system currently,
-     * either as one of the default system wide vlans or as one of the
-     * configured interface vlans.
-     *
-     * @param vlanId given vlan id
-     * @return true if vlan is not currently in use
-     */
-    public boolean canUseVlanId(int vlanId) {
-        if (vlanId >= 4095 || vlanId <= 1) {
-            log.error("Vlan id {} value is not in valid range 2 <--> 4094",
-                      vlanId);
-            return false;
-        }
-
-       VlanId vid = VlanId.vlanId((short) vlanId);
-        if (getDefaultInternalVlan().equals(vid) || getPwTransportVlan().equals(vid)) {
-            log.warn("Vlan id {} value is already in use system-wide. "
-                    + "DefaultInternalVlan:{} PwTransportVlan:{} ", vlanId,
-                     getDefaultInternalVlan(), getPwTransportVlan());
-            return false;
-        }
-
-        if (interfaceService.inUse(vid)) {
-            log.warn("Vlan id {} value is already in use on a configured "
-                    + "interface in the system", vlanId);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Returns the VlanId assigned internally by default to unconfigured ports.
-     *
-     * @return the default internal vlan id
-     */
-    public VlanId getDefaultInternalVlan() {
-        return VlanId.vlanId((short) defaultInternalVlan);
-    }
-
-    /**
-     * Returns the Vlan id used to transport pseudowire traffic across the
-     * network.
-     *
-     * @return the pseudowire transport vlan id
-     */
-    public VlanId getPwTransportVlan() {
-        return VlanId.vlanId((short) pwTransportVlan);
-    }
-
-    @Override
-    public List<Tunnel> getTunnels() {
-        return tunnelHandler.getTunnels();
-    }
-
-    @Override
-    public TunnelHandler.Result createTunnel(Tunnel tunnel) {
-        return tunnelHandler.createTunnel(tunnel);
-    }
-
-    @Override
-    public TunnelHandler.Result removeTunnel(Tunnel tunnel) {
-        for (Policy policy: policyHandler.getPolicies()) {
-            if (policy.type() == Policy.Type.TUNNEL_FLOW) {
-                TunnelPolicy tunnelPolicy = (TunnelPolicy) policy;
-                if (tunnelPolicy.tunnelId().equals(tunnel.id())) {
-                    log.warn("Cannot remove the tunnel used by a policy");
-                    return TunnelHandler.Result.TUNNEL_IN_USE;
-                }
-            }
-        }
-        return tunnelHandler.removeTunnel(tunnel);
-    }
-
-    @Override
-    public PolicyHandler.Result removePolicy(Policy policy) {
-        return policyHandler.removePolicy(policy);
-    }
-
-    @Override
-    public PolicyHandler.Result createPolicy(Policy policy) {
-        return policyHandler.createPolicy(policy);
-    }
-
-    @Override
-    public List<Policy> getPolicies() {
-        return policyHandler.getPolicies();
-    }
-
-    @Override
-    public Set<L2TunnelDescription> getL2TunnelDescriptions(boolean pending) {
-        return l2TunnelHandler.getL2Descriptions(pending);
-    }
-
-    @Override
-    public List<L2Tunnel> getL2Tunnels() {
-        return l2TunnelHandler.getL2Tunnels();
-    }
-
-    @Override
-    public List<L2TunnelPolicy> getL2Policies() {
-        return l2TunnelHandler.getL2Policies();
-    }
-
-    @Override
-    @Deprecated
-    public L2TunnelHandler.Result addPseudowiresBulk(List<DefaultL2TunnelDescription> bulkPseudowires) {
-
-        // get both added and pending pseudowires
-        List<L2TunnelDescription> pseudowires = new ArrayList<>();
-        pseudowires.addAll(l2TunnelHandler.getL2Descriptions(false));
-        pseudowires.addAll(l2TunnelHandler.getL2Descriptions(true));
-        pseudowires.addAll(bulkPseudowires);
-
-        Set<L2TunnelDescription> newPseudowires = new HashSet(bulkPseudowires);
-
-        L2TunnelHandler.Result retRes = L2TunnelHandler.Result.SUCCESS;
-        L2TunnelHandler.Result res;
-        for (DefaultL2TunnelDescription pw : bulkPseudowires) {
-            res = addPseudowire(pw);
-            if (res != L2TunnelHandler.Result.SUCCESS) {
-                log.error("Pseudowire with id {} can not be instantiated !", res);
-                retRes = res;
-            }
-        }
-
-        return retRes;
-    }
-
-    @Override
-    public L2TunnelHandler.Result addPseudowire(L2TunnelDescription l2TunnelDescription) {
-        return l2TunnelHandler.deployPseudowire(l2TunnelDescription);
-    }
-
-    @Override
-    public L2TunnelHandler.Result removePseudowire(Integer pwId) {
-        return l2TunnelHandler.tearDownPseudowire(pwId);
-    }
-
-    @Override
-    public void rerouteNetwork() {
-        cfgListener.configureNetwork();
-    }
-
-    @Override
-    public Map<DeviceId, Set<IpPrefix>> getDeviceSubnetMap() {
-        Map<DeviceId, Set<IpPrefix>> deviceSubnetMap = Maps.newHashMap();
-        deviceConfiguration.getRouters().forEach(device ->
-            deviceSubnetMap.put(device, deviceConfiguration.getSubnets(device)));
-        return deviceSubnetMap;
-    }
-
-
-    @Override
-    public ImmutableMap<DeviceId, EcmpShortestPathGraph> getCurrentEcmpSpg() {
-        if (defaultRoutingHandler != null) {
-            return defaultRoutingHandler.getCurrentEmcpSpgMap();
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public ImmutableMap<DestinationSetNextObjectiveStoreKey, NextNeighbors> getDstNextObjStore() {
-        if (dsNextObjStore != null) {
-            return ImmutableMap.copyOf(dsNextObjStore.entrySet());
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public ImmutableMap<VlanNextObjectiveStoreKey, Integer> getVlanNextObjStore() {
-        if (vlanNextObjStore != null) {
-            return ImmutableMap.copyOf(vlanNextObjStore.entrySet());
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public ImmutableMap<MacVlanNextObjectiveStoreKey, Integer> getMacVlanNextObjStore() {
-        if (macVlanNextObjStore != null) {
-            return ImmutableMap.copyOf(macVlanNextObjStore.entrySet());
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public ImmutableMap<PortNextObjectiveStoreKey, Integer> getPortNextObjStore() {
-        if (portNextObjStore != null) {
-            return ImmutableMap.copyOf(portNextObjStore.entrySet());
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public ImmutableMap<String, NextObjective> getPwInitNext() {
-        if (l2TunnelHandler != null) {
-            return l2TunnelHandler.getInitNext();
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public ImmutableMap<String, NextObjective> getPwTermNext() {
-        if (l2TunnelHandler != null) {
-            return l2TunnelHandler.getTermNext();
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public void invalidateNextObj(int nextId) {
-        if (dsNextObjStore != null) {
-            dsNextObjStore.entrySet().forEach(e -> {
-                if (e.getValue().nextId() == nextId) {
-                    dsNextObjStore.remove(e.getKey());
-                }
-            });
-        }
-        if (vlanNextObjStore != null) {
-            vlanNextObjStore.entrySet().forEach(e -> {
-                if (e.getValue() == nextId) {
-                    vlanNextObjStore.remove(e.getKey());
-                }
-            });
-        }
-        if (macVlanNextObjStore != null) {
-            macVlanNextObjStore.entrySet().forEach(e -> {
-                if (e.getValue() == nextId) {
-                    macVlanNextObjStore.remove(e.getKey());
-                }
-            });
-        }
-        if (portNextObjStore != null) {
-            portNextObjStore.entrySet().forEach(e -> {
-                if (e.getValue() == nextId) {
-                    portNextObjStore.remove(e.getKey());
-                }
-            });
-        }
-        if (mcastHandler != null) {
-            mcastHandler.removeNextId(nextId);
-        }
-        if (l2TunnelHandler != null) {
-            l2TunnelHandler.removeNextId(nextId);
-        }
-        if (xconnectService != null) {
-            xconnectService.removeNextId(nextId);
-        }
-    }
-
-    @Override
-    public void verifyGroups(DeviceId id) {
-        DefaultGroupHandler gh = groupHandlerMap.get(id);
-        if (gh != null) {
-            gh.triggerBucketCorrector();
-        }
-    }
-
-    @Override
-    public ImmutableMap<Link, Boolean> getSeenLinks() {
-        return linkHandler.getSeenLinks();
-    }
-
-    @Override
-    public ImmutableMap<DeviceId, Set<PortNumber>> getDownedPortState() {
-        return linkHandler.getDownedPorts();
-    }
-
-    @Override
-    public Map<McastStoreKey, Integer> getMcastNextIds(IpAddress mcastIp) {
-        return mcastHandler.getNextIds(mcastIp);
-    }
-
-    @Override
-    public Map<McastRoleStoreKey, McastRole> getMcastRoles(IpAddress mcastIp, ConnectPoint sourcecp) {
-        return mcastHandler.getMcastRoles(mcastIp, sourcecp);
-    }
-
-    @Override
-    public Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp,
-                                                                    ConnectPoint sourcecp) {
-        return mcastHandler.getMcastTrees(mcastIp, sourcecp);
-    }
-
-    @Override
-    public Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp) {
-        return mcastHandler.getMcastLeaders(mcastIp);
-    }
-
-    @Override
-    public Map<DeviceId, List<McastFilteringObjStoreKey>> getMcastFilters() {
-        return mcastHandler.getMcastFilters();
-    }
-
-    @Override
-    public Map<Set<DeviceId>, NodeId> getShouldProgram() {
-        return defaultRoutingHandler == null ? ImmutableMap.of() :
-                ImmutableMap.copyOf(defaultRoutingHandler.shouldProgram);
-    }
-
-    @Override
-    public Map<DeviceId, Boolean> getShouldProgramCache() {
-        return defaultRoutingHandler == null ? ImmutableMap.of() :
-                ImmutableMap.copyOf(defaultRoutingHandler.shouldProgramCache);
-    }
-
-    @Override
-    public boolean shouldProgram(DeviceId deviceId) {
-        return defaultRoutingHandler.shouldProgram(deviceId);
-    }
-
-    @Override
-    public boolean isRoutingStable() {
-        return defaultRoutingHandler.isRoutingStable();
-    }
-
-    @Override
-    public void initHost(DeviceId deviceId) {
-        hostEventExecutor.execute(() -> hostHandler.init(deviceId));
-    }
-
-    @Override
-    public void initRoute(DeviceId deviceId) {
-        routeEventExecutor.execute(() -> routeHandler.init(deviceId));
-    }
-
-    @Override
-    public ApplicationId appId() {
-        return appId;
-    }
-
-    /**
-     * Returns the device configuration.
-     *
-     * @return device configuration
-     */
-    public DeviceConfiguration deviceConfiguration() {
-        return deviceConfiguration;
-    }
-
-    /**
-     * Per device next objective ID store with (device id + destination set) as key.
-     * Used to keep track on MPLS group information.
-     *
-     * @return next objective ID store
-     */
-    public EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
-                dsNextObjStore() {
-        return dsNextObjStore;
-    }
-
-    /**
-     * Per device next objective ID store with (device id + vlanid) as key.
-     * Used to keep track on L2 flood group information.
-     *
-     * @return vlan next object store
-     */
-    public EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer> vlanNextObjStore() {
-        return vlanNextObjStore;
-    }
-
-    /**
-     * Per device next objective ID store with (device id + MAC address + vlan) as key.
-     * Used to keep track on L3 Unicast group information for indirect hosts.
-     *
-     * @return mac vlan next object store
-     */
-    public EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer> macVlanNextObjStore() {
-        return macVlanNextObjStore;
-    }
-
-    /**
-     * Per device next objective ID store with (device id + port + treatment + meta) as key.
-     * Used to keep track on L2 interface group and L3 unicast group information for direct hosts.
-     *
-     * @return port next object store.
-     */
-    public EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer> portNextObjStore() {
-        return portNextObjStore;
-    }
-
-    /**
-     * Returns the MPLS-ECMP configuration which indicates whether ECMP on
-     * labeled packets should be programmed or not.
-     *
-     * @return MPLS-ECMP value
-     */
-    public boolean getMplsEcmp() {
-        SegmentRoutingAppConfig segmentRoutingAppConfig = cfgService
-                .getConfig(this.appId, SegmentRoutingAppConfig.class);
-        return segmentRoutingAppConfig != null && segmentRoutingAppConfig.mplsEcmp();
-    }
-
-    /**
-     * Returns the tunnel object with the tunnel ID.
-     *
-     * @param tunnelId Tunnel ID
-     * @return Tunnel reference
-     */
-    public Tunnel getTunnel(String tunnelId) {
-        return tunnelHandler.getTunnel(tunnelId);
-    }
-
-    @Override
-    public VlanId getInternalVlanId(ConnectPoint connectPoint) {
-        VlanId untaggedVlanId = interfaceService.getUntaggedVlanId(connectPoint);
-        VlanId nativeVlanId = interfaceService.getNativeVlanId(connectPoint);
-        return untaggedVlanId != null ? untaggedVlanId : nativeVlanId;
-    }
-
-    @Override
-    public Optional<DeviceId> getPairDeviceId(DeviceId deviceId) {
-        SegmentRoutingDeviceConfig deviceConfig =
-                cfgService.getConfig(deviceId, SegmentRoutingDeviceConfig.class);
-        return Optional.ofNullable(deviceConfig).map(SegmentRoutingDeviceConfig::pairDeviceId);
-    }
-
-    @Override
-    public Optional<PortNumber> getPairLocalPort(DeviceId deviceId) {
-        SegmentRoutingDeviceConfig deviceConfig =
-                cfgService.getConfig(deviceId, SegmentRoutingDeviceConfig.class);
-        return Optional.ofNullable(deviceConfig).map(SegmentRoutingDeviceConfig::pairLocalPort);
-    }
-
-    @Override
-    public Set<PortNumber> getInfraPorts(DeviceId deviceId) {
-        return deviceService.getPorts(deviceId).stream()
-                .map(port -> new ConnectPoint(port.element().id(), port.number()))
-                .filter(cp -> interfaceService.getInterfacesByPort(cp).isEmpty())
-                .map(ConnectPoint::port)
-                .collect(Collectors.toSet());
-    }
-
-    @Override
-    public Set<PortNumber> getEdgePorts(DeviceId deviceId) {
-        return deviceService.getPorts(deviceId).stream()
-                .map(port -> new ConnectPoint(port.element().id(), port.number()))
-                .filter(cp -> !interfaceService.getInterfacesByPort(cp).isEmpty() &&
-                        !cp.port().equals(getPairLocalPort(deviceId).orElse(null)))
-                .map(ConnectPoint::port)
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Returns locations of given resolved route.
-     *
-     * @param resolvedRoute resolved route
-     * @return locations of nexthop. Might be empty if next hop is not found
-     */
-    public Set<ConnectPoint> nextHopLocations(ResolvedRoute resolvedRoute) {
-        HostId hostId = HostId.hostId(resolvedRoute.nextHopMac(), resolvedRoute.nextHopVlan());
-        return Optional.ofNullable(hostService.getHost(hostId))
-                .map(Host::locations).orElse(Sets.newHashSet())
-                .stream().map(l -> (ConnectPoint) l).collect(Collectors.toSet());
-    }
-
-    /**
-     * Returns vlan port map of given device.
-     *
-     * @param deviceId device id
-     * @return vlan-port multimap
-     */
-    public Multimap<VlanId, PortNumber> getVlanPortMap(DeviceId deviceId) {
-        HashMultimap<VlanId, PortNumber> vlanPortMap = HashMultimap.create();
-
-        interfaceService.getInterfaces().stream()
-                .filter(intf -> intf.connectPoint().deviceId().equals(deviceId))
-                .forEach(intf -> {
-                    vlanPortMap.put(intf.vlanUntagged(), intf.connectPoint().port());
-                    intf.vlanTagged().forEach(vlanTagged ->
-                        vlanPortMap.put(vlanTagged, intf.connectPoint().port())
-                    );
-                    vlanPortMap.put(intf.vlanNative(), intf.connectPoint().port());
-                });
-        vlanPortMap.removeAll(VlanId.NONE);
-
-        return vlanPortMap;
-    }
-
-    /**
-     * Returns the next objective ID for the given vlan id. It is expected
-     * that the next-objective has been pre-created from configuration.
-     *
-     * @param deviceId Device ID
-     * @param vlanId VLAN ID
-     * @return next objective ID or -1 if it was not found
-     */
-    int getVlanNextObjectiveId(DeviceId deviceId, VlanId vlanId) {
-        if (groupHandlerMap.get(deviceId) != null) {
-            log.trace("getVlanNextObjectiveId query in device {}", deviceId);
-            return groupHandlerMap.get(deviceId).getVlanNextObjectiveId(vlanId);
-        } else {
-            log.warn("getVlanNextObjectiveId query - groupHandler for "
-                    + "device {} not found", deviceId);
-            return -1;
-        }
-    }
-
-    /**
-     * Returns the next objective ID for the given portNumber, given the treatment.
-     * There could be multiple different treatments to the same outport, which
-     * would result in different objectives. If the next object does not exist,
-     * and should be created, a new one is created and its id is returned.
-     *
-     * @param deviceId Device ID
-     * @param portNum port number on device for which NextObjective is queried
-     * @param treatment the actions to apply on the packets (should include outport)
-     * @param meta metadata passed into the creation of a Next Objective if necessary
-     * @param createIfMissing true if a next object should be created if not found
-     * @return next objective ID or -1 if an error occurred during retrieval or creation
-     */
-    public int getPortNextObjectiveId(DeviceId deviceId, PortNumber portNum,
-                                      TrafficTreatment treatment,
-                                      TrafficSelector meta,
-                                      boolean createIfMissing) {
-        DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
-        if (ghdlr != null) {
-            return ghdlr.getPortNextObjectiveId(portNum, treatment, meta, createIfMissing);
-        } else {
-            log.warn("getPortNextObjectiveId query - groupHandler for device {}"
-                    + " not found", deviceId);
-            return -1;
-        }
-    }
-
-    /**
-     * Returns the next Objective ID for the given mac and vlan, given the treatment.
-     * There could be multiple different treatments to the same outport, which
-     * would result in different objectives. If the next object does not exist,
-     * and should be created, a new one is created and its id is returned.
-     *
-     * @param deviceId Device ID
-     * @param macAddr mac of host for which Next ID is required.
-     * @param vlanId vlan of host for which Next ID is required.
-     * @param port port with which to create the Next Obj.
-     * @param createIfMissing true if a next object should be created if not found
-     * @return next objective ID or -1 if an error occurred during retrieval or creation
-     */
-    public int getMacVlanNextObjectiveId(DeviceId deviceId, MacAddress macAddr, VlanId vlanId,
-                                      PortNumber port, boolean createIfMissing) {
-        DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
-        if (ghdlr != null) {
-            return ghdlr.getMacVlanNextObjectiveId(macAddr, vlanId, port, createIfMissing);
-        } else {
-            log.warn("getMacVlanNextObjectiveId query - groupHandler for device {}"
-                    + " not found", deviceId);
-            return -1;
-        }
-    }
-
-    /**
-     * Updates the next objective for the given nextId .
-     *
-     * @param deviceId Device ID
-     * @param hostMac mac of host for which Next obj is to be updated.
-     * @param hostVlanId vlan of host for which Next obj is to be updated.
-     * @param port port with which to update the Next Obj.
-     * @param nextId of Next Obj which needs to be updated.
-     */
-    public void updateMacVlanTreatment(DeviceId deviceId, MacAddress hostMac,
-                             VlanId hostVlanId, PortNumber port, int nextId) {
-        // Check if we are the king of this device
-        // just one instance should perform this update
-        if (!defaultRoutingHandler.shouldProgram(deviceId)) {
-            log.debug("This instance is not handling the routing towards the "
-                              + "device {}", deviceId);
-            return;
-        }
-        // Get the handler and perform the update
-        DefaultGroupHandler ghdlr = groupHandlerMap.get(deviceId);
-        if (ghdlr != null) {
-            ghdlr.updateL3UcastGroupBucket(hostMac, hostVlanId, port, nextId);
-        } else {
-            log.warn("updateL3UcastGroupBucket query - groupHandler for device {}"
-                    + " not found", deviceId);
-        }
-    }
-
-
-    /**
-     * Returns the group handler object for the specified device id.
-     *
-     * @param devId the device identifier
-     * @return the groupHandler object for the device id, or null if not found
-     */
-    DefaultGroupHandler getGroupHandler(DeviceId devId) {
-        return groupHandlerMap.get(devId);
-    }
-
-    /**
-     * Returns the default routing handler object.
-     *
-     * @return the default routing handler object
-     */
-    public DefaultRoutingHandler getRoutingHandler() {
-        return defaultRoutingHandler;
-    }
-
-    private class InternalPacketProcessor implements PacketProcessor {
-        @Override
-        public void process(PacketContext context) {
-            packetExecutor.execute(() -> processPacketInternal(context));
-        }
-
-        private void processPacketInternal(PacketContext context) {
-            if (context.isHandled()) {
-                return;
-            }
-
-            InboundPacket pkt = context.inPacket();
-            Ethernet ethernet = pkt.parsed();
-
-            if (ethernet == null) {
-                return;
-            }
-
-            log.trace("Rcvd pktin from {}: {}", context.inPacket().receivedFrom(),
-                      ethernet);
-            if (ethernet.getEtherType() == TYPE_ARP) {
-                log.warn("Received unexpected ARP packet on {}",
-                         context.inPacket().receivedFrom());
-                log.trace("{}", ethernet);
-                return;
-            } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
-                IPv4 ipv4Packet = (IPv4) ethernet.getPayload();
-                //ipHandler.addToPacketBuffer(ipv4Packet);
-                if (ipv4Packet.getProtocol() == IPv4.PROTOCOL_ICMP) {
-                    icmpHandler.processIcmp(ethernet, pkt.receivedFrom());
-                } else {
-                    // NOTE: We don't support IP learning at this moment so this
-                    //       is not necessary. Also it causes duplication of DHCP packets.
-                    // ipHandler.processPacketIn(ipv4Packet, pkt.receivedFrom());
-                }
-            } else if (ethernet.getEtherType() == Ethernet.TYPE_IPV6) {
-                IPv6 ipv6Packet = (IPv6) ethernet.getPayload();
-                //ipHandler.addToPacketBuffer(ipv6Packet);
-                // We deal with the packet only if the packet is a ICMP6 ECHO/REPLY
-                if (ipv6Packet.getNextHeader() == IPv6.PROTOCOL_ICMP6) {
-                    ICMP6 icmp6Packet = (ICMP6) ipv6Packet.getPayload();
-                    if (icmp6Packet.getIcmpType() == ICMP6.ECHO_REQUEST ||
-                            icmp6Packet.getIcmpType() == ICMP6.ECHO_REPLY) {
-                        icmpHandler.processIcmpv6(ethernet, pkt.receivedFrom());
-                    } else {
-                        log.trace("Received ICMPv6 0x{} - not handled",
-                                Integer.toHexString(icmp6Packet.getIcmpType() & 0xff));
-                    }
-                } else {
-                   // NOTE: We don't support IP learning at this moment so this
-                   //       is not necessary. Also it causes duplication of DHCPv6 packets.
-                   // ipHandler.processPacketIn(ipv6Packet, pkt.receivedFrom());
-                }
-            }
-        }
-    }
-
-    private class InternalEventHandler implements Runnable {
-        private Event event;
-
-        InternalEventHandler(Event event) {
-            this.event = event;
-        }
-
-        @Override
-        public void run() {
-            try {
-                // TODO We should also change SR routing and PW to listen to TopologyEvents
-                if (event.type() == LinkEvent.Type.LINK_ADDED ||
-                        event.type() == LinkEvent.Type.LINK_UPDATED) {
-                    linkHandler.processLinkAdded((Link) event.subject());
-                } else if (event.type() == LinkEvent.Type.LINK_REMOVED) {
-                    linkHandler.processLinkRemoved((Link) event.subject());
-                } else if (event.type() == DeviceEvent.Type.DEVICE_ADDED ||
-                        event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
-                        event.type() == DeviceEvent.Type.DEVICE_UPDATED) {
-                    DeviceId deviceId = ((Device) event.subject()).id();
-                    if (deviceService.isAvailable(deviceId)) {
-                        log.info("** DEVICE UP Processing device event {} "
-                                + "for available device {}",
-                                 event.type(), ((Device) event.subject()).id());
-                        processDeviceAdded((Device) event.subject());
-                    } else {
-                        if (event.type() == DeviceEvent.Type.DEVICE_ADDED) {
-                            // Note: For p4 devices, the device will be added but unavailable at the beginning.
-                            //       The device will later on being marked as available once the pipeline is pushed
-                            //       to the device.
-                            log.info("** DEVICE ADDED but unavailable. Ignore");
-                            return;
-                        }
-                        log.info(" ** DEVICE DOWN Processing device event {}"
-                                + " for unavailable device {}",
-                                 event.type(), ((Device) event.subject()).id());
-                        processDeviceRemoved((Device) event.subject());
-                    }
-                } else if (event.type() == DeviceEvent.Type.PORT_ADDED) {
-                    // typically these calls come when device is added first time
-                    // so port filtering rules are handled at the device_added event.
-                    // port added calls represent all ports on the device,
-                    // enabled or not.
-                    log.trace("** PORT ADDED {}/{} -> {}",
-                              ((DeviceEvent) event).subject().id(),
-                              ((DeviceEvent) event).port().number(),
-                              event.type());
-                } else if (event.type() == DeviceEvent.Type.PORT_UPDATED) {
-                    // these calls happen for every subsequent event
-                    // ports enabled, disabled, switch goes away, comes back
-                    log.info("** PORT UPDATED {}/{} -> {}",
-                             event.subject(),
-                             ((DeviceEvent) event).port(),
-                             event.type());
-                    processPortUpdatedInternal(((Device) event.subject()),
-                                       ((DeviceEvent) event).port());
-                    mcastHandler.processPortUpdate(((Device) event.subject()),
-                                                   ((DeviceEvent) event).port());
-                } else if (event.type() == TopologyEvent.Type.TOPOLOGY_CHANGED) {
-                    // Process topology event, needed for all modules relying on
-                    // topology service for path computation
-                    TopologyEvent topologyEvent = (TopologyEvent) event;
-                    log.info("Processing topology event {}, topology age {}, reasons {}",
-                             event.type(), topologyEvent.subject().time(),
-                             topologyEvent.reasons().size());
-                    topologyHandler.processTopologyChange(topologyEvent.reasons());
-                } else if (event.type() == HostEvent.Type.HOST_ADDED) {
-                    hostHandler.processHostAddedEvent((HostEvent) event);
-                } else if (event.type() == HostEvent.Type.HOST_MOVED) {
-                    hostHandler.processHostMovedEvent((HostEvent) event);
-                    routeHandler.processHostMovedEvent((HostEvent) event);
-                } else if (event.type() == HostEvent.Type.HOST_AUX_MOVED) {
-                    hostHandler.processHostMovedEvent((HostEvent) event);
-                    // TODO RouteHandler also needs to process this event in order to
-                    //      support nexthops that has auxLocations
-                } else if (event.type() == HostEvent.Type.HOST_REMOVED) {
-                    hostHandler.processHostRemovedEvent((HostEvent) event);
-                } else if (event.type() == HostEvent.Type.HOST_UPDATED) {
-                    hostHandler.processHostUpdatedEvent((HostEvent) event);
-                } else if (event.type() == RouteEvent.Type.ROUTE_ADDED) {
-                    routeHandler.processRouteAdded((RouteEvent) event);
-                } else if (event.type() == RouteEvent.Type.ROUTE_UPDATED) {
-                    routeHandler.processRouteUpdated((RouteEvent) event);
-                } else if (event.type() == RouteEvent.Type.ROUTE_REMOVED) {
-                    routeHandler.processRouteRemoved((RouteEvent) event);
-                } else if (event.type() == RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED) {
-                    routeHandler.processAlternativeRoutesChanged((RouteEvent) event);
-                } else if (event.type() == McastEvent.Type.SOURCES_ADDED ||
-                        event.type() == McastEvent.Type.SOURCES_REMOVED ||
-                        event.type() == McastEvent.Type.SINKS_ADDED ||
-                        event.type() == McastEvent.Type.SINKS_REMOVED ||
-                        event.type() == McastEvent.Type.ROUTE_ADDED ||
-                        event.type() == McastEvent.Type.ROUTE_REMOVED) {
-                    mcastHandler.processMcastEvent((McastEvent) event);
-                } else if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
-                    NetworkConfigEvent netcfgEvent = (NetworkConfigEvent) event;
-                    Class configClass = netcfgEvent.configClass();
-                    if (configClass.equals(SegmentRoutingAppConfig.class)) {
-                        appCfgHandler.processAppConfigAdded(netcfgEvent);
-                        log.info("App config event .. configuring network");
-                        cfgListener.configureNetwork();
-                    } else if (configClass.equals(SegmentRoutingDeviceConfig.class)) {
-                        log.info("Segment Routing Device Config added for {}", event.subject());
-                        cfgListener.configureNetwork();
-                    } else if (configClass.equals(InterfaceConfig.class)) {
-                        log.info("Interface Config added for {}", event.subject());
-                        cfgListener.configureNetwork();
-                    } else {
-                        log.error("Unhandled config class: {}", configClass);
-                    }
-                } else if (event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) {
-                    NetworkConfigEvent netcfgEvent = (NetworkConfigEvent) event;
-                    Class configClass = netcfgEvent.configClass();
-                    if (configClass.equals(SegmentRoutingAppConfig.class)) {
-                        appCfgHandler.processAppConfigUpdated(netcfgEvent);
-                        log.info("App config event .. configuring network");
-                        cfgListener.configureNetwork();
-                    } else if (configClass.equals(SegmentRoutingDeviceConfig.class)) {
-                        log.info("Segment Routing Device Config updated for {}", event.subject());
-                        createOrUpdateDeviceConfiguration();
-                    } else if (configClass.equals(InterfaceConfig.class)) {
-                        log.info("Interface Config updated for {}", event.subject());
-                        createOrUpdateDeviceConfiguration();
-                        updateInterface((InterfaceConfig) netcfgEvent.config().get(),
-                                (InterfaceConfig) netcfgEvent.prevConfig().get());
-                    } else {
-                        log.error("Unhandled config class: {}", configClass);
-                    }
-                } else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
-                    NetworkConfigEvent netcfgEvent = (NetworkConfigEvent) event;
-                    Class configClass = netcfgEvent.configClass();
-                    if (configClass.equals(SegmentRoutingAppConfig.class)) {
-                        appCfgHandler.processAppConfigRemoved(netcfgEvent);
-                        log.info("App config event .. configuring network");
-                        cfgListener.configureNetwork();
-                    } else if (configClass.equals(SegmentRoutingDeviceConfig.class)) {
-                        // TODO Handle sr device config removal
-                        log.info("SegmentRoutingDeviceConfig removal is not handled in current implementation");
-                    } else if (configClass.equals(InterfaceConfig.class)) {
-                        // TODO Handle interface removal
-                        log.info("InterfaceConfig removal is not handled in current implementation");
-                    } else {
-                        log.error("Unhandled config class: {}", configClass);
-                    }
-                } else if (event.type() == MastershipEvent.Type.MASTER_CHANGED) {
-                    MastershipEvent me = (MastershipEvent) event;
-                    DeviceId deviceId = me.subject();
-                    Optional<DeviceId> pairDeviceId = getPairDeviceId(deviceId);
-                    log.info(" ** MASTERSHIP CHANGED Invalidating shouldProgram cache"
-                            + " for {}/pair={} due to change", deviceId, pairDeviceId);
-                    defaultRoutingHandler.invalidateShouldProgramCache(deviceId);
-                    pairDeviceId.ifPresent(defaultRoutingHandler::invalidateShouldProgramCache);
-                    defaultRoutingHandler.checkFullRerouteForMasterChange(deviceId, me);
-                } else {
-                    log.warn("Unhandled event type: {}", event.type());
-                }
-            } catch (Exception e) {
-                log.error("SegmentRouting event handler thread thrown an exception: {}",
-                          e.getMessage(), e);
-            }
-        }
-    }
-
-    void processDeviceAdded(Device device) {
-        log.info("** DEVICE ADDED with ID {}", device.id());
-
-        // NOTE: Punt ARP/NDP even when the device is not configured.
-        //       Host learning without network config is required for CORD config generator.
-        routingRulePopulator.populateIpPunts(device.id());
-        routingRulePopulator.populateArpNdpPunts(device.id());
-
-        if (deviceConfiguration == null || !deviceConfiguration.isConfigured(device.id())) {
-            log.warn("Device configuration unavailable. Device {} will be "
-                    + "processed after configuration.", device.id());
-            return;
-        }
-        processDeviceAddedInternal(device.id());
-    }
-
-    private void processDeviceAddedInternal(DeviceId deviceId) {
-        // Irrespective of whether the local is a MASTER or not for this device,
-        // we need to create a SR-group-handler instance. This is because in a
-        // multi-instance setup, any instance can initiate forwarding/next-objectives
-        // for any switch (even if this instance is a SLAVE or not even connected
-        // to the switch). To handle this, a default-group-handler instance is necessary
-        // per switch.
-        log.debug("Current groupHandlerMap devs: {}", groupHandlerMap.keySet());
-        if (groupHandlerMap.get(deviceId) == null) {
-            DefaultGroupHandler groupHandler;
-            try {
-                groupHandler = DefaultGroupHandler.
-                        createGroupHandler(deviceId,
-                                appId,
-                                deviceConfiguration,
-                                linkService,
-                                flowObjectiveService,
-                                this);
-            } catch (DeviceConfigNotFoundException e) {
-                log.warn(e.getMessage() + " Aborting processDeviceAdded.");
-                return;
-            }
-            log.debug("updating groupHandlerMap with new grpHdlr for device: {}",
-                    deviceId);
-            groupHandlerMap.put(deviceId, groupHandler);
-        }
-
-        if (mastershipService.isLocalMaster(deviceId)) {
-            defaultRoutingHandler.populatePortAddressingRules(deviceId);
-            defaultRoutingHandler.purgeSeenBeforeRoutes(deviceId);
-            DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
-            groupHandler.createGroupsFromVlanConfig();
-            routingRulePopulator.populateSubnetBroadcastRule(deviceId);
-            phasedRecoveryService.init(deviceId);
-        }
-
-        appCfgHandler.init(deviceId);
-    }
-
-    private void processDeviceRemoved(Device device) {
-        dsNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(device.id()))
-                .forEach(entry -> dsNextObjStore.remove(entry.getKey()));
-        vlanNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(device.id()))
-                .forEach(entry -> vlanNextObjStore.remove(entry.getKey()));
-        macVlanNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(device.id()))
-                .forEach(entry -> macVlanNextObjStore.remove(entry.getKey()));
-        portNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(device.id()))
-                .forEach(entry -> portNextObjStore.remove(entry.getKey()));
-        linkHandler.processDeviceRemoved(device);
-
-        DefaultGroupHandler gh = groupHandlerMap.remove(device.id());
-        if (gh != null) {
-            gh.shutdown();
-        }
-        // Note that a switch going down is associated with all of its links
-        // going down as well, but it is treated as a single switch down event
-        // while the link-downs are ignored. We cannot rely on the ordering of
-        // events - i.e we cannot expect all link-downs to come before the
-        // switch down - so we purge all seen-links for the switch before
-        // handling route-path changes for the switch-down
-        defaultRoutingHandler
-            .populateRoutingRulesForLinkStatusChange(null, null, device.id(), true);
-        defaultRoutingHandler.purgeEcmpGraph(device.id());
-
-        // Cleanup all internal groupHandler stores for this device. Should be
-        // done after all rerouting or rehashing has been completed
-        groupHandlerMap.entrySet()
-            .forEach(entry -> entry.getValue().cleanUpForNeighborDown(device.id()));
-
-        phasedRecoveryService.reset(device.id());
-    }
-
-    /**
-     * Purge the destinationSet nextObjective store of entries with this device
-     * as key. Erases app-level knowledge of hashed groups in this device.
-     *
-     * @param devId the device identifier
-     */
-    void purgeHashedNextObjectiveStore(DeviceId devId) {
-        log.debug("Purging hashed next-obj store for dev:{}", devId);
-        dsNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(devId))
-                .forEach(entry -> dsNextObjStore.remove(entry.getKey()));
-    }
-
-    private void processPortUpdatedInternal(Device device, Port port) {
-        if (deviceConfiguration == null || !deviceConfiguration.isConfigured(device.id())) {
-            log.warn("Device configuration uploading. Not handling port event for"
-                    + "dev: {} port: {}", device.id(), port.number());
-            return;
-        }
-
-        if (interfaceService.isConfigured(new ConnectPoint(device.id(), port.number()))) {
-            lastEdgePortEvent = Instant.now();
-        }
-
-        if (!mastershipService.isLocalMaster(device.id()))  {
-            log.debug("Not master for dev:{} .. not handling port updated event"
-                    + "for port {}", device.id(), port.number());
-            return;
-        }
-        processPortUpdated(device.id(), port);
-    }
-
-    /**
-     * Adds or remove filtering rules for the given switchport. If switchport is
-     * an edge facing port, additionally handles host probing and broadcast
-     * rules. Must be called by local master of device.
-     *
-     * @param deviceId the device identifier
-     * @param port the port to update
-     */
-    void processPortUpdated(DeviceId deviceId, Port port) {
-        // first we handle filtering rules associated with the port
-        if (port.isEnabled()) {
-            log.info("Switchport {}/{} enabled..programming filters",
-                     deviceId, port.number());
-            routingRulePopulator.processSinglePortFilters(deviceId, port.number(), true);
-        } else {
-            log.info("Switchport {}/{} disabled..removing filters",
-                     deviceId, port.number());
-            routingRulePopulator.processSinglePortFilters(deviceId, port.number(), false);
-        }
-
-        // portUpdated calls are for ports that have gone down or up. For switch
-        // to switch ports, link-events should take care of any re-routing or
-        // group editing necessary for port up/down. Here we only process edge ports
-        // that are already configured.
-        ConnectPoint cp = new ConnectPoint(deviceId, port.number());
-        VlanId untaggedVlan = interfaceService.getUntaggedVlanId(cp);
-        VlanId nativeVlan = interfaceService.getNativeVlanId(cp);
-        Set<VlanId> taggedVlans = interfaceService.getTaggedVlanId(cp);
-
-        if (untaggedVlan == null && nativeVlan == null && taggedVlans.isEmpty()) {
-            log.debug("Not handling port updated event for non-edge port (unconfigured) "
-                    + "dev/port: {}/{}", deviceId, port.number());
-            return;
-        }
-        if (untaggedVlan != null) {
-            processEdgePort(deviceId, port, untaggedVlan, true);
-        }
-        if (nativeVlan != null) {
-            processEdgePort(deviceId, port, nativeVlan, true);
-        }
-        if (!taggedVlans.isEmpty()) {
-            taggedVlans.forEach(tag -> processEdgePort(deviceId, port, tag, false));
-        }
-    }
-
-    private void processEdgePort(DeviceId deviceId, Port port, VlanId vlanId,
-                                 boolean popVlan) {
-        boolean portUp = port.isEnabled();
-        if (portUp) {
-            log.info("Device:EdgePort {}:{} is enabled in vlan: {}", deviceId,
-                     port.number(), vlanId);
-            hostEventExecutor.execute(() -> hostHandler.processPortUp(new ConnectPoint(deviceId, port.number())));
-        } else {
-            log.info("Device:EdgePort {}:{} is disabled in vlan: {}", deviceId,
-                     port.number(), vlanId);
-        }
-
-        DefaultGroupHandler groupHandler = groupHandlerMap.get(deviceId);
-        if (groupHandler != null) {
-            groupHandler.processEdgePort(port.number(), vlanId, popVlan, portUp);
-        } else {
-            log.warn("Group handler not found for dev:{}. Not handling edge port"
-                    + " {} event for port:{}", deviceId,
-                    (portUp) ? "UP" : "DOWN", port.number());
-        }
-    }
-
-    private void createOrUpdateDeviceConfiguration() {
-        if (deviceConfiguration == null) {
-            log.info("Creating new DeviceConfiguration");
-            deviceConfiguration = new DeviceConfiguration(this);
-        } else {
-            log.info("Updating DeviceConfiguration");
-            deviceConfiguration.updateConfig();
-        }
-    }
-
-    private void createOrUpdateDefaultRoutingHandler() {
-        if (defaultRoutingHandler == null) {
-            log.info("Creating new DefaultRoutingHandler");
-            defaultRoutingHandler = new DefaultRoutingHandler(this);
-        } else {
-            log.info("Updating DefaultRoutingHandler");
-            defaultRoutingHandler.update(this);
-        }
-    }
-
-    /**
-     * Registers the given connect point with the NRS, this is necessary
-     * to receive the NDP and ARP packets from the NRS.
-     *
-     * @param portToRegister connect point to register
-     */
-    public void registerConnectPoint(ConnectPoint portToRegister) {
-        neighbourResolutionService.registerNeighbourHandler(
-                portToRegister,
-                neighbourHandler,
-                appId
-        );
-    }
-
-    private class InternalConfigListener implements NetworkConfigListener {
-        private static final long PROGRAM_DELAY = 2;
-        SegmentRoutingManager srManager;
-
-        /**
-         * Constructs the internal network config listener.
-         *
-         * @param srManager segment routing manager
-         */
-        InternalConfigListener(SegmentRoutingManager srManager) {
-            this.srManager = srManager;
-        }
-
-        /**
-         * Reads network config and initializes related data structure accordingly.
-         */
-        void configureNetwork() {
-            log.info("Configuring network ...");
-
-            // Setting handling of network configuration events completable future
-            // The completable future is needed because of the async behaviour of the configureNetwork,
-            // listener registration and event arrival
-            // Enables us to buffer the events and execute them when the configure network is done.
-            synchronized (networkConfigCompletionLock) {
-                networkConfigCompletion = new CompletableFuture<>();
-
-                // add a small delay to absorb multiple network config added notifications
-                if (!programmingScheduled.get()) {
-                    log.info("Buffering config calls for {} secs", PROGRAM_DELAY);
-                    programmingScheduled.set(true);
-                    mainEventExecutor.schedule(new ConfigChange(), PROGRAM_DELAY, TimeUnit.SECONDS);
-                }
-
-                createOrUpdateDeviceConfiguration();
-
-                arpHandler = new ArpHandler(srManager);
-                icmpHandler = new IcmpHandler(srManager);
-                ipHandler = new IpHandler(srManager);
-                routingRulePopulator = new RoutingRulePopulator(srManager);
-                createOrUpdateDefaultRoutingHandler();
-
-                tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
-                    groupHandlerMap, tunnelStore);
-                policyHandler = new PolicyHandler(appId, deviceConfiguration,
-                    flowObjectiveService,
-                    tunnelHandler, policyStore);
-                networkConfigCompletion.complete(true);
-            }
-
-            mcastHandler.init();
-
-        }
-
-        @Override
-        public void event(NetworkConfigEvent event) {
-            if (mainEventExecutor == null) {
-                return;
-            }
-            checkState(appCfgHandler != null, "NetworkConfigEventHandler is not initialized");
-            switch (event.type()) {
-                case CONFIG_ADDED:
-                case CONFIG_UPDATED:
-                case CONFIG_REMOVED:
-                    if (networkConfigCompletion == null || networkConfigCompletion.isDone()) {
-                        log.debug("Process netcfg {}", event);
-                        mainEventExecutor.execute(new InternalEventHandler(event));
-                    } else {
-                        log.debug("Queue netcfg {}", event);
-                        queuedEvents.add(event);
-                    }
-                    break;
-                default:
-                    break;
-            }
-        }
-
-        @Override
-        public boolean isRelevant(NetworkConfigEvent event) {
-            if (event.type() == CONFIG_REGISTERED ||
-                    event.type() == CONFIG_UNREGISTERED) {
-                log.debug("Ignore event {} due to type mismatch", event);
-                return false;
-            }
-
-            if (!event.configClass().equals(SegmentRoutingDeviceConfig.class) &&
-                    !event.configClass().equals(SegmentRoutingAppConfig.class) &&
-                    !event.configClass().equals(InterfaceConfig.class)) {
-                log.debug("Ignore event {} due to class mismatch", event);
-                return false;
-            }
-
-            return true;
-        }
-
-        private final class ConfigChange implements Runnable {
-            @Override
-            public void run() {
-                programmingScheduled.set(false);
-                log.info("Reacting to config changes after buffer delay");
-                for (Device device : deviceService.getDevices()) {
-                    processDeviceAdded(device);
-                }
-                defaultRoutingHandler.startPopulationProcess();
-            }
-        }
-    }
-
-    private class InternalLinkListener implements LinkListener {
-        @Override
-        public void event(LinkEvent event) {
-            if (mainEventExecutor == null) {
-                return;
-            }
-            if (event.type() == LinkEvent.Type.LINK_ADDED ||
-                    event.type() == LinkEvent.Type.LINK_UPDATED ||
-                    event.type() == LinkEvent.Type.LINK_REMOVED) {
-                log.trace("Schedule Link event {}", event);
-                if (networkConfigCompletion == null || networkConfigCompletion.isDone()) {
-                    mainEventExecutor.execute(new InternalEventHandler(event));
-                } else {
-                    queuedEvents.add(event);
-                }
-            }
-        }
-    }
-
-    private class InternalDeviceListener implements DeviceListener {
-        @Override
-        public void event(DeviceEvent event) {
-            if (mainEventExecutor == null) {
-                return;
-            }
-            switch (event.type()) {
-                case DEVICE_ADDED:
-                case PORT_UPDATED:
-                case PORT_ADDED:
-                case DEVICE_UPDATED:
-                case DEVICE_AVAILABILITY_CHANGED:
-                    log.trace("Schedule Device event {}", event);
-                    if (networkConfigCompletion == null || networkConfigCompletion.isDone()) {
-                        mainEventExecutor.execute(new InternalEventHandler(event));
-                    } else {
-                        queuedEvents.add(event);
-                    }
-                    break;
-                default:
-            }
-        }
-    }
-
-    private class InternalTopologyListener implements TopologyListener {
-        @Override
-        public void event(TopologyEvent event) {
-            if (mainEventExecutor == null) {
-                return;
-            }
-            switch (event.type()) {
-                case TOPOLOGY_CHANGED:
-                    log.trace("Schedule Topology event {}", event);
-                    if (networkConfigCompletion == null || networkConfigCompletion.isDone()) {
-                        mainEventExecutor.execute(new InternalEventHandler(event));
-                    } else {
-                        queuedEvents.add(event);
-                    }
-                    break;
-                default:
-            }
-        }
-    }
-
-    private class InternalHostListener implements HostListener {
-        @Override
-        public void event(HostEvent event) {
-            if (hostEventExecutor == null) {
-                return;
-            }
-            switch (event.type()) {
-                case HOST_ADDED:
-                case HOST_MOVED:
-                case HOST_REMOVED:
-                case HOST_UPDATED:
-                    log.trace("Schedule Host event {}", event);
-                    hostEventExecutor.execute(new InternalEventHandler(event));
-                    break;
-                default:
-                    log.warn("Unsupported host event type: {}", event.type());
-                    break;
-            }
-        }
-    }
-
-    private class InternalMcastListener implements McastListener {
-        @Override
-        public void event(McastEvent event) {
-            if (mcastEventExecutor == null) {
-                return;
-            }
-            switch (event.type()) {
-                case SOURCES_ADDED:
-                case SOURCES_REMOVED:
-                case SINKS_ADDED:
-                case SINKS_REMOVED:
-                case ROUTE_REMOVED:
-                case ROUTE_ADDED:
-                    log.trace("Schedule Mcast event {}", event);
-                    mcastEventExecutor.execute(new InternalEventHandler(event));
-                    break;
-                default:
-                    log.warn("Unsupported mcast event type: {}", event.type());
-                    break;
-            }
-        }
-    }
-
-    private class InternalRouteEventListener implements RouteListener {
-        @Override
-        public void event(RouteEvent event) {
-            if (routeEventExecutor == null) {
-                return;
-            }
-            switch (event.type()) {
-                case ROUTE_ADDED:
-                case ROUTE_UPDATED:
-                case ROUTE_REMOVED:
-                case ALTERNATIVE_ROUTES_CHANGED:
-                    log.trace("Schedule Route event {}", event);
-                    routeEventExecutor.execute(new InternalEventHandler(event));
-                    break;
-                default:
-                    log.warn("Unsupported route event type: {}", event.type());
-                    break;
-            }
-        }
-    }
-
-    private class InternalMastershipListener implements MastershipListener {
-        @Override
-        public void event(MastershipEvent event) {
-            if (mainEventExecutor == null) {
-                return;
-            }
-            switch (event.type()) {
-            case MASTER_CHANGED:
-                log.debug("Mastership event: {}/{}", event.subject(),
-                          event.roleInfo());
-                mainEventExecutor.execute(new InternalEventHandler(event));
-                break;
-            case BACKUPS_CHANGED:
-            case SUSPENDED:
-            default:
-                log.debug("Mastership event type {} not handled", event.type());
-                break;
-            }
-        }
-    }
-
-    class InternalClusterListener implements ClusterEventListener {
-        private Instant lastClusterEvent = Instant.EPOCH;
-
-        long timeSinceLastClusterEvent() {
-            return Instant.now().toEpochMilli() - lastClusterEvent.toEpochMilli();
-        }
-
-        @Override
-        public void event(ClusterEvent event) {
-            switch (event.type()) {
-            case INSTANCE_ACTIVATED:
-            case INSTANCE_ADDED:
-            case INSTANCE_READY:
-                log.debug("Cluster event {} ignored", event.type());
-                break;
-            case INSTANCE_DEACTIVATED:
-            case INSTANCE_REMOVED:
-                log.info("** Cluster event {}", event.type());
-                lastClusterEvent = Instant.now();
-                break;
-            default:
-                break;
-            }
-
-        }
-
-    }
-
-    private void updateInterface(InterfaceConfig conf, InterfaceConfig prevConf) {
-        try {
-            Set<Interface> intfs = conf.getInterfaces();
-            Set<Interface> prevIntfs = prevConf.getInterfaces();
-
-            // Now we only handle one interface config at each port.
-            if (intfs.size() != 1 || prevIntfs.size() != 1) {
-                log.warn("Interface update aborted - one at a time is allowed, " +
-                                 "but {} / {}(prev) received.", intfs.size(), prevIntfs.size());
-                return;
-            }
-
-            //The system is in an incoherent state, abort
-            if (defaultRoutingHandler == null) {
-                log.warn("Interface update aborted, defaultRoutingHandler is null");
-                return;
-            }
-
-            Interface intf = intfs.stream().findFirst().get();
-            Interface prevIntf = prevIntfs.stream().findFirst().get();
-
-            DeviceId deviceId = intf.connectPoint().deviceId();
-            PortNumber portNum = intf.connectPoint().port();
-
-            removeSubnetConfig(prevIntf.connectPoint(),
-                               Sets.difference(new HashSet<>(prevIntf.ipAddressesList()),
-                                               new HashSet<>(intf.ipAddressesList())));
-
-            if (!prevIntf.vlanNative().equals(VlanId.NONE)
-                    && !prevIntf.vlanNative().equals(intf.vlanUntagged())
-                    && !prevIntf.vlanNative().equals(intf.vlanNative())) {
-                if (intf.vlanTagged().contains(prevIntf.vlanNative())) {
-                    // Update filtering objective and L2IG group bucket
-                    updatePortVlanTreatment(deviceId, portNum, prevIntf.vlanNative(), false);
-                } else {
-                    // RemoveVlanNative
-                    updateVlanConfigInternal(deviceId, portNum, prevIntf.vlanNative(), true, false);
-                }
-            }
-
-            if (!prevIntf.vlanUntagged().equals(VlanId.NONE)
-                    && !prevIntf.vlanUntagged().equals(intf.vlanUntagged())
-                    && !prevIntf.vlanUntagged().equals(intf.vlanNative())) {
-                if (intf.vlanTagged().contains(prevIntf.vlanUntagged())) {
-                    // Update filtering objective and L2IG group bucket
-                    updatePortVlanTreatment(deviceId, portNum, prevIntf.vlanUntagged(), false);
-                } else {
-                    // RemoveVlanUntagged
-                    updateVlanConfigInternal(deviceId, portNum, prevIntf.vlanUntagged(), true, false);
-                }
-            }
-
-            if (!prevIntf.vlanTagged().isEmpty() && !intf.vlanTagged().equals(prevIntf.vlanTagged())) {
-                // RemoveVlanTagged
-                Sets.difference(prevIntf.vlanTagged(), intf.vlanTagged()).stream()
-                        .filter(i -> !intf.vlanUntagged().equals(i))
-                        .filter(i -> !intf.vlanNative().equals(i))
-                        .forEach(vlanId -> updateVlanConfigInternal(
-                                deviceId, portNum, vlanId, false, false));
-            }
-
-            if (!intf.vlanNative().equals(VlanId.NONE)
-                    && !prevIntf.vlanNative().equals(intf.vlanNative())
-                    && !prevIntf.vlanUntagged().equals(intf.vlanNative())) {
-                if (prevIntf.vlanTagged().contains(intf.vlanNative())) {
-                    // Update filtering objective and L2IG group bucket
-                    updatePortVlanTreatment(deviceId, portNum, intf.vlanNative(), true);
-                } else {
-                    // AddVlanNative
-                    updateVlanConfigInternal(deviceId, portNum, intf.vlanNative(), true, true);
-                }
-            }
-
-            if (!intf.vlanTagged().isEmpty() && !intf.vlanTagged().equals(prevIntf.vlanTagged())) {
-                // AddVlanTagged
-                Sets.difference(intf.vlanTagged(), prevIntf.vlanTagged()).stream()
-                        .filter(i -> !prevIntf.vlanUntagged().equals(i))
-                        .filter(i -> !prevIntf.vlanNative().equals(i))
-                        .forEach(vlanId -> updateVlanConfigInternal(
-                                deviceId, portNum, vlanId, false, true)
-                );
-            }
-
-            if (!intf.vlanUntagged().equals(VlanId.NONE)
-                    && !prevIntf.vlanUntagged().equals(intf.vlanUntagged())
-                    && !prevIntf.vlanNative().equals(intf.vlanUntagged())) {
-                if (prevIntf.vlanTagged().contains(intf.vlanUntagged())) {
-                    // Update filtering objective and L2IG group bucket
-                    updatePortVlanTreatment(deviceId, portNum, intf.vlanUntagged(), true);
-                } else {
-                    // AddVlanUntagged
-                    updateVlanConfigInternal(deviceId, portNum, intf.vlanUntagged(), true, true);
-                }
-            }
-            addSubnetConfig(prevIntf.connectPoint(),
-                            Sets.difference(new HashSet<>(intf.ipAddressesList()),
-                                            new HashSet<>(prevIntf.ipAddressesList())));
-        } catch (ConfigException e) {
-            log.error("Error in configuration");
-        }
-    }
-
-    private void updatePortVlanTreatment(DeviceId deviceId, PortNumber portNum,
-                                         VlanId vlanId, boolean pushVlan) {
-        DefaultGroupHandler grpHandler = getGroupHandler(deviceId);
-        if (grpHandler == null) {
-            log.warn("Failed to retrieve group handler for device {}", deviceId);
-            return;
-        }
-
-        // Update filtering objective for a single port
-        routingRulePopulator.updateSinglePortFilters(deviceId, portNum, !pushVlan, vlanId, false);
-        routingRulePopulator.updateSinglePortFilters(deviceId, portNum, pushVlan, vlanId, true);
-
-        if (getVlanNextObjectiveId(deviceId, vlanId) != -1) {
-            // Update L2IG bucket of the port
-            grpHandler.updateL2InterfaceGroupBucket(portNum, vlanId, pushVlan);
-            // Update bridging and unicast routing rule for each host
-            hostEventExecutor.execute(() -> hostHandler.processIntfVlanUpdatedEvent(deviceId, portNum,
-                    vlanId, !pushVlan, false));
-            hostEventExecutor.execute(() -> hostHandler.processIntfVlanUpdatedEvent(deviceId, portNum,
-                    vlanId, pushVlan, true));
-        } else {
-            log.warn("Failed to retrieve next objective for vlan {} in device {}:{}", vlanId, deviceId, portNum);
-        }
-    }
-
-    private void updateVlanConfigInternal(DeviceId deviceId, PortNumber portNum,
-                                          VlanId vlanId, boolean pushVlan, boolean install) {
-        DefaultGroupHandler grpHandler = getGroupHandler(deviceId);
-        if (grpHandler == null) {
-            log.warn("Failed to retrieve group handler for device {}", deviceId);
-            return;
-        }
-
-        // Update filtering objective for a single port
-        routingRulePopulator.updateSinglePortFilters(deviceId, portNum, pushVlan, vlanId, install);
-
-        // Update filtering objective for multicast ingress port
-        mcastHandler.updateFilterToDevice(deviceId, portNum, vlanId, install);
-
-        int nextId = getVlanNextObjectiveId(deviceId, vlanId);
-
-        if (nextId != -1 && !install) {
-            // Remove L2 Bridging rule and L3 Unicast rule to the host
-            hostEventExecutor.execute(() -> hostHandler.processIntfVlanUpdatedEvent(deviceId, portNum,
-                    vlanId, pushVlan, install));
-            // Remove broadcast forwarding rule and corresponding L2FG for VLAN
-            // only if there is no port configured on that VLAN ID
-            if (!getVlanPortMap(deviceId).containsKey(vlanId)) {
-                // Remove broadcast forwarding rule for the VLAN
-                routingRulePopulator.updateSubnetBroadcastRule(deviceId, vlanId, install);
-                // Remove L2FG for VLAN
-                grpHandler.removeBcastGroupFromVlan(deviceId, portNum, vlanId, pushVlan);
-            } else {
-                // Remove a single port from L2FG
-                grpHandler.updateGroupFromVlanConfiguration(vlanId, portNum, nextId, install);
-            }
-            // Remove L2IG of the port
-            grpHandler.removePortNextObjective(deviceId, portNum, vlanId, pushVlan);
-        } else if (install) {
-            // Create L2IG of the port
-            grpHandler.createPortNextObjective(deviceId, portNum, vlanId, pushVlan);
-            // Create L2 Bridging rule and L3 Unicast rule to the host
-            hostEventExecutor.execute(() -> hostHandler.processIntfVlanUpdatedEvent(deviceId, portNum,
-                    vlanId, pushVlan, install));
-            if (nextId != -1) {
-                // Add a single port to L2FG
-                grpHandler.updateGroupFromVlanConfiguration(vlanId, portNum, nextId, install);
-            } else {
-                // Create L2FG for VLAN
-                grpHandler.createBcastGroupFromVlan(vlanId, Collections.singleton(portNum));
-                routingRulePopulator.updateSubnetBroadcastRule(deviceId, vlanId, install);
-            }
-        } else {
-            log.warn("Failed to retrieve next objective for vlan {} in device {}:{}", vlanId, deviceId, portNum);
-        }
-    }
-
-    private void removeSubnetConfig(ConnectPoint cp, Set<InterfaceIpAddress> ipAddressSet) {
-        Set<IpPrefix> ipPrefixSet = ipAddressSet.stream().
-                map(InterfaceIpAddress::subnetAddress).collect(Collectors.toSet());
-
-        Set<InterfaceIpAddress> deviceIntfIpAddrs = interfaceService.getInterfaces().stream()
-                .filter(intf -> intf.connectPoint().deviceId().equals(cp.deviceId()))
-                .filter(intf -> !intf.connectPoint().equals(cp))
-                .flatMap(intf -> intf.ipAddressesList().stream())
-                .collect(Collectors.toSet());
-        // 1. Partial subnet population
-        // Remove routing rules for removed subnet from previous configuration,
-        // which does not also exist in other interfaces in the same device
-        Set<IpPrefix> deviceIpPrefixSet = deviceIntfIpAddrs.stream()
-                .map(InterfaceIpAddress::subnetAddress)
-                .collect(Collectors.toSet());
-
-        Set<IpPrefix> subnetsToBeRevoked = ipPrefixSet.stream()
-                .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
-                .collect(Collectors.toSet());
-
-        // Check if any of the subnets to be revoked is configured in the pairDevice.
-        // If any, repopulate the subnet with pairDevice connectPoint instead of revoking.
-        Optional<DeviceId> pairDevice = getPairDeviceId(cp.deviceId());
-        if (pairDevice.isPresent()) {
-            Set<IpPrefix> pairDeviceIpPrefix = getDeviceSubnetMap().get(pairDevice.get());
-
-            Set<IpPrefix> subnetsExistingInPairDevice = subnetsToBeRevoked.stream()
-                    .filter(ipPrefix -> pairDeviceIpPrefix.contains(ipPrefix))
-                    .collect(Collectors.toSet());
-
-            // Update the subnets existing in pair device with pair device connect point.
-            if (!subnetsExistingInPairDevice.isEmpty()) {
-                // PortNumber of connect point is not relevant in populate subnet and hence providing as ANY.
-                ConnectPoint pairDeviceCp = new ConnectPoint(pairDevice.get(), PortNumber.ANY);
-                log.debug("Updating the subnets: {} with pairDevice connectPoint as it exists in the Pair device: {}",
-                        subnetsExistingInPairDevice, pairDeviceCp);
-                defaultRoutingHandler.populateSubnet(Collections.singleton(pairDeviceCp), subnetsExistingInPairDevice);
-            }
-
-            // Remove only the subnets that are not configured in the pairDevice.
-            subnetsToBeRevoked = Sets.difference(subnetsToBeRevoked, subnetsExistingInPairDevice);
-        }
-
-        if (!subnetsToBeRevoked.isEmpty()) {
-            log.debug("Removing subnets for connectPoint: {}, subnets: {}", cp, subnetsToBeRevoked);
-            defaultRoutingHandler.revokeSubnet(subnetsToBeRevoked);
-        }
-
-        // 2. Interface IP punts
-        // Remove IP punts for old Intf address
-        Set<IpAddress> deviceIpAddrs = deviceIntfIpAddrs.stream()
-                .map(InterfaceIpAddress::ipAddress)
-                .collect(Collectors.toSet());
-        ipAddressSet.stream()
-                .map(InterfaceIpAddress::ipAddress)
-                .filter(interfaceIpAddress -> !deviceIpAddrs.contains(interfaceIpAddress))
-                .forEach(interfaceIpAddress ->
-                                 routingRulePopulator.revokeSingleIpPunts(
-                                         cp.deviceId(), interfaceIpAddress));
-
-        // 3. Host unicast routing rule
-        // Remove unicast routing rule
-        hostEventExecutor.execute(() -> hostHandler.processIntfIpUpdatedEvent(cp, ipPrefixSet, false));
-    }
-
-    private void addSubnetConfig(ConnectPoint cp, Set<InterfaceIpAddress> ipAddressSet) {
-        Set<IpPrefix> ipPrefixSet = ipAddressSet.stream().
-                map(InterfaceIpAddress::subnetAddress).collect(Collectors.toSet());
-
-        Set<InterfaceIpAddress> deviceIntfIpAddrs = interfaceService.getInterfaces().stream()
-                .filter(intf -> intf.connectPoint().deviceId().equals(cp.deviceId()))
-                .filter(intf -> !intf.connectPoint().equals(cp))
-                .flatMap(intf -> intf.ipAddressesList().stream())
-                .collect(Collectors.toSet());
-        // 1. Partial subnet population
-        // Add routing rules for newly added subnet, which does not also exist in
-        // other interfaces in the same device
-        Set<IpPrefix> deviceIpPrefixSet = deviceIntfIpAddrs.stream()
-                .map(InterfaceIpAddress::subnetAddress)
-                .collect(Collectors.toSet());
-        Set<IpPrefix> subnetsToBePopulated = ipPrefixSet.stream()
-                .filter(ipPrefix -> !deviceIpPrefixSet.contains(ipPrefix))
-                .collect(Collectors.toSet());
-
-        if (!subnetsToBePopulated.isEmpty()) {
-            log.debug("Adding subnets for connectPoint: {}, subnets: {}", cp, subnetsToBePopulated);
-
-            // check if pair-device has the same subnet configured?
-            Optional<DeviceId> pairDevice = getPairDeviceId(cp.deviceId());
-            if (pairDevice.isPresent()) {
-                Set<IpPrefix> pairDeviceIpPrefix = getDeviceSubnetMap().get(pairDevice.get());
-
-                Set<IpPrefix>  subnetsToBePopulatedAsDualHomed = subnetsToBePopulated.stream()
-                        .filter(ipPrefix -> pairDeviceIpPrefix.contains(ipPrefix))
-                        .collect(Collectors.toSet());
-                Set<IpPrefix> subnetsToBePopulatedAsSingleHomed = Sets.difference(subnetsToBePopulated,
-                        subnetsToBePopulatedAsDualHomed);
-
-                if (!subnetsToBePopulatedAsSingleHomed.isEmpty()) {
-                    defaultRoutingHandler.populateSubnet(
-                            Collections.singleton(cp),
-                            subnetsToBePopulatedAsSingleHomed);
-                }
-
-                if (!subnetsToBePopulatedAsDualHomed.isEmpty()) {
-                    Set<ConnectPoint> cpts = new HashSet<>();
-                    cpts.add(cp);
-                    // As Subnets is DualHomed adding the pairDevice also as ConnectPoint.
-                    // PortNumber of connect point is not relevant in populate subnet and hence providing as ANY.
-                    ConnectPoint pairCp = new ConnectPoint(pairDevice.get(), PortNumber.ANY);
-                    cpts.add(pairCp);
-
-                    log.debug("Adding DualHomed subnets for connectPoint: {} and its pair device: {}, subnets: {}",
-                            cp, pairCp, subnetsToBePopulatedAsDualHomed);
-
-                    // populating the subnets as DualHomed
-                    defaultRoutingHandler.populateSubnet(
-                            cpts,
-                            subnetsToBePopulated);
-
-                    // revoking the subnets populated in the device as it is now Dualhomed.
-                    defaultRoutingHandler.revokeSubnet(Collections.singleton(cp.deviceId()),
-                            subnetsToBePopulatedAsDualHomed);
-                }
-            } else {
-                defaultRoutingHandler.populateSubnet(
-                        Collections.singleton(cp),
-                        subnetsToBePopulated);
-            }
-        }
-
-        // 2. Interface IP punts
-        // Add IP punts for new Intf address
-        Set<IpAddress> deviceIpAddrs = deviceIntfIpAddrs.stream()
-                .map(InterfaceIpAddress::ipAddress)
-                .collect(Collectors.toSet());
-        ipAddressSet.stream()
-                .map(InterfaceIpAddress::ipAddress)
-                .filter(interfaceIpAddress -> !deviceIpAddrs.contains(interfaceIpAddress))
-                .forEach(interfaceIpAddress ->
-                                 routingRulePopulator.populateSingleIpPunts(
-                                         cp.deviceId(), interfaceIpAddress));
-
-        // 3. Host unicast routing rule
-        // Add unicast routing rule
-        hostEventExecutor.execute(() -> hostHandler.processIntfIpUpdatedEvent(cp, ipPrefixSet, true));
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java
deleted file mode 100644
index 3cf0e32..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourDispatcher.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.net.neighbour.NeighbourMessageContext;
-import org.onosproject.net.neighbour.NeighbourMessageHandler;
-import org.onosproject.net.host.HostService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * This handler dispatches to the appropriate handlers the
- * neighbour discovery protocols.
- */
-public class SegmentRoutingNeighbourDispatcher implements NeighbourMessageHandler {
-
-    private static Logger log = LoggerFactory.getLogger(SegmentRoutingNeighbourDispatcher.class);
-    private SegmentRoutingManager manager;
-
-    /**
-     * Create a segment routing neighbour dispatcher.
-     *
-     * @param segmentRoutingManager the segment routing manager
-     */
-    public SegmentRoutingNeighbourDispatcher(SegmentRoutingManager segmentRoutingManager) {
-        this.manager = segmentRoutingManager;
-    }
-
-    @Override
-    public void handleMessage(NeighbourMessageContext context, HostService hostService) {
-        manager.neighborExecutor.execute(() -> handleMessageInternal(context, hostService));
-    }
-
-    private void handleMessageInternal(NeighbourMessageContext context, HostService hostService) {
-        log.trace("Received {} packet on {}: {}", context.protocol(),
-                  context.inPort(), context.packet());
-        switch (context.protocol()) {
-            case ARP:
-                if (this.manager.arpHandler != null) {
-                    this.manager.arpHandler.processPacketIn(context, hostService);
-                }
-                break;
-            case NDP:
-                if (this.manager.icmpHandler != null) {
-                    this.manager.icmpHandler.processPacketIn(context, hostService);
-                }
-                break;
-            default:
-                log.warn("Unknown protocol", context.protocol());
-        }
-    }
-
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourHandler.java b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourHandler.java
deleted file mode 100644
index 8ef8e7e..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingNeighbourHandler.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.neighbour.NeighbourMessageContext;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.packet.DefaultOutboundPacket;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.nio.ByteBuffer;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * This handler provides provides useful functions to the
- * neighbour handlers (ARP, NDP).
- */
-public class SegmentRoutingNeighbourHandler {
-
-    private static Logger log = LoggerFactory.getLogger(SegmentRoutingNeighbourHandler.class);
-
-    protected SegmentRoutingManager srManager;
-    protected DeviceConfiguration config;
-
-    /**
-     * Creates an SegmentRoutingNeighbourHandler object.
-     *
-     * @param srManager SegmentRoutingManager object
-     */
-    public SegmentRoutingNeighbourHandler(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        this.config = checkNotNull(srManager.deviceConfiguration);
-    }
-
-    /**
-     * Creates an SegmentRoutingNeighbourHandler object.
-     */
-    public SegmentRoutingNeighbourHandler() {
-        this.srManager = null;
-        this.config = null;
-    }
-
-    /**
-     * Retrieve router (device) info.
-     *
-     * @param mac where to copy the mac
-     * @param ip where to copy the ip
-     * @param deviceId the device id
-     * @param targetAddress the target address
-     * @return true if it was possible to get the necessary info.
-     * False for errors
-     */
-    protected boolean getSenderInfo(byte[] mac,
-                                 byte[] ip,
-                                 DeviceId deviceId,
-                                 IpAddress targetAddress) {
-        byte[] senderMacAddress;
-        byte[] senderIpAddress;
-        IpAddress sender;
-        try {
-            senderMacAddress = config.getDeviceMac(deviceId).toBytes();
-            if (targetAddress.isIp4()) {
-                sender = config.getRouterIpAddressForASubnetHost(targetAddress.getIp4Address());
-            } else {
-                sender = config.getRouterIpAddressForASubnetHost(targetAddress.getIp6Address());
-            }
-            // If sender is null we abort.
-            if (sender == null) {
-                log.warn("Sender ip is null. Aborting getSenderInfo");
-                return false;
-            }
-            senderIpAddress = sender.toOctets();
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting getSenderInfo");
-            return false;
-        }
-        System.arraycopy(senderMacAddress, 0, mac, 0, senderMacAddress.length);
-        System.arraycopy(senderIpAddress, 0, ip, 0, senderIpAddress.length);
-        return true;
-    }
-
-    /**
-     * Reads the boolean configuration for responding to unknown hosts.
-     *
-     * @return respondToUnknownHosts boolean.
-     */
-    protected boolean respondToUnknownHosts() {
-        return srManager.respondToUnknownHosts;
-    }
-
-    /**
-     * Utility to send a ND reply using the supplied information.
-     *
-     * @param pkt the request
-     * @param targetMac the target mac
-     * @param hostService the host service
-     * @param isRouter true if this reply is sent on behalf of a router
-     */
-    protected void sendResponse(NeighbourMessageContext pkt, MacAddress targetMac, HostService hostService,
-                                boolean isRouter) {
-        // if this is false, check if host exists in the store
-        if (!respondToUnknownHosts()) {
-            short vlanId = pkt.packet().getQinQVID();
-            HostId dstId = HostId.hostId(pkt.srcMac(), vlanId == Ethernet.VLAN_UNTAGGED
-                    ? pkt.vlan() : VlanId.vlanId(vlanId));
-            Host dst = hostService.getHost(dstId);
-            if (dst == null) {
-                log.warn("Cannot send {} response to host {} - does not exist in the store",
-                         pkt.protocol(), dstId);
-                return;
-            }
-        }
-        pkt.setIsRouter(isRouter);
-        pkt.reply(targetMac);
-    }
-
-    /**
-     * Flood to all ports in the same subnet.
-     *
-     * @param packet packet to be flooded
-     * @param inPort where the packet comes from
-     * @param targetAddress the target address
-     */
-    protected void flood(Ethernet packet, ConnectPoint inPort, IpAddress targetAddress) {
-        try {
-            srManager.deviceConfiguration
-                    .getSubnetPortsMap(inPort.deviceId()).forEach((subnet, ports) -> {
-                if (subnet.contains(targetAddress)) {
-                    ports.stream()
-                            .filter(port -> port != inPort.port())
-                            .forEach(port -> {
-                                forward(packet, new ConnectPoint(inPort.deviceId(), port));
-                            });
-                }
-            });
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage()
-                             + " Cannot flood in subnet as device config not available"
-                             + " for device: " + inPort.deviceId());
-        }
-    }
-
-    /*
-     * Floods only on the port which have been configured with the subnet
-     * of the target address. The in port is excluded.
-     *
-     * @param pkt the ndp/arp packet and context information
-     */
-    protected void flood(NeighbourMessageContext pkt) {
-        try {
-            srManager.deviceConfiguration
-                    .getSubnetPortsMap(pkt.inPort().deviceId()).forEach((subnet, ports) -> {
-                if (subnet.contains(pkt.target())) {
-                    ports.stream()
-                            .filter(port -> port != pkt.inPort().port())
-                            .forEach(port -> {
-                                ConnectPoint outPoint = new ConnectPoint(
-                                        pkt.inPort().deviceId(),
-                                        port
-                                );
-                                pkt.forward(outPoint);
-                            });
-                }
-            });
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage()
-                             + " Cannot flood in subnet as device config not available"
-                             + " for device: " + pkt.inPort().deviceId());
-        }
-    }
-
-    /**
-     * Packet out to given port.
-     *
-     * Note: In current implementation, we expect all communication with
-     * end hosts within a subnet to be untagged.
-     * <p>
-     * For those pipelines that internally assigns a VLAN, the VLAN tag will be
-     * removed before egress.
-     * <p>
-     * For those pipelines that do not assign internal VLAN, the packet remains
-     * untagged.
-     *
-     * @param packet packet to be forwarded
-     * @param outPort where the packet should be forwarded
-     */
-    private void forward(Ethernet packet, ConnectPoint outPort) {
-        ByteBuffer buf = ByteBuffer.wrap(packet.serialize());
-
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.setOutput(outPort.port());
-        srManager.packetService.emit(new DefaultOutboundPacket(outPort.deviceId(),
-                                                               tbuilder.build(), buf));
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java b/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
deleted file mode 100644
index 3bfd88b..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingService.java
+++ /dev/null
@@ -1,439 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Multimap;
-import org.apache.commons.lang3.NotImplementedException;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.VlanId;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
-import org.onosproject.segmentrouting.mcast.McastFilteringObjStoreKey;
-import org.onosproject.segmentrouting.mcast.McastRole;
-import org.onosproject.segmentrouting.mcast.McastRoleStoreKey;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
-import org.onosproject.segmentrouting.pwaas.L2Tunnel;
-import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
-import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
-import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
-import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
-
-import com.google.common.collect.ImmutableMap;
-import org.onosproject.segmentrouting.mcast.McastStoreKey;
-import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * Segment Routing Service for REST API.
- */
-public interface SegmentRoutingService {
-    /**
-     * VLAN cross-connect ACL priority.
-     *
-     * @deprecated in ONOS 1.12. Replaced by {@link org.onosproject.segmentrouting.xconnect.api.XconnectService}
-     */
-    @Deprecated
-    int XCONNECT_ACL_PRIORITY = 60000;
-
-    /**
-     * VLAN cross-connect Bridging priority.
-     *
-     * @deprecated in ONOS 1.12. Replaced by {@link org.onosproject.segmentrouting.xconnect.api.XconnectService}
-     */
-    @Deprecated
-    int XCONNECT_PRIORITY = 1000;
-
-    /**
-     * Default flow priority.
-     */
-    int DEFAULT_PRIORITY = 100;
-
-    /**
-     * Minimum IP priority.
-     *
-     * Should &lt; 0 such that priority of /0 will not conflict with lowest
-     * priority default entries.
-     */
-    int MIN_IP_PRIORITY = 10;
-
-    /**
-     * Subnet flooding flow priority.
-     */
-    int FLOOD_PRIORITY = 5;
-
-    /**
-     * Returns all tunnels.
-     *
-     * @return list of tunnels
-     */
-    List<Tunnel> getTunnels();
-
-    /**
-     * Creates a tunnel.
-     *
-     * @param tunnel tunnel reference to create
-     * @return WRONG_PATH if the tunnel path is wrong, ID_EXISTS if the tunnel ID
-     * exists already, TUNNEL_EXISTS if the same tunnel exists, INTERNAL_ERROR
-     * if the tunnel creation failed internally, SUCCESS if the tunnel is created
-     * successfully
-     */
-    TunnelHandler.Result createTunnel(Tunnel tunnel);
-
-    /**
-     * Returns all policies.
-     *
-     * @return list of policy
-     */
-    List<Policy> getPolicies();
-
-    /**
-     * Returns the l2 tunnel descriptions.
-     *
-     * @param pending if true fetch pending pseudowires, else fetch installed
-     * @return set of l2 tunnel descriptions.
-     */
-    Set<L2TunnelDescription> getL2TunnelDescriptions(boolean pending);
-
-    /**
-     * Returns all l2 tunnels of pseudowires.
-     *
-     * @return list of l2 tunnels
-     */
-    List<L2Tunnel> getL2Tunnels();
-
-    /**
-     * Returns all l2 policie of pseudowires.
-     *
-     * @return list of l2 policies.
-     */
-    List<L2TunnelPolicy> getL2Policies();
-
-    /**
-     * Removes pseudowire.
-     *
-     * @param pwId The id of the pseudowire.
-     * @return SUCCESS if operation successful or a descriptive error otherwise.
-     */
-    L2TunnelHandler.Result removePseudowire(Integer pwId);
-
-    /**
-     * Adds a Pseudowire to the system.
-     *
-     * @param tunnel The pseudowire tunnel.
-     * @return SUCCESS if operation is successful or a descriptive error otherwise.
-     */
-    L2TunnelHandler.Result addPseudowire(L2TunnelDescription tunnel);
-
-    /**
-     * Adds a set of pseudowires.
-     *
-     *
-     * @param l2TunnelDescriptions The pseudowires to add.
-     * @return SUCCESS if ALL pseudowires can be instantiated and are deployed, or a
-     *         a descriptive error otherwise, without deploying any pseudowire.
-     * @deprecated onos-1.12 use addPseudowire instead
-     */
-    @Deprecated
-    L2TunnelHandler.Result addPseudowiresBulk(List<DefaultL2TunnelDescription> l2TunnelDescriptions);
-
-    /**
-     * Creates a policy.
-     *
-     * @param policy policy reference to create
-     * @return ID_EXISTS if the same policy ID exists,
-     *  POLICY_EXISTS if the same policy exists, TUNNEL_NOT_FOUND if the tunnel
-     *  does not exists, UNSUPPORTED_TYPE if the policy type is not supported,
-     *  SUCCESS if the policy is created successfully.
-     */
-    PolicyHandler.Result createPolicy(Policy policy);
-
-    /**
-     * Removes a tunnel.
-     *
-     * @param tunnel tunnel reference to remove
-     * @return TUNNEL_NOT_FOUND if the tunnel to remove does not exists,
-     * INTERNAL_ERROR if the tunnel creation failed internally, SUCCESS
-     * if the tunnel is created successfully.
-     */
-    TunnelHandler.Result removeTunnel(Tunnel tunnel);
-
-    /**
-     * Removes a policy.
-     *
-     * @param policy policy reference to remove
-     * @return POLICY_NOT_FOUND if the policy to remove does not exists,
-     * SUCCESS if it is removed successfully
-     */
-    PolicyHandler.Result removePolicy(Policy policy);
-
-    /**
-     * Use current state of the network to repopulate forwarding rules.
-     *
-     */
-    void rerouteNetwork();
-
-    /**
-     * Returns device-subnet mapping.
-     *
-     * @return device-subnet mapping
-     */
-    Map<DeviceId, Set<IpPrefix>> getDeviceSubnetMap();
-
-    /**
-     * Returns the current ECMP shortest path graph in this controller instance.
-     *
-     * @return ECMP shortest path graph
-     */
-    ImmutableMap<DeviceId, EcmpShortestPathGraph> getCurrentEcmpSpg();
-
-    /**
-     * Returns the destinatiomSet-NextObjective store contents.
-     *
-     * @return current contents of the dstNextObjStore
-     */
-    ImmutableMap<DestinationSetNextObjectiveStoreKey, NextNeighbors> getDstNextObjStore();
-
-    /**
-     * Returns the VLAN next objective store.
-     *
-     * @return current contents of the vlanNextObjStore
-     */
-    ImmutableMap<VlanNextObjectiveStoreKey, Integer> getVlanNextObjStore();
-
-    /**
-     * Returns the Mac Vlan next objective store.
-     *
-     * @return current contents of the macVlanNextObjStore
-     */
-    ImmutableMap<MacVlanNextObjectiveStoreKey, Integer> getMacVlanNextObjStore();
-
-    /**
-     * Returns the port next objective store.
-     *
-     * @return current contents of the portNextObjStore
-     */
-    ImmutableMap<PortNextObjectiveStoreKey, Integer> getPortNextObjStore();
-
-    /**
-     * Returns the associated next ids to the mcast groups or to the single
-     * group if mcastIp is present.
-     *
-     * @param mcastIp the group ip
-     * @return the mapping mcastIp-device to next id
-     */
-    Map<McastStoreKey, Integer> getMcastNextIds(IpAddress mcastIp);
-
-    /**
-     * Returns the PW init next objective store.
-     *
-     * @return current contents of the l2InitiationNextObjStore
-     */
-    ImmutableMap<String, NextObjective> getPwInitNext();
-
-    /**
-     * Returns the PW termination next objective store.
-     *
-     * @return current contents of the l2TerminationNextObjStore
-     */
-    ImmutableMap<String, NextObjective> getPwTermNext();
-
-    /**
-     * Removes all entries in dst/vlan/port/mcast NextObjectiveStore that are associated with the given nextId.
-     *
-     * @param nextId nextId
-     */
-    void invalidateNextObj(int nextId);
-
-    /**
-     * Triggers the verification of all ECMP groups in the specified device.
-     * Adjusts the group buckets if verification finds that there are more or less
-     * buckets than what should be there.
-     *
-     * @param id the device identifier
-     */
-    void verifyGroups(DeviceId id);
-
-    /**
-     * Returns the internal link state as seen by this instance of the
-     * controller.
-     *
-     * @return the internal link state
-     */
-    ImmutableMap<Link, Boolean> getSeenLinks();
-
-    /**
-     * Returns the ports administratively disabled by the controller.
-     *
-     * @return a map of devices and port numbers for administratively disabled
-     *         ports. Does not include ports manually disabled by the operator.
-     */
-    ImmutableMap<DeviceId, Set<PortNumber>> getDownedPortState();
-
-    /**
-     * Returns the associated roles to the mcast groups.
-     *
-     * @param mcastIp the group ip
-     * @param sourcecp the source connect point
-     * @return the mapping mcastIp-device to mcast role
-     */
-    Map<McastRoleStoreKey, McastRole> getMcastRoles(IpAddress mcastIp,
-                                                    ConnectPoint sourcecp);
-
-    /**
-     * Returns the associated trees to the mcast group.
-     *
-     * @param mcastIp the group ip
-     * @param sourcecp the source connect point
-     * @return the mapping egress point to mcast path
-     */
-    Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp,
-                                                             ConnectPoint sourcecp);
-
-    /**
-     * Return the leaders of the mcast groups.
-     *
-     * @param mcastIp the group ip
-     * @return the mapping group-node
-     */
-    Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp);
-
-    /**
-     * Returns shouldProgram map.
-     *
-     * @return shouldProgram map
-     */
-    Map<Set<DeviceId>, NodeId> getShouldProgram();
-
-    /**
-     * Returns shouldProgram local cache.
-     *
-     * @return shouldProgram local cache
-     */
-    Map<DeviceId, Boolean> getShouldProgramCache();
-
-    /**
-     * Returns whether instance should program device or not.
-     *
-     * @param deviceId .
-     * @return boolean status saying instance should program device or not.
-     */
-    boolean shouldProgram(DeviceId deviceId);
-
-    /**
-     * Returns the mcast filtering obj.
-     *
-     * @return the mapping group-node
-     */
-    Map<DeviceId, List<McastFilteringObjStoreKey>> getMcastFilters();
-
-    /**
-     * Determines if routing in the network has been stable in the last
-     * STABILITY_THRESHOLD seconds, by comparing the current time to the last
-     * routing change timestamp.
-     *
-     * @return true if stable
-     */
-    boolean isRoutingStable();
-
-    /**
-     * Invoke hostHandler.init() for given device.
-     *
-     * @param deviceId device ID
-     */
-    void initHost(DeviceId deviceId);
-
-    /**
-     * Invoke routeHandler.init() for given device.
-     *
-     * @param deviceId device ID
-     */
-    void initRoute(DeviceId deviceId);
-
-    /**
-     * Gets application id.
-     *
-     * @return application id
-     */
-    default ApplicationId appId() {
-        throw new NotImplementedException("appId not implemented");
-    }
-
-    /**
-     * Returns internal VLAN for untagged hosts on given connect point.
-     * <p>
-     * The internal VLAN is either vlan-untagged for an access port,
-     * or vlan-native for a trunk port.
-     *
-     * @param connectPoint connect point
-     * @return internal VLAN or null if both vlan-untagged and vlan-native are undefined
-     */
-    default VlanId getInternalVlanId(ConnectPoint connectPoint) {
-        throw new NotImplementedException("getInternalVlanId not implemented");
-    }
-
-    /**
-     * Returns optional pair device ID of given device.
-     *
-     * @param deviceId device ID
-     * @return optional pair device ID. Might be empty if pair device is not configured
-     */
-    default Optional<DeviceId> getPairDeviceId(DeviceId deviceId) {
-        throw new NotImplementedException("getPairDeviceId not implemented");
-    }
-
-    /**
-     * Returns optional pair device local port of given device.
-     *
-     * @param deviceId device ID
-     * @return optional pair device ID. Might be empty if pair device is not configured
-     */
-    default Optional<PortNumber> getPairLocalPort(DeviceId deviceId) {
-        throw new NotImplementedException("getPairLocalPort not implemented");
-    }
-
-    /**
-     * Returns a set of infrastructure ports on the given device.
-     *
-     * @param deviceId device ID
-     * @return a set of ports that does not have interface configuration
-     */
-    default Set<PortNumber> getInfraPorts(DeviceId deviceId) {
-        throw new NotImplementedException("getInfraPorts not implemented");
-    }
-
-    /**
-     * Returns a set of edge ports on the given device.
-     *
-     * @param deviceId device ID
-     * @return a set of ports that has interface configuration
-     */
-    default Set<PortNumber> getEdgePorts(DeviceId deviceId) {
-        throw new NotImplementedException("getEdgePorts not implemented");
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/TopologyHandler.java b/app/src/main/java/org/onosproject/segmentrouting/TopologyHandler.java
deleted file mode 100644
index 72b1a70..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/TopologyHandler.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Maps;
-import org.onosproject.event.Event;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.link.LinkEvent;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-/**
- * Handler for topology events.
- */
-class TopologyHandler {
-
-    // Logging instance
-    private static final Logger log = LoggerFactory.getLogger(TopologyHandler.class);
-    // Internal reference for SR manager and its services
-    private final SegmentRoutingManager srManager;
-
-    /**
-     * Constructs the TopologyHandler.
-     *
-     * @param srManager Segment Routing manager
-     */
-    TopologyHandler(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-    }
-
-    // Check if the link event is valid
-    private boolean isValid(LinkEvent linkEvent) {
-        Link link = linkEvent.subject();
-        // Verify if the link is valid with the link handler
-        if (!srManager.linkHandler.isLinkValid(link)) {
-            log.debug("Link {} ignored by the LinkHandler", link);
-            return false;
-        }
-        // Processing for LINK_REMOVED
-        if (linkEvent.type() == LinkEvent.Type.LINK_REMOVED) {
-            // device availability check helps to ensure that multiple link-removed
-            // events are actually treated as a single switch removed event.
-            // purgeSeenLink is necessary so we do rerouting (instead of rehashing)
-            // when switch comes back.
-            if (link.src().elementId() instanceof DeviceId
-                    && !srManager.deviceService.isAvailable(link.src().deviceId())) {
-                log.debug("Link {} ignored device {} is down", link, link.src().deviceId());
-                return false;
-            }
-            if (link.dst().elementId() instanceof DeviceId
-                    && !srManager.deviceService.isAvailable(link.dst().deviceId())) {
-                log.debug("Link {} ignored device {} is down", link, link.dst().deviceId());
-                return false;
-            }
-            // LINK_REMOVED is ok
-            return true;
-        }
-        // Processing for LINK_ADDED and LINK_UPDATED
-        // Verify if source device is configured
-        if (srManager.deviceConfiguration == null ||
-                !srManager.deviceConfiguration.isConfigured(link.src().deviceId())) {
-            // Source device is not configured, not valid for us
-            log.warn("Source device of this link is not configured.. "
-                             + "not processing further");
-            return false;
-        }
-        // LINK_ADDED/LINK_UPDATED is ok
-        return true;
-    }
-
-    // Check if the device event is valid
-    private boolean isValid(DeviceEvent deviceEvent) {
-        Device device = deviceEvent.subject();
-        // We don't process the event if the device is available
-        return !srManager.deviceService.isAvailable(device.id());
-    }
-
-    /**
-     * Process the TOPOLOGY_CHANGE event. An initial optimization
-     * is performed to avoid the processing of not relevant events.
-     *
-     * @param reasons list of events that triggered topology change
-     */
-    void processTopologyChange(List<Event> reasons) {
-        // Store temporary in the map all the link events,
-        // events having the same subject will be automatically
-        // overridden.
-        Map<Link, LinkEvent> linkEvents = Maps.newHashMap();
-        // Store temporary in the map all the device events,
-        // events having the same subject will be automatically
-        // overridden.
-        Map<DeviceId, DeviceEvent> deviceEvents = Maps.newHashMap();
-        // Pre-process all the events putting them in the right map
-        reasons.forEach(reason -> {
-            // Relevant events for devices
-            if (reason.type() == DeviceEvent.Type.DEVICE_ADDED ||
-                    reason.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
-                    reason.type() == DeviceEvent.Type.DEVICE_UPDATED) {
-                // Take the event and save in the map
-                DeviceEvent deviceEvent = (DeviceEvent) reason;
-                deviceEvents.put(deviceEvent.subject().id(), deviceEvent);
-                /// Relevant events for links
-            } else if (reason.type() == LinkEvent.Type.LINK_ADDED ||
-                    reason.type() == LinkEvent.Type.LINK_UPDATED ||
-                    reason.type() == LinkEvent.Type.LINK_REMOVED) {
-                // Take the event and store the link in the map
-                LinkEvent linkEvent = (LinkEvent) reason;
-                linkEvents.put(linkEvent.subject(), linkEvent);
-                // Other events are not relevant
-            } else {
-                log.debug("Unhandled event type: {}", reason.type());
-            }
-        });
-        // Verify if the link events are valid
-        // before sent for mcast handling
-        List<LinkEvent> toProcessLinkEvent = linkEvents.values()
-                .stream()
-                .filter(this::isValid)
-                .collect(Collectors.toList());
-        // Verify if the device events are valid
-        // before sent for mcast handling
-        List<DeviceEvent> toProcessDeviceEvent = deviceEvents.values()
-                .stream()
-                .filter(this::isValid)
-                .collect(Collectors.toList());
-
-        // Processing part of the received events
-        // We don't need to process all LINK_ADDED
-        boolean isLinkAdded = false;
-        // Iterate on link events
-        for (LinkEvent linkEvent : toProcessLinkEvent) {
-            // We process just the first one
-            if (linkEvent.type() == LinkEvent.Type.LINK_ADDED ||
-                    linkEvent.type() == LinkEvent.Type.LINK_UPDATED) {
-                // Other ones are skipped
-                if (isLinkAdded) {
-                    continue;
-                }
-                log.info("Processing - Event: {}", linkEvent);
-                // First time, let's process it
-                isLinkAdded = true;
-                // McastHandler, reroute all the mcast tree
-                srManager.mcastHandler.init();
-            } else {
-                log.info("Processing - Event: {}", linkEvent);
-                // We compute each time a LINK_DOWN event
-                srManager.mcastHandler.processLinkDown(linkEvent.subject());
-            }
-        }
-        // Process all the device events
-        toProcessDeviceEvent.forEach(deviceEvent -> {
-            log.info("Processing - Event: {}", deviceEvent);
-            srManager.mcastHandler.processDeviceDown(deviceEvent.subject().id());
-        });
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/Tunnel.java b/app/src/main/java/org/onosproject/segmentrouting/Tunnel.java
deleted file mode 100644
index 470662f..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/Tunnel.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import java.util.List;
-
-/**
- * Tunnel interface.
- */
-public interface Tunnel {
-
-    /**
-     * Returns the tunnel ID.
-     *
-     * @return tunnel ID
-     */
-    String id();
-
-    /**
-     * Returns Segment IDs for the tunnel including source and destination.
-     *
-     * @return List of Node ID
-     */
-    List<Integer> labelIds();
-
-    /**
-     * Returns the group ID for the tunnel.
-     *
-     * @return group ID
-     */
-    int groupId();
-
-    /**
-     * Sets group ID for the tunnel.
-     *
-     * @param groupId group identifier
-     */
-    void setGroupId(int groupId);
-
-    /**
-     * Sets the flag to allow to remove the group or not.
-     *
-     * @param ok the flag; true - allow to remove
-     */
-    void allowToRemoveGroup(boolean ok);
-
-    /**
-     * Checks if it is allowed to remove the group for the tunnel.
-     *
-     * @return true if allowed, false otherwise
-     */
-    boolean isAllowedToRemoveGroup();
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java b/app/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
deleted file mode 100644
index 7031507..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/TunnelHandler.java
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.link.LinkService;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.grouphandler.DefaultGroupHandler;
-import org.onosproject.segmentrouting.grouphandler.DestinationSet;
-import org.onosproject.store.service.EventuallyConsistentMap;
-import org.slf4j.Logger;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Tunnel Handler.
- */
-public class TunnelHandler {
-    protected final Logger log = getLogger(getClass());
-
-    private final DeviceConfiguration config;
-    private final EventuallyConsistentMap<String, Tunnel> tunnelStore;
-    private Map<DeviceId, DefaultGroupHandler> groupHandlerMap;
-    private LinkService linkService;
-
-    /**
-     * Result of tunnel creation or removal.
-     */
-    public enum Result {
-        /**
-         * Success.
-         */
-        SUCCESS,
-
-        /**
-         * More than one router needs to specified to created a tunnel.
-         */
-        WRONG_PATH,
-
-        /**
-         * The same tunnel exists already.
-         */
-        TUNNEL_EXISTS,
-
-        /**
-         * The same tunnel ID exists already.
-         */
-        ID_EXISTS,
-
-        /**
-         * Tunnel not found.
-         */
-        TUNNEL_NOT_FOUND,
-
-        /**
-         * Cannot remove the tunnel used by a policy.
-         */
-        TUNNEL_IN_USE,
-
-        /**
-         * Failed to create/remove groups for the tunnel.
-         */
-        INTERNAL_ERROR
-    }
-
-    /**
-     * Constructs tunnel handler.
-     *
-     * @param linkService link service
-     * @param deviceConfiguration device configuration
-     * @param groupHandlerMap group handler map
-     * @param tunnelStore tunnel store
-     */
-    public TunnelHandler(LinkService linkService,
-                         DeviceConfiguration deviceConfiguration,
-                         Map<DeviceId, DefaultGroupHandler> groupHandlerMap,
-                         EventuallyConsistentMap<String, Tunnel> tunnelStore) {
-        this.linkService = linkService;
-        this.config = deviceConfiguration;
-        this.groupHandlerMap = groupHandlerMap;
-        this.tunnelStore = tunnelStore;
-    }
-
-    /**
-     * Creates a tunnel.
-     *
-     * @param tunnel tunnel reference to create a tunnel
-     * @return WRONG_PATH if the tunnel path is wrong, ID_EXISTS if the tunnel ID
-     * exists already, TUNNEL_EXISTS if the same tunnel exists, INTERNAL_ERROR
-     * if the tunnel creation failed internally, SUCCESS if the tunnel is created
-     * successfully
-     */
-    public Result createTunnel(Tunnel tunnel) {
-
-        if (tunnel.labelIds().isEmpty() || tunnel.labelIds().size() < 3) {
-            log.error("More than one router needs to specified to created a tunnel");
-            return Result.WRONG_PATH;
-        }
-
-        if (tunnelStore.containsKey(tunnel.id())) {
-            log.warn("The same tunnel ID exists already");
-            return Result.ID_EXISTS;
-        }
-
-        if (tunnelStore.containsValue(tunnel)) {
-            log.warn("The same tunnel exists already");
-            return Result.TUNNEL_EXISTS;
-        }
-
-        int groupId = createGroupsForTunnel(tunnel);
-        if (groupId < 0) {
-            log.error("Failed to create groups for the tunnel");
-            return Result.INTERNAL_ERROR;
-        }
-
-        tunnel.setGroupId(groupId);
-        tunnelStore.put(tunnel.id(), tunnel);
-
-        return Result.SUCCESS;
-    }
-
-    /**
-     * Removes the tunnel with the tunnel ID given.
-     *
-     * @param tunnelInfo tunnel information to delete tunnels
-     * @return TUNNEL_NOT_FOUND if the tunnel to remove does not exists,
-     * INTERNAL_ERROR if the tunnel creation failed internally, SUCCESS
-     * if the tunnel is created successfully.
-     */
-    public Result removeTunnel(Tunnel tunnelInfo) {
-
-        Tunnel tunnel = tunnelStore.get(tunnelInfo.id());
-        if (tunnel != null) {
-            DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0));
-            if (tunnel.isAllowedToRemoveGroup()) {
-                if (groupHandlerMap.get(deviceId).removeGroup(tunnel.groupId())) {
-                    tunnelStore.remove(tunnel.id());
-                } else {
-                    log.error("Failed to remove the tunnel {}", tunnelInfo.id());
-                    return Result.INTERNAL_ERROR;
-                }
-            } else {
-                log.debug("The group is not removed because it is being used.");
-                tunnelStore.remove(tunnel.id());
-            }
-        } else {
-            log.error("No tunnel found for tunnel ID {}", tunnelInfo.id());
-            return Result.TUNNEL_NOT_FOUND;
-        }
-
-        return Result.SUCCESS;
-    }
-
-    /**
-     * Returns the tunnel with the tunnel ID given.
-     *
-     * @param tid Tunnel ID
-     * @return Tunnel reference
-     */
-    public Tunnel getTunnel(String tid) {
-        return tunnelStore.get(tid);
-    }
-
-    /**
-     * Returns all tunnels.
-     *
-     * @return list of Tunnels
-     */
-    public List<Tunnel> getTunnels() {
-        List<Tunnel> tunnels = new ArrayList<>();
-        tunnelStore.values().forEach(tunnel -> tunnels.add(
-                new DefaultTunnel((DefaultTunnel) tunnel)));
-
-        return tunnels;
-    }
-
-    private int createGroupsForTunnel(Tunnel tunnel) {
-
-        Set<Integer> portNumbers;
-        final int groupError = -1;
-
-        DeviceId deviceId = config.getDeviceId(tunnel.labelIds().get(0));
-        if (deviceId == null) {
-            log.warn("No device found for SID {}", tunnel.labelIds().get(0));
-            return groupError;
-        } else if (groupHandlerMap.get(deviceId) == null) {
-            log.warn("group handler not found for {}", deviceId);
-            return groupError;
-        }
-        Set<DeviceId> deviceIds = new HashSet<>();
-        int sid = tunnel.labelIds().get(1);
-        if (config.isAdjacencySid(deviceId, sid)) {
-            portNumbers = config.getPortsForAdjacencySid(deviceId, sid);
-            for (Link link: linkService.getDeviceEgressLinks(deviceId)) {
-                for (Integer port: portNumbers) {
-                    if (link.src().port().toLong() == port) {
-                        deviceIds.add(link.dst().deviceId());
-                    }
-                }
-            }
-        } else {
-            deviceIds.add(config.getDeviceId(sid));
-        }
-        // For these NeighborSet isMpls is meaningless.
-        // TODO : Revisit this, the code and also the type
-        DestinationSet ns = DestinationSet.createTypePushBos(
-                                               tunnel.labelIds().get(2),
-                                               DeviceId.NONE);
-
-        // If the tunnel reuses any existing groups, then tunnel handler
-        // should not remove the group.
-        if (groupHandlerMap.get(deviceId).hasNextObjectiveId(ns)) {
-            tunnel.allowToRemoveGroup(false);
-        } else {
-            tunnel.allowToRemoveGroup(true);
-        }
-
-        return groupHandlerMap.get(deviceId).getNextObjectiveId(ns, null, null, true);
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java b/app/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java
deleted file mode 100644
index 5df82af..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/TunnelPolicy.java
+++ /dev/null
@@ -1,291 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Tunnel Policy.
- */
-public final class TunnelPolicy implements Policy {
-
-    private final Type type;
-    private final String id;
-    private final int priority;
-    private final String tunnelId;
-    private String dstIp;
-    private String srcIp;
-    private String ipProto;
-    private short srcPort;
-    private short dstPort;
-
-    private TunnelPolicy(String policyId, Type type, int priority, String tunnelId, String srcIp,
-                         String dstIp, String ipProto, short srcPort, short dstPort) {
-        this.id = checkNotNull(policyId);
-        this.type = type;
-        this.tunnelId = tunnelId;
-        this.priority = priority;
-        this.dstIp = dstIp;
-        this.srcIp = srcIp;
-        this.ipProto = ipProto;
-        this.srcPort = srcPort;
-        this.dstPort = dstPort;
-
-    }
-
-    /**
-     * Creates a TunnelPolicy reference.
-     *
-     * @param p TunnelPolicy reference
-     */
-    public TunnelPolicy(TunnelPolicy p) {
-        this.id = p.id;
-        this.type = p.type;
-        this.tunnelId = p.tunnelId;
-        this.priority = p.priority;
-        this.srcIp = p.srcIp;
-        this.dstIp = p.dstIp;
-        this.ipProto = p.ipProto;
-        this.srcPort = p.srcPort;
-        this.dstPort = p.dstPort;
-    }
-
-    /**
-     * Returns the TunnelPolicy builder reference.
-     *
-     * @return TunnelPolicy builder
-     */
-    public static TunnelPolicy.Builder builder() {
-        return new Builder();
-    }
-
-    @Override
-    public String id() {
-        return this.id;
-    }
-
-    @Override
-    public int priority() {
-        return priority;
-    }
-
-    @Override
-    public Type type() {
-        return type;
-    }
-
-    @Override
-    public String srcIp() {
-        return srcIp;
-    }
-
-    @Override
-    public String dstIp() {
-        return dstIp;
-    }
-
-    @Override
-    public String ipProto() {
-        return ipProto;
-    }
-
-    @Override
-    public short srcPort() {
-        return srcPort;
-    }
-
-    @Override
-    public short dstPort() {
-        return dstPort;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (o instanceof TunnelPolicy) {
-            TunnelPolicy that = (TunnelPolicy) o;
-            // We do not compare the policy ID
-            if (this.type.equals(that.type) &&
-                    this.tunnelId.equals(that.tunnelId) &&
-                    this.priority == that.priority &&
-                    this.srcIp.equals(that.srcIp) &&
-                    this.dstIp.equals(that.dstIp) &&
-                    this.srcPort == that.srcPort &&
-                    this.dstPort == that.dstPort &&
-                    this.ipProto.equals(that.ipProto)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(type, tunnelId, srcIp, dstIp, ipProto,
-                srcPort, dstPort, priority);
-    }
-
-    /**
-     * Returns the tunnel ID of the policy.
-     *
-     * @return Tunnel ID
-     */
-    public String tunnelId() {
-        return this.tunnelId;
-    }
-
-
-    /**
-     * Tunnel Policy Builder.
-     */
-    public static final class Builder {
-
-        private String id;
-        private Type type;
-        private int priority;
-        private String tunnelId;
-        private String dstIp;
-        private String srcIp;
-        private String ipProto;
-        private short srcPort;
-        private short dstPort;
-
-        /**
-         * Sets the policy Id.
-         *
-         * @param id policy Id
-         * @return Builder object
-         */
-        public Builder setPolicyId(String id) {
-            this.id = id;
-
-            return this;
-        }
-
-        /**
-         * Sets the policy type.
-         *
-         * @param type policy type
-         * @return Builder object
-         */
-        public Builder setType(Type type) {
-            this.type = type;
-
-            return this;
-        }
-
-        /**
-         * Sets the source IP address.
-         *
-         * @param srcIp source IP address
-         * @return Builder object
-         */
-        public Builder setSrcIp(String srcIp) {
-            this.srcIp = srcIp;
-
-            return this;
-        }
-
-        /**
-         * Sets the destination IP address.
-         *
-         * @param dstIp destination IP address
-         * @return Builder object
-         */
-        public Builder setDstIp(String dstIp) {
-            this.dstIp = dstIp;
-
-            return this;
-        }
-
-        /**
-         * Sets the IP protocol.
-         *
-         * @param proto IP protocol
-         * @return Builder object
-         */
-        public Builder setIpProto(String proto) {
-            this.ipProto = proto;
-
-            return this;
-        }
-
-        /**
-         * Sets the source port.
-         *
-         * @param srcPort source port
-         * @return Builder object
-         */
-        public Builder setSrcPort(short srcPort) {
-            this.srcPort = srcPort;
-
-            return this;
-        }
-
-        /**
-         * Sets the destination port.
-         *
-         * @param dstPort destination port
-         * @return Builder object
-         */
-        public Builder setDstPort(short dstPort) {
-            this.dstPort = dstPort;
-
-            return this;
-        }
-
-        /**
-         * Sets the priority of the policy.
-         *
-         * @param p priority
-         * @return Builder object
-         */
-        public Builder setPriority(int p) {
-            this.priority = p;
-
-            return this;
-        }
-
-        /**
-         * Sets the tunnel Id.
-         *
-         * @param tunnelId tunnel Id
-         * @return Builder object
-         */
-        public Builder setTunnelId(String tunnelId) {
-            this.tunnelId = tunnelId;
-
-            return this;
-        }
-
-        /**
-         * Builds the policy.
-         *
-         * @return Tunnel Policy reference
-         */
-        public Policy build() {
-            return new TunnelPolicy(id, type, priority, tunnelId, srcIp, dstIp,
-                    ipProto, srcPort, dstPort);
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/BlackHoleCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/BlackHoleCommand.java
deleted file mode 100644
index c45ff8e..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/BlackHoleCommand.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.Sets;
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.IpPrefix;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
-
-import java.util.Set;
-
-/**
- * CLI command for managing black hole routes.
- */
-@Service
-@Command(scope = "onos", name = "sr-blackhole",
-        description = "Manage black hole routes")
-public class BlackHoleCommand extends AbstractShellCommand {
-    @Argument(index = 0, name = "op",
-            description = "list, add or remove",
-            required = true, multiValued = false)
-    private String op;
-
-    @Argument(index = 1, name = "prefix",
-            description = "IP prefix",
-            required = false, multiValued = false)
-    private String prefix;
-
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService = AbstractShellCommand.get(SegmentRoutingService.class);
-        NetworkConfigService netcfgService = AbstractShellCommand.get(NetworkConfigService.class);
-
-        SegmentRoutingAppConfig appConfig = netcfgService.getConfig(srService.appId(), SegmentRoutingAppConfig.class);
-        if (appConfig == null) {
-            JsonNode jsonNode = new ObjectMapper().createObjectNode();
-            netcfgService.applyConfig(srService.appId(), SegmentRoutingAppConfig.class, jsonNode);
-            appConfig = netcfgService.getConfig(srService.appId(), SegmentRoutingAppConfig.class);
-        }
-
-        Set<IpPrefix> blackHoleIps;
-        switch (op) {
-            case "list":
-                appConfig.blackholeIPs().forEach(prefix -> print(prefix.toString()));
-                break;
-            case "add":
-                blackHoleIps = Sets.newConcurrentHashSet(appConfig.blackholeIPs());
-                blackHoleIps.add(IpPrefix.valueOf(prefix));
-                appConfig.setBalckholeIps(blackHoleIps);
-                break;
-            case "remove":
-                blackHoleIps = Sets.newConcurrentHashSet(appConfig.blackholeIPs());
-                blackHoleIps.remove(IpPrefix.valueOf(prefix));
-                appConfig.setBalckholeIps(blackHoleIps);
-                break;
-            default:
-                throw new UnsupportedOperationException("Unknown operation " + op);
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/DeviceSubnetListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/DeviceSubnetListCommand.java
deleted file mode 100644
index 8f706b0..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/DeviceSubnetListCommand.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.IpPrefix;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Command to list device-subnet mapping in Segment Routing.
- */
-@Service
-@Command(scope = "onos", name = "sr-device-subnets",
-        description = "List device-subnet mapping in Segment Routing")
-public class DeviceSubnetListCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        printDeviceSubnetMap(srService.getDeviceSubnetMap());
-    }
-
-    private void printDeviceSubnetMap(Map<DeviceId, Set<IpPrefix>> deviceSubnetMap) {
-        deviceSubnetMap.forEach(((deviceId, ipPrefices) -> {
-            print("%s", deviceId);
-            ipPrefices.forEach(ipPrefix -> print("    %s", ipPrefix));
-        }));
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/EcmpGraphCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/EcmpGraphCommand.java
deleted file mode 100644
index 269134d..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/EcmpGraphCommand.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import java.util.Map;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.EcmpShortestPathGraph;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-/**
- * Command to read the current state of the ECMP shortest-path graph.
- *
- */
-@Service
-@Command(scope = "onos", name = "sr-ecmp-spg",
-        description = "Displays the current ecmp shortest-path-graph in this "
-                + "controller instance")
-public class EcmpGraphCommand extends AbstractShellCommand {
-
-    private static final String FORMAT_MAPPING = "  %s";
-
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        printEcmpGraph(srService.getCurrentEcmpSpg());
-    }
-
-    private void printEcmpGraph(Map<DeviceId, EcmpShortestPathGraph> currentEcmpSpg) {
-        if (currentEcmpSpg == null) {
-            print(FORMAT_MAPPING, "No ECMP graph found");
-            return;
-        }
-        StringBuilder ecmp = new StringBuilder();
-        currentEcmpSpg.forEach((key, value) -> {
-            ecmp.append("\n\nRoot Device: " + key + " ECMP Paths: " + value);
-        });
-        print(FORMAT_MAPPING, ecmp.toString());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/InvalidateNextCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/InvalidateNextCommand.java
deleted file mode 100644
index 094bea9..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/InvalidateNextCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.osgi.ServiceNotFoundException;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-/**
- * Command to invalidate next id from SR internal stores.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-invalidate",
-        description = "Invalidate given next id from SR internal stores")
-public class InvalidateNextCommand extends AbstractShellCommand {
-
-    private static final String CONFIRM_PHRASE = "please";
-
-    @Argument(name = "nextId", description = "Next ID", index = 0)
-    private String nextId = null;
-
-    @Argument(name = "confirm", description = "Confirmation phrase", index = 1)
-    private String please = null;
-
-    @Override
-    protected void doExecute() {
-        if (please == null || !please.equals(CONFIRM_PHRASE)) {
-            print("WARNING: System may enter an unpredictable state if the next ID is force invalidated." +
-                    "Enter confirmation phrase to continue.");
-            return;
-        }
-
-        try {
-            SegmentRoutingService srService = AbstractShellCommand.get(SegmentRoutingService.class);
-            srService.invalidateNextObj(Integer.parseInt(nextId));
-        } catch (ServiceNotFoundException e) {
-            print("SegmentRoutingService unavailable");
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/LinkStateCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/LinkStateCommand.java
deleted file mode 100644
index 9d7147b..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/LinkStateCommand.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import java.util.Comparator;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.PortNumber;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-
-
-/**
- * Command to read the current state of the DestinationSetNextObjectiveStore.
- *
- */
-@Service
-@Command(scope = "onos", name = "sr-link-state", description = "Displays the current internal link state "
-        + "noted by this instance of the controller")
-public class LinkStateCommand extends AbstractShellCommand {
-    private static final String FORMAT_MAPPING = "  %s";
-
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService = AbstractShellCommand
-                .get(SegmentRoutingService.class);
-        printLinkState(srService.getSeenLinks(),
-                       srService.getDownedPortState());
-    }
-
-    private void printLinkState(ImmutableMap<Link, Boolean> seenLinks,
-                                ImmutableMap<DeviceId, Set<PortNumber>> downedPortState) {
-        List<Link> a = Lists.newArrayList();
-        a.addAll(seenLinks.keySet());
-        a.sort(new CompLinks());
-
-        StringBuilder slbldr = new StringBuilder();
-        slbldr.append("\n Seen Links: ");
-        for (int i = 0; i < a.size(); i++) {
-            slbldr.append("\n "
-                    + (seenLinks.get(a.get(i)) == Boolean.TRUE ? "  up : "
-                                                               : "down : "));
-            slbldr.append(a.get(i).src() + " --> " + a.get(i).dst());
-        }
-        print(FORMAT_MAPPING, slbldr.toString());
-
-        StringBuilder dpbldr = new StringBuilder();
-        dpbldr.append("\n\n Administratively Disabled Ports: ");
-        downedPortState.entrySet().forEach(entry -> dpbldr
-                .append("\n " + entry.getKey() + entry.getValue()));
-        print(FORMAT_MAPPING, dpbldr.toString());
-    }
-
-    static class CompLinks implements Comparator<Link> {
-
-        @Override
-        public int compare(Link o1, Link o2) {
-            int res = o1.src().deviceId().toString()
-                    .compareTo(o2.src().deviceId().toString());
-            if (res < 0) {
-                return -1;
-            } else if (res > 0) {
-                return +1;
-            }
-            if (o1.src().port().toLong() < o2.src().port().toLong()) {
-                return -1;
-            }
-            return +1;
-        }
-
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/McastFilterListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/McastFilterListCommand.java
deleted file mode 100644
index bc1eb42..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/McastFilterListCommand.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.mcast.McastFilteringObjStoreKey;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * Command to show the list of mcast filtering obj.
- */
-@Service
-@Command(scope = "onos", name = "sr-filt-mcast",
-        description = "Lists all mcast filtering objs")
-public class McastFilterListCommand extends AbstractShellCommand {
-
-    private static final String FORMAT_HEADER = "device=%s";
-    private static final String FILTER_HEADER = "\t%s,%s,%s";
-
-    @Override
-    protected void doExecute() {
-        // Get SR service
-        SegmentRoutingService srService = get(SegmentRoutingService.class);
-        // Get the filt objs
-        Map<DeviceId, List<McastFilteringObjStoreKey>> filteringObjKeys = srService.getMcastFilters();
-        filteringObjKeys.forEach(this::printMcastFilter);
-    }
-
-    private void printMcastFilter(DeviceId deviceId, List<McastFilteringObjStoreKey> filteringObjs) {
-        print(FORMAT_HEADER, deviceId);
-        filteringObjs.forEach(filteringObj -> print(FILTER_HEADER, filteringObj.ingressCP(),
-                                                    filteringObj.isIpv4() ? "IPv4" : "IPv6",
-                                                    filteringObj.vlanId()));
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/McastLeaderListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/McastLeaderListCommand.java
deleted file mode 100644
index 633745c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/McastLeaderListCommand.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.Option;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.IpAddress;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.mcast.cli.McastGroupCompleter;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-import java.util.Map;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-
-/**
- * Command to show the mcast leaders of the groups.
- */
-@Service
-@Command(scope = "onos", name = "sr-mcast-leader",
-        description = "Lists all mcast leaders")
-public class McastLeaderListCommand extends AbstractShellCommand {
-
-    // OSGi workaround to introduce package dependency
-    McastGroupCompleter completer;
-
-    // Format for group line
-    private static final String G_FORMAT_MAPPING = "group=%s, leader=%s";
-
-    @Option(name = "-gAddr", aliases = "--groupAddress",
-            description = "IP Address of the multicast group",
-            valueToShowInHelp = "224.0.0.0",
-            required = false, multiValued = false)
-    @Completion(McastGroupCompleter.class)
-    String gAddr = null;
-
-    @Override
-    protected void doExecute() {
-        // Verify mcast group
-        IpAddress mcastGroup = null;
-        if (!isNullOrEmpty(gAddr)) {
-            mcastGroup = IpAddress.valueOf(gAddr);
-        }
-        // Get SR service
-        SegmentRoutingService srService = get(SegmentRoutingService.class);
-        // Get the mapping
-        Map<IpAddress, NodeId> keyToRole = srService.getMcastLeaders(mcastGroup);
-        // And print local cache
-        keyToRole.forEach(this::printMcastLeder);
-    }
-
-    private void printMcastLeder(IpAddress mcastGroup,
-                                 NodeId nodeId) {
-        print(G_FORMAT_MAPPING, mcastGroup, nodeId);
-    }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/McastNextListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/McastNextListCommand.java
deleted file mode 100644
index 7dd6fd3..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/McastNextListCommand.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import com.google.common.collect.Maps;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.Option;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.mcast.cli.McastGroupCompleter;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.mcast.McastStoreKey;
-import org.apache.commons.lang3.tuple.Pair;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-
-/**
- * Command to show the list of mcast nextids.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-mcast",
-        description = "Lists all mcast nextids")
-public class McastNextListCommand extends AbstractShellCommand {
-
-    // OSGi workaround to introduce package dependency
-    McastGroupCompleter completer;
-
-    // Format for group line
-    private static final String FORMAT_MAPPING = "group=%s, deviceIds-nextIds=%s";
-
-    @Option(name = "-gAddr", aliases = "--groupAddress",
-            description = "IP Address of the multicast group",
-            valueToShowInHelp = "224.0.0.0",
-            required = false, multiValued = false)
-    @Completion(McastGroupCompleter.class)
-    String gAddr = null;
-
-    @Override
-    protected void doExecute() {
-        // Verify mcast group
-        IpAddress mcastGroup = null;
-        if (!isNullOrEmpty(gAddr)) {
-            mcastGroup = IpAddress.valueOf(gAddr);
-        }
-        // Get SR service
-        SegmentRoutingService srService = get(SegmentRoutingService.class);
-        // Get the mapping
-        Map<McastStoreKey, Integer> keyToNextId = srService.getMcastNextIds(mcastGroup);
-        // Reduce to the set of mcast groups
-        Set<IpAddress> mcastGroups = keyToNextId.keySet().stream()
-                .map(McastStoreKey::mcastIp)
-                .collect(Collectors.toSet());
-        // Print the nextids for each group
-        mcastGroups.forEach(group -> {
-            // Create a new map for the group
-            Map<Pair<DeviceId, VlanId>, Integer> deviceIdNextMap = Maps.newHashMap();
-            keyToNextId.entrySet()
-                    .stream()
-                    // Filter only the elements related to this group
-                    .filter(entry -> entry.getKey().mcastIp().equals(group))
-                    // For each create a new entry in the group related map
-                    .forEach(entry -> deviceIdNextMap.put(Pair.of(entry.getKey().deviceId(),
-                                                          entry.getKey().vlanId()), entry.getValue()));
-            // Print the map
-            printMcastNext(group, deviceIdNextMap);
-        });
-    }
-
-    private void printMcastNext(IpAddress mcastGroup, Map<Pair<DeviceId, VlanId>, Integer> deviceIdNextMap) {
-        print(FORMAT_MAPPING, mcastGroup, deviceIdNextMap);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/McastRoleListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/McastRoleListCommand.java
deleted file mode 100644
index 2b22c67..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/McastRoleListCommand.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import com.google.common.collect.ArrayListMultimap;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.Option;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.IpAddress;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cli.net.ConnectPointCompleter;
-import org.onosproject.mcast.cli.McastGroupCompleter;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.mcast.McastRole;
-import org.onosproject.segmentrouting.mcast.McastRoleStoreKey;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-
-/**
- * Command to show the list of mcast roles.
- */
-@Service
-@Command(scope = "onos", name = "sr-mcast-role",
-        description = "Lists all mcast roles")
-public class McastRoleListCommand extends AbstractShellCommand {
-
-    // OSGi workaround to introduce package dependency
-    McastGroupCompleter completer;
-
-    // Format for group line
-    private static final String FORMAT_MAPPING = "%s,%s  ingress=%s\ttransit=%s\tegress=%s";
-
-    @Option(name = "-gAddr", aliases = "--groupAddress",
-            description = "IP Address of the multicast group",
-            valueToShowInHelp = "224.0.0.0",
-            required = false, multiValued = false)
-    @Completion(McastGroupCompleter.class)
-    String gAddr = null;
-
-    @Option(name = "-src", aliases = "--connectPoint",
-            description = "Source port of:XXXXXXXXXX/XX",
-            valueToShowInHelp = "of:0000000000000001/1",
-            required = false, multiValued = false)
-    @Completion(ConnectPointCompleter.class)
-    String source = null;
-
-    @Override
-    protected void doExecute() {
-        // Verify mcast group
-        IpAddress mcastGroup = null;
-        // We want to use source cp only for a specific group
-        ConnectPoint sourcecp = null;
-        if (!isNullOrEmpty(gAddr)) {
-            mcastGroup = IpAddress.valueOf(gAddr);
-            if (!isNullOrEmpty(source)) {
-                sourcecp = ConnectPoint.deviceConnectPoint(source);
-            }
-        }
-        // Get SR service, the roles and the groups
-        SegmentRoutingService srService = get(SegmentRoutingService.class);
-        Map<McastRoleStoreKey, McastRole> keyToRole = srService.getMcastRoles(mcastGroup, sourcecp);
-        Set<IpAddress> mcastGroups = keyToRole.keySet().stream()
-                .map(McastRoleStoreKey::mcastIp)
-                .collect(Collectors.toSet());
-        // Print the trees for each group
-        mcastGroups.forEach(group -> {
-            // Create a new map for the group
-            Map<ConnectPoint, Multimap<McastRole, DeviceId>> roleDeviceIdMap = Maps.newHashMap();
-            keyToRole.entrySet()
-                    .stream()
-                    .filter(entry -> entry.getKey().mcastIp().equals(group))
-                    .forEach(entry -> roleDeviceIdMap.compute(entry.getKey().source(), (gsource, map) -> {
-                        map = map == null ? ArrayListMultimap.create() : map;
-                        map.put(entry.getValue(), entry.getKey().deviceId());
-                        return map;
-                    }));
-            roleDeviceIdMap.forEach((gsource, map) -> {
-                // Print the map
-                printMcastRole(group, gsource,
-                               map.get(McastRole.INGRESS),
-                               map.get(McastRole.TRANSIT),
-                               map.get(McastRole.EGRESS));
-            });
-        });
-    }
-
-    private void printMcastRole(IpAddress mcastGroup, ConnectPoint source,
-                                Collection<DeviceId> ingress,
-                                Collection<DeviceId> transit,
-                                Collection<DeviceId> egress) {
-        print(FORMAT_MAPPING, mcastGroup, source, ingress, transit, egress);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/McastTreeListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/McastTreeListCommand.java
deleted file mode 100644
index 09c795c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/McastTreeListCommand.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.Option;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.IpAddress;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cli.net.ConnectPointCompleter;
-import org.onosproject.mcast.cli.McastGroupCompleter;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Strings.isNullOrEmpty;
-
-/**
- * Command to show the list of mcast trees.
- */
-@Service
-@Command(scope = "onos", name = "sr-mcast-tree",
-        description = "Lists all mcast trees")
-public class McastTreeListCommand extends AbstractShellCommand {
-
-    // OSGi workaround to introduce package dependency
-    McastGroupCompleter completer;
-
-    // Format for group line
-    private static final String G_FORMAT_MAPPING = "group=%s";
-    // Format for sink line
-    private static final String S_FORMAT_MAPPING = "  sink=%s\tpath=%s";
-
-    @Option(name = "-gAddr", aliases = "--groupAddress",
-            description = "IP Address of the multicast group",
-            valueToShowInHelp = "224.0.0.0",
-            required = false, multiValued = false)
-    @Completion(McastGroupCompleter.class)
-    String gAddr = null;
-
-    @Option(name = "-src", aliases = "--connectPoint",
-            description = "Source port of:XXXXXXXXXX/XX",
-            valueToShowInHelp = "of:0000000000000001/1",
-            required = false, multiValued = false)
-    @Completion(ConnectPointCompleter.class)
-    String source = null;
-
-    @Override
-    protected void doExecute() {
-        // Get SR service and the handled mcast groups
-        SegmentRoutingService srService = get(SegmentRoutingService.class);
-        Set<IpAddress> mcastGroups = ImmutableSet.copyOf(srService.getMcastLeaders(null)
-                                                                         .keySet());
-
-        if (!isNullOrEmpty(gAddr)) {
-            mcastGroups = mcastGroups.stream()
-                    .filter(mcastIp -> mcastIp.equals(IpAddress.valueOf(gAddr)))
-                    .collect(Collectors.toSet());
-        }
-
-        ObjectMapper mapper = new ObjectMapper();
-        ObjectNode root = mapper.createObjectNode();
-
-        // Print the trees for each group or build json objects
-        mcastGroups.forEach(group -> {
-            // We want to use source cp only for a specific group
-            ConnectPoint sourcecp = null;
-            if (!isNullOrEmpty(source) &&
-                    !isNullOrEmpty(gAddr)) {
-                sourcecp = ConnectPoint.deviceConnectPoint(source);
-            }
-            Multimap<ConnectPoint, List<ConnectPoint>> mcastTree = srService.getMcastTrees(group,
-                                                                                           sourcecp);
-            if (!mcastTree.isEmpty()) {
-                // Build a json object for each group
-                if (outputJson()) {
-                    root.putPOJO(group.toString(), json(mcastTree));
-                } else {
-                    // Banner and then the trees
-                    printMcastGroup(group);
-                    mcastTree.forEach(this::printMcastSink);
-                }
-            }
-        });
-
-        // Print the json object at the end
-        if (outputJson()) {
-            print("%s", root);
-        }
-
-    }
-
-    private void printMcastGroup(IpAddress mcastGroup) {
-        print(G_FORMAT_MAPPING, mcastGroup);
-    }
-
-    private void printMcastSink(ConnectPoint sink, List<ConnectPoint> path) {
-        print(S_FORMAT_MAPPING, sink, path);
-    }
-
-    private ObjectNode json(Multimap<ConnectPoint, List<ConnectPoint>> mcastTree) {
-        ObjectMapper mapper = new ObjectMapper();
-        ObjectNode jsonSinks = mapper.createObjectNode();
-        mcastTree.asMap().forEach((sink, paths) -> {
-            ArrayNode jsonPaths = mapper.createArrayNode();
-            paths.forEach(path -> {
-                ArrayNode jsonPath = mapper.createArrayNode();
-                path.forEach(connectPoint -> jsonPath.add(connectPoint.toString()));
-                jsonPaths.addPOJO(jsonPath);
-            });
-            jsonSinks.putPOJO(sink.toString(), jsonPaths);
-        });
-        return jsonSinks;
-    }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/NextDstCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/NextDstCommand.java
deleted file mode 100644
index e6da695..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/NextDstCommand.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Map;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.grouphandler.NextNeighbors;
-import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
-
-/**
- * Command to read the current state of the DestinationSetNextObjectiveStore.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-dst",
-        description = "Displays the current next-hops seen by each switch "
-                + "towards a set of destinations and the next-id it maps to")
-public class NextDstCommand extends AbstractShellCommand {
-
-    private static final String FORMAT_MAPPING = "  %s";
-
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        printDestinationSet(srService.getDstNextObjStore());
-    }
-
-    private void printDestinationSet(Map<DestinationSetNextObjectiveStoreKey,
-                                      NextNeighbors> ds) {
-        ArrayList<DestinationSetNextObjectiveStoreKey> a = new ArrayList<>();
-        ds.keySet().forEach(key -> a.add(key));
-        a.sort(new Comp());
-
-        StringBuilder dsbldr = new StringBuilder();
-        for (int i = 0; i < a.size(); i++) {
-            dsbldr.append("\n " + a.get(i));
-            dsbldr.append(" --> via: " + ds.get(a.get(i)));
-        }
-        print(FORMAT_MAPPING, dsbldr.toString());
-    }
-
-    static class Comp implements Comparator<DestinationSetNextObjectiveStoreKey> {
-
-        @Override
-        public int compare(DestinationSetNextObjectiveStoreKey o1,
-                           DestinationSetNextObjectiveStoreKey o2) {
-            int res = o1.deviceId().toString().compareTo(o2.deviceId().toString());
-            if (res < 0) {
-                return -1;
-            } else if (res > 0) {
-                return +1;
-            }
-            return 0;
-        }
-
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/NextMacVlanCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/NextMacVlanCommand.java
deleted file mode 100644
index e31e693..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/NextMacVlanCommand.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Command to read the current state of the macVlanNextObjStore.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-mac-vlan",
-        description = "Displays the current next-hop / next-id it mapping")
-public class NextMacVlanCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        print(srService.getMacVlanNextObjStore());
-    }
-
-    private void print(Map<MacVlanNextObjectiveStoreKey, Integer> macVlanNextObjStore) {
-        ArrayList<MacVlanNextObjectiveStoreKey> a = new ArrayList<>(macVlanNextObjStore.keySet());
-        a.sort(Comparator
-                .comparing((MacVlanNextObjectiveStoreKey o) -> o.deviceId().toString())
-                .thenComparing((MacVlanNextObjectiveStoreKey o) -> o.vlanId().toString())
-                .thenComparing((MacVlanNextObjectiveStoreKey o) -> o.macAddr().toString()));
-
-        StringBuilder builder = new StringBuilder();
-        a.forEach(k ->
-            builder.append("\n")
-                    .append(k)
-                    .append(" --> ")
-                    .append(macVlanNextObjStore.get(k))
-        );
-        print(builder.toString());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/NextPortCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/NextPortCommand.java
deleted file mode 100644
index fddc1d7..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/NextPortCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Command to read the current state of the portNextObjStore.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-port",
-        description = "Displays the current port / next-id it mapping")
-public class NextPortCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        print(srService.getPortNextObjStore());
-    }
-
-    private void print(Map<PortNextObjectiveStoreKey, Integer> portNextObjStore) {
-        ArrayList<PortNextObjectiveStoreKey> a = new ArrayList<>(portNextObjStore.keySet());
-        a.sort(Comparator
-                .comparing((PortNextObjectiveStoreKey o) -> o.deviceId().toString())
-                .thenComparing((PortNextObjectiveStoreKey o) -> o.portNumber().toLong()));
-
-        StringBuilder builder = new StringBuilder();
-        a.forEach(k ->
-            builder.append("\n")
-                    .append(k)
-                    .append(" --> ")
-                    .append(portNextObjStore.get(k))
-        );
-        print(builder.toString());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/NextVlanCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/NextVlanCommand.java
deleted file mode 100644
index 8518a5f..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/NextVlanCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Command to read the current state of the vlanNextObjStore.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-vlan",
-        description = "Displays the current vlan / next-id it mapping")
-public class NextVlanCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        print(srService.getVlanNextObjStore());
-    }
-
-    private void print(Map<VlanNextObjectiveStoreKey, Integer> vlanNextObjStore) {
-        ArrayList<VlanNextObjectiveStoreKey> a = new ArrayList<>(vlanNextObjStore.keySet());
-        a.sort(Comparator
-                .comparing((VlanNextObjectiveStoreKey o) -> o.deviceId().toString())
-                .thenComparing((VlanNextObjectiveStoreKey o) -> o.vlanId().toShort()));
-
-        StringBuilder builder = new StringBuilder();
-        a.forEach(k ->
-            builder.append("\n")
-                    .append(k)
-                    .append(" --> ")
-                    .append(vlanNextObjStore.get(k))
-        );
-        print(builder.toString());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PhaseCompleter.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PhaseCompleter.java
deleted file mode 100644
index bd16068..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PhaseCompleter.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2014-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import com.google.common.collect.Lists;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractChoicesCompleter;
-import org.onosproject.segmentrouting.phasedrecovery.api.Phase;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Phase completer.
- */
-@Service
-public class PhaseCompleter extends AbstractChoicesCompleter {
-    @Override
-    protected List<String> choices() {
-        return Lists.newArrayList(Phase.values()).stream().map(Enum::toString).collect(Collectors.toList());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PhasedRecoveryListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PhasedRecoveryListCommand.java
deleted file mode 100644
index 6efdaaa..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PhasedRecoveryListCommand.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
-
-@Service
-@Command(scope = "onos", name = "sr-pr-list", description = "List current recovery phase of each device")
-
-public class PhasedRecoveryListCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        PhasedRecoveryService prService = get(PhasedRecoveryService.class);
-        print(prService.getPhases().toString());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PhasedRecoverySetCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PhasedRecoverySetCommand.java
deleted file mode 100644
index 50e3bc6..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PhasedRecoverySetCommand.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cli.net.DeviceIdCompleter;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.phasedrecovery.api.Phase;
-import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
-
-@Service
-@Command(scope = "onos", name = "sr-pr-set", description = "Set recovery phase of given device")
-
-public class PhasedRecoverySetCommand extends AbstractShellCommand {
-    @Argument(index = 0, name = "deviceId",
-            description = "Device ID",
-            required = true, multiValued = false)
-    @Completion(DeviceIdCompleter.class)
-    private String deviceIdStr;
-
-    @Argument(index = 1, name = "phase",
-            description = "Recovery phase",
-            required = true, multiValued = false)
-    @Completion(PhaseCompleter.class)
-    private String phaseStr;
-
-    @Override
-    protected void doExecute() {
-        DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
-        Phase newPhase = Phase.valueOf(phaseStr);
-
-        PhasedRecoveryService prService = get(PhasedRecoveryService.class);
-        prService.setPhase(deviceId, newPhase);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java
deleted file mode 100644
index 1f75317..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyAddCommand.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.Policy;
-import org.onosproject.segmentrouting.PolicyHandler;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.TunnelPolicy;
-
-/**
- * Command to add a new policy.
- */
-@Service
-@Command(scope = "onos", name = "sr-policy-add",
-        description = "Create a new policy")
-public class PolicyAddCommand extends AbstractShellCommand {
-
-    // TODO: Need to support skipping some parameters
-
-    @Argument(index = 0, name = "ID",
-            description = "policy ID",
-            required = true, multiValued = false)
-    String policyId;
-
-    @Argument(index = 1, name = "priority",
-            description = "priority",
-            required = true, multiValued = false)
-    int priority;
-
-    @Argument(index = 2, name = "src_IP",
-            description = "src IP",
-            required = false, multiValued = false)
-    String srcIp;
-
-    @Argument(index = 3, name = "src_port",
-            description = "src port",
-            required = false, multiValued = false)
-    short srcPort;
-
-    @Argument(index = 4, name = "dst_IP",
-            description = "dst IP",
-            required = false, multiValued = false)
-    String dstIp;
-
-    @Argument(index = 5, name = "dst_port",
-            description = "dst port",
-            required = false, multiValued = false)
-    short dstPort;
-
-    @Argument(index = 6, name = "proto",
-            description = "IP protocol",
-            required = false, multiValued = false)
-    String proto;
-
-    @Argument(index = 7, name = "policy_type",
-            description = "policy type",
-            required = true, multiValued = false)
-    String policyType;
-
-    @Argument(index = 8, name = "tunnel_ID",
-            description = "tunnel ID",
-            required = false, multiValued = false)
-    String tunnelId;
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(policyId);
-        tpb.setPriority(priority);
-        tpb.setType(Policy.Type.valueOf(policyType));
-
-        if (srcIp != null) {
-            tpb.setSrcIp(srcIp);
-        }
-        if (dstIp != null) {
-            tpb.setDstIp(dstIp);
-        }
-        if (srcPort != 0) {
-            tpb.setSrcPort(srcPort);
-        }
-        if (dstPort != 0) {
-            tpb.setDstPort(dstPort);
-        }
-        if (!"ip".equals(proto)) {
-            tpb.setIpProto(proto);
-        }
-        if (Policy.Type.valueOf(policyType) == Policy.Type.TUNNEL_FLOW) {
-            if (tunnelId == null) {
-                error("tunnel ID must be specified for TUNNEL_FLOW policy");
-                return;
-            }
-            tpb.setTunnelId(tunnelId);
-        }
-        PolicyHandler.Result result = srService.createPolicy(tpb.build());
-
-        switch (result) {
-            case POLICY_EXISTS:
-                error("the same policy exists");
-                break;
-            case ID_EXISTS:
-                error("the same policy ID exists");
-                break;
-            case TUNNEL_NOT_FOUND:
-                error("the tunnel is not found");
-                break;
-            case UNSUPPORTED_TYPE:
-                error("the policy type specified is not supported");
-                break;
-            default:
-                break;
-        }
-
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java
deleted file mode 100644
index 3520fba..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyListCommand.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.Policy;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.TunnelPolicy;
-
-/**
- * Command to show the list of policies.
- */
-@Service
-@Command(scope = "onos", name = "sr-policy-list",
-        description = "Lists all policies")
-public class PolicyListCommand extends AbstractShellCommand {
-
-    private static final String FORMAT_MAPPING_TUNNEL =
-            "  id=%s, type=%s,  prio=%d, src=%s, port=%d, dst=%s, port=%d, proto=%s, tunnel=%s";
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        srService.getPolicies().forEach(policy -> printPolicy(policy));
-    }
-
-    private void printPolicy(Policy policy) {
-        if (policy.type() == Policy.Type.TUNNEL_FLOW) {
-            print(FORMAT_MAPPING_TUNNEL, policy.id(), policy.type(), policy.priority(),
-                    policy.srcIp(), policy.srcPort(), policy.dstIp(), policy.dstPort(),
-                    (policy.ipProto() == null) ? "" : policy.ipProto(),
-                    ((TunnelPolicy) policy).tunnelId());
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java
deleted file mode 100644
index 575d35f..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PolicyRemoveCommand.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.PolicyHandler;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.TunnelPolicy;
-
-/**
- * Command to remove a policy.
- */
-@Service
-@Command(scope = "onos", name = "sr-policy-remove",
-        description = "Remove a policy")
-public class PolicyRemoveCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "policy ID",
-            description = "policy ID",
-            required = true, multiValued = false)
-    String policyId;
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        TunnelPolicy.Builder tpb = TunnelPolicy.builder().setPolicyId(policyId);
-        PolicyHandler.Result result = srService.removePolicy(tpb.build());
-        if (result == PolicyHandler.Result.POLICY_NOT_FOUND) {
-            print("ERROR: the policy is not found");
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PortsCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PortsCommand.java
deleted file mode 100644
index f24d28f..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PortsCommand.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cli.PlaceholderCompleter;
-import org.onosproject.cli.net.DeviceIdCompleter;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-
-@Service
-@Command(scope = "onos", name = "sr-ports", description = "Enable/Disable group of ports on a specific device")
-
-public class PortsCommand extends AbstractShellCommand {
-    @Argument(index = 0, name = "deviceId",
-            description = "Device ID",
-            required = true, multiValued = false)
-    @Completion(DeviceIdCompleter.class)
-    private String deviceIdStr;
-
-    @Argument(index = 1, name = "ports",
-            description = "Ports to be enabled/disabled: ALL, PAIR, INFRA, EDGE",
-            required = true, multiValued = false)
-    @Completion(PlaceholderCompleter.class)
-    private String portsStr;
-
-    @Argument(index = 2, name = "action",
-            description = "Action: ENABLE, DISABLE",
-            required = true, multiValued = false)
-    @Completion(PlaceholderCompleter.class)
-    private String actionStr;
-
-    @Override
-    protected void doExecute() {
-        PhasedRecoveryService prService = get(PhasedRecoveryService.class);
-
-        DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
-
-        boolean enabled;
-        switch (actionStr.toUpperCase()) {
-            case "ENABLE":
-                enabled = true;
-                break;
-            case "DISABLE":
-                enabled = false;
-                break;
-            default:
-                print("Action should be either ENABLE or DISABLE");
-                return;
-        }
-
-        Set<PortNumber> portsChanged;
-        switch (portsStr.toUpperCase()) {
-            case "ALL":
-                portsChanged = prService.changeAllPorts(deviceId, enabled);
-                break;
-            case "PAIR":
-                portsChanged = prService.changePairPort(deviceId, enabled);
-                break;
-            case "INFRA":
-                portsChanged = prService.changeInfraPorts(deviceId, enabled);
-                break;
-            case "EDGE":
-                portsChanged = prService.changeEdgePorts(deviceId, enabled);
-                break;
-            default:
-                print("Ports should be ALL, PAIR, INFRA, EDGE");
-                return;
-        }
-        print("Ports set to %s: %s",
-                enabled ? "enabled" : "disabled",
-                portsChanged.stream().map(PortNumber::toLong).collect(Collectors.toSet()));
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java
deleted file mode 100644
index d595d5c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireAddCommand.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.pwaas.DefaultL2Tunnel;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelDescription;
-import org.onosproject.segmentrouting.pwaas.DefaultL2TunnelPolicy;
-import org.onosproject.segmentrouting.pwaas.L2Tunnel;
-import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
-import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
-import org.onosproject.segmentrouting.pwaas.L2TunnelPolicy;
-
-import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
-
-
-/**
- * Command to add a pseuwodire.
- */
-@Service
-@Command(scope = "onos", name = "sr-pw-add",
-        description = "Add a pseudowire to the network configuration, if it already exists update it.")
-public class PseudowireAddCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "pwId",
-            description = "Pseudowire ID",
-            required = true, multiValued = false)
-    String pwId;
-
-    @Argument(index = 1, name = "pwLabel",
-            description = "Pseudowire Label",
-            required = true, multiValued = false)
-    String pwLabel;
-
-    @Argument(index = 2, name = "mode",
-            description = "Mode used for pseudowire",
-            required = true, multiValued = false)
-    String mode;
-
-    @Argument(index = 3, name = "sDTag",
-            description = "Service delimiting tag",
-            required = true, multiValued = false)
-    String sDTag;
-
-    @Argument(index = 4, name = "cP1",
-            description = "Connection Point 1",
-            required = true, multiValued = false)
-    String cP1;
-
-    @Argument(index = 5, name = "cP1InnerVlan",
-            description = "Inner Vlan of Connection Point 1",
-            required = true, multiValued = false)
-    String cP1InnerVlan;
-
-    @Argument(index = 6, name = "cP1OuterVlan",
-            description = "Outer Vlan of Connection Point 1",
-            required = true, multiValued = false)
-    String cP1OuterVlan;
-
-    @Argument(index = 7, name = "cP2",
-            description = "Connection Point 2",
-            required = true, multiValued = false)
-    String cP2;
-
-    @Argument(index = 8, name = "cP2InnerVlan",
-            description = "Inner Vlan of Connection Point 2",
-            required = true, multiValued = false)
-    String cP2InnerVlan;
-
-    @Argument(index = 9, name = "cP2OuterVlan",
-            description = "Outer Vlan of Connection Point 2",
-            required = true, multiValued = false)
-    String cP2OuterVlan;
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        L2Tunnel tun;
-        L2TunnelPolicy policy;
-
-        try {
-            tun = new DefaultL2Tunnel(parseMode(mode), parseVlan(sDTag), parsePwId(pwId), parsePWLabel(pwLabel));
-        } catch (IllegalArgumentException e) {
-            log.error("Exception while parsing L2Tunnel : \n\t %s", e.getMessage());
-            print("Exception while parsing L2Tunnel : \n\t %s", e.getMessage());
-            return;
-        }
-
-        try {
-            policy = new DefaultL2TunnelPolicy(parsePwId(pwId),
-                                               ConnectPoint.deviceConnectPoint(cP1), parseVlan(cP1InnerVlan),
-                                               parseVlan(cP1OuterVlan), ConnectPoint.deviceConnectPoint(cP2),
-                                               parseVlan(cP2InnerVlan), parseVlan(cP2OuterVlan));
-
-        } catch (IllegalArgumentException e) {
-            log.error("Exception while parsing L2TunnelPolicy : \n\t %s", e.getMessage());
-            print("Exception while parsing L2TunnelPolicy : \n\t %s", e.getMessage());
-            return;
-        }
-
-        L2TunnelDescription pw = new DefaultL2TunnelDescription(tun, policy);
-        L2TunnelHandler.Result res = srService.addPseudowire(pw);
-        log.info("Deploying pseudowire {} via the command line.", pw);
-        switch (res) {
-            case WRONG_PARAMETERS:
-                print("Pseudowire could not be added , error in the parameters : \n\t%s",
-                      res.getSpecificError());
-                break;
-            case CONFIGURATION_ERROR:
-                print("Pseudowire could not be added, configuration error : \n\t%s",
-                      res.getSpecificError());
-                break;
-            case PATH_NOT_FOUND:
-                print("Pseudowire path not found : \n\t%s",
-                      res.getSpecificError());
-                break;
-            case INTERNAL_ERROR:
-                print("Pseudowire could not be added, internal error : \n\t%s",
-                      res.getSpecificError());
-                break;
-            case SUCCESS:
-                break;
-            default:
-                break;
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java
deleted file mode 100644
index abf0c0c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireIdCompleter.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright 2014-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.apache.karaf.shell.api.console.CommandLine;
-import org.apache.karaf.shell.api.console.Completer;
-import org.apache.karaf.shell.api.console.Session;
-import org.apache.karaf.shell.support.completers.StringsCompleter;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.pwaas.L2Tunnel;
-
-import java.util.Iterator;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.stream.Collectors;
-
-/**
- * Device ID completer.
- */
-@Service
-public class PseudowireIdCompleter implements Completer {
-    @Override
-    public int complete(Session session, CommandLine commandLine, List<String> candidates) {
-        // Delegate string completer
-        StringsCompleter delegate = new StringsCompleter();
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-
-        List<L2Tunnel> tunnels = srService.getL2Tunnels();
-
-        // combine polices and tunnels to pseudowires
-        Iterator<String> pseudowires = tunnels.stream()
-                .map(l2Tunnel -> Long.toString(l2Tunnel.tunnelId()))
-                .collect(Collectors.toList()).iterator();
-
-        SortedSet<String> strings = delegate.getStrings();
-        while (pseudowires.hasNext()) {
-            strings.add(pseudowires.next());
-        }
-
-        // Now let the completer do the work for figuring out what to offer.
-        return delegate.complete(session, commandLine, candidates);
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
deleted file mode 100644
index 475ce26..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireListCommand.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.VlanId;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.pwaas.L2TunnelDescription;
-/**
- * Command to show the pseudowires.
- */
-@Service
-@Command(scope = "onos", name = "sr-pw-list",
-        description = "Lists all pseudowires")
-public class PseudowireListCommand extends AbstractShellCommand {
-
-    private static final String FORMAT_PSEUDOWIRE =
-            "Pseudowire id = %s \n" +
-                    "   mode : %s, sdTag : %s, pwLabel : %s \n" +
-                    "   cP1 : %s , cP1OuterTag : %s, cP1InnerTag : %s \n" +
-                    "   cP2 : %s , cP2OuterTag : %s, cP2InnerTag : %s \n" +
-                    "   transportVlan : %s \n" +
-                    "   pending = %s";
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        srService.getL2TunnelDescriptions(false)
-                .forEach(pw -> printPseudowire(pw, false));
-
-        srService.getL2TunnelDescriptions(true)
-                .forEach(pw -> printPseudowire(pw, true));
-    }
-
-    private void printPseudowire(L2TunnelDescription pseudowire, boolean pending) {
-        VlanId vlan = pseudowire.l2Tunnel().transportVlan().equals(VlanId.vlanId((short) 4094)) ?
-                VlanId.NONE : pseudowire.l2Tunnel().transportVlan();
-
-        print(FORMAT_PSEUDOWIRE, pseudowire.l2Tunnel().tunnelId(), pseudowire.l2Tunnel().pwMode(),
-              pseudowire.l2Tunnel().sdTag(), pseudowire.l2Tunnel().pwLabel(),
-              pseudowire.l2TunnelPolicy().cP1(), pseudowire.l2TunnelPolicy().cP1OuterTag(),
-              pseudowire.l2TunnelPolicy().cP1InnerTag(), pseudowire.l2TunnelPolicy().cP2(),
-              pseudowire.l2TunnelPolicy().cP2OuterTag(), pseudowire.l2TunnelPolicy().cP2InnerTag(),
-              vlan, pending);
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireNextListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireNextListCommand.java
deleted file mode 100644
index f2b3e8d..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireNextListCommand.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Command to read the current state of the pseudowire next stores.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-pw",
-        description = "Displays the current next-id for pseudowire")
-public class PseudowireNextListCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        print(srService.getPwInitNext());
-        print(srService.getPwTermNext());
-    }
-
-    private void print(Map<String, NextObjective> nextStore) {
-        ArrayList<String> a = new ArrayList<>(nextStore.keySet());
-        a.sort(Comparator.comparing((String o) -> o));
-
-        StringBuilder builder = new StringBuilder();
-        a.forEach(k ->
-            builder.append("\n")
-                    .append(k)
-                    .append(" --> ")
-                    .append(nextStore.get(k).id())
-        );
-        print(builder.toString());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java
deleted file mode 100644
index 2bb28b0..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/PseudowireRemoveCommand.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.pwaas.L2TunnelHandler;
-
-import static org.onosproject.segmentrouting.pwaas.PwaasUtil.parsePwId;
-
-
-/**
- * Command to remove a pseudowire.
- */
-@Service
-@Command(scope = "onos", name = "sr-pw-remove",
-        description = "Remove a pseudowire")
-public class PseudowireRemoveCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "pwId",
-            description = "pseudowire ID",
-            required = true, multiValued = false)
-    @Completion(PseudowireIdCompleter.class)
-    String pwId;
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        // remove the pseudowire
-        SegmentRoutingManager mngr = (SegmentRoutingManager) srService;
-        int pwIntId;
-        try {
-            pwIntId = parsePwId(pwId);
-        } catch (IllegalArgumentException e) {
-            log.error("Exception while parsing pseudowire id : \n\t %s", e.getMessage());
-            print("Exception while parsing pseudowire id : \n\t %s", e.getMessage());
-            return;
-        }
-
-        log.info("Removing pseudowire {} from the command line.", pwIntId);
-        L2TunnelHandler.Result res = mngr.removePseudowire(pwIntId);
-        switch (res) {
-            case WRONG_PARAMETERS:
-                error("Pseudowire could not be removed , wrong parameters: \n\t %s\n",
-                      res.getSpecificError());
-                break;
-            case INTERNAL_ERROR:
-                error("Pseudowire could not be removed, internal error : \n\t %s\n",
-                      res.getSpecificError());
-                break;
-            case SUCCESS:
-                break;
-            default:
-                break;
-            }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/RerouteNetworkCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/RerouteNetworkCommand.java
deleted file mode 100644
index 6e8d3e9..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/RerouteNetworkCommand.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-/**
- * Command to manually trigger routing and rule-population in the network.
- *
- */
-@Service
-@Command(scope = "onos", name = "sr-reroute-network",
-        description = "Repopulate routing rules given current network state")
-public class RerouteNetworkCommand extends AbstractShellCommand {
-
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-        srService.rerouteNetwork();
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/ShouldProgramCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/ShouldProgramCommand.java
deleted file mode 100644
index 250fb3b..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/ShouldProgramCommand.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Display current shouldProgram map.
- */
-@Service
-@Command(scope = "onos", name = "sr-should-program",
-        description = "Display current shouldProgram map")
-public class ShouldProgramCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService = AbstractShellCommand.get(SegmentRoutingService.class);
-        Map<Set<DeviceId>, NodeId> shouldProgram = srService.getShouldProgram();
-        Map<DeviceId, Boolean> shouldProgramCache = srService.getShouldProgramCache();
-
-        print("shouldProgram");
-        shouldProgram.forEach((k, v) -> print("%s -> %s", k, v));
-
-        print("shouldProgramCache");
-        shouldProgramCache.forEach((k, v) -> print("%s -> %s", k, v));
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java
deleted file mode 100644
index 0e487d0..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelAddCommand.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.DefaultTunnel;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.Tunnel;
-import org.onosproject.segmentrouting.TunnelHandler;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.StringTokenizer;
-
-/**
- * Command to add a new tunnel.
- */
-@Service
-@Command(scope = "onos", name = "sr-tunnel-add",
-        description = "Create a new tunnel")
-public class TunnelAddCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "tunnel ID",
-            description = "tunnel ID",
-            required = true, multiValued = false)
-    String tunnelId;
-
-    @Argument(index = 1, name = "label path",
-            description = "label path",
-            required = true, multiValued = false)
-    String labels;
-
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        List<Integer> labelIds = new ArrayList<>();
-        StringTokenizer strToken = new StringTokenizer(labels, ",");
-        while (strToken.hasMoreTokens()) {
-            labelIds.add(Integer.valueOf(strToken.nextToken()));
-        }
-        Tunnel tunnel = new DefaultTunnel(tunnelId, labelIds);
-
-        TunnelHandler.Result result = srService.createTunnel(tunnel);
-        switch (result) {
-            case ID_EXISTS:
-                print("ERROR: the same tunnel ID exists");
-                break;
-            case TUNNEL_EXISTS:
-                print("ERROR: the same tunnel exists");
-                break;
-            case INTERNAL_ERROR:
-                print("ERROR: internal tunnel creation error");
-                break;
-            case WRONG_PATH:
-                print("ERROR: the tunnel path is wrong");
-                break;
-            default:
-                break;
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java
deleted file mode 100644
index 6c5f33d..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelListCommand.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.Tunnel;
-
-/**
- * Command to show the list of tunnels.
- */
-@Service
-@Command(scope = "onos", name = "sr-tunnel-list",
-        description = "Lists all tunnels")
-public class TunnelListCommand extends AbstractShellCommand {
-
-    private static final String FORMAT_MAPPING =
-            "  id=%s, path=%s";
-
-    @Override
-    protected void doExecute() {
-
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        srService.getTunnels().forEach(tunnel -> printTunnel(tunnel));
-    }
-
-    private void printTunnel(Tunnel tunnel) {
-        print(FORMAT_MAPPING, tunnel.id(), tunnel.labelIds());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java
deleted file mode 100644
index f5f0299..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/TunnelRemoveCommand.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-
-import com.google.common.collect.Lists;
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.DefaultTunnel;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.Tunnel;
-import org.onosproject.segmentrouting.TunnelHandler;
-
-/**
- * Command to remove a tunnel.
- */
-@Service
-@Command(scope = "onos", name = "sr-tunnel-remove",
-        description = "Remove a tunnel")
-public class TunnelRemoveCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "tunnel ID",
-            description = "tunnel ID",
-            required = true, multiValued = false)
-    String tunnelId;
-
-    @Override
-    protected void doExecute() {
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        Tunnel tunnel = new DefaultTunnel(tunnelId, Lists.newArrayList());
-        TunnelHandler.Result result = srService.removeTunnel(tunnel);
-        switch (result) {
-            case TUNNEL_IN_USE:
-                print("ERROR: the tunnel is still in use");
-                break;
-            case TUNNEL_NOT_FOUND:
-                print("ERROR: the tunnel is not found");
-                break;
-            default:
-                break;
-        }
-    }
-}
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/VerifyGroupsCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/VerifyGroupsCommand.java
deleted file mode 100644
index 385260e..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/VerifyGroupsCommand.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cli.net.DeviceIdCompleter;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-
-/**
- * Triggers the verification of hashed group buckets in the specified device,
- * and corrects the buckets if necessary. Outcome can be viewed in the 'groups'
- * command.
- */
-@Service
-@Command(scope = "onos", name = "sr-verify-groups",
-        description = "Triggers the verification of hashed groups in the specified "
-                + "device. Does not return any output; users can query the results "
-                + "in the 'groups' command")
-public class VerifyGroupsCommand extends AbstractShellCommand {
-
-    @Argument(index = 0, name = "uri", description = "Device ID",
-            required = true, multiValued = false)
-    @Completion(DeviceIdCompleter.class)
-    String uri = null;
-
-    @Override
-    protected void doExecute() {
-        DeviceService deviceService = get(DeviceService.class);
-        SegmentRoutingService srService =
-                AbstractShellCommand.get(SegmentRoutingService.class);
-
-        if (uri != null) {
-            Device dev = deviceService.getDevice(DeviceId.deviceId(uri));
-            if (dev != null) {
-                srService.verifyGroups(dev.id());
-            }
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectAddCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectAddCommand.java
deleted file mode 100644
index 22daeb5..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectAddCommand.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import com.google.common.collect.Sets;
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.VlanId;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cli.PlaceholderCompleter;
-import org.onosproject.cli.net.DeviceIdCompleter;
-import org.onosproject.cli.net.PortNumberCompleter;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.xconnect.api.XconnectEndpoint;
-import org.onosproject.segmentrouting.xconnect.api.XconnectPortEndpoint;
-import org.onosproject.segmentrouting.xconnect.api.XconnectService;
-
-import java.util.Set;
-
-/**
- * Creates Xconnect.
- */
-@Service
-@Command(scope = "onos", name = "sr-xconnect-add", description = "Create Xconnect")
-public class XconnectAddCommand extends AbstractShellCommand {
-    private static final String EP_DESC = "Can be a physical port number or a load balancer key. " +
-            "Use integer to specify physical port number. " +
-            "Use " + XconnectPortEndpoint.LB_KEYWORD + "key to specify load balancer key";
-
-    @Argument(index = 0, name = "deviceId",
-            description = "Device ID",
-            required = true, multiValued = false)
-    @Completion(DeviceIdCompleter.class)
-    private String deviceIdStr;
-
-    @Argument(index = 1, name = "vlanId",
-            description = "VLAN ID",
-            required = true, multiValued = false)
-    @Completion(PlaceholderCompleter.class)
-    private String vlanIdStr;
-
-    @Argument(index = 2, name = "ep1",
-            description = "First endpoint. " + EP_DESC,
-            required = true, multiValued = false)
-    @Completion(PortNumberCompleter.class)
-    private String ep1Str;
-
-    @Argument(index = 3, name = "ep2",
-            description = "Second endpoint. " + EP_DESC,
-            required = true, multiValued = false)
-    @Completion(PortNumberCompleter.class)
-    private String ep2Str;
-
-    private static final String L2LB_PATTERN = "^(\\d*|L2LB\\(\\d*\\))$";
-
-    @Override
-    protected void doExecute() {
-        DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
-        VlanId vlanId = VlanId.vlanId(vlanIdStr);
-
-        XconnectEndpoint ep1 = XconnectEndpoint.fromString(ep1Str);
-        XconnectEndpoint ep2 = XconnectEndpoint.fromString(ep2Str);
-
-        Set<XconnectEndpoint> endpoints = Sets.newHashSet(ep1, ep2);
-
-        XconnectService xconnectService = get(XconnectService.class);
-        xconnectService.addOrUpdateXconnect(deviceId, vlanId, endpoints);
-    }
-
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectListCommand.java
deleted file mode 100644
index 44a7d37..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectListCommand.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.xconnect.api.XconnectService;
-
-/**
- * Lists Xconnects.
- */
-@Service
-@Command(scope = "onos", name = "sr-xconnect", description = "Lists all Xconnects")
-public class XconnectListCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        XconnectService xconnectService = get(XconnectService.class);
-        xconnectService.getXconnects().forEach(desc -> print("%s", desc));
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectNextListCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectNextListCommand.java
deleted file mode 100644
index 0b81186..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectNextListCommand.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.segmentrouting.xconnect.api.XconnectKey;
-import org.onosproject.segmentrouting.xconnect.api.XconnectService;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Map;
-
-/**
- * Command to read the current state of the xconnect next stores.
- */
-@Service
-@Command(scope = "onos", name = "sr-next-xconnect",
-        description = "Displays the current next-id for xconnect")
-public class XconnectNextListCommand extends AbstractShellCommand {
-    @Override
-    protected void doExecute() {
-        XconnectService xconnectService =
-                AbstractShellCommand.get(XconnectService.class);
-        print(xconnectService.getNext());
-    }
-
-    private void print(Map<XconnectKey, Integer> nextStore) {
-        ArrayList<XconnectKey> a = new ArrayList<>(nextStore.keySet());
-        a.sort(Comparator
-                .comparing((XconnectKey o) -> o.deviceId().toString())
-                .thenComparing((XconnectKey o) -> o.vlanId().toShort()));
-
-        StringBuilder builder = new StringBuilder();
-        a.forEach(k ->
-            builder.append("\n")
-                    .append(k)
-                    .append(" --> ")
-                    .append(nextStore.get(k))
-        );
-        print(builder.toString());
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectRemoveCommand.java b/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectRemoveCommand.java
deleted file mode 100644
index e651338..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/XconnectRemoveCommand.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.cli;
-
-import org.apache.karaf.shell.api.action.Argument;
-import org.apache.karaf.shell.api.action.Command;
-import org.apache.karaf.shell.api.action.Completion;
-import org.apache.karaf.shell.api.action.lifecycle.Service;
-import org.onlab.packet.VlanId;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.cli.PlaceholderCompleter;
-import org.onosproject.cli.net.DeviceIdCompleter;
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.xconnect.api.XconnectService;
-
-/**
- * Deletes Xconnect.
- */
-@Service
-@Command(scope = "onos", name = "sr-xconnect-remove", description = "Remove Xconnect")
-public class XconnectRemoveCommand extends AbstractShellCommand {
-    @Argument(index = 0, name = "deviceId",
-            description = "Device ID",
-            required = true, multiValued = false)
-    @Completion(DeviceIdCompleter.class)
-    private String deviceIdStr;
-
-    @Argument(index = 1, name = "vlanId",
-            description = "VLAN ID",
-            required = true, multiValued = false)
-    @Completion(PlaceholderCompleter.class)
-    private String vlanIdStr;
-
-    @Override
-    protected void doExecute() {
-        DeviceId deviceId = DeviceId.deviceId(deviceIdStr);
-        VlanId vlanId = VlanId.vlanId(vlanIdStr);
-
-        XconnectService xconnectService = get(XconnectService.class);
-        xconnectService.removeXonnect(deviceId, vlanId);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/cli/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/cli/package-info.java
deleted file mode 100644
index 0f2cea6..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/cli/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Segment routing application CLI handlers.
- */
-package org.onosproject.segmentrouting.cli;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
deleted file mode 100644
index d161f63..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ /dev/null
@@ -1,878 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.config;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Multimaps;
-import com.google.common.collect.SetMultimap;
-import com.google.common.collect.Sets;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip6Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.HostId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.config.ConfigException;
-import org.onosproject.net.config.basics.BasicDeviceConfig;
-import org.onosproject.net.config.basics.InterfaceConfig;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.routeservice.Route;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Segment Routing configuration component that reads the
- * segment routing related configuration from Network Configuration Manager
- * component and organizes in more accessible formats.
- */
-public class DeviceConfiguration implements DeviceProperties {
-
-    private static final String NO_SUBNET = "No subnet configured on {}";
-
-    private static final Logger log = LoggerFactory.getLogger(DeviceConfiguration.class);
-    private final List<Integer> allSegmentIds = new ArrayList<>();
-    private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
-    private SegmentRoutingManager srManager;
-
-    private class SegmentRouterInfo {
-        int ipv4NodeSid = -1;
-        int ipv6NodeSid = -1;
-        DeviceId deviceId;
-        Ip4Address ipv4Loopback;
-        Ip6Address ipv6Loopback;
-        MacAddress mac;
-        boolean isEdge;
-        SetMultimap<PortNumber, IpAddress> gatewayIps;
-        SetMultimap<PortNumber, IpPrefix> subnets;
-        Map<Integer, Set<Integer>> adjacencySids;
-        DeviceId pairDeviceId;
-        PortNumber pairLocalPort;
-        int pwRoutingLabel;
-
-        public SegmentRouterInfo() {
-            gatewayIps = Multimaps.synchronizedSetMultimap(HashMultimap.create());
-            subnets = Multimaps.synchronizedSetMultimap(HashMultimap.create());
-        }
-    }
-
-    /**
-     * Constructs device configuration for all Segment Router devices,
-     * organizing the data into various maps for easier access.
-     *
-     * @param srManager Segment Routing Manager
-     */
-    public DeviceConfiguration(SegmentRoutingManager srManager) {
-        this.srManager = srManager;
-        updateConfig();
-    }
-
-    public void updateConfig() {
-        // Read config from device subject, excluding gatewayIps and subnets.
-        Set<DeviceId> deviceSubjects =
-                srManager.cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
-        deviceSubjects.forEach(subject -> {
-            BasicDeviceConfig basicDeviceConfig = srManager.cfgService.addConfig(subject, BasicDeviceConfig.class);
-            if (!basicDeviceConfig.purgeOnDisconnection()) {
-                // Setting purge on disconnection flag for the device SR has control over.
-                // addConfig returns a config if it exists or creates a new one.
-                log.info("PurgeOnDisconnection set to true for device {}", subject);
-                basicDeviceConfig.purgeOnDisconnection(true);
-                srManager.cfgService.applyConfig(subject, BasicDeviceConfig.class, basicDeviceConfig.node());
-            }
-            SegmentRoutingDeviceConfig config =
-                    srManager.cfgService.getConfig(subject, SegmentRoutingDeviceConfig.class);
-            SegmentRouterInfo info = new SegmentRouterInfo();
-            info.deviceId = subject;
-            info.ipv4NodeSid = config.nodeSidIPv4();
-            info.ipv6NodeSid = config.nodeSidIPv6();
-            info.ipv4Loopback = config.routerIpv4();
-            info.ipv6Loopback = config.routerIpv6();
-            info.mac = config.routerMac();
-            info.isEdge = config.isEdgeRouter();
-            info.adjacencySids = config.adjacencySids();
-            info.pairDeviceId = config.pairDeviceId();
-            info.pairLocalPort = config.pairLocalPort();
-            info.pwRoutingLabel = info.ipv4NodeSid + 1000;
-            deviceConfigMap.put(info.deviceId, info);
-            log.debug("Read device config for device: {}", info.deviceId);
-            /*
-             * IPv6 sid is not inserted. this part of the code is not used for now.
-             */
-            allSegmentIds.add(info.ipv4NodeSid);
-        });
-
-        // Read gatewayIps and subnets from port subject. Ignore suppressed ports.
-        Set<ConnectPoint> portSubjects = srManager.cfgService
-                .getSubjects(ConnectPoint.class, InterfaceConfig.class);
-        portSubjects.stream()
-                .filter(subject -> deviceConfigMap.containsKey(subject.deviceId()))
-                .filter(subject -> !isSuppressedPort(subject)).forEach(subject -> {
-            InterfaceConfig config =
-                    srManager.cfgService.getConfig(subject, InterfaceConfig.class);
-            Set<Interface> networkInterfaces;
-            try {
-                networkInterfaces = config.getInterfaces();
-            } catch (ConfigException e) {
-                log.error("Error loading port configuration");
-                return;
-            }
-            networkInterfaces.forEach(networkInterface -> {
-                VlanId vlanId = networkInterface.vlan();
-                ConnectPoint connectPoint = networkInterface.connectPoint();
-                DeviceId dpid = connectPoint.deviceId();
-                PortNumber port = connectPoint.port();
-                MacAddress mac = networkInterface.mac();
-                SegmentRouterInfo info = deviceConfigMap.get(dpid);
-
-                // skip if there is no corresponding device for this ConenctPoint
-                if (info != null) {
-                    // Extract subnet information
-                    List<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddressesList();
-                    interfaceAddresses.forEach(interfaceAddress -> {
-                        // Do not add /0, /32 and /128 to gateway IP list
-                        int prefixLength = interfaceAddress.subnetAddress().prefixLength();
-                        IpPrefix ipPrefix = interfaceAddress.subnetAddress();
-                        if (ipPrefix.isIp4()) {
-                            if (prefixLength != 0 && prefixLength != IpPrefix.MAX_INET_MASK_LENGTH) {
-                                info.gatewayIps.put(port, interfaceAddress.ipAddress());
-                            }
-                            info.subnets.put(port, interfaceAddress.subnetAddress());
-                        } else {
-                            if (prefixLength != 0 && prefixLength != IpPrefix.MAX_INET6_MASK_LENGTH) {
-                                info.gatewayIps.put(port, interfaceAddress.ipAddress());
-                            }
-                            info.subnets.put(port, interfaceAddress.subnetAddress());
-                        }
-                    });
-
-                    // Override interface mac with router mac
-                    if (!mac.equals(info.mac)) {
-                        ArrayNode array = (ArrayNode) config.node();
-                        for (JsonNode intfNode : array) {
-                            ObjectNode objNode = (ObjectNode) intfNode;
-                            objNode.put(InterfaceConfig.MAC, info.mac.toString());
-                        }
-                        srManager.cfgService.applyConfig(connectPoint, InterfaceConfig.class, array);
-                    }
-                }
-            });
-            // We register the connect point with the NRS.
-            srManager.registerConnectPoint(subject);
-        });
-    }
-
-    public Collection<DeviceId> getRouters() {
-        return deviceConfigMap.keySet();
-    }
-
-    @Override
-    public boolean isConfigured(DeviceId deviceId) {
-        return deviceConfigMap.get(deviceId) != null;
-    }
-
-    @Override
-    public int getIPv4SegmentId(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            log.trace("getIPv4SegmentId for device{} is {}", deviceId, srinfo.ipv4NodeSid);
-            return srinfo.ipv4NodeSid;
-        } else {
-            String message = "getIPv4SegmentId fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    @Override
-    public int getIPv6SegmentId(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            log.trace("getIPv6SegmentId for device{} is {}", deviceId, srinfo.ipv6NodeSid);
-            return srinfo.ipv6NodeSid;
-        } else {
-            String message = "getIPv6SegmentId fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    @Override
-    public int getPWRoutingLabel(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            log.trace("pwRoutingLabel for device{} is {}", deviceId, srinfo.pwRoutingLabel);
-            return srinfo.pwRoutingLabel;
-        } else {
-            String message = "getPWRoutingLabel fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    /**
-     * Returns the IPv4 Node segment id of a segment router given its Router mac address.
-     *
-     * @param routerMac router mac address
-     * @return node segment id, or -1 if not found in config
-     */
-    public int getIPv4SegmentId(MacAddress routerMac) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
-                    deviceConfigMap.entrySet()) {
-            if (entry.getValue().mac.equals(routerMac)) {
-                return entry.getValue().ipv4NodeSid;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Returns the IPv6 Node segment id of a segment router given its Router mac address.
-     *
-     * @param routerMac router mac address
-     * @return node segment id, or -1 if not found in config
-     */
-    public int getIPv6SegmentId(MacAddress routerMac) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
-                deviceConfigMap.entrySet()) {
-            if (entry.getValue().mac.equals(routerMac)) {
-                return entry.getValue().ipv6NodeSid;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Returns the IPv4 Node segment id of a segment router given its Router ip address.
-     *
-     * @param routerAddress router ip address
-     * @return node segment id, or -1 if not found in config
-     */
-    public int getIPv4SegmentId(Ip4Address routerAddress) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry: deviceConfigMap.entrySet()) {
-            Ip4Address ipv4Loopback = entry.getValue().ipv4Loopback;
-            if (ipv4Loopback == null) {
-                continue;
-            }
-            if (entry.getValue().ipv4Loopback.equals(routerAddress)) {
-                if (entry.getValue().ipv4NodeSid == -1) {
-                    continue;
-                }
-                return entry.getValue().ipv4NodeSid;
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Returns the IPv6 Node segment id of a segment router given its Router ip address.
-     *
-     * @param routerAddress router ip address
-     * @return node segment id, or -1 if not found in config
-     */
-    public int getIPv6SegmentId(Ip6Address routerAddress) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry: deviceConfigMap.entrySet()) {
-            Ip6Address ipv6Loopback = entry.getValue().ipv6Loopback;
-            if (ipv6Loopback == null) {
-                continue;
-            }
-            if (entry.getValue().ipv6Loopback.equals(routerAddress)) {
-                if (entry.getValue().ipv6NodeSid == -1) {
-                    continue;
-                }
-                return entry.getValue().ipv6NodeSid;
-            }
-        }
-
-        return -1;
-    }
-
-    @Override
-    public MacAddress getDeviceMac(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            return srinfo.mac;
-        } else {
-            String message = "getDeviceMac fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    @Override
-    public Ip4Address getRouterIpv4(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            log.trace("getRouterIpv4 for device{} is {}", deviceId, srinfo.ipv4Loopback);
-            return srinfo.ipv4Loopback;
-        } else {
-            String message = "getRouterIpv4 fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    @Override
-    public Ip6Address getRouterIpv6(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            log.trace("getRouterIpv6 for device{} is {}", deviceId, srinfo.ipv6Loopback);
-            return srinfo.ipv6Loopback;
-        } else {
-            String message = "getRouterIpv6 fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    /**
-     * Gets router ip address based on the destination ip address.
-     *
-     * @param destIpAddress the destination ip address
-     * @param routerDeviceId the device id
-     * @return the ip address of the routes
-     */
-    public IpAddress getRouterIpAddress(IpAddress destIpAddress, DeviceId routerDeviceId) {
-        IpAddress routerIpAddress;
-        try {
-            routerIpAddress = destIpAddress.isIp4() ? getRouterIpv4(routerDeviceId) :
-                    getRouterIpv6(routerDeviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            routerIpAddress = null;
-        }
-        return routerIpAddress;
-    }
-
-    @Override
-    public boolean isEdgeDevice(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            log.trace("isEdgeDevice for device{} is {}", deviceId, srinfo.isEdge);
-            return srinfo.isEdge;
-        } else {
-            String message = "isEdgeDevice fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    @Override
-    public List<Integer> getAllDeviceSegmentIds() {
-        return allSegmentIds;
-    }
-
-    @Override
-    public Map<IpPrefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId)
-            throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo == null) {
-            String message = "getSubnetPortsMap fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-        // Construct subnet-port mapping from port-subnet mapping
-        SetMultimap<PortNumber, IpPrefix> portSubnetMap = srinfo.subnets;
-        Map<IpPrefix, List<PortNumber>> subnetPortMap = new HashMap<>();
-
-        portSubnetMap.entries().forEach(entry -> {
-            PortNumber port = entry.getKey();
-            IpPrefix subnet = entry.getValue();
-
-            if (subnet.prefixLength() == IpPrefix.MAX_INET_MASK_LENGTH ||
-                    subnet.prefixLength() == IpPrefix.MAX_INET6_MASK_LENGTH) {
-                return;
-            }
-
-            if (subnetPortMap.containsKey(subnet)) {
-                subnetPortMap.get(subnet).add(port);
-            } else {
-                ArrayList<PortNumber> ports = new ArrayList<>();
-                ports.add(port);
-                subnetPortMap.put(subnet, ports);
-            }
-        });
-        return subnetPortMap;
-    }
-
-    /**
-     * Returns the device identifier or data plane identifier (dpid)
-     * of a segment router given its segment id.
-     *
-     * @param sid segment id
-     * @return deviceId device identifier
-     */
-    public DeviceId getDeviceId(int sid) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
-            deviceConfigMap.entrySet()) {
-            if (entry.getValue().ipv4NodeSid == sid ||
-                    entry.getValue().ipv6NodeSid == sid) {
-                return entry.getValue().deviceId;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the device identifier or data plane identifier (dpid)
-     * of a segment router given its router ip address.
-     *
-     * @param ipAddress router ip address
-     * @return deviceId device identifier
-     */
-    public DeviceId getDeviceId(Ip4Address ipAddress) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
-            deviceConfigMap.entrySet()) {
-            if (entry.getValue().ipv4Loopback.equals(ipAddress)) {
-                return entry.getValue().deviceId;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the device identifier or data plane identifier (dpid)
-     * of a segment router given its router ipv6 address.
-     *
-     * @param ipAddress router ipv6 address
-     * @return deviceId device identifier
-     */
-    public DeviceId getDeviceId(Ip6Address ipAddress) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
-                deviceConfigMap.entrySet()) {
-            if (entry.getValue().ipv6Loopback.equals(ipAddress)) {
-                return entry.getValue().deviceId;
-            }
-        }
-
-        return null;
-    }
-
-    /**
-     * Returns the configured port ip addresses for a segment router.
-     * These addresses serve as gateway IP addresses for the subnets configured
-     * on those ports.
-     *
-     * @param deviceId device identifier
-     * @return immutable set of ip addresses configured on the ports or null if not found
-     */
-    public Set<IpAddress> getPortIPs(DeviceId deviceId) {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            log.trace("getSubnetGatewayIps for device{} is {}", deviceId,
-                      srinfo.gatewayIps.values());
-            return ImmutableSet.copyOf(srinfo.gatewayIps.values());
-        }
-        return null;
-    }
-
-    /**
-     * Returns configured subnets for a segment router.
-     *
-     * @param deviceId device identifier
-     * @return list of ip prefixes or null if not found
-     */
-    public Set<IpPrefix> getConfiguredSubnets(DeviceId deviceId) {
-        Set<IpPrefix> subnets = srManager.interfaceService.getInterfaces().stream()
-                .filter(intf -> Objects.equals(deviceId, intf.connectPoint().deviceId()))
-                .flatMap(intf -> intf.ipAddressesList().stream())
-                .map(InterfaceIpAddress::subnetAddress)
-                .collect(Collectors.toSet());
-
-        if (subnets.isEmpty()) {
-            log.debug(NO_SUBNET, deviceId);
-            return Collections.emptySet();
-        }
-        return subnets;
-    }
-
-    /**
-     * Returns all subnets for a segment router, including subnets learnt from route service.
-     *
-     * @param deviceId device identifier
-     * @return set of ip prefixes or null if not found
-     * @deprecated use getBatchedSubnets(DeviceId deviceId) instead
-     */
-    @Deprecated
-    public Set<IpPrefix> getSubnets(DeviceId deviceId) {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null && srinfo.subnets != null) {
-            // Note: ImmutableSet.Builder.addAll calls the iterator of parameter internally,
-            //       which is not protected by SynchronizedCollection mutex.
-            ImmutableSet.Builder<IpPrefix> builder = ImmutableSet.builder();
-            srinfo.subnets.forEach((k, v) -> builder.add(v));
-            return builder.build();
-        }
-        return null;
-    }
-
-    /**
-     * Returns batches of all subnets reachable via given next hop
-     * <p>
-     * First batch includes FPM and STATIC routes
-     * Second batch includes all other type of routes obtained from routeService, including DHCP routes.
-     *
-     * @param hostId next hop host id
-     * @return list of subnet batches, each batch includes a set of prefixes.
-     */
-    // TODO Querying routeService directly may be expensive. Some kind of reverse lookup cache should be developed.
-    public List<Set<IpPrefix>> getBatchedSubnets(HostId hostId) {
-        Set<IpPrefix> high = Sets.newHashSet();
-        Set<IpPrefix> low = Sets.newHashSet();
-
-        srManager.routeService.getRouteTables().stream()
-                .map(tableId -> srManager.routeService.getResolvedRoutes(tableId))
-                .flatMap(Collection::stream)
-                .forEach(resolvedRoute -> {
-                    // Continue if next hop is not what we are looking for
-                    if (!Objects.equals(hostId.mac(), resolvedRoute.nextHopMac()) ||
-                            !Objects.equals(hostId.vlanId(), resolvedRoute.nextHopVlan())) {
-                        return;
-                    }
-                    // Prioritize STATIC and FPM among others
-                    if (resolvedRoute.route().source() == Route.Source.STATIC ||
-                            resolvedRoute.route().source() == Route.Source.FPM) {
-                        high.add(resolvedRoute.prefix());
-                    } else {
-                        low.add(resolvedRoute.prefix());
-                    }
-                });
-        return Stream.of(high, low).filter(set -> !set.isEmpty()).collect(Collectors.toList());
-    }
-
-    /**
-     * Returns batches of all subnets reachable on the given device.
-     * <p>
-     * First batch includes configured subnets, FPM and STATIC routes
-     * Second batch includes all other type of routes obtained from routeService, including DHCP routes.
-     *
-     * @param deviceId device identifier
-     * @return list of subnet batches, each batch includes a set of prefixes.
-     */
-    // TODO Querying routeService directly may be expensive. Some kind of reverse lookup cache should be developed.
-    public List<Set<IpPrefix>> getBatchedSubnets(DeviceId deviceId) {
-        Set<IpPrefix> high = Sets.newHashSet();
-        Set<IpPrefix> low = Sets.newHashSet();
-
-        high.addAll(getConfiguredSubnets(deviceId));
-        srManager.routeService.getRouteTables().stream()
-                .map(tableId -> srManager.routeService.getResolvedRoutes(tableId))
-                .flatMap(Collection::stream)
-                .forEach(resolvedRoute -> {
-                    // Continue to next resolved route if none of the next hop attaches to given device
-                    if (srManager.nextHopLocations(resolvedRoute).stream()
-                            .noneMatch(cp -> Objects.equals(deviceId, cp.deviceId()))) {
-                        return;
-                    }
-                    // Prioritize STATIC and FPM among others
-                    if (resolvedRoute.route().source() == Route.Source.STATIC ||
-                            resolvedRoute.route().source() == Route.Source.FPM) {
-                        high.add(resolvedRoute.prefix());
-                    } else {
-                        low.add(resolvedRoute.prefix());
-                    }
-                });
-        return Lists.newArrayList(high, low);
-    }
-
-    /**
-     * Returns the subnet configuration of given device and port.
-     *
-     * @param deviceId Device ID
-     * @param port Port number
-     * @return The subnets configured on given port or empty set if
-     *         the port is unconfigured or suppressed.
-     */
-    public Set<IpPrefix> getPortSubnets(DeviceId deviceId, PortNumber port) {
-        ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
-
-        if (isSuppressedPort(connectPoint)) {
-            return Collections.emptySet();
-        }
-
-        Set<IpPrefix> subnets = srManager.interfaceService.getInterfacesByPort(connectPoint).stream()
-                .flatMap(intf -> intf.ipAddressesList().stream())
-                .map(InterfaceIpAddress::subnetAddress)
-                .collect(Collectors.toSet());
-
-        if (subnets.isEmpty()) {
-            log.debug(NO_SUBNET, connectPoint);
-            return Collections.emptySet();
-        }
-        return subnets;
-    }
-
-    /**
-     * Returns all ports that has a subnet that contains any of the given IP addresses.
-     *
-     * @param ips a set of IP addresses
-     * @return a set of connect point that has a subnet that contains any of the given IP addresses
-     */
-    public Set<ConnectPoint> getPortByIps(Set<IpAddress> ips) {
-        return srManager.interfaceService.getInterfaces().stream()
-                .filter(intf -> intf.ipAddressesList().stream().anyMatch(intfAddress ->
-                            ips.stream().anyMatch(ip -> intfAddress.subnetAddress().contains(ip))))
-                .map(Interface::connectPoint)
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Returns all the connect points of the segment routers that have the
-     * specified ip address in their subnets.
-     *
-     * @param destIpAddress target ip address
-     * @return connect points of the segment routers
-     */
-    public Set<ConnectPoint> getConnectPointsForASubnetHost(IpAddress destIpAddress) {
-        return srManager.interfaceService.getMatchingInterfaces(destIpAddress).stream()
-                .map(Interface::connectPoint)
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Returns the router ip address of segment router that has the
-     * specified ip address in its subnets.
-     *
-     * @param destIpAddress target ip address
-     * @return router ip address
-     */
-    public Ip4Address getRouterIpAddressForASubnetHost(Ip4Address destIpAddress) {
-        Interface matchIntf = srManager.interfaceService.getMatchingInterface(destIpAddress);
-
-        if (matchIntf == null) {
-            log.debug("No router was found for {}", destIpAddress);
-            return null;
-        }
-
-        DeviceId routerDeviceId = matchIntf.connectPoint().deviceId();
-        SegmentRouterInfo srInfo = deviceConfigMap.get(routerDeviceId);
-        if (srInfo == null) {
-            log.debug("No device config was found for {}", routerDeviceId);
-            return null;
-        }
-
-        return srInfo.ipv4Loopback;
-    }
-
-    /**
-     * Returns the router ipv6 address of segment router that has the
-     * specified ip address in its subnets.
-     *
-     * @param destIpAddress target ip address
-     * @return router ip address
-     */
-    public Ip6Address getRouterIpAddressForASubnetHost(Ip6Address destIpAddress) {
-        Interface matchIntf = srManager.interfaceService.getMatchingInterface(destIpAddress);
-
-        if (matchIntf == null) {
-            log.debug("No router was found for {}", destIpAddress);
-            return null;
-        }
-
-        DeviceId routerDeviceId = matchIntf.connectPoint().deviceId();
-        SegmentRouterInfo srInfo = deviceConfigMap.get(routerDeviceId);
-        if (srInfo == null) {
-            log.debug("No device config was found for {}", routerDeviceId);
-            return null;
-        }
-
-        return srInfo.ipv6Loopback;
-    }
-
-    /**
-     * Returns the router mac address of segment router that has the
-     * specified ip address as one of its subnet gateway ip address.
-     *
-     * @param gatewayIpAddress router gateway ip address
-     * @return router mac address or null if not found
-     */
-    public MacAddress getRouterMacForAGatewayIp(IpAddress gatewayIpAddress) {
-        for (Map.Entry<DeviceId, SegmentRouterInfo> entry:
-                deviceConfigMap.entrySet()) {
-            if (entry.getValue().gatewayIps.
-                    values().contains(gatewayIpAddress)) {
-                return entry.getValue().mac;
-            }
-        }
-
-        log.debug("Cannot find a router for {}", gatewayIpAddress);
-        return null;
-    }
-
-    /**
-     * Checks if the host IP is in any of the subnet defined in the router with the
-     * device ID given.
-     *
-     * @param deviceId device identification of the router
-     * @param hostIp   host IP address to check
-     * @return true if the given IP is within any of the subnet defined in the router,
-     * false if no subnet is defined in the router or if the host is not
-     * within any subnet defined in the router
-     */
-    public boolean inSameSubnet(DeviceId deviceId, IpAddress hostIp) {
-        Set<IpPrefix> subnets = getConfiguredSubnets(deviceId);
-        if (subnets == null) {
-            return false;
-        }
-
-        for (IpPrefix subnet: subnets) {
-            // Exclude /0 since it is a special case used for default route
-            if (subnet.prefixLength() != 0 && subnet.contains(hostIp)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Checks if the IP is in the subnet defined on given connect point.
-     *
-     * @param connectPoint Connect point
-     * @param ip The IP address to check
-     * @return True if the IP belongs to the subnet.
-     *         False if the IP does not belong to the subnet, or
-     *         there is no subnet configuration on given connect point.
-     */
-    public boolean inSameSubnet(ConnectPoint connectPoint, IpAddress ip) {
-        return getPortSubnets(connectPoint.deviceId(), connectPoint.port()).stream()
-                .anyMatch(ipPrefix -> ipPrefix.contains(ip));
-    }
-
-    /**
-     * Returns the ports corresponding to the adjacency Sid given.
-     *
-     * @param deviceId device identification of the router
-     * @param sid adjacency Sid
-     * @return set of port numbers
-     */
-    public Set<Integer> getPortsForAdjacencySid(DeviceId deviceId, int sid) {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        return srinfo != null ?
-                ImmutableSet.copyOf(srinfo.adjacencySids.get(sid)) :
-                ImmutableSet.copyOf(new HashSet<>());
-    }
-
-    /**
-     * Check if the Sid given is whether adjacency Sid of the router device or not.
-     *
-     * @param deviceId device identification of the router
-     * @param sid Sid to check
-     * @return true if the Sid given is the adjacency Sid of the device,
-     * otherwise false
-     */
-    public boolean isAdjacencySid(DeviceId deviceId, int sid) {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        return srinfo != null && srinfo.adjacencySids.containsKey(sid);
-    }
-
-    /**
-     * Add subnet to specific connect point.
-     *
-     * @param cp connect point
-     * @param ipPrefix subnet being added to the device
-     */
-    public void addSubnet(ConnectPoint cp, IpPrefix ipPrefix) {
-        checkNotNull(cp);
-        checkNotNull(ipPrefix);
-        SegmentRouterInfo srinfo = deviceConfigMap.get(cp.deviceId());
-        if (srinfo == null) {
-            log.warn("Device {} is not configured. Abort.", cp.deviceId());
-            return;
-        }
-        srinfo.subnets.put(cp.port(), ipPrefix);
-    }
-
-    /**
-     * Remove subnet from specific connect point.
-     *
-     * @param cp connect point
-     * @param ipPrefix subnet being removed to the device
-     */
-    public void removeSubnet(ConnectPoint cp, IpPrefix ipPrefix) {
-        checkNotNull(cp);
-        checkNotNull(ipPrefix);
-        SegmentRouterInfo srinfo = deviceConfigMap.get(cp.deviceId());
-        if (srinfo == null) {
-            log.warn("Device {} is not configured. Abort.", cp.deviceId());
-            return;
-        }
-        srinfo.subnets.remove(cp.port(), ipPrefix);
-    }
-
-    private boolean isSuppressedPort(ConnectPoint connectPoint) {
-        SegmentRoutingAppConfig appConfig = srManager.cfgService
-                .getConfig(srManager.appId(), SegmentRoutingAppConfig.class);
-        if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
-            log.info("Interface configuration on port {} is ignored", connectPoint);
-            return true;
-        }
-        return false;
-    }
-
-    public boolean isPairedEdge(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        if (!isEdgeDevice(deviceId)) {
-            return false;
-        }
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        return (srinfo.pairDeviceId == null) ? false : true;
-    }
-
-    public DeviceId getPairDeviceId(DeviceId deviceId) throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            return srinfo.pairDeviceId;
-        } else {
-            String message = "getPairDeviceId fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    public PortNumber getPairLocalPort(DeviceId deviceId)
-            throws DeviceConfigNotFoundException {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            return srinfo.pairLocalPort;
-        } else {
-            String message = "getPairLocalPort fails for device: " + deviceId + ".";
-            throw new DeviceConfigNotFoundException(message);
-        }
-    }
-
-    public boolean isPairLocalPort(DeviceId devId, PortNumber pnum) {
-        return pnum.equals(srManager.getPairLocalPort(devId).orElse(null));
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/config/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/config/package-info.java
deleted file mode 100644
index a664a8f..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/config/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Segment routing network configuration mechanism.
- */
-package org.onosproject.segmentrouting.config;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
deleted file mode 100644
index f8d3053..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ /dev/null
@@ -1,1794 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-
-import org.apache.commons.lang3.RandomUtils;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.MplsLabel;
-import org.onlab.packet.VlanId;
-import org.onlab.util.KryoNamespace;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.PortNumber;
-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.flowobjective.DefaultNextObjective;
-import org.onosproject.net.flowobjective.DefaultObjectiveContext;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.link.LinkService;
-import org.onosproject.segmentrouting.DefaultRoutingHandler;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceProperties;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.storekey.DestinationSetNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.PortNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.storekey.MacVlanNextObjectiveStoreKey;
-import org.onosproject.store.service.EventuallyConsistentMap;
-import org.slf4j.Logger;
-
-import java.net.URI;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.util.concurrent.Executors.newScheduledThreadPool;
-import static org.onlab.util.Tools.groupedThreads;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Default ECMP group handler creation module. This component creates a set of
- * ECMP groups for every neighbor that this device is connected to based on
- * whether the current device is an edge device or a transit device.
- */
-public class DefaultGroupHandler {
-    private static final Logger log = getLogger(DefaultGroupHandler.class);
-
-    private static final long VERIFY_INTERVAL = 30; // secs
-
-    protected final DeviceId deviceId;
-    protected final ApplicationId appId;
-    protected final DeviceProperties deviceConfig;
-    protected final List<Integer> allSegmentIds;
-    protected int ipv4NodeSegmentId = -1;
-    protected int ipv6NodeSegmentId = -1;
-    protected boolean isEdgeRouter = false;
-    protected MacAddress nodeMacAddr = null;
-    protected LinkService linkService;
-    protected FlowObjectiveService flowObjectiveService;
-    private DeviceConfiguration config;
-
-    /**
-     * local store for neighbor-device-ids and the set of ports on this device
-     * that connect to the same neighbor.
-     */
-    protected ConcurrentHashMap<DeviceId, Set<PortNumber>> devicePortMap =
-            new ConcurrentHashMap<>();
-    /**
-     *  local store for ports on this device connected to neighbor-device-id.
-     */
-    protected ConcurrentHashMap<PortNumber, DeviceId> portDeviceMap =
-            new ConcurrentHashMap<>();
-
-    // distributed store for (device+destination-set) mapped to next-id and neighbors
-    protected EventuallyConsistentMap<DestinationSetNextObjectiveStoreKey, NextNeighbors>
-            dsNextObjStore = null;
-    // distributed store for (device+subnet-ip-prefix) mapped to next-id
-    protected EventuallyConsistentMap<VlanNextObjectiveStoreKey, Integer>
-            vlanNextObjStore = null;
-    // distributed store for (device+mac+vlan+treatment) mapped to next-id
-    protected EventuallyConsistentMap<MacVlanNextObjectiveStoreKey, Integer>
-            macVlanNextObjStore = null;
-    // distributed store for (device+port+treatment) mapped to next-id
-    protected EventuallyConsistentMap<PortNextObjectiveStoreKey, Integer>
-            portNextObjStore = null;
-    private SegmentRoutingManager srManager;
-
-    private ScheduledExecutorService executorService
-    = newScheduledThreadPool(1, groupedThreads("bktCorrector", "bktC-%d", log));
-
-    protected KryoNamespace.Builder kryo = new KryoNamespace.Builder()
-            .register(URI.class).register(HashSet.class)
-            .register(DeviceId.class).register(PortNumber.class)
-            .register(DestinationSet.class).register(PolicyGroupIdentifier.class)
-            .register(PolicyGroupParams.class)
-            .register(GroupBucketIdentifier.class)
-            .register(GroupBucketIdentifier.BucketOutputType.class);
-
-    protected DefaultGroupHandler(DeviceId deviceId, ApplicationId appId,
-                                  DeviceProperties config,
-                                  LinkService linkService,
-                                  FlowObjectiveService flowObjService,
-                                  SegmentRoutingManager srManager) {
-        this.deviceId = checkNotNull(deviceId);
-        this.appId = checkNotNull(appId);
-        this.deviceConfig = checkNotNull(config);
-        this.linkService = checkNotNull(linkService);
-        this.allSegmentIds = checkNotNull(config.getAllDeviceSegmentIds());
-        try {
-            this.ipv4NodeSegmentId = config.getIPv4SegmentId(deviceId);
-            this.ipv6NodeSegmentId = config.getIPv6SegmentId(deviceId);
-            this.isEdgeRouter = config.isEdgeDevice(deviceId);
-            this.nodeMacAddr = checkNotNull(config.getDeviceMac(deviceId));
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage()
-                    + " Skipping value assignment in DefaultGroupHandler");
-        }
-        this.flowObjectiveService = flowObjService;
-        this.dsNextObjStore = srManager.dsNextObjStore();
-        this.vlanNextObjStore = srManager.vlanNextObjStore();
-        this.portNextObjStore = srManager.portNextObjStore();
-        this.macVlanNextObjStore = srManager.macVlanNextObjStore();
-        this.srManager = srManager;
-        executorService.scheduleWithFixedDelay(new BucketCorrector(), 10,
-                                               VERIFY_INTERVAL,
-                                               TimeUnit.SECONDS);
-        populateNeighborMaps();
-    }
-
-    /**
-     * Gracefully shuts down a groupHandler. Typically called when the handler is
-     * no longer needed.
-     */
-    public void shutdown() {
-        executorService.shutdown();
-    }
-
-    /**
-     * Creates a group handler object.
-     *
-     * @param deviceId device identifier
-     * @param appId application identifier
-     * @param config interface to retrieve the device properties
-     * @param linkService link service object
-     * @param flowObjService flow objective service object
-     * @param srManager segment routing manager
-     * @throws DeviceConfigNotFoundException if the device configuration is not found
-     * @return default group handler type
-     */
-    public static DefaultGroupHandler createGroupHandler(
-                                                         DeviceId deviceId,
-                                                         ApplicationId appId,
-                                                         DeviceProperties config,
-                                                         LinkService linkService,
-                                                         FlowObjectiveService flowObjService,
-                                                         SegmentRoutingManager srManager)
-                                                                 throws DeviceConfigNotFoundException {
-        return new DefaultGroupHandler(deviceId, appId, config,
-                                       linkService,
-                                       flowObjService,
-                                       srManager);
-    }
-
-    /**
-     * Updates local stores for link-src-device/port to neighbor (link-dst) for
-     * link that has come up.
-     *
-     * @param link the infrastructure link
-     */
-    public void portUpForLink(Link link) {
-        if (!link.src().deviceId().equals(deviceId)) {
-            log.warn("linkUp: deviceId{} doesn't match with link src {}",
-                     deviceId, link.src().deviceId());
-            return;
-        }
-
-        log.info("* portUpForLink: Device {} linkUp at local port {} to "
-                + "neighbor {}", deviceId, link.src().port(), link.dst().deviceId());
-        // ensure local state is updated even if linkup is aborted later on
-        addNeighborAtPort(link.dst().deviceId(),
-                          link.src().port());
-    }
-
-    /**
-     * Updates local stores for link-src-device/port to neighbor (link-dst) for
-     * link that has gone down.
-     *
-     * @param link the infrastructure link
-     */
-    public void portDownForLink(Link link) {
-        PortNumber port = link.src().port();
-        if (portDeviceMap.get(port) == null) {
-            log.warn("portDown: unknown port");
-            return;
-        }
-
-        log.debug("Device {} portDown {} to neighbor {}", deviceId, port,
-                  portDeviceMap.get(port));
-        devicePortMap.get(portDeviceMap.get(port)).remove(port);
-        portDeviceMap.remove(port);
-    }
-
-    /**
-     * Cleans up local stores for removed neighbor device.
-     *
-     * @param neighborId the device identifier for the neighbor device
-     */
-    public void cleanUpForNeighborDown(DeviceId neighborId) {
-        Set<PortNumber> ports = devicePortMap.remove(neighborId);
-        if (ports != null) {
-            ports.forEach(p -> portDeviceMap.remove(p));
-        }
-    }
-
-    /**
-     * Checks all groups in the src-device of link for neighbor sets that include
-     * the dst-device of link, and edits the hash groups according to link up
-     * or down. Should only be called by the master instance of the src-switch
-     * of link. Typically used when there are no route-path changes due to the
-     * link up or down, as the ECMPspg does not change.
-     *
-     * @param link the infrastructure link that has gone down or come up
-     * @param linkDown true if link has gone down
-     * @param firstTime true if link has come up for the first time i.e a link
-     *                  not seen-before
-     */
-    public void retryHash(Link link, boolean linkDown, boolean firstTime) {
-        MacAddress neighborMac;
-        try {
-            neighborMac = deviceConfig.getDeviceMac(link.dst().deviceId());
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " Aborting retryHash.");
-            return;
-        }
-        // find all the destinationSets related to link
-        Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
-                .stream()
-                .filter(entry -> entry.getKey().deviceId().equals(deviceId))
-                // Filter out PW transit groups or include them if MPLS ECMP is supported
-                .filter(entry -> !entry.getKey().destinationSet().notBos() ||
-                        (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
-                // Filter out simple SWAP groups or include them if MPLS ECMP is supported
-                .filter(entry -> !entry.getKey().destinationSet().swap() ||
-                        (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
-                .filter(entry -> entry.getValue().containsNextHop(link.dst().deviceId()))
-                .map(entry -> entry.getKey())
-                .collect(Collectors.toSet());
-
-        log.debug("retryHash: dsNextObjStore contents for linkSrc {} -> linkDst {}: {}",
-                  deviceId, link.dst().deviceId(), dsKeySet);
-
-        for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
-            NextNeighbors nextHops = dsNextObjStore.get(dsKey);
-            if (nextHops == null) {
-                log.warn("retryHash in device {}, but global store has no record "
-                         + "for dsKey:{}", deviceId, dsKey);
-                continue;
-            }
-            int nextId = nextHops.nextId();
-            Set<DeviceId> dstSet = nextHops.getDstForNextHop(link.dst().deviceId());
-            if (!linkDown) {
-                List<PortLabel> pl = Lists.newArrayList();
-                if (firstTime) {
-                    // some links may have come up before the next-objective was created
-                    // we take this opportunity to ensure other ports to same next-hop-dst
-                    // are part of the hash group (see CORD-1180). Duplicate additions
-                    // to the same hash group are avoided by the driver.
-                    for (PortNumber p : devicePortMap.get(link.dst().deviceId())) {
-                        dstSet.forEach(dst -> {
-                            int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
-                            pl.add(new PortLabel(p, edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
-                        });
-                    }
-                    addToHashedNextObjective(pl, neighborMac, nextId);
-                } else {
-                    // handle only the port that came up
-                    dstSet.forEach(dst -> {
-                        int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
-                        pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
-                    });
-                    addToHashedNextObjective(pl, neighborMac, nextId);
-                }
-            } else {
-                // linkdown
-                List<PortLabel> pl = Lists.newArrayList();
-                dstSet.forEach(dst -> {
-                    int edgeLabel = dsKey.destinationSet().getEdgeLabel(dst);
-                    pl.add(new PortLabel(link.src().port(), edgeLabel, popVlanInHashGroup(dsKey.destinationSet())));
-                });
-                removeFromHashedNextObjective(pl, neighborMac, nextId);
-            }
-        }
-    }
-
-    /**
-     * Utility class for associating output ports and the corresponding MPLS
-     * labels to push. In dual-homing, there are different labels to push
-     * corresponding to the destination switches in an edge-pair. If both
-     * destinations are reachable via the same spine, then the output-port to
-     * the spine will be associated with two labels i.e. there will be two
-     * PortLabel objects for the same port but with different labels.
-     */
-    private class PortLabel {
-        PortNumber port;
-        int edgeLabel;
-        boolean popVlan;
-
-        PortLabel(PortNumber port, int edgeLabel, boolean popVlan) {
-            this.port = port;
-            this.edgeLabel = edgeLabel;
-            this.popVlan = popVlan;
-        }
-
-        @Override
-        public String toString() {
-            return port.toString() + "/" + String.valueOf(edgeLabel) + (popVlan ? "/popVlan" : "");
-        }
-    }
-
-    /**
-     * Makes a call to the FlowObjective service to add buckets to
-     * a hashed group. User must ensure that all the ports & labels are meant
-     * same neighbor (ie. dstMac).
-     *
-     * @param portLabels a collection of port & label combinations to add
-     *                   to the hash group identified by the nextId
-     * @param dstMac destination mac address of next-hop
-     * @param nextId id for next-objective to which buckets will be added
-     *
-     */
-    private void addToHashedNextObjective(Collection<PortLabel> portLabels,
-                                          MacAddress dstMac, Integer nextId) {
-        // setup metadata to pass to nextObjective - indicate the vlan on egress
-        // if needed by the switch pipeline. Since hashed next-hops are always to
-        // other neighboring routers, there is no subnet assigned on those ports.
-        TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
-        metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
-                .withId(nextId)
-                .withType(NextObjective.Type.HASHED)
-                .withMeta(metabuilder.build())
-                .fromApp(appId);
-        // Create the new buckets to be updated
-        portLabels.forEach(pl -> {
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-            tBuilder.setOutput(pl.port)
-                .setEthDst(dstMac)
-                .setEthSrc(nodeMacAddr);
-            if (pl.popVlan) {
-                tBuilder.popVlan();
-            }
-            if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
-                tBuilder.pushMpls()
-                    .copyTtlOut()
-                    .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
-            }
-            nextObjBuilder.addTreatment(tBuilder.build());
-        });
-
-        log.debug("addToHash in device {}: Adding Bucket with port/label {} "
-                + "to nextId {}", deviceId, portLabels, nextId);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("addToHash port/label {} addedTo "
-                        + "NextObj {} on {}", portLabels, nextId, deviceId),
-                (objective, error) -> {
-                    log.warn("addToHash failed to add port/label {} to NextObj {} on {}: {}",
-                            portLabels, nextId, deviceId, error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-        NextObjective nextObjective = nextObjBuilder.addToExisting(context);
-        flowObjectiveService.next(deviceId, nextObjective);
-    }
-
-    /**
-     * Makes a call to the FlowObjective service to remove buckets from
-     * a hash group. User must ensure that all the ports & labels are meant
-     * same neighbor (ie. dstMac).
-     *
-     * @param portLabels a collection of port & label combinations to remove
-     *                   from the hash group identified by the nextId
-     * @param dstMac destination mac address of next-hop
-     * @param nextId id for next-objective from which buckets will be removed
-     */
-    private void removeFromHashedNextObjective(Collection<PortLabel> portLabels,
-                                               MacAddress dstMac, Integer nextId) {
-        TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
-        metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder()
-                .withType(NextObjective.Type.HASHED) //same as original
-                .withMeta(metabuilder.build())
-                .withId(nextId)
-                .fromApp(appId);
-        // Create the buckets to be removed
-        portLabels.forEach(pl -> {
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-            tBuilder.setOutput(pl.port)
-                .setEthDst(dstMac)
-                .setEthSrc(nodeMacAddr);
-            if (pl.popVlan) {
-                tBuilder.popVlan();
-            }
-            if (pl.edgeLabel != DestinationSet.NO_EDGE_LABEL) {
-                tBuilder.pushMpls()
-                    .copyTtlOut()
-                    .setMpls(MplsLabel.mplsLabel(pl.edgeLabel));
-            }
-            nextObjBuilder.addTreatment(tBuilder.build());
-        });
-        log.debug("removeFromHash in device {}: Removing Bucket with port/label"
-                + " {} from nextId {}", deviceId, portLabels, nextId);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("port/label {} removedFrom NextObj"
-                        + " {} on {}", portLabels, nextId, deviceId),
-                (objective, error) -> {
-                    log.warn("port/label {} failed to removeFrom NextObj {} on {}: {}",
-                            portLabels, nextId, deviceId, error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-        NextObjective nextObjective = nextObjBuilder.removeFromExisting(context);
-        flowObjectiveService.next(deviceId, nextObjective);
-    }
-
-    /**
-     * Checks all the hash-groups in the target-switch meant for the destination
-     * switch, and either adds or removes buckets to make the neighbor-set
-     * match the given next-hops. Typically called by the master instance of the
-     * destination switch, which may be different from the master instance of the
-     * target switch where hash-group changes are made.
-     *
-     * @param targetSw the switch in which the hash groups will be edited
-     * @param nextHops the current next hops for the target switch to reach
-     *                  the dest sw
-     * @param destSw  the destination switch
-     * @param revoke true if hash groups need to remove buckets from the
-     *                          the groups to match the current next hops
-     * @return true if calls are made to edit buckets, or if no edits are required
-     */
-    public boolean fixHashGroups(DeviceId targetSw, Set<DeviceId> nextHops,
-                                 DeviceId destSw, boolean revoke) {
-        // temporary storage of keys to be updated
-        Map<DestinationSetNextObjectiveStoreKey, Set<DeviceId>> tempStore =
-                new HashMap<>();
-        boolean foundNextObjective = false, success = true;
-
-        // retrieve hash-groups meant for destSw, which have destinationSets
-        // with different neighbors than the given next-hops
-        for (DestinationSetNextObjectiveStoreKey dskey : dsNextObjStore.keySet()) {
-            if (!dskey.deviceId().equals(targetSw) ||
-                    !dskey.destinationSet().getDestinationSwitches().contains(destSw)) {
-                continue;
-            }
-            foundNextObjective = true;
-            NextNeighbors nhops = dsNextObjStore.get(dskey);
-            Set<DeviceId> currNeighbors = nhops.nextHops(destSw);
-            int edgeLabel = dskey.destinationSet().getEdgeLabel(destSw);
-            Integer nextId = nhops.nextId();
-            if (currNeighbors == null || nextHops == null) {
-                log.warn("fixing hash groups but found currNeighbors:{} or nextHops:{}"
-                        + " in targetSw:{} for dstSw:{}", currNeighbors,
-                         nextHops, targetSw, destSw);
-                success &= false;
-                continue;
-            }
-
-            // some store elements may not be hashed next-objectives - ignore them
-            if (isSimpleNextObjective(dskey)) {
-                log.debug("Ignoring {} of SIMPLE nextObj for targetSw:{}"
-                        + " -> dstSw:{} with current nextHops:{} to new"
-                        + " nextHops: {} in nextId:{}",
-                          (revoke) ? "removal" : "addition", targetSw, destSw,
-                          currNeighbors, nextHops, nextId);
-                if ((revoke && !nextHops.isEmpty())
-                        || (!revoke && !nextHops.equals(currNeighbors))) {
-                    log.debug("Simple next objective cannot be edited to "
-                            + "move from {} to {}", currNeighbors, nextHops);
-                }
-                continue;
-            }
-
-            Set<DeviceId> diff;
-            if (revoke) {
-                diff = Sets.difference(currNeighbors, nextHops);
-                log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
-                        + "hops:{} ..removing {}", targetSw, destSw, nextId,
-                        currNeighbors, diff);
-            } else {
-                diff = Sets.difference(nextHops, currNeighbors);
-                log.debug("targetSw:{} -> dstSw:{} in nextId:{} has current next "
-                        + "hops:{} ..adding {}", targetSw, destSw, nextId,
-                        currNeighbors, diff);
-            }
-            boolean suc = updateAllPortsToNextHop(diff, edgeLabel, nextId, popVlanInHashGroup(dskey.destinationSet()),
-                                                  revoke);
-            if (suc) {
-                // to update neighbor set with changes made
-                if (revoke) {
-                    tempStore.put(dskey, Sets.difference(currNeighbors, diff));
-                } else {
-                    tempStore.put(dskey, Sets.union(currNeighbors, diff));
-                }
-            }
-            success &= suc;
-        }
-
-        if (!foundNextObjective) {
-            log.debug("Cannot find any nextObjectives for route targetSw:{} "
-                    + "-> dstSw:{}", targetSw, destSw);
-            return false; // nothing to do, return false so re-route will be performed
-        }
-
-        // update the dsNextObjectiveStore with new destinationSet to nextId mappings
-        for (DestinationSetNextObjectiveStoreKey key : tempStore.keySet()) {
-            NextNeighbors currentNextHops = dsNextObjStore.get(key);
-            if (currentNextHops == null) {
-                log.warn("fixHashGroups could not update global store in "
-                        + "device {} .. missing nextNeighbors for key {}",
-                        deviceId, key);
-                continue;
-            }
-            Set<DeviceId> newNeighbors = new HashSet<>();
-            newNeighbors.addAll(tempStore.get(key));
-            Map<DeviceId, Set<DeviceId>> oldDstNextHops =
-                    ImmutableMap.copyOf(currentNextHops.dstNextHops());
-            currentNextHops.dstNextHops().put(destSw, newNeighbors); //local change
-            log.debug("Updating nsNextObjStore target:{} -> dst:{} in key:{} nextId:{}",
-                      targetSw, destSw, key, currentNextHops.nextId());
-            log.debug("Old dstNextHops: {}", oldDstNextHops);
-            log.debug("New dstNextHops: {}", currentNextHops.dstNextHops());
-            // update global store
-            dsNextObjStore.put(key,
-                               new NextNeighbors(currentNextHops.dstNextHops(),
-                                                 currentNextHops.nextId()));
-        }
-
-        // even if one fails and others succeed, return false so ECMPspg not updated
-        return success;
-    }
-
-    /**
-     * Updates the DestinationSetNextObjectiveStore with any per-destination nexthops
-     * that are not already in the store for the given DestinationSet. Note that
-     * this method does not remove existing next hops for the destinations in the
-     * DestinationSet.
-     *
-     * @param ds the DestinationSet for which the next hops need to be updated
-     * @param newDstNextHops a map of per-destination next hops to update the
-     *                          destinationSet with
-     * @return true if successful in updating all next hops
-     */
-    private boolean updateNextHops(DestinationSet ds,
-                                  Map<DeviceId, Set<DeviceId>> newDstNextHops) {
-        DestinationSetNextObjectiveStoreKey key =
-                new DestinationSetNextObjectiveStoreKey(deviceId, ds);
-        NextNeighbors currNext = dsNextObjStore.get(key);
-        Map<DeviceId, Set<DeviceId>> currDstNextHops = currNext.dstNextHops();
-
-        // add newDstNextHops to currDstNextHops for each dst
-        boolean success = true;
-        for (DeviceId dstSw : ds.getDestinationSwitches()) {
-            Set<DeviceId> currNhops = currDstNextHops.get(dstSw);
-            Set<DeviceId> newNhops = newDstNextHops.get(dstSw);
-            currNhops = (currNhops == null) ? Sets.newHashSet() : currNhops;
-            newNhops = (newNhops == null) ? Sets.newHashSet() : newNhops;
-            int edgeLabel = ds.getEdgeLabel(dstSw);
-            int nextId = currNext.nextId();
-
-            // new next hops should be added
-            boolean suc = updateAllPortsToNextHop(Sets.difference(newNhops, currNhops),
-                                                  edgeLabel, nextId, popVlanInHashGroup(key.destinationSet()), false);
-            if (suc) {
-                currNhops.addAll(newNhops);
-                currDstNextHops.put(dstSw, currNhops); // this is only a local change
-            }
-            success &= suc;
-        }
-
-        if (success) {
-            // update global store
-            dsNextObjStore.put(key, new NextNeighbors(currDstNextHops,
-                                                      currNext.nextId()));
-            log.debug("Updated device:{} ds:{} new next-hops: {}", deviceId, ds,
-                      dsNextObjStore.get(key));
-        }
-        return success;
-    }
-
-    /**
-     * Adds or removes buckets for all ports to a set of neighbor devices. Caller
-     * needs to ensure that the  given neighbors are all next hops towards the
-     * same destination (represented by the given edgeLabel).
-     *
-     * @param neighbors set of neighbor device ids
-     * @param edgeLabel MPLS label to use in buckets
-     * @param nextId the nextObjective to change
-     * @param popVlan this hash group bucket shuold includes a popVlan action
-     * @param revoke true if buckets need to be removed, false if they need to
-     *          be added
-     * @return true if successful in adding or removing buckets for all ports
-     *                  to the neighbors
-     */
-    private boolean updateAllPortsToNextHop(Set<DeviceId> neighbors, int edgeLabel,
-                                         int nextId, boolean popVlan, boolean revoke) {
-        for (DeviceId neighbor : neighbors) {
-            MacAddress neighborMac;
-            try {
-                neighborMac = deviceConfig.getDeviceMac(neighbor);
-            } catch (DeviceConfigNotFoundException e) {
-                log.warn(e.getMessage() + " Aborting updateAllPortsToNextHop"
-                        + " for nextId:" + nextId);
-                return false;
-            }
-            Collection<PortNumber> portsToNeighbor = devicePortMap.get(neighbor);
-            if (portsToNeighbor == null || portsToNeighbor.isEmpty()) {
-                log.warn("No ports found in dev:{} for neighbor:{} .. cannot "
-                        + "updateAllPortsToNextHop for nextId: {}",
-                         deviceId, neighbor, nextId);
-                return false;
-            }
-            List<PortLabel> pl = Lists.newArrayList();
-            portsToNeighbor.forEach(p -> pl.add(new PortLabel(p, edgeLabel, popVlan)));
-            if (revoke) {
-                log.debug("updateAllPortsToNextHops in device {}: Removing Bucket(s) "
-                        + "with Port/Label:{} to next object id {}",
-                        deviceId, pl, nextId);
-                removeFromHashedNextObjective(pl, neighborMac, nextId);
-            } else {
-                log.debug("fixHashGroup in device {}: Adding Bucket(s) "
-                        + "with Port/Label: {} to next object id {}",
-                        deviceId, pl, nextId);
-                addToHashedNextObjective(pl, neighborMac, nextId);
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Returns true if the destination set is meant for swap or multi-labeled
-     * packet transport, and MPLS ECMP is not supported.
-     *
-     * @param dskey the key representing the destination set
-     * @return true if destination set is meant for simple next objectives
-     */
-    boolean isSimpleNextObjective(DestinationSetNextObjectiveStoreKey dskey) {
-        return (dskey.destinationSet().notBos() || dskey.destinationSet().swap())
-                && !srManager.getMplsEcmp();
-    }
-
-    /**
-     * Adds or removes a port that has been configured with a vlan to a broadcast group
-     * for bridging. Should only be called by the master instance for this device.
-     *
-     * @param port the port on this device that needs to be added/removed to a bcast group
-     * @param vlanId the vlan id corresponding to the broadcast domain/group
-     * @param popVlan indicates if packets should be sent out untagged or not out
-     *                of the port. If true, indicates an access (untagged) or native vlan
-     *                configuration. If false, indicates a trunk (tagged) vlan config.
-     * @param portUp true if port is enabled, false if disabled
-     */
-    public void processEdgePort(PortNumber port, VlanId vlanId,
-                                boolean popVlan, boolean portUp) {
-        //get the next id for the subnet and edit it.
-        Integer nextId = getVlanNextObjectiveId(vlanId);
-        if (nextId == -1) {
-            if (portUp) {
-                log.debug("**Creating flooding group for first port enabled in"
-                        + " vlan {} on dev {} port {}", vlanId, deviceId, port);
-                createBcastGroupFromVlan(vlanId, Collections.singleton(port));
-            } else {
-                log.warn("Could not find flooding group for subnet {} on dev:{} when"
-                        + " removing port:{}", vlanId, deviceId, port);
-            }
-            return;
-        }
-
-        log.info("**port{} in device {}: {} Bucket with Port {} to"
-                + " next-id {}", (portUp) ? "UP" : "DOWN", deviceId,
-                                          (portUp) ? "Adding" : "Removing",
-                                          port, nextId);
-        // Create the bucket to be added or removed
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        if (popVlan) {
-            tBuilder.popVlan();
-        }
-        tBuilder.setOutput(port);
-
-        TrafficSelector metadata =
-                DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.BROADCAST).fromApp(appId)
-                .addTreatment(tBuilder.build())
-                .withMeta(metadata);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-            (objective) -> log.debug("port {} successfully {} NextObj {} on {}",
-                                     port, (portUp) ? "addedTo" : "removedFrom",
-                                     nextId, deviceId),
-            (objective, error) -> {
-                log.warn("port {} failed to {} NextObj {} on {}: {}",
-                        port, (portUp) ? "addTo" : "removeFrom", nextId, deviceId, error);
-                srManager.invalidateNextObj(objective.id());
-            });
-
-        NextObjective nextObj = (portUp) ? nextObjBuilder.addToExisting(context)
-                                         : nextObjBuilder.removeFromExisting(context);
-        log.debug("edgePort processed: Submited next objective {} in device {}",
-                  nextId, deviceId);
-        flowObjectiveService.next(deviceId, nextObj);
-    }
-
-    /**
-     * Returns the next objective of type hashed (or simple) associated with the
-     * destination set. In addition, updates the existing next-objective if new
-     * route-paths found have resulted in the addition of new next-hops to a
-     * particular destination. If there is no existing next objective for this
-     * destination set, this method would create a next objective and return the
-     * nextId. Optionally metadata can be passed in for the creation of the next
-     * objective. If the parameter simple is true then a simple next objective
-     * is created instead of a hashed one.
-     *
-     * @param ds destination set
-     * @param nextHops a map of per destination next hops
-     * @param meta metadata passed into the creation of a Next Objective
-     * @param simple if true, a simple next objective will be created instead of
-     *            a hashed next objective
-     * @return int if found or -1 if there are errors in the creation of the
-     *         neighbor set.
-     */
-    public int getNextObjectiveId(DestinationSet ds,
-                                  Map<DeviceId, Set<DeviceId>> nextHops,
-                                  TrafficSelector meta, boolean simple) {
-        NextNeighbors next = dsNextObjStore.
-                get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
-        if (next == null) {
-            log.debug("getNextObjectiveId in device{}: Next objective id "
-                    + "not found for {} ... creating", deviceId, ds);
-            log.trace("getNextObjectiveId: nsNextObjStore contents for device {}: {}",
-                      deviceId,
-                      dsNextObjStore.entrySet()
-                      .stream()
-                      .filter((nsStoreEntry) ->
-                      (nsStoreEntry.getKey().deviceId().equals(deviceId)))
-                      .collect(Collectors.toList()));
-
-            createGroupFromDestinationSet(ds, nextHops, meta, simple);
-            next = dsNextObjStore.
-                    get(new DestinationSetNextObjectiveStoreKey(deviceId, ds));
-            if (next == null) {
-                log.warn("getNextObjectiveId: unable to create next objective");
-                // failure in creating group
-                return -1;
-            } else {
-                log.debug("getNextObjectiveId in device{}: Next objective id {} "
-                    + "created for {}", deviceId, next.nextId(), ds);
-            }
-        } else {
-            log.trace("getNextObjectiveId in device{}: Next objective id {} "
-                    + "found for {}", deviceId, next.nextId(), ds);
-            // should fix hash groups too if next-hops have changed
-            if (!next.dstNextHops().equals(nextHops)) {
-                log.debug("Nexthops have changed for dev:{} nextId:{} ..updating",
-                          deviceId, next.nextId());
-                if (!updateNextHops(ds, nextHops)) {
-                    // failure in updating group
-                    return -1;
-                }
-            }
-        }
-        return next.nextId();
-    }
-
-    /**
-     * Returns the next objective of type broadcast associated with the vlan,
-     * or -1 if no such objective exists. Note that this method does NOT create
-     * the next objective as a side-effect. It is expected that is objective is
-     * created at startup from network configuration. Typically this is used
-     * for L2 flooding within the subnet configured on the switch.
-     *
-     * @param vlanId vlan id
-     * @return int if found or -1
-     */
-    public int getVlanNextObjectiveId(VlanId vlanId) {
-        Integer nextId = vlanNextObjStore.
-                get(new VlanNextObjectiveStoreKey(deviceId, vlanId));
-
-        return (nextId != null) ? nextId : -1;
-    }
-
-    /**
-     * Returns the next objective of type simple associated with the mac/vlan on the
-     * device, given the treatment. Different treatments to the same mac/vlan result
-     * in different next objectives. If no such objective exists, this method
-     * creates one (if requested) and returns the id. Optionally metadata can be passed in for
-     * the creation of the objective. Typically this is used for L2 and L3 forwarding
-     * to compute nodes and containers/VMs on the compute nodes directly attached
-     * to the switch.
-     *
-     * @param macAddr the mac addr for the simple next objective
-     * @param vlanId the vlan for the simple next objective
-     * @param port port with which to create the Next Obj.
-     * @param createIfMissing true if a next object should be created if not found
-     * @return int if found or created, -1 if there are errors during the
-     *          creation of the next objective.
-     */
-    public int getMacVlanNextObjectiveId(MacAddress macAddr, VlanId vlanId, PortNumber port,
-                                      boolean createIfMissing) {
-
-        Integer nextId = macVlanNextObjStore
-                .get(new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId));
-
-        if (nextId != null) {
-            return nextId;
-        }
-
-        log.debug("getMacVlanNextObjectiveId in device {}: Next objective id "
-                + "not found for host : {}/{} .. {}", deviceId, macAddr, vlanId,
-                (createIfMissing) ? "creating" : "aborting");
-
-        if (!createIfMissing) {
-            return -1;
-        }
-
-        MacAddress deviceMac;
-        try {
-            deviceMac = deviceConfig.getDeviceMac(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " in getMacVlanNextObjectiveId");
-            return -1;
-        }
-
-        // since we are creating now, port cannot be null
-        if (port == null) {
-           log.debug("getMacVlanNextObjectiveId : port information cannot be null "
-                          + "for device {}, host {}/{}", deviceId, macAddr, vlanId);
-           return -1;
-        }
-
-        TrafficSelector.Builder meta = DefaultTrafficSelector.builder();
-
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-        treatment.deferred()
-                .setEthDst(macAddr)
-                .setEthSrc(deviceMac)
-                .setOutput(port);
-
-        ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
-        VlanId untaggedVlan = srManager.interfaceService.getUntaggedVlanId(connectPoint);
-        Set<VlanId> taggedVlans = srManager.interfaceService.getTaggedVlanId(connectPoint);
-        VlanId nativeVlan = srManager.interfaceService.getNativeVlanId(connectPoint);
-
-        // Adjust the meta according to VLAN configuration
-        if (taggedVlans.contains(vlanId)) {
-            treatment.setVlanId(vlanId);
-        } else if (vlanId.equals(VlanId.NONE)) {
-            if (untaggedVlan != null) {
-                meta.matchVlanId(untaggedVlan);
-            } else if (nativeVlan != null) {
-                meta.matchVlanId(nativeVlan);
-            } else {
-                log.warn("Untagged nexthop {}/{} is not allowed on {} without untagged or native vlan",
-                        macAddr, vlanId, connectPoint);
-                return -1;
-            }
-        } else {
-            log.warn("Tagged nexthop {}/{} is not allowed on {} without VLAN listed"
-                    + " in tagged vlan", macAddr, vlanId, connectPoint);
-            return -1;
-        }
-
-        /* create missing next objective */
-        nextId = createGroupFromMacVlan(macAddr, vlanId, treatment.build(), meta.build());
-        if (nextId == null) {
-            log.warn("getMacVlanNextObjectiveId: unable to create next obj"
-                    + "for dev:{} host:{}/{}", deviceId, macAddr, vlanId);
-            return -1;
-        }
-        return nextId;
-    }
-
-
-    /**
-     * Returns the next objective of type simple associated with the port on the
-     * device, given the treatment. Different treatments to the same port result
-     * in different next objectives. If no such objective exists, this method
-     * creates one (if requested) and returns the id. Optionally metadata can be passed in for
-     * the creation of the objective. Typically this is used for L2 and L3 forwarding
-     * to compute nodes and containers/VMs on the compute nodes directly attached
-     * to the switch.
-     *
-     * @param portNum the port number for the simple next objective
-     * @param treatment the actions to apply on the packets (should include outport)
-     * @param meta optional metadata passed into the creation of the next objective
-     * @param createIfMissing true if a next object should be created if not found
-     * @return int if found or created, -1 if there are errors during the
-     *          creation of the next objective.
-     */
-    public int getPortNextObjectiveId(PortNumber portNum, TrafficTreatment treatment,
-                                      TrafficSelector meta, boolean createIfMissing) {
-        Integer nextId = portNextObjStore
-                .get(new PortNextObjectiveStoreKey(deviceId, portNum, treatment, meta));
-        if (nextId != null) {
-            return nextId;
-        }
-        log.debug("getPortNextObjectiveId in device {}: Next objective id "
-                + "not found for port: {} .. {}", deviceId, portNum,
-                (createIfMissing) ? "creating" : "aborting");
-        if (!createIfMissing) {
-            return -1;
-        }
-        // create missing next objective
-        createGroupFromPort(portNum, treatment, meta);
-        nextId = portNextObjStore.get(new PortNextObjectiveStoreKey(deviceId, portNum,
-                                                                    treatment, meta));
-        if (nextId == null) {
-            log.warn("getPortNextObjectiveId: unable to create next obj"
-                    + "for dev:{} port:{}", deviceId, portNum);
-            return -1;
-        }
-        return nextId;
-    }
-
-    /**
-     * Checks if the next objective ID (group) for the neighbor set exists or not.
-     *
-     * @param ns neighbor set to check
-     * @return true if it exists, false otherwise
-     */
-    public boolean hasNextObjectiveId(DestinationSet ns) {
-        NextNeighbors nextHops = dsNextObjStore.
-                get(new DestinationSetNextObjectiveStoreKey(deviceId, ns));
-        if (nextHops == null) {
-            return false;
-        }
-
-        return true;
-    }
-
-    private void populateNeighborMaps() {
-        Set<Link> outgoingLinks = linkService.getDeviceEgressLinks(deviceId);
-        for (Link link : outgoingLinks) {
-            if (link.type() != Link.Type.DIRECT) {
-                continue;
-            }
-            addNeighborAtPort(link.dst().deviceId(), link.src().port());
-        }
-    }
-
-    protected void addNeighborAtPort(DeviceId neighborId,
-                                     PortNumber portToNeighbor) {
-        // Update DeviceToPort database
-        log.debug("Device {} addNeighborAtPort: neighbor {} at port {}",
-                  deviceId, neighborId, portToNeighbor);
-        Set<PortNumber> ports = Collections
-                .newSetFromMap(new ConcurrentHashMap<PortNumber, Boolean>());
-        ports.add(portToNeighbor);
-        Set<PortNumber> portnums = devicePortMap.putIfAbsent(neighborId, ports);
-        if (portnums != null) {
-            portnums.add(portToNeighbor);
-        }
-
-        // Update portToDevice database
-        // should always update as neighbor could have changed on this port
-        DeviceId prev = portDeviceMap.put(portToNeighbor, neighborId);
-        if (prev != null) {
-            log.warn("Device/port: {}/{} previous neighbor: {}, current neighbor: {} ",
-                      deviceId, portToNeighbor, prev, neighborId);
-        }
-    }
-
-    /**
-     * Creates a NextObjective for a hash group in this device from a given
-     * DestinationSet. If the parameter simple is true, a simple next objective
-     * is created instead.
-     *
-     * @param ds the DestinationSet
-     * @param neighbors a map for each destination and its next-hops
-     * @param meta metadata passed into the creation of a Next Objective
-     * @param simple if true, a simple next objective will be created instead of
-     *            a hashed next objective
-     */
-    public void createGroupFromDestinationSet(DestinationSet ds,
-                                              Map<DeviceId, Set<DeviceId>> neighbors,
-                                              TrafficSelector meta,
-                                              boolean simple) {
-        int nextId = flowObjectiveService.allocateNextId();
-        NextObjective.Type type = (simple) ? NextObjective.Type.SIMPLE
-                                           : NextObjective.Type.HASHED;
-        if (neighbors == null || neighbors.isEmpty()) {
-            log.warn("createGroupsFromDestinationSet: needs at least one neighbor"
-                    + "to create group in dev:{} for ds: {} with next-hops {}",
-                    deviceId, ds, neighbors);
-            return;
-        }
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder()
-                .withId(nextId)
-                .withType(type)
-                .fromApp(appId);
-        if (meta != null) {
-            nextObjBuilder.withMeta(meta);
-        }
-
-        // create treatment buckets for each neighbor for each dst Device
-        // except in the special case where we only want to pick a single
-        // neighbor/port for a simple nextObj
-        boolean foundSingleNeighbor = false;
-        boolean treatmentAdded = false;
-        Map<DeviceId, Set<DeviceId>> dstNextHops = new ConcurrentHashMap<>();
-        for (DeviceId dst : ds.getDestinationSwitches()) {
-            Set<DeviceId> nextHops = neighbors.get(dst);
-            if (nextHops == null || nextHops.isEmpty()) {
-                continue;
-            }
-
-            if (foundSingleNeighbor) {
-                break;
-            }
-
-            for (DeviceId neighborId : nextHops) {
-                if (devicePortMap.get(neighborId) == null) {
-                    log.warn("Neighbor {} is not in the port map yet for dev:{}",
-                             neighborId, deviceId);
-                    return;
-                } else if (devicePortMap.get(neighborId).isEmpty()) {
-                    log.warn("There are no ports for "
-                            + "the Device {} in the port map yet", neighborId);
-                    return;
-                }
-
-                MacAddress neighborMac;
-                try {
-                    neighborMac = deviceConfig.getDeviceMac(neighborId);
-                } catch (DeviceConfigNotFoundException e) {
-                    log.warn(e.getMessage() + " Aborting createGroupsFromDestinationset.");
-                    return;
-                }
-                // For each port to the neighbor, we create a new treatment
-                Set<PortNumber> neighborPorts = devicePortMap.get(neighborId);
-                // In this case we need a SIMPLE nextObj. We randomly pick a port
-                if (simple) {
-                    int size = devicePortMap.get(neighborId).size();
-                    int index = RandomUtils.nextInt(0, size);
-                    neighborPorts = Collections.singleton(
-                                        Iterables.get(devicePortMap.get(neighborId),
-                                                      index));
-                    foundSingleNeighbor = true;
-                }
-
-                for (PortNumber sp : neighborPorts) {
-                    TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment
-                            .builder();
-                    tBuilder.setEthDst(neighborMac).setEthSrc(nodeMacAddr);
-                    int edgeLabel = ds.getEdgeLabel(dst);
-                    if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
-                        if (simple) {
-                            // swap label case
-                            tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
-                        } else {
-                            // ecmp with label push case
-                            tBuilder.pushMpls().copyTtlOut()
-                                    .setMpls(MplsLabel.mplsLabel(edgeLabel));
-                        }
-                    }
-
-                    // Set VLAN ID for PW transport. Otherwise pop vlan
-                    if (!popVlanInHashGroup(ds)) {
-                        tBuilder.setVlanId(srManager.getPwTransportVlan());
-                    } else {
-                        tBuilder.popVlan();
-                    }
-
-                    tBuilder.setOutput(sp);
-                    nextObjBuilder.addTreatment(tBuilder.build());
-                    treatmentAdded = true;
-                    //update store
-                    Set<DeviceId> existingNeighbors = dstNextHops.get(dst);
-                    if (existingNeighbors == null) {
-                        existingNeighbors = new HashSet<>();
-                    }
-                    existingNeighbors.add(neighborId);
-                    dstNextHops.put(dst, existingNeighbors);
-                    log.debug("creating treatment for port/label {}/{} in next:{}",
-                              sp, edgeLabel, nextId);
-                }
-
-                if (foundSingleNeighbor) {
-                    break;
-                }
-            }
-        }
-
-        if (!treatmentAdded) {
-            log.warn("Could not createGroup from DestinationSet {} without any"
-                    + "next hops {}", ds, neighbors);
-            return;
-        }
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) ->
-                log.debug("createGroupsFromDestinationSet installed "
-                        + "NextObj {} on {}", nextId, deviceId),
-                (objective, error) -> {
-                    log.warn("createGroupsFromDestinationSet failed to install NextObj {} on {}: {}",
-                            nextId, deviceId, error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-        NextObjective nextObj = nextObjBuilder.add(context);
-        log.debug(".. createGroupsFromDestinationSet: Submitted "
-                + "next objective {} in device {}", nextId, deviceId);
-        flowObjectiveService.next(deviceId, nextObj);
-        //update store
-        dsNextObjStore.put(new DestinationSetNextObjectiveStoreKey(deviceId, ds),
-                           new NextNeighbors(dstNextHops, nextId));
-    }
-
-    /**
-     * Creates broadcast groups for all ports in the same subnet for
-     * all configured subnets.
-     */
-    public void createGroupsFromVlanConfig() {
-        srManager.getVlanPortMap(deviceId).asMap().forEach((vlanId, ports) -> {
-            createBcastGroupFromVlan(vlanId, ports);
-        });
-    }
-
-    /**
-     * Creates a single broadcast group from a given vlan id and list of ports.
-     *
-     * @param vlanId vlan id
-     * @param ports list of ports in the subnet
-     */
-    public void createBcastGroupFromVlan(VlanId vlanId, Collection<PortNumber> ports) {
-        VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
-
-        if (vlanNextObjStore.containsKey(key)) {
-            log.debug("Broadcast group for device {} and subnet {} exists",
-                      deviceId, vlanId);
-            return;
-        }
-
-        TrafficSelector metadata =
-                DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
-
-        int nextId = flowObjectiveService.allocateNextId();
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.BROADCAST).fromApp(appId)
-                .withMeta(metadata);
-
-        ports.forEach(port -> {
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-            if (toPopVlan(port, vlanId)) {
-                tBuilder.popVlan();
-            }
-            tBuilder.setOutput(port);
-            nextObjBuilder.addTreatment(tBuilder.build());
-        });
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-            (objective) ->
-                log.debug("createBroadcastGroupFromVlan installed "
-                        + "NextObj {} on {}", nextId, deviceId),
-            (objective, error) -> {
-                log.warn("createBroadcastGroupFromVlan failed to install NextObj {} on {}: {}",
-                        nextId, deviceId, error);
-                srManager.invalidateNextObj(objective.id());
-            });
-        NextObjective nextObj = nextObjBuilder.add(context);
-        flowObjectiveService.next(deviceId, nextObj);
-        log.debug("createBcastGroupFromVlan: Submitted next objective {} "
-                + "for vlan: {} in device {}", nextId, vlanId, deviceId);
-
-        vlanNextObjStore.put(key, nextId);
-    }
-
-    /**
-     * Removes a single broadcast group from a given vlan id.
-     * The group should be empty.
-     * @param deviceId device Id to remove the group
-     * @param portNum port number related to the group
-     * @param vlanId vlan id of the broadcast group to remove
-     * @param popVlan true if the TrafficTreatment involves pop vlan tag action
-     */
-    public void removeBcastGroupFromVlan(DeviceId deviceId, PortNumber portNum,
-                                         VlanId vlanId, boolean popVlan) {
-        VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
-
-        if (!vlanNextObjStore.containsKey(key)) {
-            log.debug("Broadcast group for device {} and subnet {} does not exist",
-                      deviceId, vlanId);
-            return;
-        }
-
-        TrafficSelector metadata =
-                DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
-
-        int nextId = vlanNextObjStore.get(key);
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.BROADCAST).fromApp(appId)
-                .withMeta(metadata);
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        if (popVlan) {
-            tBuilder.popVlan();
-        }
-        tBuilder.setOutput(portNum);
-        nextObjBuilder.addTreatment(tBuilder.build());
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) ->
-                        log.debug("removeBroadcastGroupFromVlan removed "
-                                          + "NextObj {} on {}", nextId, deviceId),
-                (objective, error) -> {
-                    log.warn("removeBroadcastGroupFromVlan failed to remove NextObj {} on {}: {}",
-                            nextId, deviceId, error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-        NextObjective nextObj = nextObjBuilder.remove(context);
-        flowObjectiveService.next(deviceId, nextObj);
-        log.debug("removeBcastGroupFromVlan: Submited next objective {} in device {}",
-                  nextId, deviceId);
-
-        vlanNextObjStore.remove(key, nextId);
-    }
-
-    /**
-     * Determine if we should pop given vlan before sending packets to the given port.
-     *
-     * @param portNumber port number
-     * @param vlanId vlan id
-     * @return true if the vlan id is not contained in any vlanTagged config
-     */
-    private boolean toPopVlan(PortNumber portNumber, VlanId vlanId) {
-        return srManager.interfaceService
-                .getInterfacesByPort(new ConnectPoint(deviceId, portNumber))
-                .stream().noneMatch(intf -> intf.vlanTagged().contains(vlanId));
-    }
-
-    /**
-     * Create simple next objective for an indirect host mac/vlan. The treatments can include
-     * all outgoing actions that need to happen on the packet.
-     *
-     * @param macAddr the mac address of the host
-     * @param vlanId the vlan of the host
-     * @param treatment the actions to apply on the packets (should include outport)
-     * @param meta optional data to pass to the driver
-     * @return next objective ID
-     */
-    public int createGroupFromMacVlan(MacAddress macAddr, VlanId vlanId, TrafficTreatment treatment,
-                                    TrafficSelector meta) {
-        int nextId = flowObjectiveService.allocateNextId();
-        MacVlanNextObjectiveStoreKey key = new MacVlanNextObjectiveStoreKey(deviceId, macAddr, vlanId);
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.SIMPLE)
-                .addTreatment(treatment)
-                .fromApp(appId)
-                .withMeta(meta);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-            (objective) ->
-                log.debug("createGroupFromMacVlan installed "
-                        + "NextObj {} on {}", nextId, deviceId),
-            (objective, error) -> {
-                log.warn("createGroupFromMacVlan failed to install NextObj {} on {}: {}", nextId, deviceId, error);
-                srManager.invalidateNextObj(objective.id());
-            });
-        NextObjective nextObj = nextObjBuilder.add(context);
-        flowObjectiveService.next(deviceId, nextObj);
-        log.debug("createGroupFromMacVlan: Submited next objective {} in device {} "
-                + "for host {}/{}", nextId, deviceId, macAddr, vlanId);
-
-        macVlanNextObjStore.put(key, nextId);
-        return nextId;
-    }
-
-    /**
-     * Create simple next objective for a single port. The treatments can include
-     * all outgoing actions that need to happen on the packet.
-     *
-     * @param portNum  the outgoing port on the device
-     * @param treatment the actions to apply on the packets (should include outport)
-     * @param meta optional data to pass to the driver
-     */
-    public void createGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
-                                    TrafficSelector meta) {
-        int nextId = flowObjectiveService.allocateNextId();
-        PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
-                                                deviceId, portNum, treatment, meta);
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.SIMPLE)
-                .addTreatment(treatment)
-                .fromApp(appId)
-                .withMeta(meta);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-            (objective) ->
-                log.debug("createGroupFromPort installed "
-                        + "NextObj {} on {}", nextId, deviceId),
-            (objective, error) -> {
-                log.warn("createGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
-                srManager.invalidateNextObj(objective.id());
-            });
-        NextObjective nextObj = nextObjBuilder.add(context);
-        flowObjectiveService.next(deviceId, nextObj);
-        log.debug("createGroupFromPort: Submited next objective {} in device {} "
-                + "for port {}", nextId, deviceId, portNum);
-
-        portNextObjStore.put(key, nextId);
-    }
-
-    /**
-     * Creates simple next objective for a single port.
-     *
-     * @param deviceId device id that has the port to deal with
-     * @param portNum the outgoing port on the device
-     * @param vlanId vlan id associated with the port
-     * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
-     */
-    public void createPortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
-        TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
-        mbuilder.matchVlanId(vlanId);
-
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.immediate().setOutput(portNum);
-        if (popVlan) {
-            tbuilder.immediate().popVlan();
-        }
-
-        createGroupFromPort(portNum, tbuilder.build(), mbuilder.build());
-    }
-
-    /**
-     * Removes simple next objective for a single port.
-     *
-     * @param deviceId device id that has the port to deal with
-     * @param portNum the outgoing port on the device
-     * @param vlanId vlan id associated with the port
-     * @param popVlan true if POP_VLAN action is applied on the packets, false otherwise
-     */
-    public void removePortNextObjective(DeviceId deviceId, PortNumber portNum, VlanId vlanId, boolean popVlan) {
-        TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
-        mbuilder.matchVlanId(vlanId);
-
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.immediate().setOutput(portNum);
-        if (popVlan) {
-            tbuilder.immediate().popVlan();
-        }
-
-        int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
-                                                             tbuilder.build(), mbuilder.build(), false);
-
-        PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
-                deviceId, portNum, tbuilder.build(), mbuilder.build());
-        if (portNextObjId != -1 && portNextObjStore.containsKey(key)) {
-            NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                    .builder().withId(portNextObjId)
-                    .withType(NextObjective.Type.SIMPLE).fromApp(appId);
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("removePortNextObjective removes NextObj {} on {}",
-                                             portNextObjId, deviceId),
-                    (objective, error) -> {
-                        log.warn("removePortNextObjective failed to remove NextObj {} on {}: {}",
-                                portNextObjId, deviceId, error);
-                        srManager.invalidateNextObj(objective.id());
-                    });
-            NextObjective nextObjective = nextObjBuilder.remove(context);
-            log.info("**removePortNextObjective: Submitted "
-                             + "next objective {} in device {}",
-                     portNextObjId, deviceId);
-            flowObjectiveService.next(deviceId, nextObjective);
-
-            portNextObjStore.remove(key);
-        }
-    }
-
-    /**
-     * Removes groups for the next objective ID given.
-     *
-     * @param objectiveId next objective ID to remove
-     * @return true if succeeds, false otherwise
-     */
-    public boolean removeGroup(int objectiveId) {
-        for (Map.Entry<DestinationSetNextObjectiveStoreKey, NextNeighbors> e :
-                dsNextObjStore.entrySet()) {
-            if (e.getValue().nextId() != objectiveId) {
-                continue;
-            }
-            // Right now it is just used in TunnelHandler
-            // remember in future that PW transit groups could
-            // be Indirect groups
-            NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                    .builder().withId(objectiveId)
-                    .withType(NextObjective.Type.HASHED).fromApp(appId);
-            ObjectiveContext context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("RemoveGroup removes NextObj {} on {}",
-                            objectiveId, deviceId),
-                    (objective, error) -> {
-                        log.warn("RemoveGroup failed to remove NextObj {} on {}: {}", objectiveId, deviceId, error);
-                        srManager.invalidateNextObj(objective.id());
-                    });
-            NextObjective nextObjective = nextObjBuilder.remove(context);
-            log.info("**removeGroup: Submited "
-                    + "next objective {} in device {}",
-                    objectiveId, deviceId);
-            flowObjectiveService.next(deviceId, nextObjective);
-
-            dsNextObjStore.remove(e.getKey());
-            return true;
-        }
-
-        return false;
-    }
-    /**
-     * Remove simple next objective for a single port. The treatments can include
-     * all outgoing actions that need to happen on the packet.
-     *
-     * @param portNum  the outgoing port on the device
-     * @param treatment the actions applied on the packets (should include outport)
-     * @param meta optional data to pass to the driver
-     */
-    public void removeGroupFromPort(PortNumber portNum, TrafficTreatment treatment,
-                                    TrafficSelector meta) {
-        PortNextObjectiveStoreKey key = new PortNextObjectiveStoreKey(
-                deviceId, portNum, treatment, meta);
-        Integer nextId = portNextObjStore.get(key);
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.SIMPLE)
-                .addTreatment(treatment)
-                .fromApp(appId)
-                .withMeta(meta);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) ->
-                        log.info("removeGroupFromPort installed "
-                                          + "NextObj {} on {}", nextId, deviceId),
-                (objective, error) -> {
-                    log.warn("removeGroupFromPort failed to install NextObj {} on {}: {}", nextId, deviceId, error);
-                    srManager.invalidateNextObj(objective.id());
-                }
-        );
-        NextObjective nextObj = nextObjBuilder.remove(context);
-        flowObjectiveService.next(deviceId, nextObj);
-        log.info("removeGroupFromPort: Submitted next objective {} in device {} "
-                          + "for port {}", nextId, deviceId, portNum);
-
-        portNextObjStore.remove(key);
-    }
-
-    /**
-     * Removes all groups from all next objective stores.
-     */
-    /*public void removeAllGroups() {
-        for (Map.Entry<NeighborSetNextObjectiveStoreKey, NextNeighbors> entry:
-                nsNextObjStore.entrySet()) {
-            removeGroup(entry.getValue().nextId());
-        }
-        for (Map.Entry<PortNextObjectiveStoreKey, Integer> entry:
-                portNextObjStore.entrySet()) {
-            removeGroup(entry.getValue());
-        }
-        for (Map.Entry<VlanNextObjectiveStoreKey, Integer> entry:
-                vlanNextObjStore.entrySet()) {
-            removeGroup(entry.getValue());
-        }
-    }*/ //XXX revisit
-
-    /**
-     * Triggers a one time bucket verification operation on all hash groups
-     * on this device.
-     */
-    public void triggerBucketCorrector() {
-        BucketCorrector bc = new BucketCorrector();
-        bc.run();
-    }
-
-    /**
-     * Modifies L2IG bucket when the interface configuration is updated, especially
-     * when the interface has same VLAN ID but the VLAN type is changed (e.g., from
-     * vlan-tagged [10] to vlan-untagged 10), which requires changes on
-     * TrafficTreatment in turn.
-     *
-     * @param portNumber the port on this device that needs to be updated
-     * @param vlanId the vlan id corresponding to this port
-     * @param pushVlan indicates if packets should be sent out untagged or not out
-     *                 from the port. If true, updated TrafficTreatment involves
-     *                 pop vlan tag action. If false, updated TrafficTreatment
-     *                 does not involve pop vlan tag action.
-     */
-    public void updateL2InterfaceGroupBucket(PortNumber portNumber, VlanId vlanId, boolean pushVlan) {
-        TrafficTreatment.Builder oldTBuilder = DefaultTrafficTreatment.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        tBuilder.setOutput(portNumber);
-        oldTBuilder.setOutput(portNumber);
-        if (pushVlan) {
-            tBuilder.popVlan();
-        } else {
-            oldTBuilder.popVlan();
-        }
-
-        TrafficSelector metadata =
-                DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
-
-        // Update portNextObjStore with new L2IG
-        int nextId = getPortNextObjectiveId(portNumber, oldTBuilder.build(), metadata, false);
-        portNextObjStore.remove(new PortNextObjectiveStoreKey(deviceId, portNumber, oldTBuilder.build(), metadata));
-        portNextObjStore.put(new PortNextObjectiveStoreKey(deviceId, portNumber, tBuilder.build(), metadata), nextId);
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.SIMPLE).fromApp(appId)
-                .addTreatment(tBuilder.build())
-                .withMeta(metadata);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("port {} successfully updated NextObj {} on {}",
-                                         portNumber, nextId, deviceId),
-                (objective, error) -> {
-                    log.warn("port {} failed to updated NextObj {} on {}: {}", portNumber, nextId, deviceId, error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-
-        flowObjectiveService.next(deviceId, nextObjBuilder.modify(context));
-    }
-
-    /**
-     * Updates the next objective for the given nextId .
-     *
-     * @param hostMac mac of host for which Next obj is to be updated.
-     * @param hostVlanId vlan of host for which Next obj is to be updated.
-     * @param port port with which to update the Next Obj.
-     * @param nextId of Next Obj which needs to be updated.
-     */
-    public void updateL3UcastGroupBucket(MacAddress hostMac, VlanId hostVlanId, PortNumber port, int nextId) {
-
-       MacAddress deviceMac;
-        try {
-            deviceMac = deviceConfig.getDeviceMac(deviceId);
-        } catch (DeviceConfigNotFoundException e) {
-            log.warn(e.getMessage() + " in updateL3UcastGroupBucket");
-            return;
-        }
-
-        TrafficSelector metadata =
-                 DefaultTrafficSelector.builder().matchVlanId(hostVlanId).build();
-
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-        tbuilder.deferred()
-                .setEthDst(hostMac)
-                .setEthSrc(deviceMac)
-                .setVlanId(hostVlanId)
-                .setOutput(port);
-
-        log.debug(" update L3Ucast : deviceMac {}, port {}, host {}/{}, nextid {}, Treatment {} Meta {}",
-                                     deviceMac, port, hostMac, hostVlanId, nextId, tbuilder.build(), metadata);
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.SIMPLE).fromApp(appId)
-                .addTreatment(tbuilder.build())
-                .withMeta(metadata);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug(" NextId {} successfully updated host {} vlan {} with port {}",
-                                         nextId, hostMac, hostVlanId, port),
-                (objective, error) -> {
-                    log.warn(" NextId {} failed to update host {} vlan {} with port {}, error : {}",
-                                         nextId, hostMac, hostVlanId, port, error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-
-        NextObjective nextObj = nextObjBuilder.modify(context);
-        flowObjectiveService.next(deviceId, nextObj);
-
-    }
-
-    /**
-     * Adds a single port to the L2FG or removes it from the L2FG.
-     *
-     * @param vlanId the vlan id corresponding to this port
-     * @param portNum the port on this device to be updated
-     * @param nextId the next objective ID for the given vlan id
-     * @param install if true, adds the port to L2FG. If false, removes it from L2FG.
-     */
-    public void updateGroupFromVlanConfiguration(VlanId vlanId, PortNumber portNum, int nextId, boolean install) {
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        if (toPopVlan(portNum, vlanId)) {
-            tBuilder.popVlan();
-        }
-        tBuilder.setOutput(portNum);
-
-        TrafficSelector metadata =
-                DefaultTrafficSelector.builder().matchVlanId(vlanId).build();
-
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.BROADCAST).fromApp(appId)
-                .addTreatment(tBuilder.build())
-                .withMeta(metadata);
-
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("port {} successfully removedFrom NextObj {} on {}",
-                                         portNum, nextId, deviceId),
-                (objective, error) -> {
-                    log.warn("port {} failed to removedFrom NextObj {} on {}: {}", portNum, nextId, deviceId, error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-
-        if (install) {
-            flowObjectiveService.next(deviceId, nextObjBuilder.addToExisting(context));
-        } else {
-            flowObjectiveService.next(deviceId, nextObjBuilder.removeFromExisting(context));
-        }
-    }
-
-    /**
-     * Performs bucket verification operation for all hash groups in this device.
-     * Checks RouteHandler to ensure that routing is stable before attempting
-     * verification. Verification involves creating a nextObjective with
-     * operation VERIFY for existing next objectives in the store, and passing
-     * it to the driver. It is the driver that actually performs the verification
-     * by adding or removing buckets to match the verification next objective
-     * created here.
-     */
-    protected final class BucketCorrector implements Runnable {
-        Integer nextId;
-
-        BucketCorrector() {
-            this.nextId = null;
-        }
-
-        BucketCorrector(Integer nextId) {
-            this.nextId = nextId;
-        }
-
-        @Override
-        public void run() {
-            if (!srManager.mastershipService.isLocalMaster(deviceId)) {
-                return;
-            }
-            DefaultRoutingHandler rh = srManager.getRoutingHandler();
-            if (rh == null) {
-                return;
-            }
-            if (!rh.isRoutingStable()) {
-                return;
-            }
-            rh.acquireRoutingLock();
-            try {
-                log.trace("running bucket corrector for dev: {}", deviceId);
-                Set<DestinationSetNextObjectiveStoreKey> dsKeySet = dsNextObjStore.entrySet()
-                        .stream()
-                        .filter(entry -> entry.getKey().deviceId().equals(deviceId))
-                        // Filter out PW transit groups or include them if MPLS ECMP is supported
-                        .filter(entry -> !entry.getKey().destinationSet().notBos() ||
-                                (entry.getKey().destinationSet().notBos() && srManager.getMplsEcmp()))
-                        // Filter out simple SWAP groups or include them if MPLS ECMP is supported
-                        .filter(entry -> !entry.getKey().destinationSet().swap() ||
-                                (entry.getKey().destinationSet().swap() && srManager.getMplsEcmp()))
-                        .map(entry -> entry.getKey())
-                        .collect(Collectors.toSet());
-                for (DestinationSetNextObjectiveStoreKey dsKey : dsKeySet) {
-                    NextNeighbors next = dsNextObjStore.get(dsKey);
-                    if (next == null) {
-                        continue;
-                    }
-                    int nid = next.nextId();
-                    if (nextId != null && nextId != nid) {
-                        continue;
-                    }
-                    log.trace("bkt-corr: dsNextObjStore for device {}: {}",
-                              deviceId, dsKey, next);
-                    TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
-                    metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
-                    NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
-                            .withId(nid)
-                            .withType(NextObjective.Type.HASHED)
-                            .withMeta(metabuilder.build())
-                            .fromApp(appId);
-
-                    next.dstNextHops().forEach((dstDev, nextHops) -> {
-                        int edgeLabel = dsKey.destinationSet().getEdgeLabel(dstDev);
-                        nextHops.forEach(neighbor -> {
-                            MacAddress neighborMac;
-                            try {
-                                neighborMac = deviceConfig.getDeviceMac(neighbor);
-                            } catch (DeviceConfigNotFoundException e) {
-                                log.warn(e.getMessage() + " Aborting neighbor"
-                                        + neighbor);
-                                return;
-                            }
-                            devicePortMap.get(neighbor).forEach(port -> {
-                                log.trace("verify in device {} nextId {}: bucket with"
-                                        + " port/label {}/{} to dst {} via {}",
-                                        deviceId, nid, port, edgeLabel,
-                                        dstDev, neighbor);
-                                nextObjBuilder
-                                    .addTreatment(treatmentBuilder(port,
-                                                                   neighborMac,
-                                                                   dsKey.destinationSet().swap(),
-                                                                   edgeLabel));
-                            });
-                        });
-                    });
-
-                    NextObjective nextObjective = nextObjBuilder.verify();
-                    flowObjectiveService.next(deviceId, nextObjective);
-                }
-            } finally {
-                rh.releaseRoutingLock();
-            }
-
-        }
-
-        TrafficTreatment treatmentBuilder(PortNumber outport, MacAddress dstMac,
-                                          boolean swap, int edgeLabel) {
-            TrafficTreatment.Builder tBuilder =
-                    DefaultTrafficTreatment.builder();
-            tBuilder.setOutput(outport)
-                .setEthDst(dstMac)
-                .setEthSrc(nodeMacAddr);
-            if (edgeLabel != DestinationSet.NO_EDGE_LABEL) {
-                if (swap) {
-                    // swap label case
-                    tBuilder.setMpls(MplsLabel.mplsLabel(edgeLabel));
-                } else {
-                    // ecmp with label push case
-                    tBuilder.pushMpls()
-                        .copyTtlOut()
-                        .setMpls(MplsLabel.mplsLabel(edgeLabel));
-                }
-            }
-            return tBuilder.build();
-        }
-    }
-
-    /**
-     * Determines whether the hash group bucket should include a popVlan action.
-     * We don't popVlan for PW.
-     *
-     * @param ds destination set
-     * @return true if VLAN needs to be popped
-     */
-    private boolean popVlanInHashGroup(DestinationSet ds) {
-        return (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.SWAP_NOT_BOS) &&
-                (ds.getTypeOfDstSet() != DestinationSet.DestinationSetType.POP_NOT_BOS);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
deleted file mode 100644
index 3ecdd61..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DestinationSet.java
+++ /dev/null
@@ -1,354 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import org.onosproject.net.DeviceId;
-import org.slf4j.Logger;
-
-import com.google.common.base.MoreObjects.ToStringHelper;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Representation of a set of destination switch dpids along with their
- * edge-node labels. Meant to be used as a lookup-key in a hash-map to retrieve
- * an ECMP-group that hashes packets towards a specific destination switch, or
- * paired-destination switches. May also be used to represent cases where the
- * forwarding does not use ECMP groups (ie SIMPLE next objectives)
- */
-public final class DestinationSet {
-    public static final int NO_EDGE_LABEL = -1;
-    private static final int NOT_ASSIGNED = 0;
-    private final DeviceId dstSw1;
-    private final int edgeLabel1;
-    private final DeviceId dstSw2;
-    private final int edgeLabel2;
-    private final DestinationSetType typeOfDstSet;
-
-    private static final Logger log = getLogger(DestinationSet.class);
-
-    /**
-     * Constructor for a single destination with no Edge label.
-     *
-     * @param dsType type of next objective
-     * @param dstSw the destination switch
-     */
-    private DestinationSet(DestinationSetType dsType, DeviceId dstSw) {
-        this.edgeLabel1 = NO_EDGE_LABEL;
-        this.dstSw1 = dstSw;
-        this.edgeLabel2 = NOT_ASSIGNED;
-        this.dstSw2 = null;
-        this.typeOfDstSet = dsType;
-    }
-
-    /**
-     * Constructor for a single destination with Edge label.
-     *
-     * @param dsType type of next objective
-     * @param edgeLabel label to be pushed as part of group operation
-     * @param dstSw the destination switch
-     */
-    private DestinationSet(DestinationSetType dsType,
-                          int edgeLabel, DeviceId dstSw) {
-        this.edgeLabel1 = edgeLabel;
-        this.dstSw1 = dstSw;
-        this.edgeLabel2 = NOT_ASSIGNED;
-        this.dstSw2 = null;
-        this.typeOfDstSet = dsType;
-    }
-
-    /**
-     * Constructor for paired destination switches and their associated edge
-     * labels.
-     *
-     * @param dsType type of next objective
-     * @param edgeLabel1 label to be pushed as part of group operation for
-     *            dstSw1
-     * @param dstSw1 one of the paired destination switches
-     * @param edgeLabel2 label to be pushed as part of group operation for
-     *            dstSw2
-     * @param dstSw2 the other paired destination switch
-     */
-    private DestinationSet(DestinationSetType dsType,
-                          int edgeLabel1, DeviceId dstSw1,
-                          int edgeLabel2, DeviceId dstSw2) {
-        if (dstSw1.toString().compareTo(dstSw2.toString()) <= 0) {
-            this.edgeLabel1 = edgeLabel1;
-            this.dstSw1 = dstSw1;
-            this.edgeLabel2 = edgeLabel2;
-            this.dstSw2 = dstSw2;
-        } else {
-            this.edgeLabel1 = edgeLabel2;
-            this.dstSw1 = dstSw2;
-            this.edgeLabel2 = edgeLabel1;
-            this.dstSw2 = dstSw1;
-        }
-        this.typeOfDstSet = dsType;
-    }
-    /**
-     * Default constructor for kryo serialization.
-     */
-    private DestinationSet() {
-        this.edgeLabel1 = NOT_ASSIGNED;
-        this.edgeLabel2 = NOT_ASSIGNED;
-        this.dstSw1 = DeviceId.NONE;
-        this.dstSw2 = DeviceId.NONE;
-        this.typeOfDstSet = null;
-    }
-
-    /**
-     * Gets the label associated with given destination switch.
-     *
-     * @param dstSw the destination switch
-     * @return integer the label associated with the destination switch
-     */
-    public int getEdgeLabel(DeviceId dstSw) {
-        if (dstSw.equals(dstSw1)) {
-            return edgeLabel1;
-        } else if (dstSw.equals(dstSw2)) {
-            return edgeLabel2;
-        }
-        return NOT_ASSIGNED;
-    }
-
-    /**
-     * Gets all the destination switches in this destination set.
-     *
-     * @return a set of destination switch ids
-     */
-    public Set<DeviceId> getDestinationSwitches() {
-        Set<DeviceId> dests = new HashSet<>();
-        dests.add(dstSw1);
-        if (dstSw2 != null) {
-            dests.add(dstSw2);
-        }
-        return dests;
-    }
-
-    /**
-     * Returns the type of this ds.
-     *
-     * @return the type of the destination set
-     */
-    public DestinationSetType getTypeOfDstSet() {
-        return typeOfDstSet;
-    }
-
-    /**
-     * Returns true if the next objective represented by this destination set
-     * is of type SWAP_NOT_BOS or POP_NOT_BOS.
-     *
-     * @return the value of notBos
-     */
-    public boolean notBos() {
-        if ((typeOfDstSet == DestinationSetType.SWAP_NOT_BOS) || (typeOfDstSet == DestinationSetType.POP_NOT_BOS)) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the next objective represented by this destination set
-     * is of type SWAP_NOT_BOS or SWAP_BOS.
-     *
-     * @return the value of swap
-     */
-    public boolean swap() {
-        if ((typeOfDstSet == DestinationSetType.SWAP_BOS) || (typeOfDstSet == DestinationSetType.SWAP_NOT_BOS)) {
-            return true;
-        }
-        return false;
-    }
-
-    // The list of destination ids and label are used for comparison.
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof DestinationSet)) {
-            return false;
-        }
-        DestinationSet that = (DestinationSet) o;
-        if (this.typeOfDstSet != that.typeOfDstSet) {
-            return false;
-        }
-        boolean equal = (this.edgeLabel1 == that.edgeLabel1 &&
-                            this.dstSw1.equals(that.dstSw1));
-        if (this.dstSw2 != null && that.dstSw2 == null ||
-                this.dstSw2 == null && that.dstSw2 != null) {
-            return false;
-        }
-        if (this.dstSw2 != null && that.dstSw2 != null) {
-            equal = equal && (this.edgeLabel2 == that.edgeLabel2 &&
-                                this.dstSw2.equals(that.dstSw2));
-        }
-        return equal;
-    }
-
-    // The list of destination ids and label are used for comparison.
-    @Override
-    public int hashCode() {
-        if (dstSw2 == null) {
-            return Objects.hash(typeOfDstSet, edgeLabel1, dstSw1);
-        }
-        return Objects.hash(typeOfDstSet, edgeLabel1, dstSw1, edgeLabel2,
-                            dstSw2);
-    }
-
-    @Override
-    public String toString() {
-        ToStringHelper h = toStringHelper(this)
-                                .add("Type", typeOfDstSet.getType())
-                                .add("DstSw1", dstSw1)
-                                .add("Label1", edgeLabel1);
-        if (dstSw2 != null) {
-            h.add("DstSw2", dstSw2)
-             .add("Label2", edgeLabel2);
-        }
-        return h.toString();
-    }
-
-    public enum DestinationSetType {
-        /**
-         * Used to represent DestinationSetType where the next hop
-         * is the same as the final destination.
-         */
-        PUSH_NONE("pushnon"),
-        /**
-         * Used to represent DestinationSetType where we need to
-         * push a single mpls label, that of the destination.
-         */
-        PUSH_BOS("pushbos"),
-        /**
-         * Used to represent DestinationSetType where we need to pop
-         * an mpls label which has the bos bit set.
-         */
-        POP_BOS("pop-bos"),
-        /**
-         * Used to represent DestinationSetType where we swap the outer
-         * mpls label with a new one, and where the outer label has the
-         * bos bit set.
-         */
-        SWAP_BOS("swapbos"),
-        /**
-         * Used to represent DestinationSetType where we need to pop
-         * an mpls label which does not have the bos bit set.
-         */
-        POP_NOT_BOS("popnbos"),
-        /**
-         * Used to represent DestinationSetType where we swap the outer
-         * mpls label with a new one, and where the outer label does not
-         * have the bos bit set.
-         */
-        SWAP_NOT_BOS("swap-nb");
-
-        private final String typeOfDstDest;
-        DestinationSetType(String s) {
-            typeOfDstDest = s;
-        }
-
-        public String getType() {
-            return typeOfDstDest;
-        }
-    }
-
-    /*
-     * Static methods for creating DestinationSet objects in
-     * order to remove ambiquity with multiple constructors.
-     */
-
-    /**
-     * Returns a DestinationSet with type PUSH_NONE.
-     *
-     * @param destSw The deviceId for this next objective.
-     * @return The DestinationSet of this type.
-     */
-    public static DestinationSet createTypePushNone(DeviceId destSw) {
-        return new DestinationSet(DestinationSetType.PUSH_NONE, destSw);
-    }
-
-    /**
-     * Returns a DestinationSet with type PUSH_BOS.
-     *
-     * @param edgeLabel1 The mpls label to push.
-     * @param destSw1 The device on which the label is assigned.
-     * @return The DestinationSet of this type.
-     */
-    public static DestinationSet createTypePushBos(int edgeLabel1, DeviceId destSw1) {
-        return new DestinationSet(DestinationSetType.PUSH_BOS, edgeLabel1, destSw1);
-    }
-
-    /**
-     * Returns a DestinationSet with type PUSH_BOS used for paired leafs.
-     *
-     * @param edgeLabel1 The label of first paired leaf.
-     * @param destSw1 The device id of first paired leaf.
-     * @param edgeLabel2 The label of the second leaf.
-     * @param destSw2 The device id of the second leaf.
-     * @return The DestinationSet of this type.
-     */
-    public static DestinationSet createTypePushBos(int edgeLabel1, DeviceId destSw1, int edgeLabel2, DeviceId destSw2) {
-        return new DestinationSet(DestinationSetType.PUSH_BOS, edgeLabel1, destSw1, edgeLabel2, destSw2);
-    }
-
-    /**
-     * Returns a DestinationSet with type POP_BOS.
-     *
-     * @param deviceId The deviceId for this next objective.
-     * @return The DestinationSet of this type.
-     */
-    public static DestinationSet createTypePopBos(DeviceId deviceId) {
-        return new DestinationSet(DestinationSetType.POP_BOS, deviceId);
-    }
-
-    /**
-     * Returns a DestinationSet with type SWAP_BOS.
-     *
-     * @param edgeLabel The edge label to swap with.
-     * @param deviceId The deviceId for this next objective.
-     * @return The DestinationSet of this type.
-     */
-    public static DestinationSet createTypeSwapBos(int edgeLabel, DeviceId deviceId) {
-        return new DestinationSet(DestinationSetType.SWAP_BOS, edgeLabel, deviceId);
-    }
-
-    /**
-     * Returns a DestinationSet with type POP_NOT_BOS.
-     *
-     * @param deviceId The device-id this next objective should be installed.
-     * @return The DestinationSet of this type.
-     */
-    public static DestinationSet createTypePopNotBos(DeviceId deviceId) {
-        return new DestinationSet(DestinationSetType.POP_NOT_BOS, deviceId);
-    }
-
-    /**
-     * Returns a DestinationSet with type SWAP_NOT_BOS.
-     *
-     * @param edgeLabel The edge label to swap with.
-     * @param deviceId The deviceId for this next objective.
-     * @return The DestinationSet of this type.
-     */
-    public static DestinationSet createTypeSwapNotBos(int edgeLabel, DeviceId deviceId) {
-        return new DestinationSet(DestinationSetType.SWAP_NOT_BOS, edgeLabel, deviceId);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java
deleted file mode 100644
index 1c2e1ac..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/GroupBucketIdentifier.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import org.onosproject.net.PortNumber;
-
-/**
- * Representation of policy group bucket identifier. Not exposed to
- * the application and only to be used internally.
- */
-public class GroupBucketIdentifier {
-    private int label;
-    private BucketOutputType type;
-    private PortNumber outPort;
-    private PolicyGroupIdentifier outGroup;
-
-    protected enum BucketOutputType {
-        PORT,
-        GROUP
-    }
-
-    protected GroupBucketIdentifier(int label,
-                                    PortNumber outPort) {
-        this.label = label;
-        this.type = BucketOutputType.PORT;
-        this.outPort = checkNotNull(outPort);
-        this.outGroup = null;
-    }
-
-    protected GroupBucketIdentifier(int label,
-                                    PolicyGroupIdentifier outGroup) {
-        this.label = label;
-        this.type = BucketOutputType.GROUP;
-        this.outPort = null;
-        this.outGroup = checkNotNull(outGroup);
-    }
-
-    protected int label() {
-        return this.label;
-    }
-
-    protected BucketOutputType type() {
-        return this.type;
-    }
-
-    protected PortNumber outPort() {
-        return this.outPort;
-    }
-
-    protected PolicyGroupIdentifier outGroup() {
-        return this.outGroup;
-    }
-}
-
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/NextNeighbors.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/NextNeighbors.java
deleted file mode 100644
index eaca1aa..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/NextNeighbors.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-import org.onosproject.net.DeviceId;
-
-/**
- * Represents the nexthop information associated with a route-path towards a
- * set of destinations.
- */
-public class NextNeighbors {
-    private final Map<DeviceId, Set<DeviceId>> dstNextHops;
-    private final int nextId;
-
-    /**
-     * Constructor.
-     *
-     * @param dstNextHops map of destinations and the next-hops towards each dest
-     * @param nextId id of nextObjective that manifests the next-hop info
-     */
-    public NextNeighbors(Map<DeviceId, Set<DeviceId>> dstNextHops, int nextId) {
-        this.dstNextHops = dstNextHops;
-        this.nextId = nextId;
-    }
-
-    /**
-     * Returns a map of destinations and the next-hops towards them.
-     *
-     * @return map of destinations and the next-hops towards them
-     */
-    public Map<DeviceId, Set<DeviceId>> dstNextHops() {
-        return dstNextHops;
-    }
-
-    /**
-     * Set of next-hops towards the given destination.
-     *
-     * @param deviceId the destination
-     * @return set of nexthops towards the destination
-     */
-    public Set<DeviceId> nextHops(DeviceId deviceId) {
-        return dstNextHops.get(deviceId);
-    }
-
-    /**
-     * Return the nextId representing the nextObjective towards the next-hops.
-     *
-     * @return nextId representing the nextObjective towards the next-hops
-     */
-    public int nextId() {
-        return nextId;
-    }
-
-    /**
-     * Checks if the given nextHopId is a valid next hop to any one of the
-     * destinations.
-     *
-     * @param nextHopId the deviceId for the next hop
-     * @return true if given next
-     */
-    public boolean containsNextHop(DeviceId nextHopId) {
-        for (Set<DeviceId> nextHops : dstNextHops.values()) {
-            if (nextHops != null && nextHops.contains(nextHopId)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns a set of destinations which have the given nextHopId as one
-     * of the next-hops to that destination.
-     *
-     * @param nextHopId the deviceId for the next hop
-     * @return set of deviceIds that have the given nextHopId as a next-hop
-     *          which could be empty if no destinations were found
-     */
-    public Set<DeviceId> getDstForNextHop(DeviceId nextHopId) {
-        Set<DeviceId> dstSet = new HashSet<>();
-        for (DeviceId dstKey : dstNextHops.keySet()) {
-            if (dstNextHops.get(dstKey).contains(nextHopId)) {
-                dstSet.add(dstKey);
-            }
-        }
-        return dstSet;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof NextNeighbors)) {
-            return false;
-        }
-        NextNeighbors that = (NextNeighbors) o;
-        return (this.nextId == that.nextId) &&
-                this.dstNextHops.equals(that.dstNextHops);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(nextId, dstNextHops);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(this)
-                .add("nextId", nextId)
-                .add("dstNextHops", dstNextHops)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
deleted file mode 100644
index c0935ce..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupHandler.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.MplsLabel;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.DeviceProperties;
-import org.onosproject.segmentrouting.grouphandler.GroupBucketIdentifier.BucketOutputType;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.group.GroupBucket;
-import org.onosproject.net.link.LinkService;
-import org.slf4j.Logger;
-
-/**
- * A module to create group chains based on the specified device
- * ports and label stack to be applied on each port.
- */
-public class PolicyGroupHandler extends DefaultGroupHandler {
-
-    private final Logger log = getLogger(getClass());
-    private HashMap<PolicyGroupIdentifier, PolicyGroupIdentifier> dependentGroups = new HashMap<>();
-
-    /**
-     * Constructs policy group handler.
-     *
-     * @param deviceId device identifier
-     * @param appId application identifier
-     * @param config interface to retrieve the device properties
-     * @param linkService link service object
-     * @param flowObjService flow objective service object
-     * @param srManager segment routing manager
-     */
-    public PolicyGroupHandler(DeviceId deviceId,
-                              ApplicationId appId,
-                              DeviceProperties config,
-                              LinkService linkService,
-                              FlowObjectiveService flowObjService,
-                              SegmentRoutingManager srManager) {
-        super(deviceId, appId, config, linkService, flowObjService, srManager);
-    }
-
-    /**
-     * Creates policy group chain.
-     *
-     * @param id unique identifier associated with the policy group
-     * @param params a list of policy group params
-     * @return policy group identifier
-     */
-    public PolicyGroupIdentifier createPolicyGroupChain(String id,
-                                                        List<PolicyGroupParams> params) {
-        List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
-        for (PolicyGroupParams param: params) {
-            List<PortNumber> ports = param.getPorts();
-            if (ports == null) {
-                log.warn("createPolicyGroupChain in sw {} with wrong "
-                        + "input parameters", deviceId);
-                return null;
-            }
-
-            int labelStackSize = (param.getLabelStack() != null) ?
-                                      param.getLabelStack().size() : 0;
-
-            if (labelStackSize > 1) {
-                for (PortNumber sp : ports) {
-                    PolicyGroupIdentifier previousGroupkey = null;
-                    DeviceId neighbor = portDeviceMap.get(sp);
-                    for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
-                        int label = param.getLabelStack().get(idx);
-                        if (idx == (labelStackSize - 1)) {
-                            // Innermost Group
-                            GroupBucketIdentifier bucketId =
-                                    new GroupBucketIdentifier(label,
-                                                              previousGroupkey);
-                            bucketIds.add(bucketId);
-                        } else if (idx == 0) {
-                            // Outermost Group
-                            List<GroupBucket> outBuckets = new ArrayList<>();
-                            GroupBucketIdentifier bucketId =
-                                    new GroupBucketIdentifier(label, sp);
-                            PolicyGroupIdentifier key = new
-                                    PolicyGroupIdentifier(id,
-                                                          Collections.singletonList(param),
-                                                          Collections.singletonList(bucketId));
-                            MacAddress neighborEthDst;
-                            try {
-                                neighborEthDst = deviceConfig.getDeviceMac(neighbor);
-                            } catch (DeviceConfigNotFoundException e) {
-                                log.warn(e.getMessage()
-                                        + " Skipping createPolicyGroupChain for this label.");
-                                continue;
-                            }
-
-                            TrafficTreatment.Builder tBuilder =
-                                    DefaultTrafficTreatment.builder();
-                            tBuilder.setOutput(sp)
-                                    .setEthDst(neighborEthDst)
-                                    .setEthSrc(nodeMacAddr)
-                                    .pushMpls()
-                                    .setMpls(MplsLabel.mplsLabel(label));
-                            /*outBuckets.add(DefaultGroupBucket.
-                                           createSelectGroupBucket(tBuilder.build()));
-                            GroupDescription desc = new
-                                    DefaultGroupDescription(deviceId,
-                                                            GroupDescription.Type.INDIRECT,
-                                                            new GroupBuckets(outBuckets));
-                            //TODO: BoS*/
-                            previousGroupkey = key;
-                            //groupService.addGroup(desc);
-                            //TODO: Use nextObjective APIs here
-                        } else {
-                            // Intermediate Groups
-                            GroupBucketIdentifier bucketId =
-                                    new GroupBucketIdentifier(label,
-                                                              previousGroupkey);
-                            PolicyGroupIdentifier key = new
-                                    PolicyGroupIdentifier(id,
-                                                          Collections.singletonList(param),
-                                                          Collections.singletonList(bucketId));
-                            // Add to group dependency list
-                            dependentGroups.put(previousGroupkey, key);
-                            previousGroupkey = key;
-                        }
-                    }
-                }
-            } else {
-                int label = -1;
-                if (labelStackSize == 1) {
-                    label = param.getLabelStack().get(0);
-                }
-                for (PortNumber sp : ports) {
-                    GroupBucketIdentifier bucketId =
-                            new GroupBucketIdentifier(label, sp);
-                    bucketIds.add(bucketId);
-                }
-            }
-        }
-        PolicyGroupIdentifier innermostGroupkey = null;
-        if (!bucketIds.isEmpty()) {
-            innermostGroupkey = new
-                    PolicyGroupIdentifier(id,
-                                          params,
-                                          bucketIds);
-            // Add to group dependency list
-            boolean fullyResolved = true;
-            for (GroupBucketIdentifier bucketId:bucketIds) {
-                if (bucketId.type() == BucketOutputType.GROUP) {
-                    dependentGroups.put(bucketId.outGroup(),
-                                        innermostGroupkey);
-                    fullyResolved = false;
-                }
-            }
-
-            if (fullyResolved) {
-                List<GroupBucket> outBuckets = new ArrayList<>();
-                for (GroupBucketIdentifier bucketId : bucketIds) {
-                    DeviceId neighbor = portDeviceMap.
-                            get(bucketId.outPort());
-
-                    MacAddress neighborEthDst;
-                    try {
-                        neighborEthDst = deviceConfig.getDeviceMac(neighbor);
-                    } catch (DeviceConfigNotFoundException e) {
-                        log.warn(e.getMessage()
-                                + " Skipping createPolicyGroupChain for this bucketId.");
-                        continue;
-                    }
-
-                    TrafficTreatment.Builder tBuilder =
-                            DefaultTrafficTreatment.builder();
-                    tBuilder.setOutput(bucketId.outPort())
-                            .setEthDst(neighborEthDst)
-                            .setEthSrc(nodeMacAddr);
-                    if (bucketId.label() != DestinationSet.NO_EDGE_LABEL) {
-                        tBuilder.pushMpls()
-                            .setMpls(MplsLabel.mplsLabel(bucketId.label()));
-                    }
-                    //TODO: BoS
-                    /*outBuckets.add(DefaultGroupBucket.
-                                   createSelectGroupBucket(tBuilder.build()));*/
-                }
-                /*GroupDescription desc = new
-                        DefaultGroupDescription(deviceId,
-                                                GroupDescription.Type.SELECT,
-                                                new GroupBuckets(outBuckets));
-                groupService.addGroup(desc);*/
-                //TODO: Use nextObjective APIs here
-            }
-        }
-        return innermostGroupkey;
-    }
-
-    //TODO: Use nextObjective APIs to handle the group chains
-    /*
-    @Override
-    protected void handleGroupEvent(GroupEvent event) {}
-    */
-
-    /**
-     * Generates policy group key.
-     *
-     * @param id unique identifier associated with the policy group
-     * @param params a list of policy group params
-     * @return policy group identifier
-     */
-    public PolicyGroupIdentifier generatePolicyGroupKey(String id,
-                                   List<PolicyGroupParams> params) {
-        List<GroupBucketIdentifier> bucketIds = new ArrayList<>();
-        for (PolicyGroupParams param: params) {
-            List<PortNumber> ports = param.getPorts();
-            if (ports == null) {
-                log.warn("generateGroupKey in sw {} with wrong "
-                        + "input parameters", deviceId);
-                return null;
-            }
-
-            int labelStackSize = (param.getLabelStack() != null)
-                    ? param.getLabelStack().size() : 0;
-
-            if (labelStackSize > 1) {
-                for (PortNumber sp : ports) {
-                    PolicyGroupIdentifier previousGroupkey = null;
-                    for (int idx = 0; idx < param.getLabelStack().size(); idx++) {
-                        int label = param.getLabelStack().get(idx);
-                        if (idx == (labelStackSize - 1)) {
-                            // Innermost Group
-                            GroupBucketIdentifier bucketId =
-                                    new GroupBucketIdentifier(label,
-                                                              previousGroupkey);
-                            bucketIds.add(bucketId);
-                        } else if (idx == 0) {
-                            // Outermost Group
-                            GroupBucketIdentifier bucketId =
-                                    new GroupBucketIdentifier(label, sp);
-                            PolicyGroupIdentifier key = new
-                                    PolicyGroupIdentifier(id,
-                                                          Collections.singletonList(param),
-                                                          Collections.singletonList(bucketId));
-                            previousGroupkey = key;
-                        } else {
-                            // Intermediate Groups
-                            GroupBucketIdentifier bucketId =
-                                    new GroupBucketIdentifier(label,
-                                                              previousGroupkey);
-                            PolicyGroupIdentifier key = new
-                                    PolicyGroupIdentifier(id,
-                                                          Collections.singletonList(param),
-                                                          Collections.singletonList(bucketId));
-                            previousGroupkey = key;
-                        }
-                    }
-                }
-            } else {
-                int label = -1;
-                if (labelStackSize == 1) {
-                    label = param.getLabelStack().get(0);
-                }
-                for (PortNumber sp : ports) {
-                    GroupBucketIdentifier bucketId =
-                            new GroupBucketIdentifier(label, sp);
-                    bucketIds.add(bucketId);
-                }
-            }
-        }
-        PolicyGroupIdentifier innermostGroupkey = null;
-        if (!bucketIds.isEmpty()) {
-            innermostGroupkey = new
-                    PolicyGroupIdentifier(id,
-                                          params,
-                                          bucketIds);
-        }
-        return innermostGroupkey;
-    }
-
-    /**
-     * Removes policy group chain.
-     *
-     * @param key policy group identifier
-     */
-    public void removeGroupChain(PolicyGroupIdentifier key) {
-        checkArgument(key != null);
-        List<PolicyGroupIdentifier> groupsToBeDeleted = new ArrayList<>();
-        groupsToBeDeleted.add(key);
-
-        Iterator<PolicyGroupIdentifier> it =
-                groupsToBeDeleted.iterator();
-
-        while (it.hasNext()) {
-            PolicyGroupIdentifier innerMostGroupKey = it.next();
-            for (GroupBucketIdentifier bucketId:
-                        innerMostGroupKey.bucketIds()) {
-                if (bucketId.type() != BucketOutputType.GROUP) {
-                    groupsToBeDeleted.add(bucketId.outGroup());
-                }
-            }
-            /*groupService.removeGroup(deviceId,
-                                     getGroupKey(innerMostGroupKey),
-                                     appId);*/
-            //TODO: Use nextObjective APIs here
-            it.remove();
-        }
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
deleted file mode 100644
index fdc6dea..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupIdentifier.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import java.util.List;
-
-/**
- * Representation of policy based group identifiers.
- * Opaque to group handler applications and only the outermost
- * policy group identifier in a chain is visible to the applications.
- */
-public class PolicyGroupIdentifier {
-    private String id;
-    private List<PolicyGroupParams> inputParams;
-    private List<GroupBucketIdentifier> bucketIds;
-
-    /**
-     * Constructs policy group identifier.
-     *
-     * @param id unique identifier associated with the policy group
-     * @param input policy group params associated with this group
-     * @param bucketIds buckets associated with this group
-     */
-    protected PolicyGroupIdentifier(String id,
-                                 List<PolicyGroupParams> input,
-                                 List<GroupBucketIdentifier> bucketIds) {
-        this.id = id;
-        this.inputParams = input;
-        this.bucketIds = bucketIds;
-    }
-
-    /**
-     * Returns the bucket identifier list associated with the policy
-     * group identifier.
-     *
-     * @return list of bucket identifier
-     */
-    protected List<GroupBucketIdentifier> bucketIds() {
-        return this.bucketIds;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-        int combinedHash = 0;
-        for (PolicyGroupParams input:inputParams) {
-            combinedHash = combinedHash + input.hashCode();
-        }
-        for (GroupBucketIdentifier bucketId:bucketIds) {
-            combinedHash = combinedHash + bucketId.hashCode();
-        }
-        result = 31 * result + combinedHash;
-
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (obj instanceof PolicyGroupIdentifier) {
-            PolicyGroupIdentifier that = (PolicyGroupIdentifier) obj;
-            boolean result = this.id.equals(that.id);
-            result = result &&
-                    this.inputParams.containsAll(that.inputParams) &&
-                    that.inputParams.containsAll(this.inputParams);
-            result = result &&
-                    this.bucketIds.containsAll(that.bucketIds) &&
-                    that.bucketIds.containsAll(this.bucketIds);
-            return result;
-        }
-
-        return false;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java
deleted file mode 100644
index 5baf849..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/PolicyGroupParams.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.List;
-import java.util.Objects;
-
-import org.onosproject.net.PortNumber;
-
-/**
- * Representation of parameters used to create policy based groups.
- */
-public class PolicyGroupParams {
-    private final List<PortNumber> ports;
-    private final List<Integer> labelStack;
-
-    /**
-     * Constructor.
-     *
-     * @param labelStack mpls label stack to be applied on the ports
-     * @param ports ports to be part of the policy group
-     */
-    public PolicyGroupParams(List<Integer> labelStack,
-                             List<PortNumber> ports) {
-        this.ports = checkNotNull(ports);
-        this.labelStack = checkNotNull(labelStack);
-    }
-
-    /**
-     * Returns the ports associated with the policy group params.
-     *
-     * @return list of port numbers
-     */
-    public List<PortNumber> getPorts() {
-        return ports;
-    }
-
-    /**
-     * Returns the label stack associated with the policy group params.
-     *
-     * @return list of integers
-     */
-    public List<Integer> getLabelStack() {
-        return labelStack;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = 17;
-        int combinedHash = 0;
-        for (PortNumber port:ports) {
-            combinedHash = combinedHash + port.hashCode();
-        }
-        combinedHash = combinedHash + Objects.hash(labelStack);
-        result = 31 * result + combinedHash;
-
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-
-        if (obj instanceof PolicyGroupParams) {
-            PolicyGroupParams that = (PolicyGroupParams) obj;
-            boolean result = this.labelStack.equals(that.labelStack);
-            result = result &&
-                    this.ports.containsAll(that.ports) &&
-                    that.ports.containsAll(this.ports);
-            return result;
-        }
-
-        return false;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/grouphandler/package-info.java
deleted file mode 100644
index d99f1d1..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/grouphandler/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Segment routing group handling.
- */
-package org.onosproject.segmentrouting.grouphandler;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastFilteringObjStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastFilteringObjStoreKey.java
deleted file mode 100644
index 6d69984..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastFilteringObjStoreKey.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Key of multicast filtering objective store.
- */
-public class McastFilteringObjStoreKey {
-
-    private final ConnectPoint ingressCP;
-    private final VlanId vlanId;
-    private final boolean isIpv4;
-
-    /**
-     * Constructs the key of multicast filtering objective store.
-     *
-     * @param ingressCP ingress ConnectPoint
-     * @param vlanId vlan id
-     * @param isIpv4 is Ipv4
-     */
-    public McastFilteringObjStoreKey(ConnectPoint ingressCP, VlanId vlanId, boolean isIpv4) {
-        checkNotNull(ingressCP, "connectpoint cannot be null");
-        checkNotNull(vlanId, "vlanid cannot be null");
-        this.ingressCP = ingressCP;
-        this.vlanId = vlanId;
-        this.isIpv4 = isIpv4;
-    }
-
-    // Constructor for serialization
-    private McastFilteringObjStoreKey() {
-        this.ingressCP = null;
-        this.vlanId = null;
-        this.isIpv4 = false;
-    }
-
-
-    /**
-     * Returns the connect point.
-     *
-     * @return ingress connectpoint
-     */
-    public ConnectPoint ingressCP() {
-        return ingressCP;
-    }
-
-    /**
-     * Returns whether the filtering is for ipv4 mcast.
-     *
-     * @return isIpv4
-     */
-    public boolean isIpv4() {
-        return isIpv4;
-    }
-
-    /**
-     * Returns the vlan ID of this key.
-     *
-     * @return vlan ID
-     */
-    public VlanId vlanId() {
-        return vlanId;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof McastFilteringObjStoreKey)) {
-            return false;
-        }
-        McastFilteringObjStoreKey that =
-                (McastFilteringObjStoreKey) o;
-        return (Objects.equals(this.ingressCP, that.ingressCP) &&
-                Objects.equals(this.isIpv4, that.isIpv4) &&
-                Objects.equals(this.vlanId, that.vlanId));
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(ingressCP, vlanId, isIpv4);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("ingressCP", ingressCP)
-                .add("isIpv4", isIpv4)
-                .add("vlanId", vlanId)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastFilteringObjStoreSerializer.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastFilteringObjStoreSerializer.java
deleted file mode 100644
index a23fa69..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastFilteringObjStoreSerializer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-
-/**
- * Custom serializer for {@link McastFilteringObjStoreKey}.
- */
-class McastFilteringObjStoreSerializer extends Serializer<McastFilteringObjStoreKey> {
-
-    /**
-     * Creates {@link McastFilteringObjStoreSerializer} serializer instance.
-     */
-    McastFilteringObjStoreSerializer() {
-        // non-null, immutable
-        super(false, true);
-    }
-
-    @Override
-    public void write(Kryo kryo, Output output, McastFilteringObjStoreKey object) {
-        kryo.writeClassAndObject(output, object.ingressCP());
-        kryo.writeClassAndObject(output, object.vlanId());
-        kryo.writeClassAndObject(output, object.isIpv4());
-    }
-
-    @Override
-    public McastFilteringObjStoreKey read(Kryo kryo, Input input, Class<McastFilteringObjStoreKey> type) {
-        ConnectPoint ingressCP = (ConnectPoint) kryo.readClassAndObject(input);
-        VlanId vlanId = (VlanId) kryo.readClassAndObject(input);
-        boolean isIpv4 = (boolean) kryo.readClassAndObject(input);
-        return new McastFilteringObjStoreKey(ingressCP, vlanId, isIpv4);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
deleted file mode 100644
index 8c4968a..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastHandler.java
+++ /dev/null
@@ -1,2055 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-import com.google.common.base.Objects;
-import com.google.common.collect.HashMultimap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Sets;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.util.KryoNamespace;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.mcast.api.McastEvent;
-import org.onosproject.mcast.api.McastRoute;
-import org.onosproject.mcast.api.McastRouteData;
-import org.onosproject.mcast.api.McastRouteUpdate;
-import org.onosproject.net.Device;
-import org.onosproject.net.HostId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.Path;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flowobjective.DefaultObjectiveContext;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.ConsistentMultimap;
-import org.onosproject.store.service.DistributedSet;
-import org.onosproject.store.service.Serializer;
-import org.onosproject.store.service.Versioned;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.time.Instant;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-
-import static java.util.concurrent.Executors.newScheduledThreadPool;
-import static org.onlab.util.Tools.groupedThreads;
-
-import static org.onosproject.mcast.api.McastEvent.Type.ROUTE_ADDED;
-import static org.onosproject.mcast.api.McastEvent.Type.ROUTE_REMOVED;
-import static org.onosproject.mcast.api.McastEvent.Type.SOURCES_ADDED;
-import static org.onosproject.mcast.api.McastEvent.Type.SOURCES_REMOVED;
-import static org.onosproject.mcast.api.McastEvent.Type.SINKS_ADDED;
-import static org.onosproject.mcast.api.McastEvent.Type.SINKS_REMOVED;
-
-import static org.onosproject.segmentrouting.mcast.McastRole.EGRESS;
-import static org.onosproject.segmentrouting.mcast.McastRole.INGRESS;
-import static org.onosproject.segmentrouting.mcast.McastRole.TRANSIT;
-
-/**
- * Handles Multicast related events.
- */
-public class McastHandler {
-    // Internal elements
-    private static final Logger log = LoggerFactory.getLogger(McastHandler.class);
-    private final SegmentRoutingManager srManager;
-    private final McastUtils mcastUtils;
-    private final ConsistentMap<McastStoreKey, NextObjective> mcastNextObjStore;
-    private final ConsistentMap<McastRoleStoreKey, McastRole> mcastRoleStore;
-    private final ConsistentMultimap<McastPathStoreKey, List<Link>> mcastPathStore;
-    private final DistributedSet<McastFilteringObjStoreKey> mcastFilteringObjStore;
-    // Stability threshold for Mcast. Seconds
-    private static final long MCAST_STABLITY_THRESHOLD = 5;
-    // Verify interval for Mcast bucket corrector
-    private static final long MCAST_VERIFY_INTERVAL = 30;
-    // Max verify that can be processed at the same time
-    private static final int MAX_VERIFY_ON_FLIGHT = 10;
-    // Last change done
-    private AtomicReference<Instant> lastMcastChange = new AtomicReference<>(Instant.now());
-    // Last bucker corrector execution
-    private AtomicReference<Instant> lastBktCorrExecution = new AtomicReference<>(Instant.now());
-    // Executors for mcast bucket corrector and for the events
-    private ScheduledExecutorService mcastCorrector
-            = newScheduledThreadPool(1, groupedThreads("onos", "m-corrector", log));
-    private ScheduledExecutorService mcastWorker
-            = newScheduledThreadPool(1, groupedThreads("onos", "m-worker-%d", log));
-
-    /**
-     * Constructs the McastEventHandler.
-     *
-     * @param srManager Segment Routing manager
-     */
-    public McastHandler(SegmentRoutingManager srManager) {
-        ApplicationId coreAppId = srManager.coreService.getAppId(CoreService.CORE_APP_NAME);
-        this.srManager = srManager;
-        KryoNamespace.Builder mcastKryo = new KryoNamespace.Builder()
-                .register(KryoNamespaces.API)
-                .register(new McastStoreKeySerializer(), McastStoreKey.class);
-        mcastNextObjStore = srManager.storageService
-                .<McastStoreKey, NextObjective>consistentMapBuilder()
-                .withName("onos-mcast-nextobj-store")
-                .withSerializer(Serializer.using(mcastKryo.build("McastHandler-NextObj")))
-                .build();
-        mcastKryo = new KryoNamespace.Builder()
-                .register(KryoNamespaces.API)
-                .register(new McastRoleStoreKeySerializer(), McastRoleStoreKey.class)
-                .register(McastRole.class);
-        mcastRoleStore = srManager.storageService
-                .<McastRoleStoreKey, McastRole>consistentMapBuilder()
-                .withName("onos-mcast-role-store")
-                .withSerializer(Serializer.using(mcastKryo.build("McastHandler-Role")))
-                .build();
-        mcastKryo = new KryoNamespace.Builder()
-                .register(KryoNamespaces.API)
-                .register(new McastFilteringObjStoreSerializer(), McastFilteringObjStoreKey.class);
-        mcastFilteringObjStore = srManager.storageService
-                .<McastFilteringObjStoreKey>setBuilder()
-                .withName("onos-mcast-filtering-store")
-                .withSerializer(Serializer.using(mcastKryo.build("McastHandler-FilteringObj")))
-                .build()
-                .asDistributedSet();
-        mcastKryo = new KryoNamespace.Builder()
-                .register(KryoNamespaces.API)
-                .register(new McastPathStoreKeySerializer(), McastPathStoreKey.class);
-        mcastPathStore = srManager.storageService
-                .<McastPathStoreKey, List<Link>>consistentMultimapBuilder()
-                .withName("onos-mcast-path-store")
-                .withSerializer(Serializer.using(mcastKryo.build("McastHandler-Path")))
-                .build();
-        mcastUtils = new McastUtils(srManager, coreAppId, log);
-        // Init the executor for the buckets corrector
-        mcastCorrector.scheduleWithFixedDelay(new McastBucketCorrector(), 10,
-                                              MCAST_VERIFY_INTERVAL, TimeUnit.SECONDS);
-    }
-
-    /**
-     * Determines if mcast in the network has been stable in the last
-     * MCAST_STABLITY_THRESHOLD seconds, by comparing the current time
-     * to the last mcast change timestamp.
-     *
-     * @return true if stable
-     */
-    private boolean isMcastStable() {
-        long last = (long) (lastMcastChange.get().toEpochMilli() / 1000.0);
-        long now = (long) (Instant.now().toEpochMilli() / 1000.0);
-        log.trace("Multicast stable since {}s", now - last);
-        return (now - last) > MCAST_STABLITY_THRESHOLD;
-    }
-
-    /**
-     * Assures there are always MCAST_VERIFY_INTERVAL seconds between each execution,
-     * by comparing the current time with the last corrector execution.
-     *
-     * @return true if stable
-     */
-    private boolean wasBktCorrRunning() {
-        long last = (long) (lastBktCorrExecution.get().toEpochMilli() / 1000.0);
-        long now = (long) (Instant.now().toEpochMilli() / 1000.0);
-        log.trace("McastBucketCorrector executed {}s ago", now - last);
-        return (now - last) < MCAST_VERIFY_INTERVAL;
-    }
-
-    /**
-     * Read initial multicast configuration from mcast store.
-     */
-    public void init() {
-        mcastWorker.execute(this::initInternal);
-    }
-
-    private void initInternal() {
-        srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> {
-            lastMcastChange.set(Instant.now());
-            log.debug("Init group {}", mcastRoute.group());
-            if (!mcastUtils.isLeader(mcastRoute.group())) {
-                log.debug("Skip {} due to lack of leadership", mcastRoute.group());
-                return;
-            }
-            McastRouteData mcastRouteData = srManager.multicastRouteService.routeData(mcastRoute);
-            // For each source process the mcast tree
-            srManager.multicastRouteService.sources(mcastRoute).forEach(source -> {
-                McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastRoute.group(), source);
-                Collection<? extends List<Link>> storedPaths = Versioned.valueOrElse(
-                        mcastPathStore.get(pathStoreKey), Lists.newArrayList());
-                Map<ConnectPoint, List<ConnectPoint>> mcastPaths = buildMcastPaths(storedPaths, mcastRoute.group(),
-                                                                                   source);
-                // Get all the sinks and process them
-                Set<ConnectPoint> sinks = processSinksToBeAdded(source, mcastRoute.group(),
-                                                                mcastRouteData.sinks());
-                // Filter out all the working sinks, we do not want to move them
-                // TODO we need a better way to distinguish flows coming from different sources
-                sinks = sinks.stream()
-                        .filter(sink -> !mcastPaths.containsKey(sink) ||
-                                !isSinkForSource(mcastRoute.group(), sink, source))
-                        .collect(Collectors.toSet());
-                if (sinks.isEmpty()) {
-                    log.debug("Skip {} for source {} nothing to do", mcastRoute.group(), source);
-                    return;
-                }
-                Map<ConnectPoint, List<Path>> mcasTree = mcastUtils.computeSinkMcastTree(mcastRoute.group(),
-                                                                                         source.deviceId(), sinks);
-                mcasTree.forEach((sink, paths) -> processSinkAddedInternal(source, sink, mcastRoute.group(),
-                                                                             null));
-            });
-        });
-    }
-
-    /**
-     * Clean up when deactivating the application.
-     */
-    public void terminate() {
-        mcastCorrector.shutdown();
-        mcastWorker.shutdown();
-        mcastNextObjStore.destroy();
-        mcastRoleStore.destroy();
-        mcastFilteringObjStore.destroy();
-        mcastPathStore.destroy();
-        mcastUtils.terminate();
-        log.info("Terminated");
-    }
-
-    /**
-     * Processes the SOURCE_ADDED, SOURCE_UPDATED, SINK_ADDED,
-     * SINK_REMOVED, ROUTE_ADDED and ROUTE_REMOVED events.
-     *
-     * @param event the multicast event to be processed
-     */
-    public void processMcastEvent(McastEvent event) {
-        mcastWorker.execute(() -> processMcastEventInternal(event));
-    }
-
-    private void processMcastEventInternal(McastEvent event) {
-        lastMcastChange.set(Instant.now());
-        // Current subject is null, for ROUTE_REMOVED events
-        final McastRouteUpdate mcastUpdate = event.subject();
-        final McastRouteUpdate mcastPrevUpdate = event.prevSubject();
-        IpAddress mcastIp = mcastPrevUpdate.route().group();
-        Set<ConnectPoint> prevSinks = mcastPrevUpdate.sinks()
-                .values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
-        Set<ConnectPoint> prevSources = mcastPrevUpdate.sources()
-                .values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
-        Set<ConnectPoint> sources;
-        // Events handling
-        if (event.type() == ROUTE_ADDED) {
-            processRouteAddedInternal(mcastUpdate.route().group());
-        } else if (event.type() == ROUTE_REMOVED) {
-            processRouteRemovedInternal(prevSources, mcastIp);
-        } else if (event.type() == SOURCES_ADDED) {
-            // Current subject and prev just differ for the source connect points
-            sources = mcastUpdate.sources()
-                    .values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
-            Set<ConnectPoint> sourcesToBeAdded = Sets.difference(sources, prevSources);
-            processSourcesAddedInternal(sourcesToBeAdded, mcastIp, mcastUpdate.sinks());
-        } else if (event.type() == SOURCES_REMOVED) {
-            // Current subject and prev just differ for the source connect points
-            sources = mcastUpdate.sources()
-                    .values().stream().flatMap(Collection::stream).collect(Collectors.toSet());
-            Set<ConnectPoint> sourcesToBeRemoved = Sets.difference(prevSources, sources);
-            processSourcesRemovedInternal(sourcesToBeRemoved, sources, mcastIp, mcastUpdate.sinks());
-        } else if (event.type() == SINKS_ADDED) {
-            processSinksAddedInternal(prevSources, mcastIp, mcastUpdate.sinks(), prevSinks);
-        } else if (event.type() == SINKS_REMOVED) {
-            processSinksRemovedInternal(prevSources, mcastIp, mcastUpdate.sinks(), mcastPrevUpdate.sinks());
-        } else {
-            log.warn("Event {} not handled", event);
-        }
-    }
-
-    /**
-     * Process the SOURCES_ADDED event.
-     *
-     * @param sources the sources connect point
-     * @param mcastIp the group address
-     * @param sinks the sinks connect points
-     */
-    private void processSourcesAddedInternal(Set<ConnectPoint> sources, IpAddress mcastIp,
-                                             Map<HostId, Set<ConnectPoint>> sinks) {
-        lastMcastChange.set(Instant.now());
-        log.info("Processing sources added {} for group {}", sources, mcastIp);
-        if (!mcastUtils.isLeader(mcastIp)) {
-            log.debug("Skip {} due to lack of leadership", mcastIp);
-            return;
-        }
-        if (sources.isEmpty()) {
-            log.debug("Skip {} due to empty sources to be added", mcastIp);
-            return;
-        }
-        sources.forEach(source -> {
-            Set<ConnectPoint> sinksToBeAdded = processSinksToBeAdded(source, mcastIp, sinks);
-            Map<ConnectPoint, List<Path>> mcasTree = mcastUtils.computeSinkMcastTree(mcastIp, source.deviceId(),
-                                                                                     sinksToBeAdded);
-            mcasTree.forEach((sink, paths) -> processSinkAddedInternal(source, sink, mcastIp, paths));
-        });
-    }
-
-    /**
-     * Process the SOURCES_REMOVED event.
-     *
-     * @param sourcesToBeRemoved the source connect points to be removed
-     * @param remainingSources the remainig source connect points
-     * @param mcastIp the group address
-     * @param sinks the sinks connect points
-     */
-    private void processSourcesRemovedInternal(Set<ConnectPoint> sourcesToBeRemoved,
-                                               Set<ConnectPoint> remainingSources,
-                                               IpAddress mcastIp,
-                                               Map<HostId, Set<ConnectPoint>> sinks) {
-        lastMcastChange.set(Instant.now());
-        log.info("Processing sources removed {} for group {}", sourcesToBeRemoved, mcastIp);
-        if (!mcastUtils.isLeader(mcastIp)) {
-            log.debug("Skip {} due to lack of leadership", mcastIp);
-            return;
-        }
-        if (remainingSources.isEmpty()) {
-            log.debug("There are no more sources for {}", mcastIp);
-            processRouteRemovedInternal(sourcesToBeRemoved, mcastIp);
-            return;
-        }
-        // Let's heal the trees
-        Set<Link> notAffectedLinks = Sets.newHashSet();
-        Map<ConnectPoint, Set<Link>> affectedLinks = Maps.newHashMap();
-        Map<ConnectPoint, Set<ConnectPoint>> candidateSinks = Maps.newHashMap();
-        Set<ConnectPoint> totalSources = Sets.newHashSet(sourcesToBeRemoved);
-        totalSources.addAll(remainingSources);
-        // Calculate all the links used by the sources and the current sinks
-        totalSources.forEach(source -> {
-            Set<ConnectPoint> currentSinks = sinks.values()
-                    .stream().flatMap(Collection::stream)
-                    .filter(sink -> isSinkForSource(mcastIp, sink, source))
-                    .collect(Collectors.toSet());
-            candidateSinks.put(source, currentSinks);
-            McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-            Collection<? extends List<Link>> storedPaths = Versioned.valueOrElse(
-                    mcastPathStore.get(pathStoreKey), Lists.newArrayList());
-            currentSinks.forEach(currentSink -> {
-                Optional<? extends List<Link>> currentPath = mcastUtils.getStoredPath(currentSink.deviceId(),
-                                                                                      storedPaths);
-                if (currentPath.isPresent()) {
-                    if (!sourcesToBeRemoved.contains(source)) {
-                        notAffectedLinks.addAll(currentPath.get());
-                    } else {
-                        affectedLinks.compute(source, (k, v) -> {
-                           v = v == null ? Sets.newHashSet() : v;
-                           v.addAll(currentPath.get());
-                           return v;
-                        });
-                    }
-                }
-            });
-        });
-        // Clean transit links
-        affectedLinks.forEach((source, currentCandidateLinks) -> {
-            Set<Link> linksToBeRemoved = Sets.difference(currentCandidateLinks, notAffectedLinks)
-                    .immutableCopy();
-            if (!linksToBeRemoved.isEmpty()) {
-                currentCandidateLinks.forEach(link -> {
-                    DeviceId srcLink = link.src().deviceId();
-                    // Remove ports only on links to be removed
-                    if (linksToBeRemoved.contains(link)) {
-                        removePortFromDevice(link.src().deviceId(), link.src().port(), mcastIp,
-                                             mcastUtils.assignedVlan(srcLink.equals(source.deviceId()) ?
-                                                                             source : null));
-                    }
-                    // Remove role on the candidate links
-                    mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, srcLink, source));
-                });
-            }
-        });
-        // Clean ingress and egress
-        sourcesToBeRemoved.forEach(source -> {
-            Set<ConnectPoint> currentSinks = candidateSinks.get(source);
-            McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-            currentSinks.forEach(currentSink -> {
-                VlanId assignedVlan = mcastUtils.assignedVlan(source.deviceId().equals(currentSink.deviceId()) ?
-                                                                      source : null);
-                // Sinks co-located with the source
-                if (source.deviceId().equals(currentSink.deviceId())) {
-                    if (source.port().equals(currentSink.port())) {
-                        log.warn("Skip {} since sink {} is on the same port of source {}. Abort",
-                                 mcastIp, currentSink, source);
-                        return;
-                    }
-                    // We need to check against the other sources and if it is
-                    // necessary remove the port from the device - no overlap
-                    Set<VlanId> otherVlans = remainingSources.stream()
-                            // Only sources co-located and having this sink
-                            .filter(remainingSource -> remainingSource.deviceId()
-                                    .equals(source.deviceId()) && candidateSinks.get(remainingSource)
-                                    .contains(currentSink))
-                            .map(remainingSource -> mcastUtils.assignedVlan(
-                                    remainingSource.deviceId().equals(currentSink.deviceId()) ?
-                                            remainingSource : null)).collect(Collectors.toSet());
-                    if (!otherVlans.contains(assignedVlan)) {
-                        removePortFromDevice(currentSink.deviceId(), currentSink.port(),
-                                             mcastIp, assignedVlan);
-                    }
-                    mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, currentSink.deviceId(),
-                                                                source));
-                    return;
-                }
-                Set<VlanId> otherVlans = remainingSources.stream()
-                        .filter(remainingSource -> candidateSinks.get(remainingSource)
-                                .contains(currentSink))
-                        .map(remainingSource -> mcastUtils.assignedVlan(
-                                remainingSource.deviceId().equals(currentSink.deviceId()) ?
-                                        remainingSource : null)).collect(Collectors.toSet());
-                // Sinks on other leaves
-                if (!otherVlans.contains(assignedVlan)) {
-                    removePortFromDevice(currentSink.deviceId(), currentSink.port(),
-                                         mcastIp, assignedVlan);
-                }
-                mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, currentSink.deviceId(),
-                                                            source));
-            });
-            // Clean the mcast paths
-            mcastPathStore.removeAll(pathStoreKey);
-        });
-    }
-
-    /**
-     * Process the ROUTE_ADDED event.
-     *
-     * @param mcastIp the group address
-     */
-    private void processRouteAddedInternal(IpAddress mcastIp) {
-        lastMcastChange.set(Instant.now());
-        log.info("Processing route added for Multicast group {}", mcastIp);
-        // Just elect a new leader
-        mcastUtils.isLeader(mcastIp);
-    }
-
-    /**
-     * Removes the entire mcast tree related to this group.
-     * @param sources the source connect points
-     * @param mcastIp multicast group IP address
-     */
-    private void processRouteRemovedInternal(Set<ConnectPoint> sources, IpAddress mcastIp) {
-        lastMcastChange.set(Instant.now());
-        log.info("Processing route removed for group {}", mcastIp);
-        if (!mcastUtils.isLeader(mcastIp)) {
-            log.debug("Skip {} due to lack of leadership", mcastIp);
-            mcastUtils.withdrawLeader(mcastIp);
-            return;
-        }
-        sources.forEach(source -> {
-            // Find out the ingress, transit and egress device of the affected group
-            DeviceId ingressDevice = getDevice(mcastIp, INGRESS, source)
-                    .stream().findFirst().orElse(null);
-            Set<DeviceId> transitDevices = getDevice(mcastIp, TRANSIT, source);
-            Set<DeviceId> egressDevices = getDevice(mcastIp, EGRESS, source);
-            McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-            // If there are no egress and transit devices, sinks could be only on the ingress
-            if (!egressDevices.isEmpty()) {
-                egressDevices.forEach(deviceId -> {
-                    removeGroupFromDevice(deviceId, mcastIp, mcastUtils.assignedVlan(null));
-                    mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, deviceId, source));
-                });
-            }
-            if (!transitDevices.isEmpty()) {
-                transitDevices.forEach(deviceId -> {
-                    removeGroupFromDevice(deviceId, mcastIp, mcastUtils.assignedVlan(null));
-                    mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, deviceId, source));
-                });
-            }
-            if (ingressDevice != null) {
-                removeGroupFromDevice(ingressDevice, mcastIp, mcastUtils.assignedVlan(source));
-                mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, ingressDevice, source));
-            }
-            // Clean the mcast paths
-            mcastPathStore.removeAll(pathStoreKey);
-        });
-        // Finally, withdraw the leadership
-        mcastUtils.withdrawLeader(mcastIp);
-    }
-
-    /**
-     * Process sinks to be removed.
-     *
-     * @param sources the source connect points
-     * @param mcastIp the ip address of the group
-     * @param newSinks the new sinks to be processed
-     * @param prevSinks the previous sinks
-     */
-    private void processSinksRemovedInternal(Set<ConnectPoint> sources, IpAddress mcastIp,
-                                             Map<HostId, Set<ConnectPoint>> newSinks,
-                                             Map<HostId, Set<ConnectPoint>> prevSinks) {
-        lastMcastChange.set(Instant.now());
-        log.info("Processing sinks removed for group {} and for sources {}",
-                  mcastIp, sources);
-        if (!mcastUtils.isLeader(mcastIp)) {
-            log.debug("Skip {} due to lack of leadership", mcastIp);
-            return;
-        }
-        Map<ConnectPoint, Map<ConnectPoint, Optional<? extends List<Link>>>> treesToBeRemoved = Maps.newHashMap();
-        Map<ConnectPoint, Set<ConnectPoint>> treesToBeAdded = Maps.newHashMap();
-        Set<Link> goodLinks = Sets.newHashSet();
-        Map<ConnectPoint, Set<DeviceId>> goodDevicesBySource = Maps.newHashMap();
-        sources.forEach(source -> {
-            // Save the path associated to the sinks to be removed
-            Set<ConnectPoint> sinksToBeRemoved = processSinksToBeRemoved(mcastIp, prevSinks,
-                                                                         newSinks, source);
-            Map<ConnectPoint, Optional<? extends List<Link>>> treeToBeRemoved = Maps.newHashMap();
-            McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-            Collection<? extends List<Link>> storedPaths = Versioned.valueOrElse(
-                    mcastPathStore.get(pathStoreKey), Lists.newArrayList());
-            sinksToBeRemoved.forEach(sink -> treeToBeRemoved.put(sink, mcastUtils.getStoredPath(sink.deviceId(),
-                                                                                                storedPaths)));
-            treesToBeRemoved.put(source, treeToBeRemoved);
-            // Save the good links and good devices
-            Set<DeviceId> goodDevices = Sets.newHashSet();
-            Set<DeviceId> totalDevices = Sets.newHashSet(getDevice(mcastIp, EGRESS, source));
-            totalDevices.addAll(getDevice(mcastIp, INGRESS, source));
-            Set<ConnectPoint> notAffectedSinks = Sets.newHashSet();
-            // Compute good sinks
-            totalDevices.forEach(device -> {
-                Set<ConnectPoint> sinks = getSinks(mcastIp, device, source);
-                notAffectedSinks.addAll(Sets.difference(sinks, sinksToBeRemoved));
-            });
-            // Compute good paths and good devices
-            notAffectedSinks.forEach(notAffectedSink -> {
-                Optional<? extends List<Link>> notAffectedPath = mcastUtils.getStoredPath(notAffectedSink.deviceId(),
-                                                                                          storedPaths);
-                if (notAffectedPath.isPresent()) {
-                    List<Link> goodPath = notAffectedPath.get();
-                    goodLinks.addAll(goodPath);
-                    goodPath.forEach(link -> goodDevices.add(link.src().deviceId()));
-                } else {
-                    goodDevices.add(notAffectedSink.deviceId());
-                }
-            });
-            goodDevicesBySource.compute(source, (k, v) -> {
-                v = v == null ? Sets.newHashSet() : v;
-                v.addAll(goodDevices);
-                return v;
-            });
-            // Recover the dual-homed sinks
-            Set<ConnectPoint> sinksToBeRecovered = processSinksToBeRecovered(mcastIp, newSinks,
-                                                                             prevSinks, source);
-            treesToBeAdded.put(source, sinksToBeRecovered);
-        });
-        // Remove the sinks taking into account the multiple sources and the original paths
-        treesToBeRemoved.forEach((source, tree) ->
-            tree.forEach((sink, path) -> processSinkRemovedInternal(source, sink, mcastIp, path,
-                                                                    goodLinks, goodDevicesBySource.get(source))));
-        // Add new sinks according to the recovery procedure
-        treesToBeAdded.forEach((source, sinks) ->
-            sinks.forEach(sink -> processSinkAddedInternal(source, sink, mcastIp, null)));
-    }
-
-    /**
-     * Removes a path from source to sink for given multicast group.
-     *
-     * @param source connect point of the multicast source
-     * @param sink connection point of the multicast sink
-     * @param mcastIp multicast group IP address
-     * @param mcastPath path associated to the sink
-     * @param usedLinks links used by the other sinks
-     * @param usedDevices devices used by other sinks
-     */
-    private void processSinkRemovedInternal(ConnectPoint source, ConnectPoint sink,
-                                            IpAddress mcastIp, Optional<? extends List<Link>> mcastPath,
-                                            Set<Link> usedLinks, Set<DeviceId> usedDevices) {
-
-        log.info("Used links {}", usedLinks);
-        log.info("Used devices {}", usedDevices);
-
-        lastMcastChange.set(Instant.now());
-        log.info("Processing sink removed {} for group {} and for source {}", sink, mcastIp, source);
-        boolean isLast;
-        // When source and sink are on the same device
-        if (source.deviceId().equals(sink.deviceId())) {
-            // Source and sink are on even the same port. There must be something wrong.
-            if (source.port().equals(sink.port())) {
-                log.warn("Skip {} since sink {} is on the same port of source {}. Abort", mcastIp, sink, source);
-                return;
-            }
-            isLast = removePortFromDevice(sink.deviceId(), sink.port(), mcastIp, mcastUtils.assignedVlan(source));
-            if (isLast) {
-                mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, sink.deviceId(), source));
-            }
-            return;
-        }
-        // Process the egress device
-        isLast = removePortFromDevice(sink.deviceId(), sink.port(), mcastIp, mcastUtils.assignedVlan(null));
-        if (isLast) {
-            mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, sink.deviceId(), source));
-        }
-        // If this is the last sink on the device, also update upstream
-        if (mcastPath.isPresent()) {
-            List<Link> links = Lists.newArrayList(mcastPath.get());
-            if (isLast) {
-                // Clean the path
-                McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-                mcastPathStore.remove(pathStoreKey, mcastPath.get());
-                Collections.reverse(links);
-                for (Link link : links) {
-                    // If nobody is using the port remove
-                    if (!usedLinks.contains(link)) {
-                        removePortFromDevice(link.src().deviceId(), link.src().port(), mcastIp,
-                          mcastUtils.assignedVlan(link.src().deviceId().equals(source.deviceId()) ? source : null));
-                    }
-                    // If nobody is using the device
-                    if (!usedDevices.contains(link.src().deviceId())) {
-                        mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, link.src().deviceId(), source));
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Process sinks to be added.
-     *
-     * @param sources the source connect points
-     * @param mcastIp the group IP
-     * @param newSinks the new sinks to be processed
-     * @param allPrevSinks all previous sinks
-     */
-    private void processSinksAddedInternal(Set<ConnectPoint> sources, IpAddress mcastIp,
-                                           Map<HostId, Set<ConnectPoint>> newSinks,
-                                           Set<ConnectPoint> allPrevSinks) {
-        lastMcastChange.set(Instant.now());
-        log.info("Processing sinks added for group {} and for sources {}", mcastIp, sources);
-        if (!mcastUtils.isLeader(mcastIp)) {
-            log.debug("Skip {} due to lack of leadership", mcastIp);
-            return;
-        }
-        sources.forEach(source -> {
-            Set<ConnectPoint> sinksToBeAdded = processSinksToBeAdded(source, mcastIp, newSinks);
-            sinksToBeAdded = Sets.difference(sinksToBeAdded, allPrevSinks);
-            sinksToBeAdded.forEach(sink -> processSinkAddedInternal(source, sink, mcastIp, null));
-        });
-    }
-
-    /**
-     * Establishes a path from source to sink for given multicast group.
-     *
-     * @param source connect point of the multicast source
-     * @param sink connection point of the multicast sink
-     * @param mcastIp multicast group IP address
-     */
-    private void processSinkAddedInternal(ConnectPoint source, ConnectPoint sink,
-                                          IpAddress mcastIp, List<Path> allPaths) {
-        lastMcastChange.set(Instant.now());
-        log.info("Processing sink added {} for group {} and for source {}", sink, mcastIp, source);
-        // Process the ingress device
-        McastFilteringObjStoreKey mcastFilterObjStoreKey = new McastFilteringObjStoreKey(source,
-                                                           mcastUtils.assignedVlan(source), mcastIp.isIp4());
-        addFilterToDevice(mcastFilterObjStoreKey, mcastIp, INGRESS);
-        if (source.deviceId().equals(sink.deviceId())) {
-            if (source.port().equals(sink.port())) {
-                log.warn("Skip {} since sink {} is on the same port of source {}. Abort",
-                         mcastIp, sink, source);
-                return;
-            }
-            addPortToDevice(sink.deviceId(), sink.port(), mcastIp, mcastUtils.assignedVlan(source));
-            mcastRoleStore.put(new McastRoleStoreKey(mcastIp, sink.deviceId(), source), INGRESS);
-            return;
-        }
-        // Find a path. If present, create/update groups and flows for each hop
-        Optional<Path> mcastPath = getPath(source.deviceId(), sink.deviceId(), mcastIp, allPaths);
-        if (mcastPath.isPresent()) {
-            List<Link> links = mcastPath.get().links();
-            McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-            // Setup mcast role for ingress
-            mcastRoleStore.put(new McastRoleStoreKey(mcastIp, source.deviceId(), source), INGRESS);
-            // Setup properly the transit forwarding
-            links.forEach(link -> {
-                addPortToDevice(link.src().deviceId(), link.src().port(), mcastIp,
-                                mcastUtils.assignedVlan(link.src().deviceId()
-                                                                .equals(source.deviceId()) ? source : null));
-                McastFilteringObjStoreKey filteringKey = new McastFilteringObjStoreKey(link.dst(),
-                                                                   mcastUtils.assignedVlan(null), mcastIp.isIp4());
-                addFilterToDevice(filteringKey, mcastIp, null);
-            });
-            // Setup mcast role for the transit
-            links.stream()
-                    .filter(link -> !link.dst().deviceId().equals(sink.deviceId()))
-                    .forEach(link -> mcastRoleStore.put(new McastRoleStoreKey(mcastIp, link.dst().deviceId(),
-                                                                              source), TRANSIT));
-            // Process the egress device
-            addPortToDevice(sink.deviceId(), sink.port(), mcastIp, mcastUtils.assignedVlan(null));
-            // Setup mcast role for egress
-            mcastRoleStore.put(new McastRoleStoreKey(mcastIp, sink.deviceId(), source), EGRESS);
-            // Store the used path
-            mcastPathStore.put(pathStoreKey, links);
-        } else {
-            log.warn("Unable to find a path from {} to {}. Abort sinkAdded", source.deviceId(), sink.deviceId());
-        }
-    }
-
-    /**
-     * Processes the PORT_UPDATED event.
-     *
-     * @param affectedDevice Affected device
-     * @param affectedPort Affected port
-     */
-    public void processPortUpdate(Device affectedDevice, Port affectedPort) {
-        mcastWorker.execute(() -> processPortUpdateInternal(affectedDevice, affectedPort));
-    }
-
-    private void processPortUpdateInternal(Device affectedDevice, Port affectedPort) {
-        // Clean the filtering obj store. Edge port case.
-        lastMcastChange.set(Instant.now());
-        ConnectPoint portDown = new ConnectPoint(affectedDevice.id(), affectedPort.number());
-        if (!affectedPort.isEnabled()) {
-            log.info("Processing port down {}", portDown);
-            updateFilterObjStoreByPort(portDown);
-        }
-    }
-
-    /**
-     * Processes the LINK_DOWN event.
-     *
-     * @param linkDown Link that is going down
-     */
-    public void processLinkDown(Link linkDown) {
-        mcastWorker.execute(() -> processLinkDownInternal(linkDown));
-    }
-
-    private void processLinkDownInternal(Link linkDown) {
-        // Get mcast groups affected by the link going down
-        Set<IpAddress> affectedGroups = getAffectedGroups(linkDown);
-        log.info("Processing link down {} for groups {}", linkDown, affectedGroups);
-        affectedGroups.forEach(mcastIp -> {
-            lastMcastChange.set(Instant.now());
-            log.debug("Processing link down {} for group {}", linkDown, mcastIp);
-            recoverFailure(mcastIp, linkDown);
-        });
-    }
-
-    /**
-     * Process the DEVICE_DOWN event.
-     *
-     * @param deviceDown device going down
-     */
-    public void processDeviceDown(DeviceId deviceDown) {
-        mcastWorker.execute(() -> processDeviceDownInternal(deviceDown));
-    }
-
-    private void processDeviceDownInternal(DeviceId deviceDown) {
-        // Get the mcast groups affected by the device going down
-        Set<IpAddress> affectedGroups = getAffectedGroups(deviceDown);
-        log.info("Processing device down {} for groups {}", deviceDown, affectedGroups);
-        updateFilterObjStoreByDevice(deviceDown);
-        affectedGroups.forEach(mcastIp -> {
-            lastMcastChange.set(Instant.now());
-            log.debug("Processing device down {} for group {}", deviceDown, mcastIp);
-            recoverFailure(mcastIp, deviceDown);
-        });
-    }
-
-    /**
-     * General failure recovery procedure.
-     *
-     * @param mcastIp the group to recover
-     * @param failedElement the failed element
-     */
-    private void recoverFailure(IpAddress mcastIp, Object failedElement) {
-        // Do not proceed if we are not the leaders
-        if (!mcastUtils.isLeader(mcastIp)) {
-            log.debug("Skip {} due to lack of leadership", mcastIp);
-            return;
-        }
-        // Skip if it is not an infra failure
-        Set<DeviceId> transitDevices = getDevice(mcastIp, TRANSIT);
-        if (!mcastUtils.isInfraFailure(transitDevices, failedElement)) {
-            log.debug("Skip {} not an infrastructure failure", mcastIp);
-            return;
-        }
-        // Do not proceed if the sources of this group are missing
-        Set<ConnectPoint> sources = getSources(mcastIp);
-        if (sources.isEmpty()) {
-            log.warn("Missing sources for group {}", mcastIp);
-            return;
-        }
-        // Get all the paths, affected paths, good links and good devices
-        Set<List<Link>> storedPaths = getStoredPaths(mcastIp);
-        Set<List<Link>> affectedPaths = mcastUtils.getAffectedPaths(storedPaths, failedElement);
-        Set<Link> goodLinks = Sets.newHashSet();
-        Map<DeviceId, Set<DeviceId>> goodDevicesBySource = Maps.newHashMap();
-        Map<DeviceId, Set<ConnectPoint>> processedSourcesByEgress = Maps.newHashMap();
-        Sets.difference(storedPaths, affectedPaths).forEach(goodPath -> {
-            goodLinks.addAll(goodPath);
-            DeviceId srcDevice = goodPath.get(0).src().deviceId();
-            Set<DeviceId> goodDevices = Sets.newHashSet();
-            goodPath.forEach(link -> goodDevices.add(link.src().deviceId()));
-            goodDevicesBySource.compute(srcDevice, (k, v) -> {
-                v = v == null ? Sets.newHashSet() : v;
-                v.addAll(goodDevices);
-                return v;
-            });
-        });
-        affectedPaths.forEach(affectedPath -> {
-            // TODO remove
-            log.info("Good links {}", goodLinks);
-            // TODO remove
-            log.info("Good devices {}", goodDevicesBySource);
-            // TODO trace
-            log.info("Healing the path {}", affectedPath);
-            DeviceId srcDevice = affectedPath.get(0).src().deviceId();
-            DeviceId dstDevice = affectedPath.get(affectedPath.size() - 1).dst().deviceId();
-            // Fix in one shot multiple sources
-            Set<ConnectPoint> affectedSources = sources.stream()
-                    .filter(device -> device.deviceId().equals(srcDevice))
-                    .collect(Collectors.toSet());
-            Set<ConnectPoint> processedSources = processedSourcesByEgress.getOrDefault(dstDevice,
-                                                                                       Collections.emptySet());
-            Optional<Path> alternativePath = getPath(srcDevice, dstDevice, mcastIp, null);
-            // If an alternative is possible go ahead
-            if (alternativePath.isPresent()) {
-                // TODO trace
-                log.info("Alternative path {}", alternativePath.get().links());
-            } else {
-                // Otherwise try to come up with an alternative
-                // TODO trace
-                log.info("No alternative path");
-                Set<ConnectPoint> notAffectedSources = Sets.difference(sources, affectedSources);
-                Set<ConnectPoint> remainingSources = Sets.difference(notAffectedSources, processedSources);
-                alternativePath = recoverSinks(dstDevice, mcastIp, affectedSources, remainingSources);
-                processedSourcesByEgress.compute(dstDevice, (k, v) -> {
-                    v = v == null ? Sets.newHashSet() : v;
-                    v.addAll(affectedSources);
-                    return v;
-                });
-            }
-            // Recover from the failure if possible
-            Optional<Path> finalPath = alternativePath;
-            affectedSources.forEach(affectedSource -> {
-                // Update the mcastPath store
-                McastPathStoreKey mcastPathStoreKey = new McastPathStoreKey(mcastIp, affectedSource);
-                // Verify if there are local sinks
-                Set<DeviceId> localSinks = getSinks(mcastIp, srcDevice, affectedSource).stream()
-                        .map(ConnectPoint::deviceId)
-                        .collect(Collectors.toSet());
-                Set<DeviceId> goodDevices = goodDevicesBySource.compute(affectedSource.deviceId(), (k, v) -> {
-                    v = v == null ? Sets.newHashSet() : v;
-                    v.addAll(localSinks);
-                    return v;
-                });
-                // TODO remove
-                log.info("Good devices {}", goodDevicesBySource);
-                Collection<? extends List<Link>> storedPathsBySource = Versioned.valueOrElse(
-                        mcastPathStore.get(mcastPathStoreKey), Lists.newArrayList());
-                Optional<? extends List<Link>> storedPath = storedPathsBySource.stream()
-                        .filter(path -> path.equals(affectedPath))
-                        .findFirst();
-                // Remove bad links
-                affectedPath.forEach(affectedLink -> {
-                    DeviceId affectedDevice = affectedLink.src().deviceId();
-                    // If there is overlap with good paths - skip it
-                    if (!goodLinks.contains(affectedLink)) {
-                        removePortFromDevice(affectedDevice, affectedLink.src().port(), mcastIp,
-                            mcastUtils.assignedVlan(affectedDevice.equals(affectedSource.deviceId()) ?
-                                                            affectedSource : null));
-                    }
-                    // Remove role on the affected links if last
-                    if (!goodDevices.contains(affectedDevice)) {
-                        mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, affectedDevice, affectedSource));
-                    }
-                });
-                // Sometimes the removal fails for serialization issue
-                // trying with the original object as workaround
-                if (storedPath.isPresent()) {
-                    mcastPathStore.remove(mcastPathStoreKey, storedPath.get());
-                } else {
-                    log.warn("Unable to find the corresponding path - trying removeal");
-                    mcastPathStore.remove(mcastPathStoreKey, affectedPath);
-                }
-                // Program new links
-                if (finalPath.isPresent()) {
-                    List<Link> links = finalPath.get().links();
-                    installPath(mcastIp, affectedSource, links);
-                    mcastPathStore.put(mcastPathStoreKey, links);
-                    links.forEach(link -> goodDevices.add(link.src().deviceId()));
-                    goodDevicesBySource.compute(srcDevice, (k, v) -> {
-                        v = v == null ? Sets.newHashSet() : v;
-                        v.addAll(goodDevices);
-                        return v;
-                    });
-                    goodLinks.addAll(finalPath.get().links());
-                }
-            });
-        });
-    }
-
-    /**
-     * Try to recover sinks using alternative locations.
-     *
-     * @param notRecovered the device not recovered
-     * @param mcastIp the group address
-     * @param affectedSources affected sources
-     * @param goodSources sources not affected
-     */
-    private Optional<Path> recoverSinks(DeviceId notRecovered, IpAddress mcastIp,
-                                    Set<ConnectPoint> affectedSources,
-                                    Set<ConnectPoint> goodSources) {
-        log.debug("Processing recover sinks on {} for group {}", notRecovered, mcastIp);
-        Map<ConnectPoint, Set<ConnectPoint>> affectedSinksBySource = Maps.newHashMap();
-        Map<ConnectPoint, Set<ConnectPoint>> sinksBySource = Maps.newHashMap();
-        Set<ConnectPoint> sources = Sets.union(affectedSources, goodSources);
-        // Hosts influenced by the failure
-        Map<HostId, Set<ConnectPoint>> hostIdSetMap = mcastUtils.getAffectedSinks(notRecovered, mcastIp);
-        // Locations influenced by the failure
-        Set<ConnectPoint> affectedSinks = hostIdSetMap.values()
-                .stream()
-                .flatMap(Collection::stream)
-                .filter(connectPoint -> connectPoint.deviceId().equals(notRecovered))
-                .collect(Collectors.toSet());
-        // All locations
-        Set<ConnectPoint> sinks = hostIdSetMap.values()
-                .stream()
-                .flatMap(Collection::stream)
-                .collect(Collectors.toSet());
-        // Maps sinks with the sources
-        sources.forEach(source -> {
-            Set<ConnectPoint> currentSinks = affectedSinks.stream()
-                    .filter(sink -> isSinkForSource(mcastIp, sink, source))
-                    .collect(Collectors.toSet());
-            affectedSinksBySource.put(source, currentSinks);
-        });
-        // Remove sinks one by one if they are not used by other sources
-        affectedSources.forEach(affectedSource -> {
-            Set<ConnectPoint> currentSinks = affectedSinksBySource.get(affectedSource);
-            log.info("Current sinks {} for source {}", currentSinks, affectedSource);
-            currentSinks.forEach(currentSink -> {
-                VlanId assignedVlan = mcastUtils.assignedVlan(
-                        affectedSource.deviceId().equals(currentSink.deviceId()) ? affectedSource : null);
-                log.info("Assigned vlan {}", assignedVlan);
-                Set<VlanId> otherVlans = goodSources.stream()
-                        .filter(remainingSource -> affectedSinksBySource.get(remainingSource).contains(currentSink))
-                        .map(remainingSource -> mcastUtils.assignedVlan(
-                                remainingSource.deviceId().equals(currentSink.deviceId()) ? remainingSource : null))
-                        .collect(Collectors.toSet());
-                log.info("Other vlans {}", otherVlans);
-                // Sinks on other leaves
-                if (!otherVlans.contains(assignedVlan)) {
-                    removePortFromDevice(currentSink.deviceId(), currentSink.port(), mcastIp, assignedVlan);
-                }
-                mcastRoleStore.remove(new McastRoleStoreKey(mcastIp, currentSink.deviceId(), affectedSource));
-            });
-        });
-        // Get the sinks to be added and the new egress
-        Set<DeviceId> newEgress = Sets.newHashSet();
-        affectedSources.forEach(affectedSource -> {
-            Set<ConnectPoint> currentSinks = affectedSinksBySource.get(affectedSource);
-            Set<ConnectPoint> newSinks = Sets.difference(sinks, currentSinks);
-            sinksBySource.put(affectedSource, newSinks);
-            newSinks.stream()
-                    .map(ConnectPoint::deviceId)
-                    .forEach(newEgress::add);
-        });
-        log.info("newEgress {}", newEgress);
-        // If there are more than one new egresses, return the problem
-        if (newEgress.size() != 1) {
-            log.warn("There are {} new egress, wrong configuration. Abort.", newEgress.size());
-            return Optional.empty();
-        }
-        DeviceId egress = newEgress.stream()
-                .findFirst()
-                .orElse(null);
-        DeviceId ingress = affectedSources.stream()
-                .map(ConnectPoint::deviceId)
-                .findFirst()
-                .orElse(null);
-        log.info("Ingress {}", ingress);
-        if (ingress == null) {
-            log.warn("No new ingress, wrong configuration. Abort.");
-            return Optional.empty();
-        }
-        // Get an alternative path
-        Optional<Path> alternativePath = getPath(ingress, egress, mcastIp, null);
-        // If there are new path install sinks and return path
-        if (alternativePath.isPresent()) {
-            log.info("Alternative path {}", alternativePath.get().links());
-            affectedSources.forEach(affectedSource -> {
-                Set<ConnectPoint> newSinks = sinksBySource.get(affectedSource);
-                newSinks.forEach(newSink -> {
-                    addPortToDevice(newSink.deviceId(), newSink.port(), mcastIp, mcastUtils.assignedVlan(null));
-                    mcastRoleStore.put(new McastRoleStoreKey(mcastIp, newSink.deviceId(), affectedSource), EGRESS);
-                });
-            });
-            return alternativePath;
-        }
-        // No new path but sinks co-located with sources install sinks and return empty
-        if (ingress.equals(egress)) {
-            log.info("No Alternative path but sinks co-located");
-            affectedSources.forEach(affectedSource -> {
-                Set<ConnectPoint> newSinks = sinksBySource.get(affectedSource);
-                newSinks.forEach(newSink -> {
-                    if (affectedSource.port().equals(newSink.port())) {
-                        log.warn("Skip {} since sink {} is on the same port of source {}. Abort",
-                                 mcastIp, newSink, affectedSource);
-                        return;
-                    }
-                    addPortToDevice(newSink.deviceId(), newSink.port(), mcastIp,
-                                    mcastUtils.assignedVlan(affectedSource));
-                    mcastRoleStore.put(new McastRoleStoreKey(mcastIp, newSink.deviceId(), affectedSource), INGRESS);
-                });
-            });
-        }
-        return Optional.empty();
-    }
-
-    /**
-     * Process all the sinks related to a mcast group and return
-     * the ones to be removed.
-     *
-     * @param mcastIp the group address
-     * @param prevsinks the previous sinks to be evaluated
-     * @param newSinks the new sinks to be evaluted
-     * @param source the source connect point
-     * @return the set of the sinks to be removed
-     */
-    private Set<ConnectPoint> processSinksToBeRemoved(IpAddress mcastIp,
-                                                      Map<HostId, Set<ConnectPoint>> prevsinks,
-                                                      Map<HostId, Set<ConnectPoint>> newSinks,
-                                                      ConnectPoint source) {
-        final Set<ConnectPoint> sinksToBeProcessed = Sets.newHashSet();
-        log.debug("Processing sinks to be removed for Multicast group {}, source {}",
-                  mcastIp, source);
-        prevsinks.forEach(((hostId, connectPoints) -> {
-            if (Objects.equal(HostId.NONE, hostId)) {
-                //in this case connect points are single homed sinks.
-                //just found the difference btw previous and new sinks for this source.
-                Set<ConnectPoint> difference = Sets.difference(connectPoints, newSinks.get(hostId));
-                sinksToBeProcessed.addAll(difference);
-                return;
-            }
-            // We have to check with the existing flows
-            ConnectPoint sinkToBeProcessed = connectPoints.stream()
-                    .filter(connectPoint -> isSinkForSource(mcastIp, connectPoint, source))
-                    .findFirst().orElse(null);
-            if (sinkToBeProcessed != null) {
-                // If the host has been removed or location has been removed
-                if (!newSinks.containsKey(hostId) ||
-                        !newSinks.get(hostId).contains(sinkToBeProcessed)) {
-                    sinksToBeProcessed.add(sinkToBeProcessed);
-                }
-            }
-        }));
-        // We have done, return the set
-        return sinksToBeProcessed;
-    }
-
-    /**
-     * Process new locations and return the set of sinks to be added
-     * in the context of the recovery.
-     *
-     * @param newSinks the remaining sinks
-     * @param prevSinks the previous sinks
-     * @param source the source connect point
-     * @return the set of the sinks to be processed
-     */
-    private Set<ConnectPoint> processSinksToBeRecovered(IpAddress mcastIp,
-                                                        Map<HostId, Set<ConnectPoint>> newSinks,
-                                                        Map<HostId, Set<ConnectPoint>> prevSinks,
-                                                        ConnectPoint source) {
-        final Set<ConnectPoint> sinksToBeProcessed = Sets.newHashSet();
-        log.debug("Processing sinks to be recovered for Multicast group {}, source {}",
-                  mcastIp, source);
-        newSinks.forEach((hostId, connectPoints) -> {
-            // If it has more than 1 locations
-            if (connectPoints.size() > 1 || connectPoints.size() == 0) {
-                log.debug("Skip {} since sink {} has {} locations",
-                         mcastIp, hostId, connectPoints.size());
-                return;
-            }
-            // If previously it had two locations, we need to recover it
-            // Filter out if the remaining location is already served
-            if (prevSinks.containsKey(hostId) && prevSinks.get(hostId).size() == 2) {
-                ConnectPoint sinkToBeProcessed = connectPoints.stream()
-                        .filter(connectPoint -> !isSinkForSource(mcastIp, connectPoint, source))
-                        .findFirst().orElse(null);
-                if (sinkToBeProcessed != null) {
-                    sinksToBeProcessed.add(sinkToBeProcessed);
-                }
-            }
-        });
-        return sinksToBeProcessed;
-    }
-
-    /**
-     * Process all the sinks related to a mcast group and return
-     * the ones to be processed.
-     *
-     * @param source the source connect point
-     * @param mcastIp the group address
-     * @param sinks the sinks to be evaluated
-     * @return the set of the sinks to be processed
-     */
-    private Set<ConnectPoint> processSinksToBeAdded(ConnectPoint source, IpAddress mcastIp,
-                                                    Map<HostId, Set<ConnectPoint>> sinks) {
-        final Set<ConnectPoint> sinksToBeProcessed = Sets.newHashSet();
-        log.debug("Processing sinks to be added for Multicast group {}, source {}",
-                  mcastIp, source);
-        sinks.forEach(((hostId, connectPoints) -> {
-            //add all connect points that are not tied with any host
-            if (Objects.equal(HostId.NONE, hostId)) {
-                sinksToBeProcessed.addAll(connectPoints);
-                return;
-            }
-            // If it has more than 2 locations
-            if (connectPoints.size() > 2 || connectPoints.size() == 0) {
-                log.debug("Skip {} since sink {} has {} locations",
-                         mcastIp, hostId, connectPoints.size());
-                return;
-            }
-            // If it has one location, just use it
-            if (connectPoints.size() == 1) {
-                sinksToBeProcessed.add(connectPoints.stream().findFirst().orElse(null));
-                return;
-            }
-            // We prefer to reuse existing flows
-            ConnectPoint sinkToBeProcessed = connectPoints.stream()
-                    .filter(connectPoint -> {
-                        if (!isSinkForGroup(mcastIp, connectPoint, source)) {
-                            return false;
-                        }
-                        if (!isSinkReachable(mcastIp, connectPoint, source)) {
-                            return false;
-                        }
-                        ConnectPoint other = connectPoints.stream()
-                                .filter(remaining -> !remaining.equals(connectPoint))
-                                .findFirst().orElse(null);
-                        // We are already serving the sink
-                        return !isSinkForSource(mcastIp, other, source);
-                    }).findFirst().orElse(null);
-
-            if (sinkToBeProcessed != null) {
-                sinksToBeProcessed.add(sinkToBeProcessed);
-                return;
-            }
-            // Otherwise we prefer to reuse existing egresses
-            Set<DeviceId> egresses = getDevice(mcastIp, EGRESS, source);
-            sinkToBeProcessed = connectPoints.stream()
-                    .filter(connectPoint -> {
-                        if (!egresses.contains(connectPoint.deviceId())) {
-                            return false;
-                        }
-                        if (!isSinkReachable(mcastIp, connectPoint, source)) {
-                            return false;
-                        }
-                        ConnectPoint other = connectPoints.stream()
-                                .filter(remaining -> !remaining.equals(connectPoint))
-                                .findFirst().orElse(null);
-                        return !isSinkForSource(mcastIp, other, source);
-                    }).findFirst().orElse(null);
-            if (sinkToBeProcessed != null) {
-                sinksToBeProcessed.add(sinkToBeProcessed);
-                return;
-            }
-            // Otherwise we prefer a location co-located with the source (if it exists)
-            sinkToBeProcessed = connectPoints.stream()
-                    .filter(connectPoint -> connectPoint.deviceId().equals(source.deviceId()))
-                    .findFirst().orElse(null);
-            if (sinkToBeProcessed != null) {
-                sinksToBeProcessed.add(sinkToBeProcessed);
-                return;
-            }
-            // Finally, we randomly pick a new location if it is reachable
-            sinkToBeProcessed = connectPoints.stream()
-                    .filter(connectPoint -> {
-                        if (!isSinkReachable(mcastIp, connectPoint, source)) {
-                            return false;
-                        }
-                        ConnectPoint other = connectPoints.stream()
-                                .filter(remaining -> !remaining.equals(connectPoint))
-                                .findFirst().orElse(null);
-                        return !isSinkForSource(mcastIp, other, source);
-                    }).findFirst().orElse(null);
-            if (sinkToBeProcessed != null) {
-                sinksToBeProcessed.add(sinkToBeProcessed);
-            }
-        }));
-        return sinksToBeProcessed;
-    }
-
-    /**
-     * Adds a port to given multicast group on given device. This involves the
-     * update of L3 multicast group and multicast routing table entry.
-     *
-     * @param deviceId device ID
-     * @param port port to be added
-     * @param mcastIp multicast group
-     * @param assignedVlan assigned VLAN ID
-     */
-    private void addPortToDevice(DeviceId deviceId, PortNumber port,
-                                 IpAddress mcastIp, VlanId assignedVlan) {
-        // TODO trace
-        log.info("Adding {} on {}/{} and vlan {}", mcastIp, deviceId, port, assignedVlan);
-        McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, deviceId, assignedVlan);
-        ImmutableSet.Builder<PortNumber> portBuilder = ImmutableSet.builder();
-        NextObjective newNextObj;
-        if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
-            // First time someone request this mcast group via this device
-            portBuilder.add(port);
-            // New nextObj
-            if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-                log.debug("Passing 0 as nextId for unconfigured device {}", deviceId);
-                newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                            portBuilder.build(), 0).add();
-            } else {
-                newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                            portBuilder.build(), null).add();
-            }
-            // Store the new port
-            mcastNextObjStore.put(mcastStoreKey, newNextObj);
-            // Create, store and apply the new nextObj and fwdObj
-            ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Successfully add {} on {}/{}, vlan {}",
-                        mcastIp, deviceId, port.toLong(), assignedVlan),
-                (objective, error) -> {
-                    log.warn("Failed to add {} on {}/{}, vlan {}: {}",
-                            mcastIp, deviceId, port.toLong(), assignedVlan, error);
-                    // Schedule the removal using directly the key
-                    mcastWorker.execute(() -> mcastNextObjStore.remove(mcastStoreKey));
-                });
-            ForwardingObjective fwdObj = mcastUtils.fwdObjBuilder(mcastIp, assignedVlan,
-                                                          newNextObj.id()).add(context);
-            if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-                log.debug("skip next and forward flowobjective addition for device: {}", deviceId);
-            } else {
-                srManager.flowObjectiveService.next(deviceId, newNextObj);
-                srManager.flowObjectiveService.forward(deviceId, fwdObj);
-            }
-        } else {
-            // This device already serves some subscribers of this mcast group
-            NextObjective nextObj = mcastNextObjStore.get(mcastStoreKey).value();
-            // Stop if the port is already in the nextobj
-            Set<PortNumber> existingPorts = mcastUtils.getPorts(nextObj.next());
-            if (existingPorts.contains(port)) {
-                log.debug("Port {}/{} already exists for {}. Abort", deviceId, port, mcastIp);
-                return;
-            }
-            // Let's add the port and reuse the previous one
-            portBuilder.addAll(existingPorts).add(port);
-            // Reuse previous nextObj
-            newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                        portBuilder.build(), nextObj.id()).addToExisting();
-            // Store the final next objective and send only the difference to the driver
-            mcastNextObjStore.put(mcastStoreKey, newNextObj);
-            // Add just the new port
-            portBuilder = ImmutableSet.builder();
-            portBuilder.add(port);
-            newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                        portBuilder.build(), nextObj.id()).addToExisting();
-            if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-                log.debug("skip next flowobjective update for device: {}", deviceId);
-            } else {
-                // no need to update the flow here since we have updated the nextobjective/group
-                // the existing flow will keep pointing to the updated nextobj
-                srManager.flowObjectiveService.next(deviceId, newNextObj);
-            }
-        }
-    }
-
-    /**
-     * Removes a port from given multicast group on given device.
-     * This involves the update of L3 multicast group and multicast routing
-     * table entry.
-     *
-     * @param deviceId device ID
-     * @param port port to be added
-     * @param mcastIp multicast group
-     * @param assignedVlan assigned VLAN ID
-     * @return true if this is the last sink on this device
-     */
-    private boolean removePortFromDevice(DeviceId deviceId, PortNumber port,
-                                         IpAddress mcastIp, VlanId assignedVlan) {
-        // TODO trace
-        log.info("Removing {} on {}/{} and vlan {}", mcastIp, deviceId, port, assignedVlan);
-        McastStoreKey mcastStoreKey =
-                new McastStoreKey(mcastIp, deviceId, assignedVlan);
-        // This device is not serving this multicast group
-        if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
-            return true;
-        }
-        NextObjective nextObj = mcastNextObjStore.get(mcastStoreKey).value();
-        Set<PortNumber> existingPorts = mcastUtils.getPorts(nextObj.next());
-        // This port does not serve this multicast group
-        if (!existingPorts.contains(port)) {
-            if (!existingPorts.isEmpty()) {
-                log.debug("{} is not serving {} on port {}. Abort.", deviceId, mcastIp, port);
-                return false;
-            }
-            return true;
-        }
-        // Copy and modify the ImmutableSet
-        existingPorts = Sets.newHashSet(existingPorts);
-        existingPorts.remove(port);
-        NextObjective newNextObj;
-        ObjectiveContext context;
-        ForwardingObjective fwdObj;
-        if (existingPorts.isEmpty()) {
-            context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("Successfully remove {} on {}/{}, vlan {}",
-                            mcastIp, deviceId, port.toLong(), assignedVlan),
-                    (objective, error) -> log.warn("Failed to remove {} on {}/{}, vlan {}: {}",
-                                    mcastIp, deviceId, port.toLong(), assignedVlan, error));
-            fwdObj = mcastUtils.fwdObjBuilder(mcastIp, assignedVlan, nextObj.id()).remove(context);
-            if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-                log.debug("skip forward flowobjective removal for device: {}", deviceId);
-            } else {
-                srManager.flowObjectiveService.forward(deviceId, fwdObj);
-            }
-            mcastNextObjStore.remove(mcastStoreKey);
-        } else {
-            // Here we store the next objective with the remaining port
-            newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                        existingPorts, nextObj.id()).removeFromExisting();
-            mcastNextObjStore.put(mcastStoreKey, newNextObj);
-             // Let's modify the next objective removing the bucket
-            newNextObj = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                    ImmutableSet.of(port), nextObj.id()).removeFromExisting();
-            if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-                log.debug("skip next flowobjective update for device: {}", deviceId);
-            } else {
-                // no need to update the flow here since we have updated the next objective + group
-                // the existing flow will keep pointing to the updated nextobj
-                srManager.flowObjectiveService.next(deviceId, newNextObj);
-            }
-        }
-        return existingPorts.isEmpty();
-    }
-
-    /**
-     * Removes entire group on given device.
-     *
-     * @param deviceId device ID
-     * @param mcastIp multicast group to be removed
-     * @param assignedVlan assigned VLAN ID
-     */
-    private void removeGroupFromDevice(DeviceId deviceId, IpAddress mcastIp,
-                                       VlanId assignedVlan) {
-        // TODO trace
-        log.info("Removing {} on {} and vlan {}", mcastIp, deviceId, assignedVlan);
-        McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, deviceId, assignedVlan);
-        // This device is not serving this multicast group
-        if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
-            log.debug("{} is not serving {}. Abort.", deviceId, mcastIp);
-            return;
-        }
-        NextObjective nextObj = mcastNextObjStore.get(mcastStoreKey).value();
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Successfully remove {} on {}, vlan {}",
-                        mcastIp, deviceId, assignedVlan),
-                (objective, error) -> log.warn("Failed to remove {} on {}, vlan {}: {}",
-                                mcastIp, deviceId, assignedVlan, error));
-        if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-            log.debug("skip flow changes on unconfigured device: {}", deviceId);
-        } else {
-            ForwardingObjective fwdObj = mcastUtils.fwdObjBuilder(mcastIp, assignedVlan, nextObj.id()).remove(context);
-            srManager.flowObjectiveService.forward(deviceId, fwdObj);
-        }
-        mcastNextObjStore.remove(mcastStoreKey);
-    }
-
-    private void installPath(IpAddress mcastIp, ConnectPoint source, List<Link> links) {
-        if (links.isEmpty()) {
-            log.warn("There is no link that can be used. Stopping installation.");
-            return;
-        }
-        // Setup new ingress mcast role
-        mcastRoleStore.put(new McastRoleStoreKey(mcastIp, links.get(0).src().deviceId(), source),
-                           INGRESS);
-        // For each link, modify the next on the source device adding the src port
-        // and a new filter objective on the destination port
-        links.forEach(link -> {
-            addPortToDevice(link.src().deviceId(), link.src().port(), mcastIp,
-                mcastUtils.assignedVlan(link.src().deviceId().equals(source.deviceId()) ? source : null));
-            McastFilteringObjStoreKey mcastFilterObjStoreKey = new McastFilteringObjStoreKey(link.dst(),
-                    mcastUtils.assignedVlan(null), mcastIp.isIp4());
-            addFilterToDevice(mcastFilterObjStoreKey, mcastIp, null);
-        });
-        // Setup mcast role for the transit
-        links.stream()
-                .filter(link -> !link.src().deviceId().equals(source.deviceId()))
-                .forEach(link -> mcastRoleStore.put(new McastRoleStoreKey(mcastIp, link.src().deviceId(), source),
-                                                    TRANSIT));
-    }
-
-    /**
-     * Gets a path from src to dst.
-     * If a path was allocated before, returns the allocated path.
-     * Otherwise, randomly pick one from available paths.
-     *
-     * @param src source device ID
-     * @param dst destination device ID
-     * @param mcastIp multicast group
-     * @param allPaths paths list
-     *
-     * @return an optional path from src to dst
-     */
-    private Optional<Path> getPath(DeviceId src, DeviceId dst,
-                                   IpAddress mcastIp, List<Path> allPaths) {
-        if (allPaths == null) {
-            allPaths = mcastUtils.getPaths(src, dst, Collections.emptySet());
-        }
-        if (allPaths.isEmpty()) {
-            return Optional.empty();
-        }
-        // Create a map index of suitability-to-list of paths. For example
-        // a path in the list associated to the index 1 shares only one link
-        // and it is less suitable of a path belonging to the index 2
-        Map<Integer, List<Path>> eligiblePaths = Maps.newHashMap();
-        int score;
-        // Let's build the multicast tree
-        Set<List<Link>> storedPaths = getStoredPaths(mcastIp);
-        Set<Link> storedTree = storedPaths.stream()
-                .flatMap(Collection::stream).collect(Collectors.toSet());
-        log.trace("Stored tree {}", storedTree);
-        Set<Link> pathLinks;
-        for (Path path : allPaths) {
-            if (!src.equals(path.links().get(0).src().deviceId())) {
-                continue;
-            }
-            pathLinks = Sets.newHashSet(path.links());
-            score = Sets.intersection(pathLinks, storedTree).size();
-            // score defines the index
-            if (score > 0) {
-                eligiblePaths.compute(score, (index, paths) -> {
-                    paths = paths == null ? Lists.newArrayList() : paths;
-                    paths.add(path);
-                    return paths;
-                });
-            }
-        }
-        if (eligiblePaths.isEmpty()) {
-            log.trace("No eligiblePath(s) found from {} to {}", src, dst);
-            Collections.shuffle(allPaths);
-            return allPaths.stream().findFirst();
-        }
-        // Let's take the best ones
-        Integer bestIndex = eligiblePaths.keySet().stream()
-                .sorted(Comparator.reverseOrder()).findFirst().orElse(null);
-        List<Path> bestPaths = eligiblePaths.get(bestIndex);
-        log.trace("{} eligiblePath(s) found from {} to {}",
-                  bestPaths.size(), src, dst);
-        Collections.shuffle(bestPaths);
-        return bestPaths.stream().findFirst();
-    }
-
-    /**
-     * Gets stored paths of the group.
-     *
-     * @param mcastIp group address
-     * @return a collection of paths
-     */
-    private Set<List<Link>> getStoredPaths(IpAddress mcastIp) {
-        return mcastPathStore.stream()
-                .filter(entry -> entry.getKey().mcastIp().equals(mcastIp))
-                .map(Entry::getValue)
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Gets device(s) of given role and of given source in given multicast tree.
-     *
-     * @param mcastIp multicast IP
-     * @param role multicast role
-     * @param source source connect point
-     * @return set of device ID or empty set if not found
-     */
-    private Set<DeviceId> getDevice(IpAddress mcastIp, McastRole role, ConnectPoint source) {
-        return mcastRoleStore.entrySet().stream()
-                .filter(entry -> entry.getKey().mcastIp().equals(mcastIp) &&
-                        entry.getKey().source().equals(source) &&
-                        entry.getValue().value() == role)
-                .map(Entry::getKey).map(McastRoleStoreKey::deviceId).collect(Collectors.toSet());
-    }
-
-    /**
-     * Gets device(s) of given role in given multicast group.
-     *
-     * @param mcastIp multicast IP
-     * @param role multicast role
-     * @return set of device ID or empty set if not found
-     */
-    private Set<DeviceId> getDevice(IpAddress mcastIp, McastRole role) {
-        return mcastRoleStore.entrySet().stream()
-                .filter(entry -> entry.getKey().mcastIp().equals(mcastIp) &&
-                        entry.getValue().value() == role)
-                .map(Entry::getKey).map(McastRoleStoreKey::deviceId).collect(Collectors.toSet());
-    }
-
-    /**
-     * Gets source(s) of given multicast group.
-     *
-     * @param mcastIp multicast IP
-     * @return set of device ID or empty set if not found
-     */
-    private Set<ConnectPoint> getSources(IpAddress mcastIp) {
-        return mcastRoleStore.entrySet().stream()
-                .filter(entry -> entry.getKey().mcastIp().equals(mcastIp))
-                .map(Entry::getKey).map(McastRoleStoreKey::source).collect(Collectors.toSet());
-    }
-
-    /**
-     * Gets sink(s) of given multicast group.
-     *
-     * @param mcastIp multicast IP
-     * @return set of connect point or empty set if not found
-     */
-    private Set<ConnectPoint> getSinks(IpAddress mcastIp, DeviceId device, ConnectPoint source) {
-        McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-        Collection<? extends List<Link>> storedPaths = Versioned.valueOrElse(
-                mcastPathStore.get(pathStoreKey), Lists.newArrayList());
-        VlanId assignedVlan = mcastUtils.assignedVlan(device.equals(source.deviceId()) ? source : null);
-        McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, device, assignedVlan);
-        NextObjective nextObjective = Versioned.valueOrNull(mcastNextObjStore.get(mcastStoreKey));
-        ImmutableSet.Builder<ConnectPoint> cpBuilder = ImmutableSet.builder();
-        if (nextObjective != null) {
-            Set<PortNumber> outputPorts = mcastUtils.getPorts(nextObjective.next());
-            outputPorts.forEach(portNumber -> cpBuilder.add(new ConnectPoint(device, portNumber)));
-        }
-        Set<ConnectPoint> egressCp = cpBuilder.build();
-        return egressCp.stream()
-                .filter(connectPoint -> !mcastUtils.isInfraPort(connectPoint, storedPaths))
-                .collect(Collectors.toSet());
-    }
-
-
-
-    /**
-     * Gets groups which is affected by the link down event.
-     *
-     * @param link link going down
-     * @return a set of multicast IpAddress
-     */
-    private Set<IpAddress> getAffectedGroups(Link link) {
-        DeviceId deviceId = link.src().deviceId();
-        PortNumber port = link.src().port();
-        return mcastNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(deviceId) &&
-                    mcastUtils.getPorts(entry.getValue().value().next()).contains(port))
-                .map(Entry::getKey).map(McastStoreKey::mcastIp).collect(Collectors.toSet());
-    }
-
-    /**
-     * Gets groups which are affected by the device down event.
-     *
-     * @param deviceId device going down
-     * @return a set of multicast IpAddress
-     */
-    private Set<IpAddress> getAffectedGroups(DeviceId deviceId) {
-        return mcastNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(deviceId))
-                .map(Entry::getKey).map(McastStoreKey::mcastIp)
-                .collect(Collectors.toSet());
-    }
-
-    /**
-     * Verify if a given connect point is sink for this group.
-     *
-     * @param mcastIp group address
-     * @param connectPoint connect point to be verified
-     * @param source source connect point
-     * @return true if the connect point is sink of the group
-     */
-    private boolean isSinkForGroup(IpAddress mcastIp, ConnectPoint connectPoint,
-                                   ConnectPoint source) {
-        VlanId assignedVlan = mcastUtils.assignedVlan(connectPoint.deviceId().equals(source.deviceId()) ?
-                                                              source : null);
-        McastStoreKey mcastStoreKey = new McastStoreKey(mcastIp, connectPoint.deviceId(), assignedVlan);
-        if (!mcastNextObjStore.containsKey(mcastStoreKey)) {
-            return false;
-        }
-        NextObjective mcastNext = mcastNextObjStore.get(mcastStoreKey).value();
-        return mcastUtils.getPorts(mcastNext.next()).contains(connectPoint.port());
-    }
-
-    /**
-     * Verify if a given connect point is sink for this group and for this source.
-     *
-     * @param mcastIp group address
-     * @param connectPoint connect point to be verified
-     * @param source source connect point
-     * @return true if the connect point is sink of the group
-     */
-    private boolean isSinkForSource(IpAddress mcastIp, ConnectPoint connectPoint,
-                                    ConnectPoint source) {
-        boolean isSink = isSinkForGroup(mcastIp, connectPoint, source);
-        DeviceId device;
-        if (connectPoint.deviceId().equals(source.deviceId())) {
-            device = getDevice(mcastIp, INGRESS, source).stream()
-                    .filter(deviceId -> deviceId.equals(connectPoint.deviceId()))
-                    .findFirst().orElse(null);
-        } else {
-            device = getDevice(mcastIp, EGRESS, source).stream()
-                    .filter(deviceId -> deviceId.equals(connectPoint.deviceId()))
-                    .findFirst().orElse(null);
-        }
-        return isSink && device != null;
-    }
-
-    /**
-     * Verify if a sink is reachable from this source.
-     *
-     * @param mcastIp group address
-     * @param sink connect point to be verified
-     * @param source source connect point
-     * @return true if the connect point is reachable from the source
-     */
-    private boolean isSinkReachable(IpAddress mcastIp, ConnectPoint sink,
-                                    ConnectPoint source) {
-        return sink.deviceId().equals(source.deviceId()) ||
-                getPath(source.deviceId(), sink.deviceId(), mcastIp, null).isPresent();
-    }
-
-    /**
-     * Updates filtering objective for given device and port.
-     * It is called in general when the mcast config has been
-     * changed.
-     *
-     * @param deviceId device ID
-     * @param portNum ingress port number
-     * @param vlanId assigned VLAN ID
-     * @param install true to add, false to remove
-     */
-    public void updateFilterToDevice(DeviceId deviceId, PortNumber portNum,
-                                        VlanId vlanId, boolean install) {
-        mcastWorker.execute(() -> updateFilterToDeviceInternal(deviceId, portNum, vlanId, install));
-    }
-
-    private void updateFilterToDeviceInternal(DeviceId deviceId, PortNumber portNum,
-                                              VlanId vlanId, boolean install) {
-        lastMcastChange.set(Instant.now());
-        // Iterates over the route and updates properly the filtering objective on the source device.
-        srManager.multicastRouteService.getRoutes().forEach(mcastRoute -> {
-            log.debug("Update filter for {}", mcastRoute.group());
-            if (!mcastUtils.isLeader(mcastRoute.group())) {
-                log.debug("Skip {} due to lack of leadership", mcastRoute.group());
-                return;
-            }
-            // Get the sources and for each one update properly the filtering objectives
-            Set<ConnectPoint> sources = srManager.multicastRouteService.sources(mcastRoute);
-            sources.forEach(source -> {
-                if (source.deviceId().equals(deviceId) && source.port().equals(portNum)) {
-                    if (install) {
-                        McastFilteringObjStoreKey mcastFilterObjStoreKey = new McastFilteringObjStoreKey(source,
-                                                                             vlanId, mcastRoute.group().isIp4());
-                        addFilterToDevice(mcastFilterObjStoreKey, mcastRoute.group(), INGRESS);
-                    } else {
-                        mcastUtils.removeFilterToDevice(deviceId, portNum, vlanId, mcastRoute.group(), null);
-                    }
-                }
-            });
-        });
-    }
-
-    /**
-     * Add filtering to the device if needed.
-     *
-     * @param filterObjStoreKey the filtering obj key
-     * @param mcastIp the multicast group
-     * @param mcastRole the multicast role
-     */
-    private void addFilterToDevice(McastFilteringObjStoreKey filterObjStoreKey,
-                                   IpAddress mcastIp,
-                                   McastRole mcastRole) {
-        if (!containsFilterInTheDevice(filterObjStoreKey)) {
-            // if this is the first sink for this group/device
-            // match additionally on mac
-            log.debug("Filtering not available for device {}, vlan {} and {}",
-                      filterObjStoreKey.ingressCP().deviceId(), filterObjStoreKey.vlanId(),
-                      filterObjStoreKey.isIpv4() ? "IPv4" : "IPv6");
-            mcastUtils.addFilterToDevice(filterObjStoreKey.ingressCP().deviceId(),
-                                         filterObjStoreKey.ingressCP().port(),
-                                         filterObjStoreKey.vlanId(), mcastIp,
-                                         mcastRole, true);
-            mcastFilteringObjStore.add(filterObjStoreKey);
-        } else if (!mcastFilteringObjStore.contains(filterObjStoreKey)) {
-            // match only vlan
-            log.debug("Filtering not available for connect point {}, vlan {} and {}",
-                      filterObjStoreKey.ingressCP(), filterObjStoreKey.vlanId(),
-                      filterObjStoreKey.isIpv4() ? "IPv4" : "IPv6");
-            mcastUtils.addFilterToDevice(filterObjStoreKey.ingressCP().deviceId(),
-                                         filterObjStoreKey.ingressCP().port(),
-                                         filterObjStoreKey.vlanId(), mcastIp,
-                                         mcastRole, false);
-            mcastFilteringObjStore.add(filterObjStoreKey);
-        } else {
-            // do nothing
-            log.debug("Filtering already present for connect point {}, vlan {} and {}. Abort",
-                      filterObjStoreKey.ingressCP(), filterObjStoreKey.vlanId(),
-                      filterObjStoreKey.isIpv4() ? "IPv4" : "IPv6");
-        }
-    }
-
-    /**
-     * Verify if there are related filtering obj in the device.
-     *
-     * @param filteringKey the filtering obj key
-     * @return true if related filtering obj are found
-     */
-    private boolean containsFilterInTheDevice(McastFilteringObjStoreKey filteringKey) {
-        // check if filters are already added on the device
-        McastFilteringObjStoreKey key = mcastFilteringObjStore.stream()
-                .filter(mcastFilteringKey ->
-                                mcastFilteringKey.ingressCP().deviceId().equals(filteringKey.ingressCP().deviceId())
-                                        && mcastFilteringKey.isIpv4() == filteringKey.isIpv4()
-                                        && mcastFilteringKey.vlanId().equals(filteringKey.vlanId())
-                ).findFirst().orElse(null);
-        // we are interested to filt obj on the same device, same vlan and same ip type
-        return key != null;
-    }
-
-    /**
-     * Update the filtering objective store upon device failure.
-     *
-     * @param affectedDevice the affected device
-     */
-    private void updateFilterObjStoreByDevice(DeviceId affectedDevice) {
-        // purge the related filter objective key
-        Set<McastFilteringObjStoreKey> filterObjs = Sets.newHashSet(mcastFilteringObjStore);
-        Iterator<McastFilteringObjStoreKey> filterIterator = filterObjs.iterator();
-        McastFilteringObjStoreKey filterKey;
-        while (filterIterator.hasNext()) {
-            filterKey = filterIterator.next();
-            if (filterKey.ingressCP().deviceId().equals(affectedDevice)) {
-                mcastFilteringObjStore.remove(filterKey);
-            }
-        }
-    }
-
-    /**
-     * Update the filtering objective store upon port failure.
-     *
-     * @param affectedPort the affected port
-     */
-    private void updateFilterObjStoreByPort(ConnectPoint affectedPort) {
-        // purge the related filter objective key
-        Set<McastFilteringObjStoreKey> filterObjs = Sets.newHashSet(mcastFilteringObjStore);
-        Iterator<McastFilteringObjStoreKey> filterIterator = filterObjs.iterator();
-        McastFilteringObjStoreKey filterKey;
-        while (filterIterator.hasNext()) {
-            filterKey = filterIterator.next();
-            if (filterKey.ingressCP().equals(affectedPort)) {
-                mcastFilteringObjStore.remove(filterKey);
-            }
-        }
-    }
-
-    /**
-     * Performs bucket verification operation for all mcast groups in the devices.
-     * Firstly, it verifies that mcast is stable before trying verification operation.
-     * Verification consists in creating new nexts with VERIFY operation. Actually,
-     * the operation is totally delegated to the driver.
-     */
-    private final class McastBucketCorrector implements Runnable {
-        private final AtomicInteger verifyOnFlight = new AtomicInteger(0);
-        // Define the context used for the back pressure mechanism
-        private final ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> {
-                    synchronized (verifyOnFlight) {
-                        log.trace("Verify {} done", objective.id());
-                        verifyOnFlight.updateAndGet(i -> i > 0 ? i - 1 : i);
-                        verifyOnFlight.notify();
-                    }
-                },
-                (objective, error) -> {
-                    synchronized (verifyOnFlight) {
-                        log.trace("Verify {} error {}", objective.id(), error);
-                        verifyOnFlight.updateAndGet(i -> i > 0 ? i - 1 : i);
-                        verifyOnFlight.notify();
-                    }
-                });
-
-        @Override
-        public void run() {
-            try {
-                // Iterates over the routes and verify the related next objectives
-                for (McastRoute mcastRoute : srManager.multicastRouteService.getRoutes()) {
-                    if (!isMcastStable() || wasBktCorrRunning()) {
-                        return;
-                    }
-                    IpAddress mcastIp = mcastRoute.group();
-                    log.trace("Running mcast buckets corrector for mcast group: {}", mcastIp);
-                    // Verify leadership on the operation
-                    if (!mcastUtils.isLeader(mcastIp)) {
-                        log.trace("Skip {} due to lack of leadership", mcastIp);
-                        continue;
-                    }
-                    // Get sources and sinks from Mcast Route Service and warn about errors
-                    Set<ConnectPoint> sources = mcastUtils.getSources(mcastIp);
-                    Set<ConnectPoint> sinks = mcastUtils.getSinks(mcastIp).values().stream()
-                            .flatMap(Collection::stream).collect(Collectors.toSet());
-                    // Do not proceed if sources of this group are missing
-                    if (sources.isEmpty()) {
-                        if (!sinks.isEmpty()) {
-                            log.warn("Unable to run buckets corrector. " +
-                                     "Missing source {} for group {}", sources, mcastIp);
-                        }
-                        continue;
-                    }
-                    // For each group we get current information in the store
-                    // and issue a check of the next objectives in place
-                    Set<McastStoreKey> processedKeys = Sets.newHashSet();
-                    for (ConnectPoint source : sources) {
-                        Set<DeviceId> ingressDevices = getDevice(mcastIp, INGRESS, source);
-                        Set<DeviceId> transitDevices = getDevice(mcastIp, TRANSIT, source);
-                        Set<DeviceId> egressDevices = getDevice(mcastIp, EGRESS, source);
-                        // Do not proceed if ingress devices are missing
-                        if (ingressDevices.isEmpty()) {
-                            if (!sinks.isEmpty()) {
-                                log.warn("Unable to run buckets corrector. " +
-                                "Missing ingress {} for source {} and for group {}",
-                                         ingressDevices, source, mcastIp);
-                            }
-                            continue;
-                        }
-                        // Create the set of the devices to be processed
-                        ImmutableSet.Builder<DeviceId> devicesBuilder = ImmutableSet.builder();
-                        devicesBuilder.addAll(ingressDevices);
-                        if (!transitDevices.isEmpty()) {
-                            devicesBuilder.addAll(transitDevices);
-                        }
-                        if (!egressDevices.isEmpty()) {
-                            devicesBuilder.addAll(egressDevices);
-                        }
-                        Set<DeviceId> devicesToProcess = devicesBuilder.build();
-                        for (DeviceId deviceId : devicesToProcess) {
-                            if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-                                log.trace("Skipping Bucket corrector for unconfigured device {}", deviceId);
-                                continue;
-                            }
-                            synchronized (verifyOnFlight) {
-                                while (verifyOnFlight.get() == MAX_VERIFY_ON_FLIGHT) {
-                                    verifyOnFlight.wait();
-                                }
-                            }
-                            VlanId assignedVlan = mcastUtils.assignedVlan(deviceId.equals(source.deviceId()) ?
-                                                                                  source : null);
-                            McastStoreKey currentKey = new McastStoreKey(mcastIp, deviceId, assignedVlan);
-                            // Check if we already processed this next - trees merge at some point
-                            if (processedKeys.contains(currentKey)) {
-                                continue;
-                            }
-                            // Verify the nextobjective or skip to next device
-                            if (mcastNextObjStore.containsKey(currentKey)) {
-                                NextObjective currentNext = mcastNextObjStore.get(currentKey).value();
-                                // Rebuild the next objective using assigned vlan
-                                currentNext = mcastUtils.nextObjBuilder(mcastIp, assignedVlan,
-                                            mcastUtils.getPorts(currentNext.next()), currentNext.id()).verify(context);
-                                // Send to the flowobjective service
-                                srManager.flowObjectiveService.next(deviceId, currentNext);
-                                verifyOnFlight.incrementAndGet();
-                                log.trace("Verify on flight {}", verifyOnFlight);
-                                processedKeys.add(currentKey);
-                            } else {
-                                log.warn("Unable to run buckets corrector. " +
-                                         "Missing next for {}, for source {} and for group {}",
-                                         deviceId, source, mcastIp);
-                            }
-                        }
-                    }
-                    // Let's wait the group before start the next one
-                    synchronized (verifyOnFlight) {
-                        while (verifyOnFlight.get() > 0) {
-                            verifyOnFlight.wait();
-                        }
-                    }
-                }
-            } catch (InterruptedException e) {
-                log.warn("BktCorr has been interrupted");
-            } finally {
-                lastBktCorrExecution.set(Instant.now());
-            }
-        }
-    }
-
-    /**
-     * Returns the associated next ids to the mcast groups or to the single
-     * group if mcastIp is present.
-     *
-     * @param mcastIp the group ip
-     * @return the mapping mcastIp-device to next id
-     */
-    public Map<McastStoreKey, Integer> getNextIds(IpAddress mcastIp) {
-        log.info("mcastNexts {}", mcastNextObjStore.size());
-        if (mcastIp != null) {
-            return mcastNextObjStore.entrySet().stream()
-                    .filter(mcastEntry -> mcastIp.equals(mcastEntry.getKey().mcastIp()))
-                    .collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().value().id()));
-        }
-        return mcastNextObjStore.entrySet().stream()
-                .collect(Collectors.toMap(Entry::getKey, entry -> entry.getValue().value().id()));
-    }
-
-    /**
-     * Removes given next ID from mcast next id store.
-     *
-     * @param nextId next id
-     */
-    public void removeNextId(int nextId) {
-        mcastNextObjStore.entrySet().forEach(e -> {
-            if (e.getValue().value().id() == nextId) {
-                mcastNextObjStore.remove(e.getKey());
-            }
-        });
-    }
-
-    /**
-     * Build the mcast paths.
-     *
-     * @param storedPaths mcast tree
-     * @param mcastIp the group ip
-     * @param source the source
-     */
-    private Map<ConnectPoint, List<ConnectPoint>> buildMcastPaths(Collection<? extends List<Link>> storedPaths,
-                                                                  IpAddress mcastIp, ConnectPoint source) {
-        Map<ConnectPoint, List<ConnectPoint>> mcastTree = Maps.newHashMap();
-        // Local sinks
-        Set<ConnectPoint> localSinks = getSinks(mcastIp, source.deviceId(), source);
-        localSinks.forEach(localSink -> mcastTree.put(localSink, Lists.newArrayList(localSink, source)));
-        // Remote sinks
-        storedPaths.forEach(path -> {
-            List<Link> links = path;
-            DeviceId egressDevice = links.get(links.size() - 1).dst().deviceId();
-            Set<ConnectPoint> remoteSinks = getSinks(mcastIp, egressDevice, source);
-            List<ConnectPoint> connectPoints = Lists.newArrayList(source);
-            links.forEach(link -> {
-                connectPoints.add(link.src());
-                connectPoints.add(link.dst());
-            });
-            Collections.reverse(connectPoints);
-            remoteSinks.forEach(remoteSink -> {
-                List<ConnectPoint> finalPath = Lists.newArrayList(connectPoints);
-                finalPath.add(0, remoteSink);
-                mcastTree.put(remoteSink, finalPath);
-            });
-        });
-        return mcastTree;
-    }
-
-    /**
-     * Returns the associated roles to the mcast groups.
-     *
-     * @param mcastIp the group ip
-     * @param sourcecp the source connect point
-     * @return the mapping mcastIp-device to mcast role
-     */
-    public Map<McastRoleStoreKey, McastRole> getMcastRoles(IpAddress mcastIp,
-                                                       ConnectPoint sourcecp) {
-        log.info("mcastRoles {}", mcastRoleStore.size());
-        if (mcastIp != null) {
-            Map<McastRoleStoreKey, McastRole> roles = mcastRoleStore.entrySet().stream()
-                    .filter(mcastEntry -> mcastIp.equals(mcastEntry.getKey().mcastIp()))
-                    .collect(Collectors.toMap(entry -> new McastRoleStoreKey(entry.getKey().mcastIp(),
-                     entry.getKey().deviceId(), entry.getKey().source()), entry -> entry.getValue().value()));
-            if (sourcecp != null) {
-                roles = roles.entrySet().stream()
-                        .filter(mcastEntry -> sourcecp.equals(mcastEntry.getKey().source()))
-                        .collect(Collectors.toMap(entry -> new McastRoleStoreKey(entry.getKey().mcastIp(),
-                         entry.getKey().deviceId(), entry.getKey().source()), Entry::getValue));
-            }
-            return roles;
-        }
-        return mcastRoleStore.entrySet().stream()
-                .collect(Collectors.toMap(entry -> new McastRoleStoreKey(entry.getKey().mcastIp(),
-                 entry.getKey().deviceId(), entry.getKey().source()), entry -> entry.getValue().value()));
-    }
-
-    /**
-     * Returns the associated trees to the mcast group.
-     *
-     * @param mcastIp the group ip
-     * @param sourcecp the source connect point
-     * @return the mapping egress point to mcast path
-     */
-    public Multimap<ConnectPoint, List<ConnectPoint>> getMcastTrees(IpAddress mcastIp,
-                                                                    ConnectPoint sourcecp) {
-        // TODO remove
-        log.info("{}", getStoredPaths(mcastIp));
-        Multimap<ConnectPoint, List<ConnectPoint>> mcastTrees = HashMultimap.create();
-        Set<ConnectPoint> sources = mcastUtils.getSources(mcastIp);
-        if (sourcecp != null) {
-            sources = sources.stream()
-                    .filter(source -> source.equals(sourcecp)).collect(Collectors.toSet());
-        }
-        if (!sources.isEmpty()) {
-            sources.forEach(source -> {
-                McastPathStoreKey pathStoreKey = new McastPathStoreKey(mcastIp, source);
-                Collection<? extends List<Link>> storedPaths = Versioned.valueOrElse(
-                        mcastPathStore.get(pathStoreKey), Lists.newArrayList());
-                // TODO remove
-                log.info("Paths for group {} and source {} - {}", mcastIp, source, storedPaths.size());
-                Map<ConnectPoint, List<ConnectPoint>> mcastTree = buildMcastPaths(storedPaths, mcastIp, source);
-                mcastTree.forEach(mcastTrees::put);
-            });
-        }
-        return mcastTrees;
-    }
-
-    /**
-     * Return the leaders of the mcast groups.
-     *
-     * @param mcastIp the group ip
-     * @return the mapping group-node
-     */
-    public Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp) {
-        return mcastUtils.getMcastLeaders(mcastIp);
-    }
-
-    /**
-     * Returns the mcast filtering obj.
-     *
-     * @return the mapping group-node
-     */
-    public Map<DeviceId, List<McastFilteringObjStoreKey>> getMcastFilters() {
-        // TODO remove
-        log.info("mcastFilters {}", mcastFilteringObjStore.size());
-        Map<DeviceId, List<McastFilteringObjStoreKey>> mapping = Maps.newHashMap();
-        Set<McastFilteringObjStoreKey> currentKeys = Sets.newHashSet(mcastFilteringObjStore);
-        currentKeys.forEach(filteringObjStoreKey ->
-            mapping.compute(filteringObjStoreKey.ingressCP().deviceId(), (k, v) -> {
-                List<McastFilteringObjStoreKey> values = v;
-                if (values == null) {
-                    values = Lists.newArrayList();
-                }
-                values.add(filteringObjStoreKey);
-                return values;
-            })
-        );
-        return mapping;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastPathStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastPathStoreKey.java
deleted file mode 100644
index 76f46fe..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastPathStoreKey.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.ConnectPoint;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Key of multicast path store.
- */
-public class McastPathStoreKey {
-    // Identify path using group address and source
-    private final IpAddress mcastIp;
-    private final ConnectPoint source;
-
-    /**
-     * Constructs the key of multicast path store.
-     *
-     * @param mcastIp multicast group IP address
-     * @param source source connect point
-     */
-    public McastPathStoreKey(IpAddress mcastIp, ConnectPoint source) {
-        checkNotNull(mcastIp, "mcastIp cannot be null");
-        checkNotNull(source, "source cannot be null");
-        checkArgument(mcastIp.isMulticast(), "mcastIp must be a multicast address");
-        this.mcastIp = mcastIp;
-        this.source = source;
-    }
-
-    // Constructor for serialization
-    private McastPathStoreKey() {
-        this.mcastIp = null;
-        this.source = null;
-    }
-
-    /**
-     * Returns the multicast IP address of this key.
-     *
-     * @return multicast IP
-     */
-    public IpAddress mcastIp() {
-        return mcastIp;
-    }
-
-    /**
-     * Returns the device ID of this key.
-     *
-     * @return device ID
-     */
-    public ConnectPoint source() {
-        return source;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof McastPathStoreKey)) {
-            return false;
-        }
-        McastPathStoreKey that =
-                (McastPathStoreKey) o;
-        return (Objects.equals(this.mcastIp, that.mcastIp) &&
-                Objects.equals(this.source, that.source));
-    }
-
-    @Override
-    public int hashCode() {
-         return Objects.hash(mcastIp, source);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("mcastIp", mcastIp)
-                .add("source", source)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastPathStoreKeySerializer.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastPathStoreKeySerializer.java
deleted file mode 100644
index 4ef0194..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastPathStoreKeySerializer.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.ConnectPoint;
-
-/**
- * Custom serializer for {@link McastPathStoreKey}.
- */
-class McastPathStoreKeySerializer extends Serializer<McastPathStoreKey> {
-
-    /**
-     * Creates {@link McastPathStoreKeySerializer} serializer instance.
-     */
-    McastPathStoreKeySerializer() {
-        // non-null, immutable
-        super(false, true);
-    }
-
-    @Override
-    public void write(Kryo kryo, Output output, McastPathStoreKey object) {
-        kryo.writeClassAndObject(output, object.mcastIp());
-        kryo.writeClassAndObject(output, object.source());
-    }
-
-    @Override
-    public McastPathStoreKey read(Kryo kryo, Input input, Class<McastPathStoreKey> type) {
-        IpAddress mcastIp = (IpAddress) kryo.readClassAndObject(input);
-        ConnectPoint source = (ConnectPoint) kryo.readClassAndObject(input);
-        return new McastPathStoreKey(mcastIp, source);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRole.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRole.java
deleted file mode 100644
index 454bb5c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRole.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-/**
- * Enum that defines role in the multicast tree.
- */
-public enum McastRole {
-    /**
-     * The device is the ingress device of this group.
-     */
-    INGRESS,
-    /**
-     * The device is the transit device of this group.
-     */
-    TRANSIT,
-    /**
-     * The device is the egress device of this group.
-     */
-    EGRESS
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRoleStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRoleStoreKey.java
deleted file mode 100644
index 26f21f2..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRoleStoreKey.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Key of multicast role store.
- */
-public class McastRoleStoreKey {
-    // Identify role using group address, deviceId and source
-    private final IpAddress mcastIp;
-    private final DeviceId deviceId;
-    private final ConnectPoint source;
-
-    /**
-     * Constructs the key of multicast role store.
-     *
-     * @param mcastIp multicast group IP address
-     * @param deviceId device ID
-     * @param source source connect point
-     */
-    public McastRoleStoreKey(IpAddress mcastIp, DeviceId deviceId, ConnectPoint source) {
-        checkNotNull(mcastIp, "mcastIp cannot be null");
-        checkNotNull(deviceId, "deviceId cannot be null");
-        checkNotNull(source, "source cannot be null");
-        checkArgument(mcastIp.isMulticast(), "mcastIp must be a multicast address");
-        this.mcastIp = mcastIp;
-        this.deviceId = deviceId;
-        this.source = source;
-    }
-
-    // Constructor for serialization
-    private McastRoleStoreKey() {
-        this.mcastIp = null;
-        this.deviceId = null;
-        this.source = null;
-    }
-
-    /**
-     * Returns the multicast IP address of this key.
-     *
-     * @return multicast IP
-     */
-    public IpAddress mcastIp() {
-        return mcastIp;
-    }
-
-    /**
-     * Returns the device ID of this key.
-     *
-     * @return device ID
-     */
-    public DeviceId deviceId() {
-        return deviceId;
-    }
-
-    /**
-     * Returns the source connect point of this key.
-     *
-     * @return the source connect point
-     */
-    public ConnectPoint source() {
-        return source;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof McastRoleStoreKey)) {
-            return false;
-        }
-        final McastRoleStoreKey that = (McastRoleStoreKey) o;
-
-        return Objects.equals(this.mcastIp, that.mcastIp) &&
-                Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.source, that.source);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mcastIp, deviceId, source);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("mcastIp", mcastIp)
-                .add("deviceId", deviceId)
-                .add("source", source)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRoleStoreKeySerializer.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRoleStoreKeySerializer.java
deleted file mode 100644
index aec7278..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastRoleStoreKeySerializer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-
-/**
- * Custom serializer for {@link McastRoleStoreKey}.
- */
-class McastRoleStoreKeySerializer extends Serializer<McastRoleStoreKey> {
-
-    /**
-     * Creates {@link McastRoleStoreKeySerializer} serializer instance.
-     */
-    McastRoleStoreKeySerializer() {
-        // non-null, immutable
-        super(false, true);
-    }
-
-    @Override
-    public void write(Kryo kryo, Output output, McastRoleStoreKey object) {
-        kryo.writeClassAndObject(output, object.mcastIp());
-        output.writeString(object.deviceId().toString());
-        kryo.writeClassAndObject(output, object.source());
-    }
-
-    @Override
-    public McastRoleStoreKey read(Kryo kryo, Input input, Class<McastRoleStoreKey> type) {
-        IpAddress mcastIp = (IpAddress) kryo.readClassAndObject(input);
-        final String str = input.readString();
-        DeviceId deviceId =  DeviceId.deviceId(str);
-        ConnectPoint source = (ConnectPoint) kryo.readClassAndObject(input);
-        return new McastRoleStoreKey(mcastIp, deviceId, source);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastStoreKey.java
deleted file mode 100644
index aa32797..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastStoreKey.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-import static com.google.common.base.MoreObjects.toStringHelper;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import java.util.Objects;
-
-/**
- * Key of multicast next objective store.
- */
-public class McastStoreKey {
-    // Identify a flow using group address, deviceId, and assigned vlan
-    private final IpAddress mcastIp;
-    private final DeviceId deviceId;
-    private final VlanId vlanId;
-
-    /**
-     * Constructs the key of multicast next objective store.
-     *
-     * @param mcastIp multicast group IP address
-     * @param deviceId device ID
-     *
-     * @deprecated in 1.12 ("Magpie") release.
-     */
-    @Deprecated
-    public McastStoreKey(IpAddress mcastIp, DeviceId deviceId) {
-        checkNotNull(mcastIp, "mcastIp cannot be null");
-        checkNotNull(deviceId, "deviceId cannot be null");
-        checkArgument(mcastIp.isMulticast(), "mcastIp must be a multicast address");
-        this.mcastIp = mcastIp;
-        this.deviceId = deviceId;
-        this.vlanId = null;
-    }
-
-    /**
-     * Constructs the key of multicast next objective store.
-     *
-     * @param mcastIp multicast group IP address
-     * @param deviceId device ID
-     * @param vlanId vlan id
-     */
-    public McastStoreKey(IpAddress mcastIp, DeviceId deviceId, VlanId vlanId) {
-        checkNotNull(mcastIp, "mcastIp cannot be null");
-        checkNotNull(deviceId, "deviceId cannot be null");
-        checkNotNull(vlanId, "vlan id cannot be null");
-        checkArgument(mcastIp.isMulticast(), "mcastIp must be a multicast address");
-        this.mcastIp = mcastIp;
-        this.deviceId = deviceId;
-        // FIXME probably we should avoid not valid values
-        this.vlanId = vlanId;
-    }
-
-    // Constructor for serialization
-    private McastStoreKey() {
-        this.mcastIp = null;
-        this.deviceId = null;
-        this.vlanId = null;
-    }
-
-    /**
-     * Returns the multicast IP address of this key.
-     *
-     * @return multicast IP
-     */
-    public IpAddress mcastIp() {
-        return mcastIp;
-    }
-
-    /**
-     * Returns the device ID of this key.
-     *
-     * @return device ID
-     */
-    public DeviceId deviceId() {
-        return deviceId;
-    }
-
-    /**
-     * Returns the vlan ID of this key.
-     *
-     * @return vlan ID
-     */
-    public VlanId vlanId() {
-        return vlanId;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof McastStoreKey)) {
-            return false;
-        }
-        McastStoreKey that =
-                (McastStoreKey) o;
-        return (Objects.equals(this.mcastIp, that.mcastIp) &&
-                Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.vlanId, that.vlanId));
-    }
-
-    @Override
-    public int hashCode() {
-         return Objects.hash(mcastIp, deviceId, vlanId);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("mcastIp", mcastIp)
-                .add("deviceId", deviceId)
-                .add("vlanId", vlanId)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastStoreKeySerializer.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastStoreKeySerializer.java
deleted file mode 100644
index c32b0ba..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastStoreKeySerializer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-
-/**
- * Custom serializer for {@link McastStoreKey}.
- */
-class McastStoreKeySerializer extends Serializer<McastStoreKey> {
-
-    /**
-     * Creates {@link McastStoreKeySerializer} serializer instance.
-     */
-    McastStoreKeySerializer() {
-        // non-null, immutable
-        super(false, true);
-    }
-
-    @Override
-    public void write(Kryo kryo, Output output, McastStoreKey object) {
-        kryo.writeClassAndObject(output, object.mcastIp());
-        output.writeString(object.deviceId().toString());
-        kryo.writeClassAndObject(output, object.vlanId());
-    }
-
-    @Override
-    public McastStoreKey read(Kryo kryo, Input input, Class<McastStoreKey> type) {
-        IpAddress mcastIp = (IpAddress) kryo.readClassAndObject(input);
-        final String str = input.readString();
-        DeviceId deviceId =  DeviceId.deviceId(str);
-        VlanId vlanId = (VlanId) kryo.readClassAndObject(input);
-        return new McastStoreKey(mcastIp, deviceId, vlanId);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
deleted file mode 100644
index 396b998..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
+++ /dev/null
@@ -1,715 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.mcast;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import com.google.common.hash.HashFunction;
-import com.google.common.hash.Hashing;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.mcast.api.McastRoute;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.HostId;
-import org.onosproject.net.Link;
-import org.onosproject.net.Path;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.config.basics.McastConfig;
-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.flow.criteria.Criteria;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flowobjective.DefaultFilteringObjective;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.DefaultNextObjective;
-import org.onosproject.net.flowobjective.DefaultObjectiveContext;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.topology.LinkWeigher;
-import org.onosproject.net.topology.Topology;
-import org.onosproject.net.topology.TopologyService;
-import org.onosproject.segmentrouting.SRLinkWeigher;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
-import org.slf4j.Logger;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Utility class for Multicast Handler.
- */
-class McastUtils {
-    // Internal reference to the log
-    private final Logger log;
-    // Internal reference to SR Manager and topology service
-    private final SegmentRoutingManager srManager;
-    private final TopologyService topologyService;
-    // Internal reference to the app id
-    private ApplicationId coreAppId;
-    // Hashing function for the multicast hasher
-    private static final HashFunction HASH_FN = Hashing.md5();
-    // Read only cache of the Mcast leader
-    private Map<IpAddress, NodeId> mcastLeaderCache;
-
-    /**
-     * Builds a new McastUtils object.
-     *
-     * @param srManager the SR manager
-     * @param coreAppId the core application id
-     * @param log log reference of the McastHandler
-     */
-    McastUtils(SegmentRoutingManager srManager, ApplicationId coreAppId, Logger log) {
-        this.srManager = srManager;
-        this.topologyService = srManager.topologyService;
-        this.coreAppId = coreAppId;
-        this.log = log;
-        this.mcastLeaderCache = Maps.newConcurrentMap();
-    }
-
-    /**
-     * Clean up when deactivating the application.
-     */
-    void terminate() {
-        mcastLeaderCache.clear();
-    }
-
-    /**
-     * Get router mac using application config and the connect point.
-     *
-     * @param deviceId the device id
-     * @param port the port number
-     * @return the router mac if the port is configured, otherwise null
-     */
-    private MacAddress getRouterMac(DeviceId deviceId, PortNumber port) {
-        // Do nothing if the port is configured as suppressed
-        ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
-        SegmentRoutingAppConfig appConfig = srManager.cfgService
-                .getConfig(srManager.appId(), SegmentRoutingAppConfig.class);
-        if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
-            log.info("Ignore suppressed port {}", connectPoint);
-            return MacAddress.NONE;
-        }
-        // Get the router mac using the device configuration
-        MacAddress routerMac;
-        try {
-            routerMac = srManager.deviceConfiguration().getDeviceMac(deviceId);
-        } catch (DeviceConfigNotFoundException dcnfe) {
-            log.warn("Failed to get device MAC since the device {} is not configured", deviceId);
-            return null;
-        }
-        return routerMac;
-    }
-
-    /**
-     * Adds filtering objective for given device and port.
-     *
-     * @param deviceId device ID
-     * @param port ingress port number
-     * @param assignedVlan assigned VLAN ID
-     * @param mcastIp the group address
-     * @param mcastRole the role of the device
-     * @param matchOnMac match or not on macaddress
-     */
-    void addFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
-                           IpAddress mcastIp, McastRole mcastRole, boolean matchOnMac) {
-        if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-            log.debug("skip update of fitering objective for unconfigured device: {}", deviceId);
-            return;
-        }
-        MacAddress routerMac = getRouterMac(deviceId, port);
-
-        if (MacAddress.NONE.equals(routerMac)) {
-            return;
-        }
-        FilteringObjective.Builder filtObjBuilder = filterObjBuilder(port, assignedVlan, mcastIp,
-                                                                     routerMac, mcastRole, matchOnMac);
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Successfully add filter on {}/{}, vlan {}",
-                                         deviceId, port.toLong(), assignedVlan),
-                (objective, error) ->
-                        log.warn("Failed to add filter on {}/{}, vlan {}: {}",
-                                 deviceId, port.toLong(), assignedVlan, error));
-        srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.add(context));
-    }
-
-    /**
-     * Removes filtering objective for given device and port.
-     *
-     * @param deviceId device ID
-     * @param port ingress port number
-     * @param assignedVlan assigned VLAN ID
-     * @param mcastIp multicast IP address
-     * @param mcastRole the multicast role of the device
-     */
-    void removeFilterToDevice(DeviceId deviceId, PortNumber port, VlanId assignedVlan,
-                              IpAddress mcastIp, McastRole mcastRole) {
-        if (!srManager.deviceConfiguration().isConfigured(deviceId)) {
-            log.debug("skip update of fitering objective for unconfigured device: {}", deviceId);
-            return;
-        }
-        MacAddress routerMac = getRouterMac(deviceId, port);
-
-        if (MacAddress.NONE.equals(routerMac)) {
-            return;
-        }
-        FilteringObjective.Builder filtObjBuilder =
-                filterObjBuilder(port, assignedVlan, mcastIp, routerMac, mcastRole, false);
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Successfully removed filter on {}/{}, vlan {}",
-                                         deviceId, port.toLong(), assignedVlan),
-                (objective, error) ->
-                        log.warn("Failed to remove filter on {}/{}, vlan {}: {}",
-                                 deviceId, port.toLong(), assignedVlan, error));
-        srManager.flowObjectiveService.filter(deviceId, filtObjBuilder.remove(context));
-    }
-
-    /**
-     * Gets ingress VLAN from McastConfig.
-     *
-     * @return ingress VLAN or VlanId.NONE if not configured
-     */
-    private VlanId ingressVlan() {
-        McastConfig mcastConfig =
-                srManager.cfgService.getConfig(coreAppId, McastConfig.class);
-        return (mcastConfig != null) ? mcastConfig.ingressVlan() : VlanId.NONE;
-    }
-
-    /**
-     * Gets egress VLAN from McastConfig.
-     *
-     * @return egress VLAN or VlanId.NONE if not configured
-     */
-    private VlanId egressVlan() {
-        McastConfig mcastConfig =
-                srManager.cfgService.getConfig(coreAppId, McastConfig.class);
-        return (mcastConfig != null) ? mcastConfig.egressVlan() : VlanId.NONE;
-    }
-
-    /**
-     * Gets assigned VLAN according to the value of egress VLAN.
-     * If connect point is specified, try to reuse the assigned VLAN on the connect point.
-     *
-     * @param cp connect point; Can be null if not specified
-     * @return assigned VLAN ID
-     */
-    VlanId assignedVlan(ConnectPoint cp) {
-        // Use the egressVlan if it is tagged
-        if (!egressVlan().equals(VlanId.NONE)) {
-            return egressVlan();
-        }
-        // Reuse unicast VLAN if the port has subnet configured
-        if (cp != null) {
-            VlanId untaggedVlan = srManager.getInternalVlanId(cp);
-            return (untaggedVlan != null) ? untaggedVlan
-                                          : srManager.getDefaultInternalVlan();
-        }
-        // Use DEFAULT_VLAN if none of the above matches
-        return srManager.getDefaultInternalVlan();
-    }
-
-
-
-    /**
-     * Gets sources connect points of given multicast group.
-     *
-     * @param mcastIp multicast IP
-     * @return sources connect points or empty set if not found
-     */
-    Set<ConnectPoint> getSources(IpAddress mcastIp) {
-        // TODO we should support different types of routes
-        McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
-                .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
-                .findFirst().orElse(null);
-        return mcastRoute == null ? ImmutableSet.of() :
-                srManager.multicastRouteService.sources(mcastRoute);
-    }
-
-    /**
-     * Gets sinks of given multicast group.
-     *
-     * @param mcastIp multicast IP
-     * @return map of sinks or empty map if not found
-     */
-    Map<HostId, Set<ConnectPoint>> getSinks(IpAddress mcastIp) {
-        // TODO we should support different types of routes
-        McastRoute mcastRoute = srManager.multicastRouteService.getRoutes().stream()
-                .filter(mcastRouteInternal -> mcastRouteInternal.group().equals(mcastIp))
-                .findFirst().orElse(null);
-        return mcastRoute == null ?
-                ImmutableMap.of() :
-                srManager.multicastRouteService.routeData(mcastRoute).sinks();
-    }
-
-    /**
-     * Get sinks affected by this egress device.
-     *
-     * @param egressDevice the egress device
-     * @param mcastIp the mcast ip address
-     * @return the map of the sinks affected
-     */
-    Map<HostId, Set<ConnectPoint>> getAffectedSinks(DeviceId egressDevice,
-                                                    IpAddress mcastIp) {
-        return getSinks(mcastIp).entrySet()
-                .stream()
-                .filter(hostIdSetEntry -> hostIdSetEntry.getValue().stream()
-                        .map(ConnectPoint::deviceId)
-                        .anyMatch(deviceId -> deviceId.equals(egressDevice))
-                ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
-    }
-
-    /**
-     * Creates a next objective builder for multicast.
-     *
-     * @param mcastIp multicast group
-     * @param assignedVlan assigned VLAN ID
-     * @param outPorts set of output port numbers
-     * @param nextId the next id
-     * @return next objective builder
-     */
-    NextObjective.Builder nextObjBuilder(IpAddress mcastIp, VlanId assignedVlan,
-                                         Set<PortNumber> outPorts, Integer nextId) {
-        // If nextId is null allocate a new one
-        if (nextId == null) {
-            nextId = srManager.flowObjectiveService.allocateNextId();
-        }
-        // Build the meta selector with the fwd objective info
-        TrafficSelector metadata =
-                DefaultTrafficSelector.builder()
-                        .matchVlanId(assignedVlan)
-                        .matchIPDst(mcastIp.toIpPrefix())
-                        .build();
-        // Define the nextobjective type
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.BROADCAST)
-                .fromApp(srManager.appId())
-                .withMeta(metadata);
-        // Add the output ports
-        outPorts.forEach(port -> {
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-            if (egressVlan().equals(VlanId.NONE)) {
-                tBuilder.popVlan();
-            }
-            tBuilder.setOutput(port);
-            nextObjBuilder.addTreatment(tBuilder.build());
-        });
-        // Done return the complete builder
-        return nextObjBuilder;
-    }
-
-    /**
-     * Creates a forwarding objective builder for multicast.
-     *
-     * @param mcastIp multicast group
-     * @param assignedVlan assigned VLAN ID
-     * @param nextId next ID of the L3 multicast group
-     * @return forwarding objective builder
-     */
-    ForwardingObjective.Builder fwdObjBuilder(IpAddress mcastIp,
-                                                      VlanId assignedVlan, int nextId) {
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        // Let's the matching on the group address
-        // TODO SSM support in future
-        if (mcastIp.isIp6()) {
-            sbuilder.matchEthType(Ethernet.TYPE_IPV6);
-            sbuilder.matchIPv6Dst(mcastIp.toIpPrefix());
-        } else {
-            sbuilder.matchEthType(Ethernet.TYPE_IPV4);
-            sbuilder.matchIPDst(mcastIp.toIpPrefix());
-        }
-        // Then build the meta selector
-        TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
-        metabuilder.matchVlanId(assignedVlan);
-        // Finally return the completed builder
-        ForwardingObjective.Builder fwdBuilder = DefaultForwardingObjective.builder();
-        fwdBuilder.withSelector(sbuilder.build())
-                .withMeta(metabuilder.build())
-                .nextStep(nextId)
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .fromApp(srManager.appId())
-                .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
-        return fwdBuilder;
-    }
-
-    /**
-     * Creates a filtering objective builder for multicast.
-     *
-     * @param ingressPort ingress port of the multicast stream
-     * @param assignedVlan assigned VLAN ID
-     * @param mcastIp the group address
-     * @param routerMac router MAC. This is carried in metadata and used from some switches that
-     *                  need to put unicast entry before multicast entry in TMAC table.
-     * @param mcastRole the Multicast role
-     * @param matchOnMac match or not on macaddress
-     * @return filtering objective builder
-     */
-    private FilteringObjective.Builder filterObjBuilder(PortNumber ingressPort, VlanId assignedVlan,
-                                                        IpAddress mcastIp, MacAddress routerMac, McastRole mcastRole,
-                                                        boolean matchOnMac) {
-        FilteringObjective.Builder filtBuilder = DefaultFilteringObjective.builder();
-        // Let's add the in port matching and the priority
-        filtBuilder.withKey(Criteria.matchInPort(ingressPort))
-                .withPriority(SegmentRoutingService.DEFAULT_PRIORITY);
-        // According to the mcast role we match on the proper vlan
-        // If the role is null we are on the transit or on the egress
-        if (mcastRole == null) {
-            filtBuilder.addCondition(Criteria.matchVlanId(egressVlan()));
-        } else {
-            filtBuilder.addCondition(Criteria.matchVlanId(ingressVlan()));
-        }
-        // Add vlan info to the treatment builder
-        TrafficTreatment.Builder ttb = DefaultTrafficTreatment.builder()
-                .pushVlan().setVlanId(assignedVlan);
-        // Additionally match on mac address and augment the treatment
-        if (matchOnMac) {
-            // According to the IP type we set the proper match on the mac address
-            if (mcastIp.isIp4()) {
-                filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV4_MULTICAST,
-                        MacAddress.IPV4_MULTICAST_MASK));
-            } else {
-                filtBuilder.addCondition(Criteria.matchEthDstMasked(MacAddress.IPV6_MULTICAST,
-                        MacAddress.IPV6_MULTICAST_MASK));
-            }
-            // We set mac address to the treatment
-            if (routerMac != null && !routerMac.equals(MacAddress.NONE)) {
-                ttb.setEthDst(routerMac);
-            }
-        }
-        // We finally build the meta treatment
-        TrafficTreatment tt = ttb.build();
-        filtBuilder.withMeta(tt);
-        // Done, we return a permit filtering objective
-        return filtBuilder.permit().fromApp(srManager.appId());
-    }
-
-    /**
-     * Gets output ports information from treatments.
-     *
-     * @param treatments collection of traffic treatments
-     * @return set of output port numbers
-     */
-    Set<PortNumber> getPorts(Collection<TrafficTreatment> treatments) {
-        ImmutableSet.Builder<PortNumber> builder = ImmutableSet.builder();
-        treatments.forEach(treatment -> treatment.allInstructions().stream()
-                    .filter(instr -> instr instanceof Instructions.OutputInstruction)
-                    .forEach(instr -> builder.add(((Instructions.OutputInstruction) instr).port())));
-        return builder.build();
-    }
-
-    /**
-     * Returns the hash of the group address.
-     *
-     * @param ipAddress the ip address
-     * @return the hash of the address
-     */
-    private Long hasher(IpAddress ipAddress) {
-        return HASH_FN.newHasher()
-                .putBytes(ipAddress.toOctets())
-                .hash()
-                .asLong();
-    }
-
-    /**
-     * Given a multicast group define a leader for it.
-     *
-     * @param mcastIp the group address
-     * @return true if the instance is the leader of the group
-     */
-    boolean isLeader(IpAddress mcastIp) {
-        // Get our id
-        final NodeId currentNodeId = srManager.clusterService.getLocalNode().id();
-        // Get the leader for this group using the ip address as key
-        final NodeId leader = srManager.workPartitionService.getLeader(mcastIp, this::hasher);
-        // If there is not a leader, let's send an error
-        if (leader == null) {
-            log.error("Fail to elect a leader for {}.", mcastIp);
-            return false;
-        }
-        // Update cache and return operation result
-        mcastLeaderCache.put(mcastIp, leader);
-        return currentNodeId.equals(leader);
-    }
-
-    /**
-     * Given a multicast group withdraw its leader.
-     *
-     * @param mcastIp the group address
-     */
-    void withdrawLeader(IpAddress mcastIp) {
-        // For now just update the cache
-        mcastLeaderCache.remove(mcastIp);
-    }
-
-    Map<IpAddress, NodeId> getMcastLeaders(IpAddress mcastIp) {
-        // If mcast ip is present
-        if (mcastIp != null) {
-            return mcastLeaderCache.entrySet().stream()
-                    .filter(entry -> entry.getKey().equals(mcastIp))
-                    .collect(Collectors.toMap(Map.Entry::getKey,
-                                              Map.Entry::getValue));
-        }
-        // Otherwise take all the groups
-        return ImmutableMap.copyOf(mcastLeaderCache);
-    }
-
-    /**
-     * Go through all the paths, looking for shared links to be used
-     * in the final path computation.
-     *
-     * @param egresses egress devices
-     * @param availablePaths all the available paths towards the egress
-     * @return shared links between egress devices
-     */
-    private Set<Link> exploreMcastTree(Set<DeviceId> egresses,
-                                       Map<DeviceId, List<Path>> availablePaths) {
-        int minLength = Integer.MAX_VALUE;
-        int length;
-        List<Path> currentPaths;
-        // Verify the source can still reach all the egresses
-        for (DeviceId egress : egresses) {
-            // From the source we cannot reach all the sinks
-            // just continue and let's figure out after
-            currentPaths = availablePaths.get(egress);
-            if (currentPaths.isEmpty()) {
-                continue;
-            }
-            // Get the length of the first one available, update the min length
-            length = currentPaths.get(0).links().size();
-            if (length < minLength) {
-                minLength = length;
-            }
-        }
-        // If there are no paths
-        if (minLength == Integer.MAX_VALUE) {
-            return Collections.emptySet();
-        }
-        int index = 0;
-        Set<Link> sharedLinks = Sets.newHashSet();
-        Set<Link> currentSharedLinks;
-        Set<Link> currentLinks;
-        DeviceId egressToRemove = null;
-        // Let's find out the shared links
-        while (index < minLength) {
-            // Initialize the intersection with the paths related to the first egress
-            currentPaths = availablePaths.get(egresses.stream().findFirst().orElse(null));
-            currentSharedLinks = Sets.newHashSet();
-            // Iterate over the paths and take the "index" links
-            for (Path path : currentPaths) {
-                currentSharedLinks.add(path.links().get(index));
-            }
-            // Iterate over the remaining egress
-            for (DeviceId egress : egresses) {
-                // Iterate over the paths and take the "index" links
-                currentLinks = Sets.newHashSet();
-                for (Path path : availablePaths.get(egress)) {
-                    currentLinks.add(path.links().get(index));
-                }
-                // Do intersection
-                currentSharedLinks = Sets.intersection(currentSharedLinks, currentLinks);
-                // If there are no shared paths exit and record the device to remove
-                // we have to retry with a subset of sinks
-                if (currentSharedLinks.isEmpty()) {
-                    egressToRemove = egress;
-                    index = minLength;
-                    break;
-                }
-            }
-            sharedLinks.addAll(currentSharedLinks);
-            index++;
-        }
-        // If the shared links is empty and there are egress let's retry another time with less sinks,
-        // we can still build optimal subtrees
-        if (sharedLinks.isEmpty() && egresses.size() > 1 && egressToRemove != null) {
-            egresses.remove(egressToRemove);
-            sharedLinks = exploreMcastTree(egresses, availablePaths);
-        }
-        return sharedLinks;
-    }
-
-    /**
-     * Build Mcast tree having as root the given source and as leaves the given egress points.
-     *
-     * @param mcastIp multicast group
-     * @param source source of the tree
-     * @param sinks leaves of the tree
-     * @return the computed Mcast tree
-     */
-    Map<ConnectPoint, List<Path>> computeSinkMcastTree(IpAddress mcastIp,
-                                                       DeviceId source,
-                                                       Set<ConnectPoint> sinks) {
-        // Get the egress devices, remove source from the egress if present
-        Set<DeviceId> egresses = sinks.stream().map(ConnectPoint::deviceId)
-                .filter(deviceId -> !deviceId.equals(source)).collect(Collectors.toSet());
-        Map<DeviceId, List<Path>> mcastTree = computeMcastTree(mcastIp, source, egresses);
-        final Map<ConnectPoint, List<Path>> finalTree = Maps.newHashMap();
-        // We need to put back the source if it was originally present
-        sinks.forEach(sink -> {
-            List<Path> sinkPaths = mcastTree.get(sink.deviceId());
-            finalTree.put(sink, sinkPaths != null ? sinkPaths : ImmutableList.of());
-        });
-        return finalTree;
-    }
-
-    /**
-     * Build Mcast tree having as root the given source and as leaves the given egress.
-     *
-     * @param mcastIp multicast group
-     * @param source source of the tree
-     * @param egresses leaves of the tree
-     * @return the computed Mcast tree
-     */
-    private Map<DeviceId, List<Path>> computeMcastTree(IpAddress mcastIp,
-                                                       DeviceId source,
-                                                       Set<DeviceId> egresses) {
-        log.debug("Computing tree for Multicast group {}, source {} and leafs {}",
-                  mcastIp, source, egresses);
-        // Pre-compute all the paths
-        Map<DeviceId, List<Path>> availablePaths = Maps.newHashMap();
-        egresses.forEach(egress -> availablePaths.put(egress, getPaths(source, egress,
-                                                                       Collections.emptySet())));
-        // Explore the topology looking for shared links amongst the egresses
-        Set<Link> linksToEnforce = exploreMcastTree(Sets.newHashSet(egresses), availablePaths);
-        // Build the final paths enforcing the shared links between egress devices
-        availablePaths.clear();
-        egresses.forEach(egress -> availablePaths.put(egress, getPaths(source, egress,
-                                                                       linksToEnforce)));
-        return availablePaths;
-    }
-
-    /**
-     * Gets path from src to dst computed using the custom link weigher.
-     *
-     * @param src source device ID
-     * @param dst destination device ID
-     * @param linksToEnforce links to be enforced
-     * @return list of paths from src to dst
-     */
-    List<Path> getPaths(DeviceId src, DeviceId dst, Set<Link> linksToEnforce) {
-        final Topology currentTopology = topologyService.currentTopology();
-        final LinkWeigher linkWeigher = new SRLinkWeigher(srManager, src, linksToEnforce);
-        List<Path> allPaths = Lists.newArrayList(topologyService.getPaths(currentTopology, src, dst, linkWeigher));
-        log.trace("{} path(s) found from {} to {}", allPaths.size(), src, dst);
-        return allPaths;
-    }
-
-    /**
-     * Gets a stored path having dst as egress.
-     *
-     * @param dst destination device ID
-     * @param storedPaths paths list
-     * @return an optional path
-     */
-    Optional<? extends List<Link>> getStoredPath(DeviceId dst, Collection<? extends List<Link>> storedPaths) {
-        return storedPaths.stream()
-                .filter(path -> path.get(path.size() - 1).dst().deviceId().equals(dst))
-                .findFirst();
-    }
-
-    /**
-     * Returns a set of affected paths by the failed element.
-     *
-     * @param paths the paths to check
-     * @param failedElement the failed element
-     * @return the affected paths
-     */
-    Set<List<Link>> getAffectedPaths(Set<List<Link>> paths, Object failedElement) {
-        if (failedElement instanceof DeviceId) {
-            return getAffectedPathsByDevice(paths, failedElement);
-        }
-        return getAffectedPathsByLink(paths, failedElement);
-    }
-
-    private Set<List<Link>> getAffectedPathsByDevice(Set<List<Link>> paths, Object failedElement) {
-        DeviceId affectedDevice = (DeviceId) failedElement;
-        Set<List<Link>> affectedPaths = Sets.newHashSet();
-        paths.forEach(path -> {
-            if (path.stream().anyMatch(link -> link.src().deviceId().equals(affectedDevice))) {
-                affectedPaths.add(path);
-            }
-        });
-        return affectedPaths;
-    }
-
-    private Set<List<Link>> getAffectedPathsByLink(Set<List<Link>> paths, Object failedElement) {
-        Link affectedLink = (Link) failedElement;
-        Set<List<Link>> affectedPaths = Sets.newHashSet();
-        paths.forEach(path -> {
-            if (path.contains(affectedLink)) {
-                affectedPaths.add(path);
-            }
-        });
-        return affectedPaths;
-    }
-
-    /**
-     * Checks if the failure is affecting the transit device.
-     *
-     * @param devices the transit devices
-     * @param failedElement the failed element
-     * @return true if the failed element is affecting the transit devices
-     */
-    boolean isInfraFailure(Set<DeviceId> devices, Object failedElement) {
-        if (failedElement instanceof DeviceId) {
-            return isInfraFailureByDevice(devices, failedElement);
-        }
-        return true;
-    }
-
-    private boolean isInfraFailureByDevice(Set<DeviceId> devices, Object failedElement) {
-        DeviceId affectedDevice = (DeviceId) failedElement;
-        return devices.contains(affectedDevice);
-    }
-
-    /**
-     * Checks if a port is an infra port.
-     *
-     * @param connectPoint port to be checked
-     * @param storedPaths paths to be checked against
-     * @return true if the port is an infra port. False otherwise.
-     */
-    boolean isInfraPort(ConnectPoint connectPoint, Collection<? extends List<Link>> storedPaths) {
-        for (List<Link> path : storedPaths) {
-            if (path.stream().anyMatch(link -> link.src().equals(connectPoint) ||
-                    link.dst().equals(connectPoint))) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/mcast/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/mcast/package-info.java
deleted file mode 100644
index dfdd39a..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/mcast/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Trellis Multicast service.
- */
-package org.onosproject.segmentrouting.mcast;
diff --git a/app/src/main/java/org/onosproject/segmentrouting/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/package-info.java
deleted file mode 100644
index 137ba3c..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Segment routing application components.
- */
-package org.onosproject.segmentrouting;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/OsgiPropertyConstants.java b/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/OsgiPropertyConstants.java
deleted file mode 100644
index cdeb8a2..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/OsgiPropertyConstants.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.phasedrecovery.api;
-
-/**
- * Constants for default values of configurable properties.
- */
-public final class OsgiPropertyConstants {
-    private OsgiPropertyConstants() {}
-
-    public static final String PROP_PHASED_RECOVERY = "phasedRecovery";
-    public static final boolean PHASED_RECOVERY_DEFAULT = false;
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/Phase.java b/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/Phase.java
deleted file mode 100644
index 7f18685..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/Phase.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.phasedrecovery.api;
-
-/**
- * Phases of recovering devices.
- */
-public enum Phase {
-
-    /**
-     * Device is waiting for ports to be reported.
-     */
-    PENDING,
-
-    /**
-     * Pair port enabled. This is the initial state for paired devices.
-     */
-    PAIR,
-
-    /**
-     * Infrastructure ports enabled.
-     */
-    INFRA,
-
-    /**
-     * Edge ports enabled. This is the initial state for non-paired and spine devices.
-     */
-    EDGE
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/PhasedRecoveryService.java b/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/PhasedRecoveryService.java
deleted file mode 100644
index 705ecb7..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/PhasedRecoveryService.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.phasedrecovery.api;
-
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Service that provides functionality related to phased recovery.
- */
-public interface PhasedRecoveryService {
-
-    // TODO Make timeout values configurable via Component Config Service
-    /**
-     * Timeout for PAIR phase in seconds.
-     */
-    int PAIR_TIMEOUT = 30;
-
-    /**
-     * Timeout for INFRA phase in seconds.
-     */
-    int INFRA_TIMEOUT = 30;
-
-    /**
-     * Timeout for EDGE phase in seconds.
-     */
-    int EDGE_TIMEOUT = 30;
-
-    //
-    // Phased recovery APIs.
-    //
-
-
-    /**
-     * Returns true if phased recovery is enabled.
-     *
-     * @return true if phased recovery is enabled.
-     */
-    boolean isEnabled();
-
-    /**
-     * Initializes a device. Only the master of the device is allowed to do this.
-     *
-     * @param deviceId device ID
-     * @return true if the device is initialized successfully and the caller should proceed,
-     *         false if the device initialization has failed and the caller should abort.
-     */
-    boolean init(DeviceId deviceId);
-
-    /**
-     * Resets a device. Only the master of the device is allowed to do this.
-     *
-     * @param deviceId device ID
-     * @return true if the device is reset successfully.
-     *         false if the device has not been previously initialized.
-     */
-    boolean reset(DeviceId deviceId);
-
-    /**
-     * Gets recovery phase of every devices.
-     *
-     * @return a map between device ID and recovery phase
-     */
-    Map<DeviceId, Phase> getPhases();
-
-    /**
-     * Gets recovery phase of given device.
-     *
-     * @param deviceId device ID
-     * @return current phase or null if the device wasn't seen before
-     */
-    Phase getPhase(DeviceId deviceId);
-
-    /**
-     * Sets given device with given recovery phase. Only the master of the device is allowed to do this.
-     *
-     * @param deviceId device ID
-     * @param newPhase recovery phase
-     * @return new phase if transition succeeded, otherwise return current phase.
-     */
-    Phase setPhase(DeviceId deviceId, Phase newPhase);
-
-    //
-    // Port manipulation APIs.
-    //
-
-    /**
-     * Enables every ports on the given device.
-     *
-     * @param deviceId device id
-     * @param enabled true to enable, false to disable
-     * @return ports that have been enabled
-     */
-    Set<PortNumber> changeAllPorts(DeviceId deviceId, boolean enabled);
-
-    /**
-     * Enables pair port on the given device.
-     *
-     * @param deviceId device id
-     * @param enabled true to enable, false to disable
-     * @return ports that have been enabled
-     */
-    Set<PortNumber> changePairPort(DeviceId deviceId, boolean enabled);
-
-    /**
-     * Enables infrastructure ports on the given device.
-     *
-     * @param deviceId device id
-     * @param enabled true to enable, false to disable
-     * @return ports that have been enabled
-     */
-    Set<PortNumber> changeInfraPorts(DeviceId deviceId, boolean enabled);
-
-    /**
-     * Enables edge ports on the given device.
-     *
-     * @param deviceId device id
-     * @param enabled true to enable, false to disable
-     * @return ports that have been enabled
-     */
-    Set<PortNumber> changeEdgePorts(DeviceId deviceId, boolean enabled);
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/package-info.java
deleted file mode 100644
index cc556be..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/api/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Phased recovery API.
- */
-package org.onosproject.segmentrouting.phasedrecovery.api;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/impl/PhasedRecoveryManager.java b/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/impl/PhasedRecoveryManager.java
deleted file mode 100644
index 5e25015..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/impl/PhasedRecoveryManager.java
+++ /dev/null
@@ -1,383 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.segmentrouting.phasedrecovery.impl;
-
-import com.google.common.collect.Sets;
-import org.onlab.util.KryoNamespace;
-import org.onlab.util.Tools;
-import org.onosproject.cfg.ComponentConfigService;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceAdminService;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.phasedrecovery.api.Phase;
-import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.Serializer;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.Versioned;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Modified;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Dictionary;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.segmentrouting.phasedrecovery.api.OsgiPropertyConstants.PHASED_RECOVERY_DEFAULT;
-import static org.onosproject.segmentrouting.phasedrecovery.api.OsgiPropertyConstants.PROP_PHASED_RECOVERY;
-
-@Component(
-        immediate = true,
-        service = PhasedRecoveryService.class,
-        property = {
-                PROP_PHASED_RECOVERY + ":Boolean=" + PHASED_RECOVERY_DEFAULT
-        }
-)
-public class PhasedRecoveryManager implements PhasedRecoveryService {
-    private static final Logger log = LoggerFactory.getLogger(PhasedRecoveryManager.class);
-    private static final String APP_NAME = "org.onosproject.phasedrecovery";
-
-    // TODO Make these configurable via Component Config
-    // Amount of time delayed to wait for port description (in second)
-    private static final int PORT_CHECKER_INTERVAL = 1;
-    // Max number of retry for port checker
-    private static final int PORT_CHECKER_RETRIES = 5;
-    // RoutingStableChecker interval (in second)
-    private static final int ROUTING_CHECKER_DELAY = 3;
-    // RoutingStableChecker timeout (in second)
-    private static final int ROUTING_CHECKER_TIMEOUT = 15;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private ComponentConfigService compCfgService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private DeviceAdminService deviceAdminService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private StorageService storageService;
-
-    @Reference(cardinality = ReferenceCardinality.OPTIONAL)
-    volatile SegmentRoutingService srService;
-
-    /** Enabling phased recovery. */
-    boolean phasedRecovery = PHASED_RECOVERY_DEFAULT;
-
-    private ApplicationId appId;
-    private ConsistentMap<DeviceId, Phase> phasedRecoveryStore;
-    private ScheduledExecutorService executor = Executors.newScheduledThreadPool(
-            Runtime.getRuntime().availableProcessors(), groupedThreads("onos/sr/pr", "executor"));
-
-    @Activate
-    protected void activate(ComponentContext context) {
-        appId = coreService.registerApplication(APP_NAME);
-
-        KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
-                .register(KryoNamespaces.API)
-                .register(Phase.class);
-        phasedRecoveryStore = storageService.<DeviceId, Phase>consistentMapBuilder()
-                .withName("onos-sr-phasedrecovery")
-                .withRelaxedReadConsistency()
-                .withSerializer(Serializer.using(serializer.build()))
-                .build();
-
-        compCfgService.registerProperties(getClass());
-        modified(context);
-        log.info("Started");
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        phasedRecoveryStore.destroy();
-        compCfgService.unregisterProperties(getClass(), false);
-        log.info("Stopped");
-    }
-
-    @Modified
-    protected void modified(ComponentContext context) {
-        Dictionary<?, ?> properties = context.getProperties();
-        if (properties == null) {
-            return;
-        }
-
-        String strPhasedRecovery = Tools.get(properties, PROP_PHASED_RECOVERY);
-        boolean expectPhasedRecovery = Boolean.parseBoolean(strPhasedRecovery);
-        if (expectPhasedRecovery != phasedRecovery) {
-            phasedRecovery = expectPhasedRecovery;
-            log.info("{} phased recovery", phasedRecovery ? "Enabling" : "Disabling");
-        }
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return phasedRecovery;
-    }
-
-    @Override
-    public boolean init(DeviceId deviceId) {
-        if (this.srService == null) {
-            log.info("SegmentRoutingService is not ready");
-            return false;
-        }
-        if (!mastershipService.isLocalMaster(deviceId)) {
-            log.info("Not master of {}", deviceId);
-            return false;
-        }
-
-        Phase phase = Optional.ofNullable(phasedRecoveryStore.putIfAbsent(deviceId, Phase.PENDING))
-                .map(Versioned::value).orElse(null);
-
-        if (phase != null) {
-            log.info("{} has been initialized already. Skipping.", deviceId);
-            return false;
-        } else {
-            Phase nextPhase = (phasedRecovery && this.srService.getPairDeviceId(deviceId).isPresent()) ?
-                    Phase.PAIR : Phase.EDGE;
-            if (nextPhase == Phase.PAIR) {
-                // Wait for the PORT_STAT before entering next phase.
-                // Note: Unlikely, when the device init fails due to PORT_STATS timeout,
-                //       it requires operator to manually move the device to the next phase by CLI command.
-                executor.schedule(new PortChecker(deviceId, PORT_CHECKER_RETRIES),
-                        PORT_CHECKER_INTERVAL, TimeUnit.SECONDS);
-            } else {
-                // We assume that all ports will be reported as enabled on devices that don't require phased recovery
-                setPhase(deviceId, Phase.EDGE);
-            }
-            return true;
-        }
-    }
-
-    @Override
-    public boolean reset(DeviceId deviceId) {
-        if (this.srService == null) {
-            log.info("SegmentRoutingService is not ready");
-            return false;
-        }
-        // FIXME Skip mastership checking since master will not be available when a device goes offline
-        //       Improve this when persistent mastership is introduced
-
-        Phase result = Optional.ofNullable(phasedRecoveryStore.remove(deviceId))
-                .map(Versioned::value).orElse(null);
-        if (result != null) {
-            log.info("{} is reset", deviceId);
-        }
-        return result != null;
-    }
-
-    @Override
-    public Map<DeviceId, Phase> getPhases() {
-        return phasedRecoveryStore.asJavaMap();
-    }
-
-    @Override
-    public Phase getPhase(DeviceId deviceId) {
-        return Optional.ofNullable(phasedRecoveryStore.get(deviceId)).map(Versioned::value).orElse(null);
-    }
-
-    @Override
-    public Phase setPhase(DeviceId deviceId, Phase newPhase) {
-        if (this.srService == null) {
-            log.info("SegmentRoutingService is not ready");
-            return null;
-        }
-        if (!mastershipService.isLocalMaster(deviceId)) {
-            log.info("Not master of {}", deviceId);
-            return null;
-        }
-
-        return Optional.ofNullable(phasedRecoveryStore.compute(deviceId, (k, v) -> {
-            if (v == null && newPhase == Phase.PENDING) {
-                log.info("Initializing {}", deviceId);
-                return newPhase;
-            } else if (v == Phase.PENDING && newPhase == Phase.PAIR) {
-                srService.initHost(deviceId);
-                // RouteHandler init is intentionally skipped when phased recovery is on.
-                // Edge ports remain down in this phase. Therefore, no nexthop will be discovered on the given device.
-                // The flow on given device will be programmed later by hostHandler.processHostMovedEvent()
-                changePairPort(deviceId, true);
-                log.info("Transitioning {} from PENDING to PAIR", deviceId);
-                return newPhase;
-            } else if (v == Phase.PAIR && newPhase == Phase.INFRA) {
-                changeInfraPorts(deviceId, true);
-                srService.initRoute(deviceId);
-                log.info("Transitioning {} from PAIR to INFRA", deviceId);
-                monitorRoutingStability(deviceId);
-                return newPhase;
-            } else if (v == Phase.INFRA && newPhase == Phase.EDGE) {
-                changeEdgePorts(deviceId, true);
-                log.info("Transitioning {} from INFRA to EDGE", deviceId);
-                return newPhase;
-            } else if (v == Phase.PENDING && newPhase == Phase.EDGE) {
-                changeAllPorts(deviceId, true);
-                srService.initHost(deviceId);
-                srService.initRoute(deviceId);
-                log.info("Transitioning {} from PENDING to EDGE", deviceId);
-                return newPhase;
-            } else {
-                log.debug("Ignore illegal state transition on {} from {} to {}", deviceId, v, newPhase);
-                return v;
-            }
-        })).map(Versioned::value).orElse(null);
-    }
-
-    private void monitorRoutingStability(DeviceId deviceId) {
-        CompletableFuture<Void> checkerFuture = new CompletableFuture<>();
-        CompletableFuture<Void> timeoutFuture =
-                Tools.completeAfter(ROUTING_CHECKER_TIMEOUT, TimeUnit.SECONDS);
-        RoutingStabilityChecker checker = new RoutingStabilityChecker(checkerFuture);
-
-        checkerFuture.runAfterEitherAsync(timeoutFuture, () -> {
-            if (checkerFuture.isDone()) {
-                log.info("Routing stable. Move {} to the next phase", deviceId);
-            } else {
-                log.info("Timeout reached. Move {} to the next phase", deviceId);
-                // Mark the future as completed to signify the termination of periodical checker
-                checkerFuture.complete(null);
-            }
-            setPhase(deviceId, Phase.EDGE);
-        });
-
-        executor.schedule(checker, ROUTING_CHECKER_DELAY, TimeUnit.SECONDS);
-    }
-
-    @Override
-    public Set<PortNumber> changeAllPorts(DeviceId deviceId, boolean enabled) {
-        if (this.srService == null) {
-            log.warn("SegmentRoutingService is not ready. Unable to changeAllPorts({}) to {}", deviceId, enabled);
-            return Sets.newHashSet();
-        }
-        Set<PortNumber> portsToBeEnabled = deviceAdminService.getPorts(deviceId)
-                .stream().map(Port::number).collect(Collectors.toSet());
-        changePorts(deviceId, portsToBeEnabled, enabled);
-        return portsToBeEnabled;
-    }
-
-    @Override
-    public Set<PortNumber> changePairPort(DeviceId deviceId, boolean enabled) {
-        if (this.srService == null) {
-            log.warn("SegmentRoutingService is not ready. Unable to changePairPort({}) to {}", deviceId, enabled);
-            return Sets.newHashSet();
-        }
-        Set<PortNumber> portsToBeEnabled = this.srService.getPairLocalPort(deviceId)
-                .map(Sets::newHashSet).orElse(Sets.newHashSet());
-        changePorts(deviceId, portsToBeEnabled, enabled);
-        return portsToBeEnabled;
-    }
-
-    @Override
-    public Set<PortNumber> changeInfraPorts(DeviceId deviceId, boolean enabled) {
-        if (this.srService == null) {
-            log.warn("SegmentRoutingService is not ready. Unable to changeInfraPorts({}) to {}", deviceId, enabled);
-            return Sets.newHashSet();
-        }
-        Set<PortNumber> portsToBeEnabled = this.srService.getInfraPorts(deviceId);
-        changePorts(deviceId, portsToBeEnabled, enabled);
-        return portsToBeEnabled;
-    }
-
-    @Override
-    public Set<PortNumber> changeEdgePorts(DeviceId deviceId, boolean enabled) {
-        if (this.srService == null) {
-            log.warn("SegmentRoutingService is not ready. Unable to changeEdgePorts({}) to {}", deviceId, enabled);
-            return Sets.newHashSet();
-        }
-        Set<PortNumber> portsToBeEnabled = this.srService.getEdgePorts(deviceId);
-        changePorts(deviceId, portsToBeEnabled, enabled);
-        return portsToBeEnabled;
-    }
-
-    private void changePorts(DeviceId deviceId, Set<PortNumber> portNumbers, boolean enabled) {
-        log.info("{} {} on {}", enabled ? "Enabled" : "Disabled", portNumbers, deviceId);
-        portNumbers.forEach(portNumber ->
-            deviceAdminService.changePortState(deviceId, portNumber, enabled));
-    }
-
-    private class PortChecker implements Runnable {
-        int retries;
-        DeviceId deviceId;
-
-        PortChecker(DeviceId deviceId, int retries) {
-            this.deviceId = deviceId;
-            this.retries = retries;
-        }
-
-        @Override
-        public void run() {
-            retries -= 1;
-            if (retries < 0) {
-                log.warn("PORT_STATS timeout. Unable to initialize {}", deviceId);
-                return;
-            }
-
-            if (!deviceAdminService.getPorts(deviceId).isEmpty()) {
-                log.info("{} reported PORT_STATS", deviceId);
-                setPhase(deviceId, Phase.PAIR);
-            }
-            log.info("{} still waiting for PORT_STATS", deviceId);
-            executor.schedule(this, PORT_CHECKER_INTERVAL, TimeUnit.SECONDS);
-        }
-    }
-
-    private class RoutingStabilityChecker implements Runnable {
-        private final CompletableFuture<Void> future;
-
-        RoutingStabilityChecker(CompletableFuture<Void> future) {
-            this.future = future;
-        }
-
-        @Override
-        public void run() {
-            // Do not continue if the future has been completed
-            if (future.isDone()) {
-                log.trace("RouteStabilityChecker is done. Stop checking");
-                return;
-            }
-
-            if (srService.isRoutingStable()) {
-                log.trace("Routing is stable");
-                future.complete(null);
-            } else {
-                log.trace("Routing is not yet stable");
-                executor.schedule(this, ROUTING_CHECKER_DELAY, TimeUnit.SECONDS);
-            }
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/impl/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/impl/package-info.java
deleted file mode 100644
index 3788085..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/phasedrecovery/impl/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2020-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Phased recovery implementation.
- */
-package org.onosproject.segmentrouting.phasedrecovery.impl;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
deleted file mode 100644
index 417a795..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2Tunnel.java
+++ /dev/null
@@ -1,252 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-
-import com.google.common.base.MoreObjects;
-import org.onlab.packet.MplsLabel;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.Link;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Implementation of the default l2 tunnel.
- */
-public class DefaultL2Tunnel implements L2Tunnel {
-
-    /**
-     * Mode of the pseudo wire.
-     */
-    private L2Mode pwMode;
-    /**
-     * Service delimiting tag.
-     */
-    private VlanId sdTag;
-    /**
-     * Tunnel id.
-     */
-    private long tunnelId;
-    /**
-     * Pseudo wire label.
-     */
-    private MplsLabel pwLabel;
-    /**
-     * Inter-CO label.
-     */
-    private MplsLabel interCoLabel;
-
-    private List<Link> pathUsed;
-
-    /**
-     * Vlan which will be used for the encapsualted
-     * vlan traffic.
-     */
-    private VlanId transportVlan;
-
-    /**
-     * Creates a inter-co l2 tunnel using the
-     * supplied parameters.
-     *
-     * @param mode         the tunnel mode
-     * @param sdtag        the service delimiting tag
-     * @param tunnelId     the tunnel id
-     * @param pwLabel      the pseudo wire label
-     * @param interCoLabel the inter central office label
-     */
-    public DefaultL2Tunnel(L2Mode mode, VlanId sdtag, long tunnelId, MplsLabel pwLabel, MplsLabel interCoLabel) {
-        checkNotNull(mode);
-        checkArgument(tunnelId > 0);
-        checkNotNull(pwLabel);
-        checkNotNull(interCoLabel);
-
-        this.pwMode = mode;
-        this.sdTag = sdtag;
-        this.tunnelId = tunnelId;
-        this.pwLabel = pwLabel;
-        this.interCoLabel = interCoLabel;
-    }
-
-    /**
-     * Creates a l2Tunnel from a given tunnel.
-     *
-     * @param l2Tunnel to replicate
-     */
-    public DefaultL2Tunnel(DefaultL2Tunnel l2Tunnel) {
-
-        this.pwMode = l2Tunnel.pwMode();
-        this.sdTag = l2Tunnel.sdTag();
-        this.tunnelId = l2Tunnel.tunnelId();
-        this.pwLabel = l2Tunnel.pwLabel();
-        this.interCoLabel = l2Tunnel.interCoLabel();
-        this.pathUsed = l2Tunnel.pathUsed();
-        this.transportVlan = l2Tunnel.transportVlan;
-    }
-
-    /**
-     * Creates a intra-co l2 tunnel using the
-     * supplied parameters.
-     *
-     * @param mode     the tunnel mode
-     * @param sdtag    the service delimiting tag
-     * @param tunnelId the tunnel id
-     * @param pwLabel  the pseudo wire label
-     */
-    public DefaultL2Tunnel(L2Mode mode, VlanId sdtag, long tunnelId, MplsLabel pwLabel) {
-        this(mode, sdtag, tunnelId, pwLabel, MplsLabel.mplsLabel(MplsLabel.MAX_MPLS));
-    }
-
-
-    /**
-     * Creates an empty l2 tunnel.
-     **/
-    public DefaultL2Tunnel() {
-        this.pwMode = null;
-        this.sdTag = null;
-        this.tunnelId = 0;
-        this.pwLabel = null;
-        this.interCoLabel = null;
-    }
-
-    /**
-     * Returns the mode of the pseudo wire.
-     *
-     * @return the pseudo wire mode
-     */
-    @Override
-    public L2Mode pwMode() {
-        return pwMode;
-    }
-
-    /**
-     * Returns the service delimitation
-     * tag.
-     *
-     * @return the service delimitation vlan id
-     */
-    @Override
-    public VlanId sdTag() {
-        return sdTag;
-    }
-
-    /**
-     * Returns the tunnel id of the pseudo wire.
-     *
-     * @return the pseudo wire tunnel id
-     */
-    @Override
-    public long tunnelId() {
-        return tunnelId;
-    }
-
-    /**
-     * Returns the pw label.
-     *
-     * @return the mpls pw label
-     */
-    @Override
-    public MplsLabel pwLabel() {
-        return pwLabel;
-    }
-
-    /**
-     * Set the path for the pseudowire.
-     *
-     * @param path The path to set
-     */
-    @Override
-    public void setPath(List<Link> path) {
-        pathUsed = new ArrayList<>(path);
-    }
-
-    /**
-     * Set the transport vlan for the pseudowire.
-     *
-     * @param vlan the vlan to use.
-     */
-    @Override
-    public void setTransportVlan(VlanId vlan) {
-        transportVlan = vlan;
-    }
-
-    /**
-     * Returns the used path of the pseudowire.
-     *
-     * @return pathUsed
-     */
-    @Override
-    public List<Link> pathUsed() {
-        return pathUsed;
-    }
-
-    @Override
-    public VlanId transportVlan() {
-        return transportVlan;
-    }
-
-
-    /**
-     * Returns the inter-co label.
-     *
-     * @return the mpls inter-co label
-     */
-    @Override
-    public MplsLabel interCoLabel() {
-        return interCoLabel;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(this.tunnelId, this.pwMode, this.sdTag, this.pwLabel, this.interCoLabel);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (o instanceof DefaultL2Tunnel) {
-            DefaultL2Tunnel that = (DefaultL2Tunnel) o;
-            return this.tunnelId == that.tunnelId &&
-                    this.pwMode.equals(that.pwMode) &&
-                    this.sdTag.equals(that.sdTag) &&
-                    this.pwLabel.equals(that.pwLabel) &&
-                    this.interCoLabel.equals(that.interCoLabel);
-        }
-
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("pwMode", pwMode())
-                .add("sdTag", sdTag())
-                .add("tunnelId", tunnelId())
-                .add("pwLabel", pwLabel())
-                .add("interCoLabel", interCoLabel())
-                .add("transportVlan", transportVlan())
-                .toString();
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelDescription.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelDescription.java
deleted file mode 100644
index 7a47972..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelDescription.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import com.google.common.base.MoreObjects;
-
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Helper class to carry the l2 tunnel
- * and its policy.
- */
-public class DefaultL2TunnelDescription implements L2TunnelDescription {
-
-    /**
-     * The l2 tunnel.
-     */
-    private L2Tunnel l2Tunnel;
-
-    /**
-     * The l2 tunnel policy.
-     */
-    private L2TunnelPolicy l2TunnelPolicy;
-
-    /**
-     * Creates a l2 tunnel description using the given info.
-     *
-     * @param l2Tunnel the l2 tunnel
-     * @param l2TunnelPolicy the l2 tunnel description
-     */
-    public DefaultL2TunnelDescription(L2Tunnel l2Tunnel,
-                                      L2TunnelPolicy l2TunnelPolicy) {
-        checkNotNull(l2Tunnel);
-        checkNotNull(l2TunnelPolicy);
-
-        this.l2Tunnel = l2Tunnel;
-        this.l2TunnelPolicy = l2TunnelPolicy;
-    }
-
-    /**
-     * Creates an empty l2 tunnel description.
-     */
-    public DefaultL2TunnelDescription() {
-        this.l2Tunnel = null;
-        this.l2TunnelPolicy = null;
-    }
-
-    @Override
-    public L2Tunnel l2Tunnel() {
-        return l2Tunnel;
-    }
-
-    @Override
-    public L2TunnelPolicy l2TunnelPolicy() {
-        return l2TunnelPolicy;
-    }
-
-    @Override
-    public void setL2Tunnel(L2Tunnel tunnel) {
-        l2Tunnel = tunnel;
-    }
-
-    @Override
-    public void setL2TunnelPolicy(L2TunnelPolicy policy) {
-        l2TunnelPolicy = policy;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(this.l2Tunnel, this.l2TunnelPolicy);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (o instanceof DefaultL2TunnelDescription) {
-            DefaultL2TunnelDescription that = (DefaultL2TunnelDescription) o;
-            // Equality is based on tunnel id and pw label
-            // which is always the last label.
-            return this.l2Tunnel.equals(that.l2Tunnel) &&
-                    this.l2TunnelPolicy.equals(that.l2TunnelPolicy);
-        }
-
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("l2Tunnel", l2Tunnel())
-                .add("l2TunnelPolicy", l2TunnelPolicy())
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
deleted file mode 100644
index 99ff091..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
+++ /dev/null
@@ -1,1490 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import org.apache.commons.lang3.RandomUtils;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.MplsLabel;
-import org.onlab.packet.VlanId;
-import org.onlab.util.KryoNamespace;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultLink;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Link;
-import org.onosproject.net.Path;
-import org.onosproject.net.PortNumber;
-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.flow.criteria.Criteria;
-import org.onosproject.net.flowobjective.DefaultFilteringObjective;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.DefaultNextObjective;
-import org.onosproject.net.flowobjective.DefaultObjectiveContext;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.topology.LinkWeigher;
-import org.onosproject.segmentrouting.SRLinkWeigher;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.config.DeviceConfigNotFoundException;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.DistributedLock;
-import org.onosproject.store.service.Serializer;
-import org.onosproject.store.service.StorageException;
-import org.onosproject.store.service.Versioned;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-import java.util.stream.Collectors;
-
-import static org.onosproject.net.flowobjective.ForwardingObjective.Flag.VERSATILE;
-import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.INITIATION;
-import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Pipeline.TERMINATION;
-import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Result.*;
-import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.FWD;
-import static org.onosproject.segmentrouting.pwaas.L2TunnelHandler.Direction.REV;
-import static org.onosproject.segmentrouting.pwaas.PwaasUtil.*;
-
-/**
- * Handler for pseudowire management.
- */
-public class DefaultL2TunnelHandler implements L2TunnelHandler {
-
-    private static final String LOCK_NAME = "l2-tunnel-handler-lock";
-    private static final Logger log = LoggerFactory.getLogger(DefaultL2TunnelHandler.class);
-
-    private final SegmentRoutingManager srManager;
-    /**
-     * To store the next objectives related to the initiation.
-     */
-    private final ConsistentMap<String, NextObjective> l2InitiationNextObjStore;
-    /**
-     * To store the next objectives related to the termination.
-     */
-    private final ConsistentMap<String, NextObjective> l2TerminationNextObjStore;
-
-    /**
-     * To store policies.
-     */
-    private final ConsistentMap<String, L2TunnelPolicy> l2PolicyStore;
-
-    /**
-     * To store tunnels.
-     */
-    private final ConsistentMap<String, L2Tunnel> l2TunnelStore;
-
-    /**
-     * To store pending tunnels that need to be installed.
-     */
-    private final ConsistentMap<String, L2Tunnel> pendingL2TunnelStore;
-
-    /**
-     * To store pending policies that need to be installed.
-     */
-    private final ConsistentMap<String, L2TunnelPolicy> pendingL2PolicyStore;
-
-    private final KryoNamespace.Builder l2TunnelKryo;
-
-    /**
-     * Lock used when creating or removing pseudowires.
-     */
-    private final DistributedLock pwLock;
-
-    private static final long LOCK_TIMEOUT = 2000;
-
-    /**
-     * Create a l2 tunnel handler for the deploy and
-     * for the tear down of pseudo wires.
-     *
-     * @param segmentRoutingManager the segment routing manager
-     */
-    public DefaultL2TunnelHandler(SegmentRoutingManager segmentRoutingManager) {
-        srManager = segmentRoutingManager;
-        l2TunnelKryo = new KryoNamespace.Builder()
-                .register(KryoNamespaces.API)
-                .register(L2Tunnel.class,
-                          L2TunnelPolicy.class,
-                          DefaultL2Tunnel.class,
-                          DefaultL2TunnelPolicy.class,
-                          L2Mode.class,
-                          MplsLabel.class,
-                          VlanId.class,
-                          ConnectPoint.class);
-
-        l2InitiationNextObjStore = srManager.
-                storageService.
-                <String, NextObjective>consistentMapBuilder().
-                withName("onos-l2initiation-nextobj-store").
-                withSerializer(Serializer.using(l2TunnelKryo.build())).
-                build();
-
-        l2TerminationNextObjStore = srManager.storageService.
-                <String, NextObjective>consistentMapBuilder()
-                .withName("onos-l2termination-nextobj-store")
-                .withSerializer(Serializer.using(l2TunnelKryo.build()))
-                .build();
-
-        l2PolicyStore = srManager.storageService
-                .<String, L2TunnelPolicy>consistentMapBuilder()
-                .withName("onos-l2-policy-store")
-                .withSerializer(Serializer.using(l2TunnelKryo.build()))
-                .build();
-
-        l2TunnelStore = srManager.storageService
-                .<String, L2Tunnel>consistentMapBuilder()
-                .withName("onos-l2-tunnel-store")
-                .withSerializer(Serializer.using(l2TunnelKryo.build()))
-                .build();
-
-        pendingL2PolicyStore = srManager.storageService
-                .<String, L2TunnelPolicy>consistentMapBuilder()
-                .withName("onos-l2-pending-policy-store")
-                .withSerializer(Serializer.using(l2TunnelKryo.build()))
-                .build();
-
-        pendingL2TunnelStore = srManager.storageService
-                .<String, L2Tunnel>consistentMapBuilder()
-                .withName("onos-l2-pending-tunnel-store")
-                .withSerializer(Serializer.using(l2TunnelKryo.build()))
-                .build();
-
-        pwLock = srManager.storageService.lockBuilder()
-                .withName(LOCK_NAME)
-                .build()
-                .asLock(LOCK_TIMEOUT);
-    }
-
-    /**
-     * Used by manager only in initialization.
-     */
-    @Override
-    public void init() {
-        // Since we have no pseudowires in netcfg there
-        // is nothing to do in initialization.
-        // I leave it here because potentially we might need to
-        // use it in the future.
-    }
-
-    @Override
-    public Set<L2TunnelDescription> getL2Descriptions(boolean pending) {
-            // get pending tunnels/policies OR installed tunnels/policies
-            List<L2Tunnel> tunnels = pending ? getL2PendingTunnels() : getL2Tunnels();
-            List<L2TunnelPolicy> policies = pending ? getL2PendingPolicies() : getL2Policies();
-            return tunnels.stream()
-                .map(l2Tunnel -> {
-                    L2TunnelPolicy policy = null;
-                    for (L2TunnelPolicy l2Policy : policies) {
-                        if (l2Policy.tunnelId() == l2Tunnel.tunnelId()) {
-                            policy = l2Policy;
-                            break;
-                        }
-                    }
-
-                    return new DefaultL2TunnelDescription(l2Tunnel, policy);
-                })
-                .collect(Collectors.toSet());
-    }
-
-    @Override
-    public List<L2TunnelPolicy> getL2Policies() {
-        return new ArrayList<>(l2PolicyStore
-                .values()
-                .stream()
-                .map(Versioned::value)
-                .collect(Collectors.toList()));
-    }
-
-    @Override
-    public List<L2Tunnel> getL2Tunnels() {
-        return new ArrayList<>(l2TunnelStore
-                .values()
-                .stream()
-                .map(Versioned::value)
-                .collect(Collectors.toList()));
-    }
-
-    @Override
-    public List<L2TunnelPolicy> getL2PendingPolicies() {
-        return new ArrayList<>(pendingL2PolicyStore
-                                       .values()
-                                       .stream()
-                                       .map(Versioned::value)
-                                       .collect(Collectors.toList()));
-    }
-
-    @Override
-    public List<L2Tunnel> getL2PendingTunnels() {
-        return new ArrayList<>(pendingL2TunnelStore
-                                       .values()
-                                       .stream()
-                                       .map(Versioned::value)
-                                       .collect(Collectors.toList()));
-    }
-
-    @Override
-    public Result verifyGlobalValidity(L2TunnelDescription pwToAdd) {
-
-        // get both added and pending pseudowires
-        List<L2TunnelDescription> newPseudowires = new ArrayList<>();
-        newPseudowires.addAll(getL2Descriptions(false));
-        newPseudowires.addAll(getL2Descriptions(true));
-        // add the new one
-        newPseudowires.add(pwToAdd);
-
-        return configurationValidity(newPseudowires);
-    }
-
-    @Override
-    public ImmutableMap<String, NextObjective> getInitNext() {
-        if (l2InitiationNextObjStore != null) {
-            return ImmutableMap.copyOf(l2InitiationNextObjStore.asJavaMap());
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public ImmutableMap<String, NextObjective> getTermNext() {
-        if (l2TerminationNextObjStore != null) {
-            return ImmutableMap.copyOf(l2TerminationNextObjStore.asJavaMap());
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public void removeNextId(int nextId) {
-        l2InitiationNextObjStore.entrySet().forEach(e -> {
-            if (e.getValue().value().id() == nextId) {
-                l2InitiationNextObjStore.remove(e.getKey());
-            }
-        });
-
-        l2TerminationNextObjStore.entrySet().forEach(e -> {
-            if (e.getValue().value().id() == nextId) {
-                l2TerminationNextObjStore.remove(e.getKey());
-            }
-        });
-    }
-
-    /**
-     * Returns the new vlan id for an ingress point of a
-     * pseudowire. For double tagged, it is the outer,
-     * For single tagged it is the single tag, and for
-     * inner it is None.
-     *
-     * @param ingressOuter vlanid of ingress outer
-     * @param ingressInner vlanid of ingress inner
-     * @param egressOuter  vlanid of egress outer
-     * @param egressInner  vlanid of egress inner
-     * @return returns the vlan id which will be installed at vlan table 1.
-     */
-    private VlanId determineEgressVlan(VlanId ingressOuter, VlanId ingressInner,
-                                      VlanId egressOuter, VlanId egressInner) {
-        // validity of vlan combinations was checked at verifyPseudowire
-        if (!(ingressOuter.equals(VlanId.NONE))) {
-            return egressOuter;
-        } else if (!(ingressInner.equals(VlanId.NONE))) {
-            return egressInner;
-        } else {
-            return VlanId.vlanId("None");
-        }
-    }
-
-    /**
-     * Returns the devices existing on a given path.
-     *
-     * @param path The path to traverse.
-     * @return The devices on the path with the order they
-     *         are traversed.
-     */
-    private List<DeviceId> getDevicesOnPath(List<Link> path) {
-
-        // iterate over links and get all devices in the order
-        // we find them
-        List<DeviceId> deviceList = new ArrayList<>();
-        for (Link link : path) {
-            if (!deviceList.contains(link.src().deviceId())) {
-                deviceList.add(link.src().deviceId());
-            }
-            if (!deviceList.contains(link.dst().deviceId())) {
-                deviceList.add(link.dst().deviceId());
-            }
-        }
-
-        return deviceList;
-    }
-
-    /**
-     * Returns true if path is valid according to the current logic.
-     * For example : leaf to spine pseudowires can be either leaf-spine or
-     * leaf-spine-spine. However, in the configuration we might specify spine device
-     * first which will result in spine-spine-leaf. If leafSpinePw == true we have one of these
-     * two cases and need to provision for them.
-     *
-     * If we have a leaf to leaf pseudowire then all the intermediate devices must
-     * be spines. However, in case of paired switches we can have two leafs interconnected
-     * with each other directly.
-     *
-     * @param path the chosen path
-     * @param leafSpinePw if it is a leaf to spine pseudowire
-     * @return True if path size is valid, false otherwise.
-     */
-    private boolean isValidPath(List<Link> path, boolean leafSpinePw) {
-
-        log.debug("Checking path validity for pseudowire.");
-        List<DeviceId> devices = getDevicesOnPath(path);
-        if (devices.size() < 2) {
-            log.error("Path size for pseudowire should be greater than 1!");
-            return false;
-        }
-
-        try {
-            if (leafSpinePw) {
-                // can either be leaf-spine-spine or leaf-spine
-                // first device is leaf, all other must be spines
-                log.debug("Devices on path are {} for leaf to spine pseudowire", devices);
-                // if first device is a leaf then all other must be spines
-                if (srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
-                    devices.remove(0);
-                    for (DeviceId devId : devices) {
-                        log.debug("Device {} should be a spine!", devId);
-                        if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
-                            return false;
-                        }
-                    }
-                } else {
-                    // if first device is spine, last device must be a leaf
-                    // all other devices must be spines
-                    if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
-                        return false;
-                    }
-                    devices.remove(devices.size() - 1);
-                    for (DeviceId devId : devices) {
-                        log.debug("Device {} should be a spine!", devId);
-                        if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
-                            return false;
-                        }
-                    }
-                }
-            } else {
-                // can either be leaf-leaf (paired leafs) / leaf-spine-leaf
-                // or leaf-spine-spine-leaf
-                log.debug("Devices on path are {} for leaf to leaf pseudowire", devices);
-                // check first device, needs to be a leaf
-                if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(0))) {
-                    return false;
-                }
-                // check last device, needs to be a leaf
-                if (!srManager.deviceConfiguration().isEdgeDevice(devices.get(devices.size() - 1))) {
-                    return false;
-                }
-                // remove these devices, rest must all be spines
-                devices.remove(0);
-                devices.remove(devices.size() - 1);
-                for (DeviceId devId : devices) {
-                    log.debug("Device {} should be a spine!", devId);
-                    if (srManager.deviceConfiguration().isEdgeDevice(devId)) {
-                        return false;
-                    }
-                }
-
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            log.error("Device not found in configuration : {}", e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Adds a single pseudowire.
-     *
-     * @param pw The pseudowire to deploy
-     * @return result of pseudowire deployment
-     */
-    public Result deployPseudowire(L2TunnelDescription pw) {
-
-        try {
-            // take the lock
-            pwLock.lock();
-            Result result;
-            long l2TunnelId;
-            log.debug("Pseudowire with {} deployment started, check log for any errors in this process!",
-                      pw.l2Tunnel().tunnelId());
-            l2TunnelId = pw.l2Tunnel().tunnelId();
-            // The tunnel id cannot be 0.
-            if (l2TunnelId == 0) {
-                log.warn("Tunnel id id must be > 0 in {}", l2TunnelId);
-                return Result.WRONG_PARAMETERS
-                        .appendError("Tunnel id id must be > 0");
-            }
-
-            result = verifyGlobalValidity(pw);
-            if (result != SUCCESS) {
-                log.error("Global validity for pseudowire {} is wrong!", l2TunnelId);
-                return result;
-            }
-
-            // leafSpinePw determines if this is a leaf-leaf
-            // or leaf-spine pseudowire
-            boolean leafSpinePw;
-            ConnectPoint cp1 = pw.l2TunnelPolicy().cP1();
-            ConnectPoint cp2 = pw.l2TunnelPolicy().cP2();
-            try {
-                // differentiate between leaf-leaf pseudowires and leaf-spine
-                if (!srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
-                        !srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
-                    log.error("Can not deploy pseudowire {} from spine to spine!", l2TunnelId);
-                    return Result.WRONG_PARAMETERS
-                            .appendError("Can not deploy pseudowire from spine to spine!");
-                } else if (srManager.deviceConfiguration().isEdgeDevice(cp1.deviceId()) &&
-                        srManager.deviceConfiguration().isEdgeDevice(cp2.deviceId())) {
-                    leafSpinePw = false;
-                } else {
-                    leafSpinePw = true;
-                }
-            } catch (DeviceConfigNotFoundException e) {
-                log.error("Device for pseudowire {} connection points does not exist in the configuration", l2TunnelId);
-                return Result.INTERNAL_ERROR
-                        .appendError("Device for pseudowire connection points does not exist in the configuration");
-            }
-
-            // reverse the policy in order for leaf switch to be at CP1
-            // this will help us for re-using SRLinkWeigher for computing valid paths
-            L2TunnelPolicy reversedPolicy = reverseL2TunnelPolicy(pw.l2TunnelPolicy());
-            if (reversedPolicy == null) {
-                log.error("Error in reversing policy, device configuration was not found for pseudowire {}.",
-                          l2TunnelId);
-                return INTERNAL_ERROR
-                        .appendError("Device configuration not found when reversing the policy.");
-            }
-            pw.setL2TunnelPolicy(reversedPolicy);
-
-            // get path here, need to use the same for fwd and rev direction
-            List<Link> path = getPath(pw.l2TunnelPolicy().cP1(),
-                                      pw.l2TunnelPolicy().cP2());
-            if (path == null || path.isEmpty()) {
-                log.error("Deploying process : No path between the connection points for pseudowire {}", l2TunnelId);
-                return PATH_NOT_FOUND.appendError("No path between the connection points for pseudowire!");
-            }
-
-            Link fwdNextHop;
-            Link revNextHop;
-            if (!isValidPath(path, leafSpinePw)) {
-                log.error("Deploying process : Path for pseudowire {} is not valid", l2TunnelId);
-                return INTERNAL_ERROR.appendError("Internal error : path for pseudowire is not valid!");
-            }
-            // oneHope flag is used to determine if we need to
-            // install transit mpls rules
-            boolean oneHop = true;
-            if (path.size() > 1) {
-                oneHop = false;
-            }
-
-            fwdNextHop = path.get(0);
-            revNextHop = reverseLink(path.get(path.size() - 1));
-
-            pw.l2Tunnel().setPath(path);
-            pw.l2Tunnel().setTransportVlan(srManager.getPwTransportVlan());
-
-            // next hops for next objectives
-            log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
-
-            VlanId egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP1OuterTag(),
-                                                    pw.l2TunnelPolicy().cP1InnerTag(),
-                                                    pw.l2TunnelPolicy().cP2OuterTag(),
-                                                    pw.l2TunnelPolicy().cP2InnerTag());
-            result = deployPseudoWireInit(pw.l2Tunnel(),
-                                          pw.l2TunnelPolicy().cP1(),
-                                          pw.l2TunnelPolicy().cP2(),
-                                          FWD,
-                                          fwdNextHop,
-                                          oneHop,
-                                          egressVlan);
-            if (result != SUCCESS) {
-                log.error("Deploying process : Error in deploying pseudowire {} initiation for CP1", l2TunnelId);
-                return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire initiation for CP1");
-            }
-
-            result = deployPolicy(l2TunnelId,
-                                  pw.l2TunnelPolicy().cP1(),
-                                  pw.l2TunnelPolicy().cP1InnerTag(),
-                                  pw.l2TunnelPolicy().cP1OuterTag(),
-                                  egressVlan,
-                                  result.getNextId());
-            if (result != SUCCESS) {
-                log.error("Deploying process : Error in deploying pseudowire {} policy for CP1", l2TunnelId);
-                return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire policy for CP1");
-            }
-
-            result = deployPseudoWireTerm(pw.l2Tunnel(),
-                                          pw.l2TunnelPolicy().cP2(),
-                                          egressVlan,
-                                          FWD,
-                                          oneHop);
-
-            if (result != SUCCESS) {
-                log.error("Deploying process : Error in deploying pseudowire {} termination for CP1", l2TunnelId);
-                return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP1");
-            }
-
-            // We establish the reverse tunnel.
-            log.info("Deploying process : Establishing reverse direction for pseudowire {}", l2TunnelId);
-            egressVlan = determineEgressVlan(pw.l2TunnelPolicy().cP2OuterTag(),
-                                             pw.l2TunnelPolicy().cP2InnerTag(),
-                                             pw.l2TunnelPolicy().cP1OuterTag(),
-                                             pw.l2TunnelPolicy().cP1InnerTag());
-
-            result = deployPseudoWireInit(pw.l2Tunnel(),
-                                          pw.l2TunnelPolicy().cP2(),
-                                          pw.l2TunnelPolicy().cP1(),
-                                          REV,
-                                          revNextHop,
-                                          oneHop,
-                                          egressVlan);
-            if (result != SUCCESS) {
-                log.error("Deploying process : Error in deploying pseudowire {} initiation for CP2", l2TunnelId);
-                return Result.INTERNAL_ERROR
-                        .appendError("Error in deploying pseudowire initiation for CP2");
-            }
-
-            result = deployPolicy(l2TunnelId,
-                                  pw.l2TunnelPolicy().cP2(),
-                                  pw.l2TunnelPolicy().cP2InnerTag(),
-                                  pw.l2TunnelPolicy().cP2OuterTag(),
-                                  egressVlan,
-                                  result.getNextId());
-            if (result != SUCCESS) {
-                log.error("Deploying process : Error in deploying policy {} for CP2", l2TunnelId);
-                return Result.INTERNAL_ERROR
-                        .appendError("Deploying process : Error in deploying policy for CP2");
-            }
-
-            result = deployPseudoWireTerm(pw.l2Tunnel(),
-                                          pw.l2TunnelPolicy().cP1(),
-                                          egressVlan,
-                                          REV,
-                                          oneHop);
-
-            if (result != SUCCESS) {
-                log.error("Deploying process : Error in deploying pseudowire {} termination for CP2", l2TunnelId);
-                return Result.INTERNAL_ERROR.appendError("Error in deploying pseudowire termination for CP2");
-            }
-
-            log.info("Deploying process : Updating relevant information for pseudowire {}", l2TunnelId);
-
-            // Populate stores as the final step of the process
-            l2TunnelStore.put(Long.toString(l2TunnelId), pw.l2Tunnel());
-            l2PolicyStore.put(Long.toString(l2TunnelId), pw.l2TunnelPolicy());
-
-            return Result.SUCCESS;
-        } catch (StorageException.Timeout e) {
-            log.error("Can not acquire distributed lock for pseudowire {}!", pw.l2Tunnel().tunnelId());
-            return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
-        } finally {
-            // release the lock
-            pwLock.unlock();
-        }
-    }
-
-    @Override
-    public Result checkIfPwExists(long tunnelId, boolean pending) {
-
-        List<L2TunnelDescription> pseudowires = getL2Descriptions(pending)
-                .stream()
-                .filter(pw -> pw.l2Tunnel().tunnelId() == tunnelId)
-                .collect(Collectors.toList());
-
-        if (pseudowires.size() == 0) {
-            String store = ((pending) ? "pending" : "installed");
-            log.debug("Pseudowire {} does not exist in {} store", tunnelId, store);
-            return Result.WRONG_PARAMETERS.
-                    appendError("Pseudowire " + tunnelId + " does not exist in " + store);
-        } else {
-            return SUCCESS;
-        }
-    }
-
-    /**
-     * Tears down connection points of pseudowires. We can either tear down both connection points,
-     * or each one of them.
-     *
-     * @param l2TunnelId The tunnel id for this pseudowire.
-     * @param tearDownFirst Boolean, true if we want to tear down cp1
-     * @param tearDownSecond Boolean, true if we want to tear down cp2
-     * @param pending Boolean, if true remove only pseudowire from pending stores since no flows/groups
-     *                in the network, else remove flows/groups in the devices also.
-     * @return Result of tearing down the pseudowire, SUCCESS if everything was ok
-     *         a descriptive error otherwise.
-     */
-    private Result tearDownConnectionPoints(long l2TunnelId, boolean tearDownFirst,
-                                            boolean tearDownSecond, boolean pending) {
-
-        Result res;
-        CompletableFuture<ObjectiveError> fwdInitNextFuture = new CompletableFuture<>();
-        CompletableFuture<ObjectiveError> fwdTermNextFuture = new CompletableFuture<>();
-        CompletableFuture<ObjectiveError> revInitNextFuture = new CompletableFuture<>();
-        CompletableFuture<ObjectiveError> revTermNextFuture = new CompletableFuture<>();
-
-        if (l2TunnelId == 0) {
-            log.error("Removal process : Tunnel id cannot be 0");
-            return Result.WRONG_PARAMETERS.appendError("Pseudowire id can not be 0.");
-        }
-
-        res = checkIfPwExists(l2TunnelId, pending);
-        if (res != Result.SUCCESS) {
-            return res;
-        }
-
-        // remove and get the tunnel and the policy from the appropriate store
-        // if null, return error.
-        Versioned<L2Tunnel> l2TunnelVersioned = pending ?
-                pendingL2TunnelStore.remove(Long.toString(l2TunnelId)) :
-                l2TunnelStore.remove(Long.toString(l2TunnelId));
-        Versioned<L2TunnelPolicy> l2TunnelPolicyVersioned = pending ?
-                pendingL2PolicyStore.remove(Long.toString(l2TunnelId)) :
-                l2PolicyStore.remove(Long.toString(l2TunnelId));
-        if ((l2TunnelVersioned == null) || (l2TunnelPolicyVersioned == null)) {
-            log.warn("Removal process : Policy and/or tunnel missing for tunnel id {}", l2TunnelId);
-            return Result.INTERNAL_ERROR
-                    .appendError("Policy and/or tunnel missing for pseudowire!");
-        }
-
-        L2TunnelDescription pwToRemove = new DefaultL2TunnelDescription(l2TunnelVersioned.value(),
-                                                                        l2TunnelPolicyVersioned.value());
-
-        if (pending) {
-            // no need to remove flows / groups for a pseudowire
-            // in pending state
-            return Result.SUCCESS;
-        }
-
-        // remove flows/groups involving with this pseudowire
-        if (tearDownFirst) {
-            log.info("Removal process : Tearing down forward direction of pseudowire {}", l2TunnelId);
-
-            VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP1OuterTag(),
-                                                    pwToRemove.l2TunnelPolicy().cP1InnerTag(),
-                                                    pwToRemove.l2TunnelPolicy().cP2OuterTag(),
-                                                    pwToRemove.l2TunnelPolicy().cP2InnerTag());
-            deletePolicy(l2TunnelId,
-                         pwToRemove.l2TunnelPolicy().cP1(),
-                         pwToRemove.l2TunnelPolicy().cP1InnerTag(),
-                         pwToRemove.l2TunnelPolicy().cP1OuterTag(),
-                         egressVlan,
-                         fwdInitNextFuture,
-                         FWD);
-
-            fwdInitNextFuture.thenAcceptAsync(status -> {
-                if (status == null) {
-                    // Finally we will tear down the pseudo wire.
-                    tearDownPseudoWireInit(l2TunnelId,
-                                           pwToRemove.l2TunnelPolicy().cP1(),
-                                           fwdTermNextFuture,
-                                           FWD);
-                }
-            });
-
-            fwdTermNextFuture.thenAcceptAsync(status -> {
-                if (status == null) {
-                    tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
-                                           pwToRemove.l2TunnelPolicy().cP2(),
-                                           null,
-                                           FWD);
-                }
-            });
-        }
-
-        if (tearDownSecond) {
-            log.info("Removal process : Tearing down reverse direction of pseudowire {}", l2TunnelId);
-
-            VlanId egressVlan = determineEgressVlan(pwToRemove.l2TunnelPolicy().cP2OuterTag(),
-                                             pwToRemove.l2TunnelPolicy().cP2InnerTag(),
-                                             pwToRemove.l2TunnelPolicy().cP1OuterTag(),
-                                             pwToRemove.l2TunnelPolicy().cP1InnerTag());
-
-            // We do the same operations on the reverse side.
-            deletePolicy(l2TunnelId,
-                         pwToRemove.l2TunnelPolicy().cP2(),
-                         pwToRemove.l2TunnelPolicy().cP2InnerTag(),
-                         pwToRemove.l2TunnelPolicy().cP2OuterTag(),
-                         egressVlan,
-                         revInitNextFuture,
-                         REV);
-
-            revInitNextFuture.thenAcceptAsync(status -> {
-                if (status == null) {
-                    tearDownPseudoWireInit(l2TunnelId,
-                                           pwToRemove.l2TunnelPolicy().cP2(),
-                                           revTermNextFuture,
-                                           REV);
-                }
-            });
-
-            revTermNextFuture.thenAcceptAsync(status -> {
-                if (status == null) {
-                    tearDownPseudoWireTerm(pwToRemove.l2Tunnel(),
-                                           pwToRemove.l2TunnelPolicy().cP1(),
-                                           null,
-                                           REV);
-                }
-            });
-        }
-
-        return Result.SUCCESS;
-    }
-
-    /**
-     * Helper function for removing a single pseudowire.
-     *
-     * Tries to remove pseudowire from any store it might reside (pending or installed).
-     *
-     * @param l2TunnelId the id of the pseudowire to tear down
-     * @return Returns SUCCESS if no error is obeserved or an appropriate
-     * error on a failure
-     */
-    public Result tearDownPseudowire(long l2TunnelId) {
-
-        try {
-            // take the lock
-            pwLock.lock();
-
-            if (checkIfPwExists(l2TunnelId, true) == Result.SUCCESS) {
-                return tearDownConnectionPoints(l2TunnelId, true, true, true);
-            } else if (checkIfPwExists(l2TunnelId, false) == Result.SUCCESS) {
-                return tearDownConnectionPoints(l2TunnelId, true, true, false);
-            } else {
-                return Result.WRONG_PARAMETERS.appendError("Pseudowire with "
-                                                                   + l2TunnelId
-                                                                   + " did not reside in any store!");
-            }
-        } catch (StorageException.Timeout e) {
-            log.error("Can not acquire distributed lock for pseudowire {}!", l2TunnelId);
-            return Result.INTERNAL_ERROR.appendError("Can not acquire distributed lock!");
-        } finally {
-            // release the lock
-            pwLock.unlock();
-        }
-    }
-
-    @Override
-    @Deprecated
-    public void tearDown(Set<L2TunnelDescription> pwToRemove) {
-
-        for (L2TunnelDescription currentL2Tunnel : pwToRemove) {
-
-            long tunnelId = currentL2Tunnel.l2TunnelPolicy().tunnelId();
-            log.info("Removing pseudowire {}", tunnelId);
-
-            Result result = tearDownPseudowire(tunnelId);
-            if (result != Result.SUCCESS) {
-                log.error("Could not remove pseudowire {}!", tunnelId);
-            }
-        }
-    }
-
-    /**
-     * Handles the policy establishment which consists in
-     * create the filtering and forwarding objectives related
-     * to the initiation and termination.
-     *
-     * @param tunnelId     the tunnel id
-     * @param ingress      the ingress point
-     * @param ingressInner the ingress inner tag
-     * @param ingressOuter the ingress outer tag
-     * @param egressVlan   Vlan-id to set, depends on ingress vlan
-     *                     combinations. For example, if pw is double tagged
-     *                     then this is the value of the outer vlan, if single
-     *                     tagged then it is the new value of the single tag.
-     *                     Should be None for untagged traffic.
-     * @param nextId       the next objective id
-     * @return the result of the operation
-     */
-    private Result deployPolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner,
-                                VlanId ingressOuter, VlanId egressVlan, int nextId) {
-        log.debug("Starting deploying policy for pseudowire {}.", tunnelId);
-
-        List<Objective> objectives = Lists.newArrayList();
-        // We create the forwarding objective for supporting
-        // the l2 tunnel.
-        ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
-        // We create and add objective context.
-        ObjectiveContext context = new DefaultObjectiveContext((objective) ->
-                                                                log.debug("FwdObj for tunnel {} populated", tunnelId),
-                                                               (objective, error) ->
-                                                                log.warn("Failed to populate fwdObj " +
-                                                                                 "for tunnel {} : {}",
-                                                                         tunnelId, error));
-        objectives.add(fwdBuilder.add(context));
-
-        // We create the filtering objective to define the
-        // permit traffic in the switch
-        FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
-
-        // We add the metadata.
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment
-                .builder()
-                .setTunnelId(tunnelId)
-                .setVlanId(egressVlan);
-        filtBuilder.withMeta(treatment.build());
-
-        // We create and add objective context.
-        context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for tunnel {} populated", tunnelId),
-                                              (objective, error) -> log.warn("Failed to populate filterObj for " +
-                                                                                     "tunnel {} : {}",
-                                                                             tunnelId, error));
-        objectives.add(filtBuilder.add(context));
-
-        for (Objective objective : objectives) {
-            if (objective instanceof ForwardingObjective) {
-                srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
-                log.debug("Creating new FwdObj for initiation NextObj with id={} for tunnel {}", nextId, tunnelId);
-            } else {
-                srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
-                log.debug("Creating new FiltObj for tunnel {}", tunnelId);
-            }
-        }
-        return SUCCESS;
-    }
-
-    /**
-     * Handles the tunnel establishment which consists in
-     * create the next objectives related to the initiation.
-     *
-     * @param l2Tunnel  the tunnel to deploy
-     * @param ingress   the ingress connect point
-     * @param egress    the egress connect point
-     * @param direction the direction of the pw
-     * @param nextHop next hop of the initiation point
-     * @param oneHop if this pseudowire has only one link
-     * @param termVlanId the termination vlan id
-     * @return the result of the operation
-     */
-    private Result deployPseudoWireInit(L2Tunnel l2Tunnel, ConnectPoint ingress,
-                                        ConnectPoint egress, Direction direction,
-                                        Link nextHop, boolean oneHop, VlanId termVlanId) {
-        log.debug("Started deploying init next objectives for pseudowire {} for tunnel {} -> {}.",
-                  l2Tunnel.tunnelId(), ingress, egress);
-        if (nextHop == null) {
-            log.warn("No path between ingress and egress connection points for tunnel {}", l2Tunnel.tunnelId());
-            return WRONG_PARAMETERS;
-        }
-
-        // We create the next objective without the metadata
-        // context and id. We check if it already exists in the
-        // store. If not we store as it is in the store.
-        NextObjective.Builder nextObjectiveBuilder = createNextObjective(INITIATION,
-                                                                         nextHop.src(),
-                                                                         nextHop.dst(),
-                                                                         l2Tunnel,
-                                                                         egress.deviceId(),
-                                                                         oneHop,
-                                                                         termVlanId);
-
-        if (nextObjectiveBuilder == null) {
-            return INTERNAL_ERROR;
-        }
-        // We set the metadata. We will use this metadata
-        // to inform the driver we are doing a l2 tunnel.
-        TrafficSelector metadata = DefaultTrafficSelector
-                .builder()
-                .matchTunnelId(l2Tunnel.tunnelId())
-                .build();
-        nextObjectiveBuilder.withMeta(metadata);
-        int nextId = srManager.flowObjectiveService.allocateNextId();
-        if (nextId < 0) {
-            log.warn("Not able to allocate a next id for initiation");
-            return INTERNAL_ERROR;
-        }
-        nextObjectiveBuilder.withId(nextId);
-        String key = generateKey(l2Tunnel.tunnelId(), direction);
-        l2InitiationNextObjStore.put(key, nextObjectiveBuilder.add());
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Initiation l2 tunnel rule for {} populated", l2Tunnel.tunnelId()),
-                (objective, error) -> {
-                    log.warn("Failed to populate Initiation l2 tunnel rule for {}: {}", l2Tunnel.tunnelId(), error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-        NextObjective nextObjective = nextObjectiveBuilder.add(context);
-        srManager.flowObjectiveService.next(ingress.deviceId(), nextObjective);
-        log.debug("Initiation next objective for {} not found. Creating new NextObj with id={}",
-                  l2Tunnel.tunnelId(), nextObjective.id());
-        Result result = SUCCESS;
-        result.setNextId(nextObjective.id());
-        return result;
-    }
-
-    /**
-     * Handles the tunnel termination, which consists in the creation
-     * of a forwarding objective and a next objective.
-     *
-     * @param l2Tunnel   the tunnel to terminate
-     * @param egress     the egress point
-     * @param egressVlan the expected vlan at egress
-     * @param direction  the direction
-     * @return the result of the operation
-     */
-    private Result deployPseudoWireTerm(L2Tunnel l2Tunnel, ConnectPoint egress,
-                                        VlanId egressVlan, Direction direction,
-                                        boolean oneHop) {
-        log.debug("Started deploying termination objectives for pseudowire {} , direction {}.",
-                  l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
-
-        // We create the group relative to the termination.
-        NextObjective.Builder nextObjectiveBuilder = createNextObjective(TERMINATION, egress, null,
-                                                                         l2Tunnel, egress.deviceId(),
-                                                                         oneHop,
-                                                                         egressVlan);
-        if (nextObjectiveBuilder == null) {
-            return INTERNAL_ERROR;
-        }
-        TrafficSelector metadata = DefaultTrafficSelector
-                .builder()
-                .matchVlanId(egressVlan)
-                .build();
-        nextObjectiveBuilder.withMeta(metadata);
-        int nextId = srManager.flowObjectiveService.allocateNextId();
-        if (nextId < 0) {
-            log.warn("Not able to allocate a next id for initiation");
-            return INTERNAL_ERROR;
-        }
-        nextObjectiveBuilder.withId(nextId);
-        String key = generateKey(l2Tunnel.tunnelId(), direction);
-        l2TerminationNextObjStore.put(key, nextObjectiveBuilder.add());
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("Termination l2 tunnel rule for {} populated", l2Tunnel.tunnelId()),
-                (objective, error) -> {
-                    log.warn("Failed to populate termination l2 tunnel rule for {}: {}", l2Tunnel.tunnelId(), error);
-                    srManager.invalidateNextObj(objective.id());
-                });
-        NextObjective nextObjective = nextObjectiveBuilder.add(context);
-        srManager.flowObjectiveService.next(egress.deviceId(), nextObjective);
-        log.debug("Termination next objective for {} not found. Creating new NextObj with id={}",
-                  l2Tunnel.tunnelId(), nextObjective.id());
-
-        // We create the flow relative to the termination.
-        ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(), l2Tunnel.tunnelId(),
-                                                                        egress.port(), nextObjective.id());
-        context = new DefaultObjectiveContext((objective) -> log.debug("FwdObj for tunnel termination {} populated",
-                                                                       l2Tunnel.tunnelId()),
-                                              (objective, error) -> log.warn("Failed to populate fwdrObj" +
-                                                                             " for tunnel termination {} : {}",
-                                                                             l2Tunnel.tunnelId(), error));
-        srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.add(context));
-        log.debug("Creating new FwdObj for termination NextObj with id={} for tunnel {}",
-                  nextId, l2Tunnel.tunnelId());
-
-        return SUCCESS;
-    }
-
-
-    /**
-     * Creates the filtering objective according to a given policy.
-     *
-     * @param inPort   the in port
-     * @param innerTag the inner vlan tag
-     * @param outerTag the outer vlan tag
-     * @return the filtering objective
-     */
-    private FilteringObjective.Builder createFiltObjective(PortNumber inPort, VlanId innerTag, VlanId outerTag) {
-
-        log.debug("Creating connection point filtering objective for vlans {} / {}", outerTag, innerTag);
-        return DefaultFilteringObjective
-                .builder()
-                .withKey(Criteria.matchInPort(inPort))
-                .addCondition(Criteria.matchInnerVlanId(innerTag))
-                .addCondition(Criteria.matchVlanId(outerTag))
-                .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
-                .permit()
-                .fromApp(srManager.appId());
-    }
-
-    /**
-     * Creates the forwarding objective for the termination.
-     *
-     * @param pwLabel    the pseudo wire label
-     * @param tunnelId   the tunnel id
-     * @param egressPort the egress port
-     * @param nextId     the next step
-     * @return the forwarding objective to support the termination
-     */
-    private ForwardingObjective.Builder createTermFwdObjective(MplsLabel pwLabel, long tunnelId,
-                                                               PortNumber egressPort, int nextId) {
-
-        log.debug("Creating forwarding objective for termination for tunnel {} : pwLabel {}, egressPort {}, nextId {}",
-                 tunnelId, pwLabel, egressPort, nextId);
-        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder trafficTreatment = DefaultTrafficTreatment.builder();
-        // The flow has to match on the pw label and bos
-        trafficSelector.matchEthType(Ethernet.MPLS_UNICAST);
-        trafficSelector.matchMplsLabel(pwLabel);
-        trafficSelector.matchMplsBos(true);
-        // The flow has to decrement ttl, restore ttl in
-        // pop mpls, set tunnel id and port.
-        trafficTreatment.decMplsTtl();
-        trafficTreatment.copyTtlIn();
-        trafficTreatment.popMpls();
-        trafficTreatment.setTunnelId(tunnelId);
-        trafficTreatment.setOutput(egressPort);
-
-        return DefaultForwardingObjective
-                .builder()
-                .fromApp(srManager.appId())
-                .makePermanent()
-                .nextStep(nextId)
-                .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
-                .withSelector(trafficSelector.build())
-                .withTreatment(trafficTreatment.build())
-                .withFlag(VERSATILE);
-    }
-
-    /**
-     * Creates the forwarding objective for the initiation.
-     *
-     * @param tunnelId the tunnel id
-     * @param inPort   the input port
-     * @param nextId   the next step
-     * @return the forwarding objective to support the initiation.
-     */
-    private ForwardingObjective.Builder createInitFwdObjective(long tunnelId, PortNumber inPort, int nextId) {
-
-        log.debug("Creating forwarding objective for tunnel {} : Port {} , nextId {}", tunnelId, inPort, nextId);
-        TrafficSelector.Builder trafficSelector = DefaultTrafficSelector.builder();
-
-        // The flow has to match on the mpls logical
-        // port and the tunnel id.
-        trafficSelector.matchTunnelId(tunnelId);
-        trafficSelector.matchInPort(inPort);
-
-        return DefaultForwardingObjective
-                .builder()
-                .fromApp(srManager.appId())
-                .makePermanent()
-                .nextStep(nextId)
-                .withPriority(SegmentRoutingService.DEFAULT_PRIORITY)
-                .withSelector(trafficSelector.build())
-                .withFlag(VERSATILE);
-
-    }
-
-    /**
-     * Creates the next objective according to a given
-     * pipeline. We don't set the next id and we don't
-     * create the final meta to check if we are re-using
-     * the same next objective for different tunnels.
-     *
-     * @param pipeline the pipeline to support
-     * @param srcCp    the source port
-     * @param dstCp    the destination port
-     * @param l2Tunnel the tunnel to support
-     * @param egressId the egress device id
-     * @param oneHop if the pw only has one hop, push only pw label
-     * @param termVlanId the outer vlan id of the packet exiting a termination point
-     * @return the next objective to support the pipeline
-     */
-    private NextObjective.Builder createNextObjective(Pipeline pipeline, ConnectPoint srcCp,
-                                                      ConnectPoint dstCp,  L2Tunnel l2Tunnel,
-                                                      DeviceId egressId, boolean oneHop,
-                                                      VlanId termVlanId) {
-        log.debug("Creating {} next objective for pseudowire {}.",
-                  pipeline == TERMINATION ? "termination" : "inititation");
-
-        NextObjective.Builder nextObjBuilder;
-        TrafficTreatment.Builder treatmentBuilder = DefaultTrafficTreatment.builder();
-        if (pipeline == INITIATION) {
-            nextObjBuilder = DefaultNextObjective
-                    .builder()
-                    .withType(NextObjective.Type.SIMPLE)
-                    .fromApp(srManager.appId());
-            // The pw label is the bottom of stack. It has to
-            // be different -1.
-            if (l2Tunnel.pwLabel().toInt() == MplsLabel.MAX_MPLS) {
-                log.error("Pw label not configured");
-                return null;
-            }
-            treatmentBuilder.pushMpls();
-            treatmentBuilder.setMpls(l2Tunnel.pwLabel());
-            treatmentBuilder.setMplsBos(true);
-            treatmentBuilder.copyTtlOut();
-
-            // If the inter-co label is present we have to set the label.
-            if (l2Tunnel.interCoLabel().toInt() != MplsLabel.MAX_MPLS) {
-                treatmentBuilder.pushMpls();
-                treatmentBuilder.setMpls(l2Tunnel.interCoLabel());
-                treatmentBuilder.setMplsBos(false);
-                treatmentBuilder.copyTtlOut();
-            }
-
-            // if not oneHop install transit mpls labels also
-            if (!oneHop) {
-                // We retrieve the sr label from the config
-                // specific for pseudowire traffic
-                // using the egress leaf device id.
-                MplsLabel srLabel;
-                try {
-                    srLabel = MplsLabel.mplsLabel(srManager.deviceConfiguration().getPWRoutingLabel(egressId));
-
-                } catch (DeviceConfigNotFoundException e) {
-                    log.error("Sr label for pw traffic not configured");
-                    return null;
-                }
-
-                treatmentBuilder.pushMpls();
-                treatmentBuilder.setMpls(srLabel);
-                treatmentBuilder.setMplsBos(false);
-                treatmentBuilder.copyTtlOut();
-            }
-
-            // We have to rewrite the src and dst mac address.
-            MacAddress ingressMac;
-            try {
-                ingressMac = srManager.deviceConfiguration().getDeviceMac(srcCp.deviceId());
-            } catch (DeviceConfigNotFoundException e) {
-                log.error("Was not able to find the ingress mac");
-                return null;
-            }
-            treatmentBuilder.setEthSrc(ingressMac);
-            MacAddress neighborMac;
-            try {
-                neighborMac = srManager.deviceConfiguration().getDeviceMac(dstCp.deviceId());
-            } catch (DeviceConfigNotFoundException e) {
-                log.error("Was not able to find the neighbor mac");
-                return null;
-            }
-            treatmentBuilder.setEthDst(neighborMac);
-
-            // set the appropriate transport vlan from tunnel information
-            treatmentBuilder.setVlanId(l2Tunnel.transportVlan());
-        } else {
-            // We create the next objective which
-            // will be a simple l2 group.
-            nextObjBuilder = DefaultNextObjective
-                    .builder()
-                    .withType(NextObjective.Type.SIMPLE)
-                    .fromApp(srManager.appId());
-
-            // for termination point we use the outer vlan of the
-            // encapsulated packet for the vlan
-            treatmentBuilder.setVlanId(termVlanId);
-        }
-
-        treatmentBuilder.setOutput(srcCp.port());
-        nextObjBuilder.addTreatment(treatmentBuilder.build());
-        return nextObjBuilder;
-    }
-
-    /**
-     * Reverse an l2 tunnel policy in order to have as CP1 the leaf switch,
-     * in case one of the switches is a spine.
-     *
-     * This makes possible the usage of SRLinkWeigher for computing valid paths,
-     * which cuts leaf-spine links from the path computation with a src different
-     * than the source leaf.
-     *
-     * @param policy The policy to reverse, if needed
-     * @return a l2TunnelPolicy containing the leaf at CP1, suitable for usage with
-     *         current SRLinkWeigher
-     */
-    private L2TunnelPolicy reverseL2TunnelPolicy(L2TunnelPolicy policy) {
-
-        log.debug("Reversing policy for pseudowire.");
-        try {
-            // if cp1 is a leaf, just return
-            if (srManager.deviceConfiguration().isEdgeDevice(policy.cP1().deviceId())) {
-                return policy;
-            } else {
-                // return a policy with reversed cp1 and cp2, and also with reversed tags
-                return new DefaultL2TunnelPolicy(policy.tunnelId(),
-                                                 policy.cP2(), policy.cP2InnerTag(), policy.cP2OuterTag(),
-                                                 policy.cP1(), policy.cP1InnerTag(), policy.cP1OuterTag());
-
-            }
-        } catch (DeviceConfigNotFoundException e) {
-            // should never come here, since it has been checked before
-            log.error("Configuration for device {}, does not exist!");
-            return null;
-        }
-    }
-
-    /**
-     * Reverses a link.
-     *
-     * @param link link to be reversed
-     * @return the reversed link
-     */
-    private Link reverseLink(Link link) {
-
-        DefaultLink.Builder linkBuilder = DefaultLink.builder();
-
-        linkBuilder.src(link.dst());
-        linkBuilder.dst(link.src());
-        linkBuilder.type(link.type());
-        linkBuilder.providerId(link.providerId());
-
-        return linkBuilder.build();
-    }
-
-    /**
-     * Returns the path betwwen two connect points.
-     *
-     * @param srcCp source connect point
-     * @param dstCp destination connect point
-     * @return the path
-     */
-    private List<Link> getPath(ConnectPoint srcCp, ConnectPoint dstCp) {
-
-        // use SRLinkWeigher to avoid pair links, and also
-        // avoid going from the spine to the leaf and to the
-        // spine again, we need to have the leaf as CP1 here.
-        LinkWeigher srLw = new SRLinkWeigher(srManager, srcCp.deviceId(), new HashSet<Link>());
-
-        Set<Path> paths = srManager.topologyService.
-                getPaths(srManager.topologyService.currentTopology(),
-                srcCp.deviceId(), dstCp.deviceId(), srLw);
-
-        log.debug("Paths obtained from topology service {}", paths);
-
-        // We randomly pick a path.
-        if (paths.isEmpty()) {
-            return null;
-        }
-        int size = paths.size();
-        int index = RandomUtils.nextInt(0, size);
-
-        List<Link> result = Iterables.get(paths, index).links();
-        log.debug("Randomly picked a path {}", result);
-
-        return result;
-    }
-
-    /**
-     * Deletes a given policy using the parameter supplied.
-     *
-     * @param tunnelId     the tunnel id
-     * @param ingress      the ingress point
-     * @param ingressInner the ingress inner vlan id
-     * @param ingressOuter the ingress outer vlan id
-     * @param future       to perform the async operation
-     * @param direction    the direction: forward or reverse
-     */
-    private void deletePolicy(long tunnelId, ConnectPoint ingress, VlanId ingressInner, VlanId ingressOuter,
-                              VlanId egressVlan, CompletableFuture<ObjectiveError> future, Direction direction) {
-
-        String key = generateKey(tunnelId, direction);
-        if (!l2InitiationNextObjStore.containsKey(key)) {
-            log.error("Abort delete of policy for tunnel {}: next does not exist in the store", tunnelId);
-            if (future != null) {
-                future.complete(null);
-            }
-            return;
-        }
-        NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
-        int nextId = nextObjective.id();
-        List<Objective> objectives = Lists.newArrayList();
-        // We create the forwarding objective.
-        ForwardingObjective.Builder fwdBuilder = createInitFwdObjective(tunnelId, ingress.port(), nextId);
-        ObjectiveContext context = new ObjectiveContext() {
-            @Override
-            public void onSuccess(Objective objective) {
-                log.debug("Previous fwdObj for policy {} removed", tunnelId);
-                if (future != null) {
-                    future.complete(null);
-                }
-            }
-
-            @Override
-            public void onError(Objective objective, ObjectiveError error) {
-                log.error("Failed to remove previous fwdObj for policy {}: {}", tunnelId, error);
-                if (future != null) {
-                    future.complete(error);
-                }
-            }
-        };
-        objectives.add(fwdBuilder.remove(context));
-        // We create the filtering objective to define the
-        // permit traffic in the switch
-        FilteringObjective.Builder filtBuilder = createFiltObjective(ingress.port(), ingressInner, ingressOuter);
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment
-                .builder()
-                .setTunnelId(tunnelId)
-                .setVlanId(egressVlan);
-        filtBuilder.withMeta(treatment.build());
-        context = new DefaultObjectiveContext((objective) -> log.debug("FilterObj for policy {} revoked", tunnelId),
-                                              (objective, error) ->
-                                                      log.warn("Failed to revoke filterObj for policy {}",
-                                                               tunnelId, error));
-        objectives.add(filtBuilder.remove(context));
-
-        for (Objective objective : objectives) {
-            if (objective instanceof ForwardingObjective) {
-                srManager.flowObjectiveService.forward(ingress.deviceId(), (ForwardingObjective) objective);
-            } else {
-                srManager.flowObjectiveService.filter(ingress.deviceId(), (FilteringObjective) objective);
-            }
-        }
-    }
-
-    /**
-     * Deletes the pseudo wire initiation.
-     *
-     * @param l2TunnelId the tunnel id
-     * @param ingress    the ingress connect point
-     * @param future     to perform an async operation
-     * @param direction  the direction: reverse of forward
-     */
-    private void tearDownPseudoWireInit(long l2TunnelId, ConnectPoint ingress,
-                                        CompletableFuture<ObjectiveError> future, Direction direction) {
-        log.debug("Starting tearing dowing initation of pseudowire {} for direction {}.",
-                  l2TunnelId, direction == FWD ? "forward" : "reverse");
-        String key = generateKey(l2TunnelId, direction);
-        if (!l2InitiationNextObjStore.containsKey(key)) {
-            log.error("Abort delete of {} for {}: next does not exist in the store", INITIATION, key);
-            if (future != null) {
-                future.complete(null);
-            }
-            return;
-        }
-
-        // un-comment in case you want to delete groups used by the pw
-        // however, this will break the update of pseudowires cause the L2 interface group can
-        // not be deleted (it is referenced by other groups)
-        /*
-        NextObjective nextObjective = l2InitiationNextObjStore.get(key).value();
-        ObjectiveContext context = new ObjectiveContext() {
-            @Override
-            public void onSuccess(Objective objective) {
-                log.debug("Previous {} next for {} removed", INITIATION, key);
-                if (future != null) {
-                    future.complete(null);
-                }
-            }
-
-            @Override
-            public void onError(Objective objective, ObjectiveError error) {
-                log.warn("Failed to remove previous {} next for {}: {}", INITIATION, key, error);
-                if (future != null) {
-                    future.complete(error);
-                }
-            }
-        };
-        srManager.flowObjectiveService.next(ingress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
-        */
-
-        future.complete(null);
-        l2InitiationNextObjStore.remove(key);
-    }
-
-    /**
-     * Deletes the pseudo wire termination.
-     *
-     * @param l2Tunnel  the tunnel
-     * @param egress    the egress connect point
-     * @param future    the async task
-     * @param direction the direction of the tunnel
-     */
-    private void tearDownPseudoWireTerm(L2Tunnel l2Tunnel,
-                                        ConnectPoint egress,
-                                        CompletableFuture<ObjectiveError> future,
-                                        Direction direction) {
-        log.debug("Starting tearing down termination for pseudowire {} direction {}.",
-                  l2Tunnel.tunnelId(), direction == FWD ? "forward" : "reverse");
-        String key = generateKey(l2Tunnel.tunnelId(), direction);
-        if (!l2TerminationNextObjStore.containsKey(key)) {
-            log.error("Abort delete of {} for {}: next does not exist in the store", TERMINATION, key);
-            if (future != null) {
-                future.complete(null);
-            }
-            return;
-        }
-        NextObjective nextObjective = l2TerminationNextObjStore.get(key).value();
-        ForwardingObjective.Builder fwdBuilder = createTermFwdObjective(l2Tunnel.pwLabel(),
-                                                                        l2Tunnel.tunnelId(),
-                                                                        egress.port(),
-                                                                        nextObjective.id());
-        ObjectiveContext context = new DefaultObjectiveContext((objective) ->
-                                                                       log.debug("FwdObj for {} {}, " +
-                                                                                         "direction {} removed",
-                                                                                        TERMINATION,
-                                                                                        l2Tunnel.tunnelId(),
-                                                                                        direction),
-                                                               (objective, error) ->
-                                                                       log.warn("Failed to remove fwdObj " +
-                                                                                        "for {} {}" +
-                                                                                        ", direction {}",
-                                                                                TERMINATION,
-                                                                                l2Tunnel.tunnelId(),
-                                                                                error,
-                                                                                direction));
-        srManager.flowObjectiveService.forward(egress.deviceId(), fwdBuilder.remove(context));
-
-        // un-comment in case you want to delete groups used by the pw
-        // however, this will break the update of pseudowires cause the L2 interface group can
-        // not be deleted (it is referenced by other groups)
-        /*
-        context = new ObjectiveContext() {
-            @Override
-            public void onSuccess(Objective objective) {
-                log.debug("Previous {} next for {} removed", TERMINATION, key);
-                if (future != null) {
-                    future.complete(null);
-                }
-            }
-
-            @Override
-            public void onError(Objective objective, ObjectiveError error) {
-                log.warn("Failed to remove previous {} next for {}: {}", TERMINATION, key, error);
-                if (future != null) {
-                    future.complete(error);
-                }
-            }
-        };
-        srManager.flowObjectiveService.next(egress.deviceId(), (NextObjective) nextObjective.copy().remove(context));
-        */
-
-        l2TerminationNextObjStore.remove(key);
-        future.complete(null);
-    }
-
-    /**
-     * Utilities to generate pw key.
-     *
-     * @param tunnelId  the tunnel id
-     * @param direction the direction of the pw
-     * @return the key of the store
-     */
-    private String generateKey(long tunnelId, Direction direction) {
-        return String.format("%s-%s", tunnelId, direction);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
deleted file mode 100644
index 4738744..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelPolicy.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import com.google.common.base.MoreObjects;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * Implementation of the default l2 tunnel policy.
- */
-public class DefaultL2TunnelPolicy implements L2TunnelPolicy {
-
-    /**
-     * Id of the tunnel associated to this policy.
-     */
-    private long tunnelId;
-    /**
-     * First connect point.
-     */
-    private ConnectPoint cP1;
-    /**
-     * Second connect point.
-     */
-    private ConnectPoint cP2;
-    /**
-     * cP1 inner vlan tag. Used in QinQ packets.
-     */
-    private VlanId cP1InnerTag;
-    /**
-     * cP1 outer vlan tag.
-     */
-    private VlanId cP1OuterTag;
-    /**
-     * cP2 inner vlan tag. Used in QinQ packets.
-     */
-    private VlanId cP2InnerTag;
-    /**
-     * cP2 outer vlan tag.
-     */
-    private VlanId cP2OuterTag;
-
-    /**
-     * Creates a default l2 tunnel policy using
-     * the given parameters.
-     *
-     * @param tunnelId the tunnel id
-     * @param cP1 the first connect point
-     * @param cP1InnerTag the cP1 inner tag
-     * @param cP1OuterTag the cP1 outer tag
-     * @param cP2 the second connect point
-     * @param cP2InnerTag the cP2 inner tag
-     * @param cP2OuterTag the cP2 outer tag
-     */
-    public DefaultL2TunnelPolicy(long tunnelId,
-                                 ConnectPoint cP1, VlanId cP1InnerTag, VlanId cP1OuterTag,
-                                 ConnectPoint cP2, VlanId cP2InnerTag, VlanId cP2OuterTag) {
-        this.cP1 = checkNotNull(cP1);
-        this.cP2 = checkNotNull(cP2);
-        this.tunnelId = tunnelId;
-        this.cP1InnerTag = cP1InnerTag;
-        this.cP1OuterTag = cP1OuterTag;
-        this.cP2InnerTag = cP2InnerTag;
-        this.cP2OuterTag = cP2OuterTag;
-    }
-
-    /**
-     * Creates a default l2 policy given the provided policy.
-     * @param policy L2policy to replicate
-     */
-    public DefaultL2TunnelPolicy(DefaultL2TunnelPolicy policy) {
-
-        this.cP1 = policy.cP1;
-        this.cP2 = policy.cP2;
-        this.tunnelId = policy.tunnelId;
-        this.cP1InnerTag = policy.cP1InnerTag;
-        this.cP1OuterTag = policy.cP1OuterTag;
-        this.cP2InnerTag = policy.cP2InnerTag;
-        this.cP2OuterTag = policy.cP2OuterTag;
-    }
-
-    @Override
-    public ConnectPoint cP1() {
-        return cP1;
-    }
-
-    @Override
-    public ConnectPoint cP2() {
-        return cP2;
-    }
-
-    @Override
-    public VlanId cP1InnerTag() {
-        return cP1InnerTag;
-    }
-
-    @Override
-    public VlanId cP1OuterTag() {
-        return cP1OuterTag;
-    }
-
-    @Override
-    public VlanId cP2InnerTag() {
-        return cP2InnerTag;
-    }
-
-    @Override
-    public VlanId cP2OuterTag() {
-        return cP2OuterTag;
-    }
-
-    @Override
-    public long tunnelId() {
-        return this.tunnelId;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(tunnelId,
-                            cP1,
-                            cP2,
-                            cP1InnerTag,
-                            cP1OuterTag,
-                            cP2InnerTag,
-                            cP2OuterTag
-        );
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-
-        if (o instanceof DefaultL2TunnelPolicy) {
-            DefaultL2TunnelPolicy that = (DefaultL2TunnelPolicy) o;
-            if (this.tunnelId == that.tunnelId &&
-                    this.cP1.equals(that.cP1) &&
-                    this.cP2.equals(that.cP2) &&
-                    this.cP1InnerTag.equals(that.cP1InnerTag) &&
-                    this.cP1OuterTag.equals(that.cP1OuterTag) &&
-                    this.cP2InnerTag.equals(that.cP2InnerTag) &&
-                    this.cP2OuterTag.equals(that.cP2OuterTag)) {
-                return true;
-            }
-        }
-
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(this)
-                .add("tunnelId", tunnelId())
-                .add("cP1", cP1())
-                .add("cP2", cP2())
-                .add("cP1InnerTag", cP1InnerTag())
-                .add("cP1OuterTag", cP1OuterTag())
-                .add("cP2InnerTag", cP2InnerTag())
-                .add("cP2OuterTag", cP2OuterTag())
-                .toString();
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2Mode.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2Mode.java
deleted file mode 100644
index 685a606..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2Mode.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-/**
- * Enum to identify mode of the pwaas.
- */
-public enum L2Mode {
-    /**
-     * Raw mode.
-     */
-    RAW,
-    /**
-     * Tagged mode. In this case the packet need
-     * the sd tag.
-     */
-    TAGGED
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2Tunnel.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2Tunnel.java
deleted file mode 100644
index 3e98480..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2Tunnel.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import org.onlab.packet.MplsLabel;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.Link;
-
-import java.util.List;
-
-public interface L2Tunnel {
-
-    /**
-     * Return the mode of the l2 tunnel.
-     *
-     * @return The pw mode.
-     */
-    L2Mode pwMode();
-
-    /**
-     * Returns the service delimiting tag.
-     *
-     * @return the sd tag
-     */
-    VlanId sdTag();
-
-    /**
-     * Returns the id of the tunnel.
-     *
-     * @return the tunnel id
-     */
-    long tunnelId();
-
-    /**
-     * Return the label of the pseudowire.
-     *
-     * @return the pw label.
-     */
-    MplsLabel pwLabel();
-
-    /**
-     * Returns the path used by the pseudowire.
-     *
-     * @return The path that is used
-     */
-    List<Link> pathUsed();
-
-    /**
-     * Returns the transport vlan used by the pseudowire.
-     *
-     * @return The transport vlan
-     */
-    VlanId transportVlan();
-
-    /**
-     * Returns the inter-co label used by the pseudowire.
-     *
-     * @return The inter CO label.
-     */
-    MplsLabel interCoLabel();
-
-    /**
-     * Sets the path that this pw uses.
-     *
-     * @param path The apth to use
-     */
-    void setPath(List<Link> path);
-
-    /**
-     * Set the transport vlan that this pw will use.
-     *
-     * @param vlan The vlan to use.
-     */
-    void setTransportVlan(VlanId vlan);
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelDescription.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelDescription.java
deleted file mode 100644
index 7be187a..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelDescription.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-public interface L2TunnelDescription {
-
-    /**
-     * Returns the l2 tunnel.
-     *
-     * @return the l2 tunnel
-     */
-    L2Tunnel l2Tunnel();
-
-    /**
-     * Returns the l2 tunnel policy.
-     *
-     * @return the l2 tunnel policy.
-     */
-    L2TunnelPolicy l2TunnelPolicy();
-
-    /**
-     * Sets the l2 tunnel.
-     *
-     * @param tunnel the l2 tunnel to set.
-     */
-    void setL2Tunnel(L2Tunnel tunnel);
-
-    /**
-     * Sets the l2 policy.
-     *
-     * @param policy the policy to set.
-     */
-    void setL2TunnelPolicy(L2TunnelPolicy policy);
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
deleted file mode 100644
index 2a003b3..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelHandler.java
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import com.google.common.collect.ImmutableMap;
-import org.onosproject.net.flowobjective.NextObjective;
-
-import java.util.List;
-import java.util.Set;
-
-public interface L2TunnelHandler {
-    void init();
-
-    /**
-     * Combines policies and tunnels to create descriptions.
-     *
-     * @param pending if it is true return pending to be installed pseudowires
-     *                from the appropriate store, else return installed pseudowires
-     * @return Set of l2 tunnel descriptions.
-     */
-    Set<L2TunnelDescription> getL2Descriptions(boolean pending);
-
-    /**
-     * Returns a copy of the l2 policies that exist in the store.
-     *
-     * @return The l2 policies
-     */
-    List<L2TunnelPolicy> getL2Policies();
-
-    /**
-     * Returns a copy of the l2 tunnels that exist in the store.
-     *
-     * @return The l2 tunnels.
-     */
-    List<L2Tunnel> getL2Tunnels();
-
-    /**
-     * Returns a copy of the pending l2 policies that exist in the store.
-     *
-     * @return The l2 policies
-     */
-    List<L2TunnelPolicy> getL2PendingPolicies();
-
-    /**
-     * Helper function to handle the pw removal.
-     * <p>
-     * This method should for the mastership of the device because it is
-     * used only from network configuration updates, thus we only want
-     * one instance only to program each pseudowire.
-     *
-     * @param pwToRemove the pseudo wires to remove
-     * @deprecated onos-1.12 Do not use this method.
-     */
-    @Deprecated
-    void tearDown(Set<L2TunnelDescription> pwToRemove);
-
-    /**
-     * Returns a copy of the pending l2 tunnels that exist in the store.
-     *
-     * @return The l2 tunnels.
-     */
-    List<L2Tunnel> getL2PendingTunnels();
-
-    /**
-     * Verifies global validity for existing pseudowires, both ones in
-     * the pending store and the ones installed.
-     *
-     * @param pwToAdd the new pseudowire to add
-     * @return a Result describing the outcome
-     */
-    Result verifyGlobalValidity(L2TunnelDescription pwToAdd);
-
-    /**
-     * Check if pseudowire exists in the store.
-     *
-     * @param tunnelId The tunnel id to check for.
-     * @param pending Check in pending store for pseudowires.
-     * @return The result of the operation.
-     */
-    Result checkIfPwExists(long tunnelId, boolean pending);
-
-    /**
-     * Returns the PW init next objective store.
-     *
-     * @return current contents of the l2InitiationNextObjStore
-     */
-    ImmutableMap<String, NextObjective> getInitNext();
-
-    /**
-     * Returns the PW termination next objective store.
-     *
-     * @return current contents of the l2TerminationNextObjStore
-     */
-    ImmutableMap<String, NextObjective> getTermNext();
-
-    /**
-     * Removes given next ID from both PW init/term next obj store.
-     *
-     * @param nextId next ID
-     */
-    void removeNextId(int nextId);
-
-    /**
-     * Pwaas pipelines.
-     */
-    enum Pipeline {
-        /**
-         * The initiation pipeline.
-         */
-        INITIATION, /**
-         * The termination pipeline.
-         */
-        TERMINATION
-    }
-
-    /**
-     * Enum helper to carry results of various operations.
-     */
-    enum Result {
-        /**
-         * Happy ending scenario.
-         */
-        SUCCESS(0, "No error occurred"),
-
-        /**
-         * We have problems with the supplied parameters.
-         */
-        WRONG_PARAMETERS(1, "Wrong parameters"),
-
-        /**
-         * We have an internal error during the deployment
-         * or removal phase.
-         */
-        INTERNAL_ERROR(3, "Internal error"),
-
-        /**
-         * No path found between the connection points.
-         */
-        PATH_NOT_FOUND(7, "Could not find valid path between connection points!"),
-
-        /**
-         * Error in global pseudowires configuration.
-         */
-        CONFIGURATION_ERROR(8, "Conflicting pseudowire configurations!");
-
-        private final int code;
-        private final String description;
-
-        private String specificError;
-        private int nextId;
-
-        Result(int code, String description) {
-            this.code = code;
-            this.description = description;
-        }
-
-        public Result appendError(String error) {
-           this.specificError = error;
-           return this;
-        }
-
-        public String getSpecificError() {
-            return specificError;
-        }
-
-        public String getDescription() {
-            return description;
-        }
-
-        public int getNextId() {
-            return nextId;
-        }
-
-        protected void setNextId(int nextId) {
-            this.nextId = nextId;
-        }
-
-        @Override
-        public String toString() {
-            return code + ": " + description;
-        }
-    }
-
-    /**
-     * Enum helper for handling the direction of the pw.
-     */
-    enum Direction {
-        /**
-         * The forward direction of the pseudo wire.
-         */
-        FWD, /**
-         * The reverse direction of the pseudo wire.
-         */
-        REV
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelPolicy.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelPolicy.java
deleted file mode 100644
index 17be45b..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/L2TunnelPolicy.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-
-public interface L2TunnelPolicy {
-
-    /**
-     * Returns the first connect point of the policy.
-     *
-     * @return first connect point
-     */
-    ConnectPoint cP1();
-
-    /**
-     * Returns the second connect point of the policy.
-     *
-     * @return second connect point
-     */
-    ConnectPoint cP2();
-
-    /**
-     * Returns the cP1 inner vlan tag of the policy.
-     *
-     * @return cP1 inner vlan tag
-     */
-    VlanId cP1InnerTag();
-
-    /**
-     * Returns the cP1 outer vlan tag of the policy.
-     *
-     * @return cP1 outer vlan tag
-     */
-    VlanId cP1OuterTag();
-
-    /**
-     * Returns the cP2 inner vlan tag of the policy.
-     *
-     * @return cP2 inner vlan tag
-     */
-    VlanId cP2InnerTag();
-
-    /**
-     * Returns the cP2 outer vlan tag of the policy.
-     *
-     * @return cP2 outer vlan tag
-     */
-    VlanId cP2OuterTag();
-
-    /**
-     * Returns the tunnel ID of the policy.
-     *
-     * @return Tunnel ID
-     */
-    long tunnelId();
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/PwaasUtil.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/PwaasUtil.java
deleted file mode 100644
index 7cb60a6..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/PwaasUtil.java
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- * Copyright 2015-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import org.onlab.osgi.ServiceNotFoundException;
-import org.onlab.packet.MplsLabel;
-import org.onlab.packet.VlanId;
-import org.onosproject.cli.AbstractShellCommand;
-import org.onosproject.net.ConnectPoint;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.intf.InterfaceService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Utility class with static methods that help
- * parse pseudowire related information and also
- * verify that a pseudowire combination is valid.
- */
-public final class PwaasUtil {
-
-    private static final Logger log = LoggerFactory.getLogger(PwaasUtil.class);
-
-    private static DeviceService deviceService;
-    private static InterfaceService intfService;
-
-    // Suppress ExceptionInInitializerError and simply set the value to null,
-    // such that unit tests have a chance to replace the variable with mocked service
-    static {
-        try {
-            deviceService = AbstractShellCommand.get(DeviceService.class);
-        } catch (NullPointerException | ServiceNotFoundException e) {
-            deviceService = null;
-        }
-        try {
-            intfService = AbstractShellCommand.get(InterfaceService.class);
-        } catch (NullPointerException | ServiceNotFoundException e) {
-            intfService = null;
-        }
-    }
-
-    static final String ERR_SERVICE_UNAVAIL = "Service %s not available";
-    static final String ERR_SAME_DEV =
-            "Pseudowire connection points can not reside in the same node, in pseudowire %d.";
-    static final String ERR_EMPTY_INNER_WHEN_OUTER_PRESENT =
-            "Inner tag should not be empty when outer tag is set for pseudowire %d for %s.";
-    static final String ERR_WILDCARD_VLAN =
-            "Wildcard VLAN matching not yet supported for pseudowire %d.";
-    static final String ERR_DOUBLE_TO_UNTAGGED =
-            "Support for double tag <-> untag is not supported for pseudowire %d.";
-    static final String ERR_DOUBLE_TO_SINGLE =
-            "Support for double-tag<-> single-tag is not supported for pseudowire %d.";
-    static final String ERR_SINGLE_TO_UNTAGGED =
-            "single-tag <-> untag is not supported for pseudowire %d.";
-    static final String ERR_VLAN_TRANSLATION =
-            "We do not support VLAN translation for pseudowire %d.";
-    static final String ERR_DEV_NOT_FOUND =
-            "Device %s does not exist for pseudowire %d.";
-    static final String ERR_PORT_NOT_FOUND =
-            "Port %s of device %s does not exist for pseudowire %d.";
-
-    private PwaasUtil() {
-
-    }
-
-    /**
-     * Parses a vlan as a string. Returns the VlanId if
-     * provided String can be parsed as an integer or is '' / '*'
-     *
-     * @param vlan string as read from configuration
-     * @return VlanId null if error
-     */
-    public static VlanId parseVlan(String vlan) {
-
-        if (vlan.equals("*") || vlan.equals("Any")) {
-            return VlanId.vlanId("Any");
-        } else if (vlan.equals("") || vlan.equals("None")) {
-            return VlanId.vlanId("None");
-        } else {
-            return VlanId.vlanId(vlan);
-        }
-    }
-
-    /**
-     *
-     * @param mode RAW or TAGGED
-     * @return the L2Mode if input is correct
-     */
-    public static L2Mode parseMode(String mode) {
-        checkArgument(mode.equals("RAW") || mode.equals("TAGGED"),
-                      "Invalid pseudowire mode of operation, should be TAGGED or RAW.");
-        return L2Mode.valueOf(mode);
-    }
-
-    /**
-     *
-     * @param label the mpls label of the pseudowire
-     * @return the MplsLabel
-     * @throws IllegalArgumentException if label is invalid
-     */
-    public static MplsLabel parsePWLabel(String label) {
-        return MplsLabel.mplsLabel(label);
-    }
-
-    /**
-     * Parses a string as a pseudowire id - which is an integer.
-     *
-     * @param id The id of pw in string form
-     * @return The id of pw as an Integer or null if it failed the conversion.
-     */
-    public static Integer parsePwId(String id) {
-        return Integer.parseInt(id);
-    }
-
-    /**
-     * Helper method to verify if the tunnel is whether or not
-     * supported.
-     *
-     * @param l2Tunnel the tunnel to verify
-     */
-    private static void verifyTunnel(L2Tunnel l2Tunnel) {
-
-        // Service delimiting tag not supported yet.
-        if (!l2Tunnel.sdTag().equals(VlanId.NONE)) {
-            throw new IllegalArgumentException(String.format("Service delimiting tag not supported yet for " +
-                                                                     "pseudowire %d.", l2Tunnel.tunnelId()));
-        }
-
-        // Tag mode not supported yet.
-        if (l2Tunnel.pwMode() == L2Mode.TAGGED) {
-            throw new IllegalArgumentException(String.format("Tagged mode not supported yet for pseudowire %d.",
-                                                             l2Tunnel.tunnelId()));
-        }
-
-        // Raw mode without service delimiting tag
-        // is the only mode supported for now.
-    }
-
-    /**
-     * Helper method to verify if the policy is whether or not
-     * supported and if policy will be successfully instantiated in the
-     * network.
-     *
-     * @param cP1 pseudo wire endpoint 1
-     * @param cP2 pseudo wire endpoint 2
-     * @param ingressInner the ingress inner tag
-     * @param ingressOuter the ingress outer tag
-     * @param egressInner the egress inner tag
-     * @param egressOuter the egress outer tag
-     * @param tunnelId tunnel ID
-     */
-    static void verifyPolicy(ConnectPoint cP1,
-                              ConnectPoint cP2,
-                              VlanId ingressInner,
-                              VlanId ingressOuter,
-                              VlanId egressInner,
-                              VlanId egressOuter,
-                              Long tunnelId) {
-
-        if (cP1.deviceId().equals(cP2.deviceId())) {
-            throw new IllegalArgumentException(String.format(ERR_SAME_DEV, tunnelId));
-        }
-
-        // We can have multiple tags, all of them can be NONE,
-        // indicating untagged traffic, however, the outer tag can
-        // not have value if the inner tag is None
-        if (ingressInner.equals(VlanId.NONE) && !ingressOuter.equals(VlanId.NONE)) {
-            throw new IllegalArgumentException(String.format(ERR_EMPTY_INNER_WHEN_OUTER_PRESENT,
-                    tunnelId, "cp1"));
-        }
-
-        if (egressInner.equals(VlanId.NONE) && !egressOuter.equals(VlanId.NONE)) {
-            throw new IllegalArgumentException(String.format(ERR_EMPTY_INNER_WHEN_OUTER_PRESENT,
-                    tunnelId, "cp2"));
-        }
-
-        if (ingressInner.equals(VlanId.ANY) ||
-                ingressOuter.equals(VlanId.ANY) ||
-                egressInner.equals(VlanId.ANY) ||
-                egressOuter.equals(VlanId.ANY)) {
-            throw new IllegalArgumentException(String.format(ERR_WILDCARD_VLAN, tunnelId));
-        }
-
-        if (((!ingressOuter.equals(VlanId.NONE) && !ingressInner.equals(VlanId.NONE)) &&
-                (egressOuter.equals(VlanId.NONE) && egressInner.equals(VlanId.NONE)))
-                || ((ingressOuter.equals(VlanId.NONE) && ingressInner.equals(VlanId.NONE)) &&
-                (!egressOuter.equals(VlanId.NONE) && !egressInner.equals(VlanId.NONE)))) {
-            throw new IllegalArgumentException(String.format(ERR_DOUBLE_TO_UNTAGGED, tunnelId));
-        }
-        if ((!ingressInner.equals(VlanId.NONE) &&
-                ingressOuter.equals(VlanId.NONE) &&
-                !egressOuter.equals(VlanId.NONE))
-                || (egressOuter.equals(VlanId.NONE) &&
-                !egressInner.equals(VlanId.NONE) &&
-                !ingressOuter.equals(VlanId.NONE))) {
-            throw new IllegalArgumentException(String.format(ERR_DOUBLE_TO_SINGLE, tunnelId));
-        }
-
-        if ((ingressInner.equals(VlanId.NONE) && !egressInner.equals(VlanId.NONE))
-                || (!ingressInner.equals(VlanId.NONE) && egressInner.equals(VlanId.NONE))) {
-            throw new IllegalArgumentException(String.format(ERR_SINGLE_TO_UNTAGGED, tunnelId));
-        }
-
-        // FIXME PW VLAN translation is not supported on Dune
-        //       Need to explore doing that in egress table later if there is a requirement
-        if (!ingressInner.equals(egressInner) || !ingressOuter.equals(egressOuter)) {
-            throw new IllegalArgumentException(String.format(ERR_VLAN_TRANSLATION, tunnelId));
-        }
-
-        if (deviceService == null) {
-            throw new IllegalStateException(String.format(ERR_SERVICE_UNAVAIL, "DeviceService"));
-        }
-
-        // check if cp1 and port of cp1 exist
-        if (deviceService.getDevice(cP1.deviceId()) == null) {
-            throw new IllegalArgumentException(String.format(ERR_DEV_NOT_FOUND, cP1.deviceId(), tunnelId));
-        }
-
-        if (deviceService.getPort(cP1) == null) {
-            throw new IllegalArgumentException(String.format(ERR_PORT_NOT_FOUND, cP1.port(),
-                    cP1.deviceId(), tunnelId));
-        }
-
-        // check if cp2 and port of cp2 exist
-        if (deviceService.getDevice(cP2.deviceId()) == null) {
-            throw new IllegalArgumentException(String.format(ERR_DEV_NOT_FOUND, cP2.deviceId(), tunnelId));
-        }
-
-        if (deviceService.getPort(cP2) == null) {
-            throw new IllegalArgumentException(String.format(ERR_PORT_NOT_FOUND, cP2.port(),
-                    cP2.deviceId(), tunnelId));
-        }
-    }
-
-    /**
-     * Verifies that the pseudowires will not conflict with each other.
-     *
-     * Further, check if vlans for connect points are already used.
-     *
-     * @param tunnel Tunnel for pw
-     * @param policy Policy for pw
-     * @param labelSet Label set used so far with this configuration
-     * @param vlanSet Vlan set used with this configuration
-     * @param tunnelSet Tunnel set used with this configuration
-     */
-    private static void verifyGlobalValidity(L2Tunnel tunnel,
-                                      L2TunnelPolicy policy,
-                                      Set<MplsLabel> labelSet,
-                                      Map<ConnectPoint, Set<VlanId>> vlanSet,
-                                      Set<Long> tunnelSet) {
-
-        if (tunnelSet.contains(tunnel.tunnelId())) {
-            throw new IllegalArgumentException(String.valueOf(String.format("Tunnel Id %d already used by" +
-                                                                                    " another pseudowire, in " +
-                                                                                    "pseudowire %d!",
-                                                                            tunnel.tunnelId(),
-                                                                            tunnel.tunnelId())));
-        }
-        tunnelSet.add(tunnel.tunnelId());
-
-        // check if tunnel id is used again
-        ConnectPoint cP1 = policy.cP1();
-        ConnectPoint cP2 = policy.cP2();
-
-        // insert cps to hashmap if this is the first time seen
-        if (!vlanSet.containsKey(cP1)) {
-            vlanSet.put(cP1, new HashSet<VlanId>());
-        }
-        if (!vlanSet.containsKey(cP2)) {
-            vlanSet.put(cP2, new HashSet<VlanId>());
-        }
-
-        // if single tagged or untagged vlan is the inner
-        // if double tagged vlan is the outer
-        VlanId vlanToCheckCP1;
-        if (policy.cP1OuterTag().equals(VlanId.NONE)) {
-            vlanToCheckCP1 = policy.cP1InnerTag();
-        } else {
-            vlanToCheckCP1 = policy.cP1OuterTag();
-        }
-
-        VlanId vlanToCheckCP2;
-        if (policy.cP2OuterTag().equals(VlanId.NONE)) {
-            vlanToCheckCP2 = policy.cP2InnerTag();
-        } else {
-            vlanToCheckCP2 = policy.cP2OuterTag();
-        }
-
-        if (labelSet.contains(tunnel.pwLabel())) {
-            throw new IllegalArgumentException(String.valueOf(String.format("Label %s already used by another" +
-                                                                                    " pseudowire, in pseudowire %d!",
-                                                                            tunnel.pwLabel(), tunnel.tunnelId())));
-        }
-        labelSet.add(tunnel.pwLabel());
-
-        if (vlanSet.get(cP1).contains(vlanToCheckCP1)) {
-            throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP1 %s already used " +
-                                                                                    "by another pseudowire, in " +
-                                                                                    "pseudowire" +
-                                                                                    " %d!", vlanToCheckCP1,  cP1,
-                                                                            tunnel.tunnelId())));
-        }
-        vlanSet.get(cP1).add(vlanToCheckCP1);
-
-        if (vlanSet.get(cP2).contains(vlanToCheckCP2)) {
-            throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP2 %s already used" +
-                                                                                    " by another pseudowire, in" +
-                                                                                    " pseudowire %d!", vlanToCheckCP2,
-                                                                            cP2,
-                                                                            tunnel.tunnelId())));
-        }
-        vlanSet.get(cP2).add(vlanToCheckCP2);
-
-        if (intfService == null) {
-            throw new IllegalStateException(String.format(ERR_SERVICE_UNAVAIL, "InterfaceService"));
-        }
-
-        // check that vlans for the connect points are not used
-        intfService.getInterfacesByPort(cP1).stream()
-                .forEach(intf -> {
-
-                    // check if tagged pw affects tagged interface
-                    if (intf.vlanTagged().contains(vlanToCheckCP1)) {
-                        throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP1 %s already" +
-                                                                                                " used for this" +
-                                                                                                " interface, in" +
-                                                                                                " pseudowire %d!",
-                                                                                        vlanToCheckCP1, cP1,
-                                                                                        tunnel.tunnelId())));
-                    }
-
-                    // if vlanNative != null this interface is configured with untagged traffic also
-                    // check if it collides with untagged interface
-                    if ((intf.vlanNative() != null) && vlanToCheckCP1.equals(VlanId.NONE)) {
-                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for cP1 " +
-                                                                                                "%s already used " +
-                                                                                                "for this " +
-                                                                                                "interface, in " +
-                                                                                                "pseudowire " +
-                                                                                                "%d!", cP1,
-                                                                                        tunnel.tunnelId())));
-                    }
-
-                    // if vlanUntagged != null this interface is configured only with untagged traffic
-                    // check if it collides with untagged interface
-                    if ((intf.vlanUntagged() != null) && vlanToCheckCP1.equals(VlanId.NONE)) {
-                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for " +
-                                                                                                "cP1 %s already" +
-                                                                                                " used for this" +
-                                                                                                " interface," +
-                                                                                                " in pseudowire %d!",
-                                                                                        cP1, tunnel.tunnelId())));
-                    }
-                });
-
-        intfService.getInterfacesByPort(cP2).stream()
-                .forEach(intf -> {
-                    if (intf.vlanTagged().contains(vlanToCheckCP2)) {
-                        throw new IllegalArgumentException(String.valueOf(String.format("Vlan '%s' for cP2 %s " +
-                                                                                                " used for  " +
-                                                                                                "this interface, " +
-                                                                                                "in pseudowire %d!",
-                                                                                        vlanToCheckCP2, cP2,
-                                                                                        tunnel.tunnelId())));
-                    }
-
-                    // if vlanNative != null this interface is configured with untagged traffic also
-                    // check if it collides with untagged interface
-                    if ((intf.vlanNative() != null) && vlanToCheckCP2.equals(VlanId.NONE)) {
-                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic " +
-                                                                                                "for cP2 %s " +
-                                                                                                "already " +
-                                                                                                "used for this" +
-                                                                                                " interface, " +
-                                                                                                "in pseudowire %d!",
-                                                                                        cP2, tunnel.tunnelId())));
-                    }
-
-                    // if vlanUntagged != null this interface is configured only with untagged traffic
-                    // check if it collides with untagged interface
-                    if ((intf.vlanUntagged() != null) && vlanToCheckCP2.equals(VlanId.NONE)) {
-                        throw new IllegalArgumentException(String.valueOf(String.format("Untagged traffic for cP2 %s" +
-                                                                                                " already" +
-                                                                                                " used for " +
-                                                                                                "this interface, " +
-                                                                                                "in pseudowire %d!",
-                                                                                        cP2, tunnel.tunnelId())));
-                    }
-                });
-
-    }
-
-    /**
-     * Helper method to verify the integrity of the pseudo wire.
-     *
-     * @param l2TunnelDescription the pseudo wire description
-     */
-    private static void verifyPseudoWire(L2TunnelDescription l2TunnelDescription,
-                                  Set<MplsLabel> labelSet,
-                                  Map<ConnectPoint, Set<VlanId>> vlanset,
-                                  Set<Long> tunnelSet) {
-
-        L2Tunnel l2Tunnel = l2TunnelDescription.l2Tunnel();
-        L2TunnelPolicy l2TunnelPolicy = l2TunnelDescription.l2TunnelPolicy();
-
-        verifyTunnel(l2Tunnel);
-
-        verifyPolicy(
-                l2TunnelPolicy.cP1(),
-                l2TunnelPolicy.cP2(),
-                l2TunnelPolicy.cP1InnerTag(),
-                l2TunnelPolicy.cP1OuterTag(),
-                l2TunnelPolicy.cP2InnerTag(),
-                l2TunnelPolicy.cP2OuterTag(),
-                l2Tunnel.tunnelId()
-        );
-
-        verifyGlobalValidity(l2Tunnel,
-                             l2TunnelPolicy,
-                             labelSet,
-                             vlanset,
-                             tunnelSet);
-
-    }
-
-    public static L2TunnelHandler.Result configurationValidity(List<L2TunnelDescription> pseudowires) {
-
-        // structures to keep pw information
-        // in order to see if instantiating them will create
-        // problems
-        Set<Long> tunIds = new HashSet<>();
-        Set<MplsLabel> labelsUsed = new HashSet<>();
-        Map<ConnectPoint, Set<VlanId>> vlanIds = new HashMap<>();
-
-        // TODO : I know we should not use exceptions for flow control,
-        // however this code was originally implemented in the configuration
-        // addition where the exceptions were propagated and the configuration was
-        // deemed not valid. I plan in the future to refactor the parts that
-        // check the pseudowire validity.
-        //
-        // Ideally we would like to return a String which could also return to
-        // the user issuing the rest request for adding the pseudowire.
-        try {
-            // check that pseudowires can be instantiated in the network
-            // we try to guarantee that all the pws will work before
-            // instantiating any of them
-            for (L2TunnelDescription pw : pseudowires) {
-                log.debug("Verifying pseudowire {}", pw);
-                verifyPseudoWire(pw, labelsUsed, vlanIds, tunIds);
-            }
-
-            return L2TunnelHandler.Result.SUCCESS;
-        } catch (Exception e) {
-            log.error("Caught exception while validating pseudowire : {}", e.getMessage());
-            return L2TunnelHandler.Result.CONFIGURATION_ERROR
-                    .appendError(e.getMessage());
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/pwaas/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/pwaas/package-info.java
deleted file mode 100644
index 463b163..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/pwaas/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Set of resources implementing the Pwaas.
- */
-package org.onosproject.segmentrouting.pwaas;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/storekey/DestinationSetNextObjectiveStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/storekey/DestinationSetNextObjectiveStoreKey.java
deleted file mode 100644
index 0baa769..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/storekey/DestinationSetNextObjectiveStoreKey.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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/licedses/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.segmentrouting.storekey;
-
-import java.util.Objects;
-
-import org.onosproject.net.DeviceId;
-import org.onosproject.segmentrouting.grouphandler.DestinationSet;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Key of Destination set next objective store.
- */
-public class DestinationSetNextObjectiveStoreKey {
-    private final DeviceId deviceId;
-    private final DestinationSet ds;
-
-    /**
-     * Constructs the key of destination set next objective store.
-     *
-     * @param deviceId device ID
-     * @param ds destination set
-     */
-    public DestinationSetNextObjectiveStoreKey(DeviceId deviceId,
-                                            DestinationSet ds) {
-        this.deviceId = deviceId;
-        this.ds = ds;
-    }
-
-    /**
-     * Returns the device ID in the key.
-     *
-     * @return device ID
-     */
-    public DeviceId deviceId() {
-        return this.deviceId;
-    }
-
-    /**
-     * Returns the destination set in the key.
-     *
-     * @return destination set
-     */
-    public DestinationSet destinationSet() {
-        return this.ds;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof DestinationSetNextObjectiveStoreKey)) {
-            return false;
-        }
-        DestinationSetNextObjectiveStoreKey that =
-                (DestinationSetNextObjectiveStoreKey) o;
-        return (Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.ds, that.ds));
-    }
-
-    // The list of destination ids and label are used for comparison.
-    @Override
-    public int hashCode() {
-        int result = 17;
-        result = 31 * result + Objects.hashCode(this.deviceId)
-                + Objects.hashCode(this.ds);
-
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("deviceId", deviceId)
-                .add("ds", ds)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/storekey/MacVlanNextObjectiveStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/storekey/MacVlanNextObjectiveStoreKey.java
deleted file mode 100644
index 4a1ab78..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/storekey/MacVlanNextObjectiveStoreKey.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting.storekey;
-
-import org.onlab.packet.VlanId;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Key of Device/Vlan/MacAddr to NextObjective store.
- */
-public class MacVlanNextObjectiveStoreKey {
-    private final DeviceId deviceId;
-    private final MacAddress macAddr;
-    private final VlanId vlanId;
-
-    /**
-     * Constructs the key of the next objective store.
-     *
-     * @param deviceId device ID
-     * @param macAddr mac of host
-     * @param vlanId vlan of host
-     */
-    public MacVlanNextObjectiveStoreKey(DeviceId deviceId, MacAddress macAddr, VlanId vlanId) {
-        this.deviceId = deviceId;
-        this.macAddr = macAddr;
-        this.vlanId = vlanId;
-    }
-
-    /**
-     * Gets device id in this MacVlanNextObjectiveStoreKey.
-     *
-     * @return device id
-     */
-    public DeviceId deviceId() {
-        return deviceId;
-    }
-
-    /**
-     * Gets vlan information in this MacVlanNextObjectiveStoreKey.
-     *
-     * @return vlan information
-     */
-    public VlanId vlanId() {
-        return vlanId;
-    }
-
-    /**
-     * Gets mac information in this MacVlanNextObjectiveStoreKey.
-     *
-     * @return mac information
-     */
-    public MacAddress macAddr() {
-        return macAddr;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof MacVlanNextObjectiveStoreKey)) {
-            return false;
-        }
-        MacVlanNextObjectiveStoreKey that =
-                (MacVlanNextObjectiveStoreKey) o;
-        return (Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.vlanId, that.vlanId) &&
-                Objects.equals(this.macAddr, that.macAddr));
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(deviceId, vlanId, macAddr);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("deviceId", deviceId)
-                .add("vlanId", vlanId)
-                .add("macAddr", macAddr)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/storekey/PortNextObjectiveStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/storekey/PortNextObjectiveStoreKey.java
deleted file mode 100644
index 1269bce..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/storekey/PortNextObjectiveStoreKey.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.storekey;
-
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Key of Device/Port to NextObjective store.
- *
- * Since there can be multiple next objectives to the same physical port,
- * we differentiate between them by including the treatment in the key.
- */
-public class PortNextObjectiveStoreKey {
-    private final DeviceId deviceId;
-    private final PortNumber portNum;
-    private final TrafficTreatment treatment;
-    private final TrafficSelector meta;
-
-    /**
-     * Constructs the key of port next objective store.
-     *
-     * @param deviceId device ID
-     * @param portNum port number
-     * @param treatment treatment that will be applied to the interface
-     * @param meta optional data to pass to the driver
-     */
-    public PortNextObjectiveStoreKey(DeviceId deviceId, PortNumber portNum,
-                                     TrafficTreatment treatment,
-                                     TrafficSelector meta) {
-        this.deviceId = deviceId;
-        this.portNum = portNum;
-        this.treatment = treatment;
-        this.meta = meta;
-    }
-
-    /**
-     * Gets device id in this PortNextObjectiveStoreKey.
-     *
-     * @return device id
-     */
-    public DeviceId deviceId() {
-        return deviceId;
-    }
-
-    /**
-     * Gets port information in this PortNextObjectiveStoreKey.
-     *
-     * @return port information
-     */
-    public PortNumber portNumber() {
-        return portNum;
-    }
-
-    /**
-     * Gets treatment information in this PortNextObjectiveStoreKey.
-     *
-     * @return treatment information
-     */
-    public TrafficTreatment treatment() {
-        return treatment;
-    }
-
-    /**
-     * Gets metadata information in this PortNextObjectiveStoreKey.
-     *
-     * @return meta information
-     */
-    public TrafficSelector meta() {
-        return meta;
-    }
-
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof PortNextObjectiveStoreKey)) {
-            return false;
-        }
-        PortNextObjectiveStoreKey that =
-                (PortNextObjectiveStoreKey) o;
-        return (Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.portNum, that.portNum) &&
-                Objects.equals(this.treatment, that.treatment) &&
-                Objects.equals(this.meta, that.meta));
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(deviceId, portNum, treatment, meta);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("deviceId", deviceId)
-                .add("portNum", portNum)
-                .add("treatment", treatment)
-                .add("meta", meta)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java
deleted file mode 100644
index 55a95cd..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/storekey/VlanNextObjectiveStoreKey.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.storekey;
-
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-import static com.google.common.base.MoreObjects.toStringHelper;
-
-/**
- * Key of VLAN to NextObjective store.
- */
-public class VlanNextObjectiveStoreKey {
-    private final DeviceId deviceId;
-    private final VlanId vlanId;
-
-    /**
-     * Constructs the key of VLAN next objective store.
-     *
-     * @param deviceId device ID
-     * @param vlanId VLAN information
-     */
-    public VlanNextObjectiveStoreKey(DeviceId deviceId,
-                                     VlanId vlanId) {
-        this.deviceId = deviceId;
-        this.vlanId = vlanId;
-    }
-
-    /**
-     * Gets device id in this VlanNextObjectiveStoreKey.
-     *
-     * @return device id
-     */
-    public DeviceId deviceId() {
-        return this.deviceId;
-    }
-
-    /**
-     * Gets vlan information in this VlanNextObjectiveStoreKey.
-     *
-     * @return vlan id
-     */
-    public VlanId vlanId() {
-        return this.vlanId;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof VlanNextObjectiveStoreKey)) {
-            return false;
-        }
-        VlanNextObjectiveStoreKey that =
-                (VlanNextObjectiveStoreKey) o;
-        return (Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.vlanId, that.vlanId));
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(deviceId, vlanId);
-    }
-
-    @Override
-    public String toString() {
-        return toStringHelper(getClass())
-                .add("deviceId", deviceId)
-                .add("vlanId", vlanId)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/storekey/XConnectStoreKey.java b/app/src/main/java/org/onosproject/segmentrouting/storekey/XConnectStoreKey.java
deleted file mode 100644
index 0e90a22..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/storekey/XConnectStoreKey.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting.storekey;
-
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-/**
- * Key of VLAN cross-connect next objective store.
- */
-public class XConnectStoreKey {
-    private final DeviceId deviceId;
-    private final VlanId vlanId;
-
-    /**
-     * Constructs the key of cross-connect next objective store.
-     *
-     * @param deviceId device ID of the VLAN cross-connection
-     * @param vlanId VLAN ID of the VLAN cross-connection
-     */
-    public XConnectStoreKey(DeviceId deviceId, VlanId vlanId) {
-        this.deviceId = deviceId;
-        this.vlanId = vlanId;
-    }
-
-    /**
-     * Returns the device ID of this key.
-     *
-     * @return device ID
-     */
-    public DeviceId deviceId() {
-        return this.deviceId;
-    }
-
-    /**
-     * Returns the VLAN ID of this key.
-     *
-     * @return VLAN ID
-     */
-    public VlanId vlanId() {
-        return this.vlanId;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (!(o instanceof XConnectStoreKey)) {
-            return false;
-        }
-        XConnectStoreKey that =
-                (XConnectStoreKey) o;
-        return (Objects.equals(this.deviceId, that.deviceId) &&
-                Objects.equals(this.vlanId, that.vlanId));
-    }
-
-    // The list of neighbor ids and label are used for comparison.
-    @Override
-    public int hashCode() {
-         return Objects.hash(deviceId, vlanId);
-    }
-
-    @Override
-    public String toString() {
-        return "Device: " + deviceId + " VlanId: " + vlanId;
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/storekey/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/storekey/package-info.java
deleted file mode 100644
index 44fc6a7..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/storekey/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * Key data structure of various stores used by Segment Routing.
- */
-package org.onosproject.segmentrouting.storekey;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectCodec.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectCodec.java
deleted file mode 100644
index 83421bc..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectCodec.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.Sets;
-import org.onlab.packet.VlanId;
-import org.onosproject.codec.CodecContext;
-import org.onosproject.codec.JsonCodec;
-import org.onosproject.net.DeviceId;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Set;
-
-/**
- * Codec for Xconnect.
- */
-public class XconnectCodec extends JsonCodec<XconnectDesc> {
-    static final String DEVICE_ID = "deviceId";
-    static final String VLAN_ID = "vlanId";
-    static final String ENDPOINTS = "endpoints";
-
-    private static Logger log = LoggerFactory.getLogger(XconnectCodec.class);
-
-    @Override
-    public ObjectNode encode(XconnectDesc desc, CodecContext context) {
-        final ObjectNode result = context.mapper().createObjectNode();
-        result.put(DEVICE_ID, desc.key().deviceId().toString());
-        result.put(VLAN_ID, desc.key().vlanId().toShort());
-        final ArrayNode portNode = result.putArray(ENDPOINTS);
-        desc.endpoints().forEach(endpoint -> portNode.add(endpoint.toString()));
-
-        return result;
-    }
-
-    @Override
-    public XconnectDesc decode(ObjectNode json, CodecContext context) {
-        DeviceId deviceId = DeviceId.deviceId(json.path(DEVICE_ID).asText());
-        VlanId vlanId = VlanId.vlanId(json.path(VLAN_ID).asText());
-
-        Set<XconnectEndpoint> endpoints = Sets.newHashSet();
-        JsonNode endpointNodes = json.get(ENDPOINTS);
-        if (endpointNodes != null) {
-            endpointNodes.forEach(endpointNode -> endpoints.add(XconnectEndpoint.fromString(endpointNode.asText())));
-        }
-
-        XconnectKey key = new XconnectKey(deviceId, vlanId);
-        return new XconnectDesc(key, endpoints);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectDesc.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectDesc.java
deleted file mode 100644
index a47823a..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectDesc.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-import com.google.common.base.MoreObjects;
-
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Xconnect description.
- */
-public class XconnectDesc {
-    private XconnectKey key;
-    private Set<XconnectEndpoint> endpoints;
-
-    /**
-     * Constructs new Xconnect description with given device ID and VLAN ID.
-     *
-     * @param key Xconnect key
-     * @param endpoints set of endpoints
-     */
-    public XconnectDesc(XconnectKey key, Set<XconnectEndpoint> endpoints) {
-        this.key = key;
-        this.endpoints = endpoints;
-    }
-
-    /**
-     * Gets Xconnect key.
-     *
-     * @return Xconnect key
-     */
-    public XconnectKey key() {
-        return key;
-    }
-
-    /**
-     * Gets endpoints.
-     *
-     * @return set of endpoints
-     */
-    public Set<XconnectEndpoint> endpoints() {
-        return endpoints;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof XconnectDesc)) {
-            return false;
-        }
-        final XconnectDesc other = (XconnectDesc) obj;
-        return Objects.equals(this.key, other.key) &&
-                Objects.equals(this.endpoints, other.endpoints);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(key, endpoints);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("key", key)
-                .add("endpoints", endpoints)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectEndpoint.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectEndpoint.java
deleted file mode 100644
index c7a2242..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectEndpoint.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-/**
- * Represents cross connect endpoint.
- */
-public abstract class XconnectEndpoint {
-    public static final String LB_KEYWORD = "LB:";
-    static final String PORT_PATTERN = "^\\d+$";
-    static final String LOAD_BALANCER_PATTERN = "^" + LB_KEYWORD + "\\d+$";
-
-    /**
-     * Types of endpoint.
-     */
-    public enum Type {
-        /**
-         * The endpoint is specified by an port number.
-         */
-        PORT,
-
-        /**
-         * The endpoint is specified by a load balancer.
-         */
-        LOAD_BALANCER
-    }
-
-    /**
-     * Type of this endpoint.
-     *
-     * @return type
-     */
-    public abstract XconnectEndpoint.Type type();
-
-    /**
-     * Constructs XconnectEndpoint from string.
-     *
-     * @param s string
-     * @return XconnectEndpoint
-     * @throws IllegalArgumentException if given string is in a wrong format
-     */
-    public static XconnectEndpoint fromString(String s) {
-        if (s.matches(XconnectEndpoint.PORT_PATTERN)) {
-            return XconnectPortEndpoint.fromString(s);
-        } else if (s.matches(XconnectEndpoint.LOAD_BALANCER_PATTERN)) {
-            return XconnectLoadBalancerEndpoint.fromString(s);
-        } else {
-            throw new IllegalArgumentException("Illegal endpoint format: " + s);
-        }
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectKey.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectKey.java
deleted file mode 100644
index 7d433cd..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectKey.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-import com.google.common.base.MoreObjects;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-/**
- * Xconnect key.
- */
-public class XconnectKey {
-    private DeviceId deviceId;
-    private VlanId vlanId;
-
-    /**
-     * Constructs new XconnectKey with given device ID and VLAN ID.
-     *
-     * @param deviceId device ID
-     * @param vlanId vlan ID
-     */
-    public XconnectKey(DeviceId deviceId, VlanId vlanId) {
-        this.deviceId = deviceId;
-        this.vlanId = vlanId;
-    }
-
-    /**
-     * Gets device ID.
-     *
-     * @return device ID of the Xconnect key
-     */
-    public DeviceId deviceId() {
-        return deviceId;
-    }
-
-    /**
-     * Gets VLAN ID.
-     *
-     * @return VLAN ID of the Xconnect key
-     */
-    public VlanId vlanId() {
-        return vlanId;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof XconnectKey)) {
-            return false;
-        }
-        final XconnectKey other = (XconnectKey) obj;
-        return Objects.equals(this.deviceId, other.deviceId) &&
-                Objects.equals(this.vlanId, other.vlanId);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(deviceId, vlanId);
-    }
-
-    @Override
-    public String toString() {
-        return MoreObjects.toStringHelper(getClass())
-                .add("deviceId", deviceId)
-                .add("vlanId", vlanId)
-                .toString();
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectLoadBalancerEndpoint.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectLoadBalancerEndpoint.java
deleted file mode 100644
index 4172292..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectLoadBalancerEndpoint.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Represents a cross connect endpoint specified by load balancer.
- */
-public final class XconnectLoadBalancerEndpoint extends XconnectEndpoint {
-    private final int key;
-
-    private XconnectLoadBalancerEndpoint(int key) {
-        this.key = key;
-    }
-
-    /**
-     * Returns load balancer key.
-     *
-     * @return load balancer key.
-     */
-    public int key() {
-        return key;
-    }
-
-    /**
-     * Returns an instance of XconnectLoadBalancerEndpoint with given load balancer key.
-     *
-     * @param key load balancer key
-     * @return an instance of XconnectLoadBalancerEndpoint
-     */
-    public static XconnectLoadBalancerEndpoint of(int key) {
-        return new XconnectLoadBalancerEndpoint(key);
-    }
-
-    /**
-     * Gets XconnectLoadBalancerEndpoint from string.
-     *
-     * @param s string
-     * @return XconnectLoadBalancerEndpoint
-     */
-    public static XconnectLoadBalancerEndpoint fromString(String s) {
-        checkArgument(s.matches(LOAD_BALANCER_PATTERN), "String {} does not match {} format", s, LOAD_BALANCER_PATTERN);
-        return new XconnectLoadBalancerEndpoint(Integer.valueOf(s.replaceFirst(LB_KEYWORD, "")));
-    }
-
-    @Override
-    public Type type() {
-        return Type.LOAD_BALANCER;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(key);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj instanceof XconnectLoadBalancerEndpoint) {
-            final XconnectLoadBalancerEndpoint other = (XconnectLoadBalancerEndpoint) obj;
-            return Objects.equals(this.key, other.key);
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return LB_KEYWORD + String.valueOf(key);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectPortEndpoint.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectPortEndpoint.java
deleted file mode 100644
index f26eaf0..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectPortEndpoint.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-import org.onosproject.net.PortNumber;
-
-import java.util.Objects;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-/**
- * Represents a cross connect endpoint specified by port number.
- */
-public final class XconnectPortEndpoint extends XconnectEndpoint {
-    private final PortNumber port;
-
-    private XconnectPortEndpoint(PortNumber port) {
-        this.port = port;
-    }
-
-    /**
-     * Returns port number.
-     *
-     * @return port number
-     */
-    public PortNumber port() {
-        return port;
-    }
-
-    /**
-     * Returns an instance of XconnectPortEndpoint with given port number.
-     *
-     * @param port port number
-     * @return an instance of XconnectPortEndpoint
-     */
-    public static XconnectPortEndpoint of(PortNumber port) {
-        return new XconnectPortEndpoint(port);
-    }
-
-    /**
-     * Gets XconnectPortEndpoint from string.
-     *
-     * @param s string
-     * @return XconnectPortEndpoint
-     */
-    public static XconnectPortEndpoint fromString(String s) {
-        checkArgument(s.matches(PORT_PATTERN), "String {} does not match {} format", s, PORT_PATTERN);
-        return new XconnectPortEndpoint(PortNumber.fromString(s));
-    }
-
-    @Override
-    public XconnectEndpoint.Type type() {
-        return Type.PORT;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(port);
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj instanceof XconnectPortEndpoint) {
-            final XconnectPortEndpoint other = (XconnectPortEndpoint) obj;
-            return Objects.equals(this.port, other.port);
-        }
-        return false;
-    }
-
-    @Override
-    public String toString() {
-        return String.valueOf(port);
-    }
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectService.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectService.java
deleted file mode 100644
index 2fe6488..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/XconnectService.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-import com.google.common.collect.ImmutableMap;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-
-import java.util.List;
-import java.util.Set;
-
-/**
- * VLAN cross connect between exactly two ports.
- */
-public interface XconnectService {
-
-    /**
-     * VLAN cross-connect ACL priority.
-     */
-    int XCONNECT_ACL_PRIORITY = 60000;
-
-    /**
-     * VLAN cross-connect Bridging priority.
-     */
-    int XCONNECT_PRIORITY = 1000;
-
-    /**
-     * Creates or updates Xconnect.
-     *
-     * @param deviceId device ID
-     * @param vlanId VLAN ID
-     * @param endpoints set of endpoints
-     */
-    void addOrUpdateXconnect(DeviceId deviceId, VlanId vlanId, Set<XconnectEndpoint> endpoints);
-
-    /**
-     * Deletes Xconnect.
-     *
-     * @param deviceId device ID
-     * @param vlanId VLAN ID
-     */
-    void removeXonnect(DeviceId deviceId, VlanId vlanId);
-
-    /**
-     * Gets Xconnects.
-     *
-     * @return set of Xconnect descriptions
-     */
-    Set<XconnectDesc> getXconnects();
-
-    /**
-     * Check if there is Xconnect configured on given connect point.
-     *
-     * @param cp connect point
-     * @return true if there is Xconnect configured on the connect point
-     */
-    boolean hasXconnect(ConnectPoint cp);
-
-    /**
-     * Gives xconnect VLAN of given port of a device.
-     *
-     * @param deviceId Device ID
-     * @param port Port number
-     * @return true if given VLAN vlanId is XConnect VLAN on device deviceId.
-     */
-    List<VlanId> getXconnectVlans(DeviceId deviceId, PortNumber port);
-
-    /**
-     * Checks given VLAN is XConnect VLAN in given device.
-     *
-     * @param deviceId Device ID
-     * @param vlanId VLAN ID
-     * @return true if given VLAN vlanId is XConnect VLAN on device deviceId.
-     */
-    boolean isXconnectVlan(DeviceId deviceId, VlanId vlanId);
-
-    /**
-     * Returns the Xconnect next objective store.
-     *
-     * @return current contents of the xconnectNextObjStore
-     */
-    ImmutableMap<XconnectKey, Integer> getNext();
-
-    /**
-     * Removes given next ID from Xconnect next objective store.
-     *
-     * @param nextId next ID
-     */
-    void removeNextId(int nextId);
-
-    /**
-     * Returns Xconnect next objective ID associated with group device + vlan.
-     *
-     * @param deviceId - Device ID
-     * @param vlanId - VLAN ID
-     * @return Current associated group ID
-     */
-    int getNextId(DeviceId deviceId, VlanId vlanId);
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/package-info.java
deleted file mode 100644
index 56df706..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/api/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * VLAN cross connect API.
- */
-package org.onosproject.segmentrouting.xconnect.api;
\ No newline at end of file
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/impl/XconnectManager.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/impl/XconnectManager.java
deleted file mode 100644
index fecba5a..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/impl/XconnectManager.java
+++ /dev/null
@@ -1,1346 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.impl;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.cache.Cache;
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.RemovalNotification;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.util.KryoNamespace;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.LeadershipService;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.codec.CodecService;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.CoreService;
-import org.onosproject.portloadbalancer.api.PortLoadBalancerEvent;
-import org.onosproject.portloadbalancer.api.PortLoadBalancerId;
-import org.onosproject.portloadbalancer.api.PortLoadBalancerListener;
-import org.onosproject.portloadbalancer.api.PortLoadBalancerService;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.Port;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.NetworkConfigService;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceService;
-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.flow.criteria.Criteria;
-import org.onosproject.net.flowobjective.DefaultFilteringObjective;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.DefaultNextObjective;
-import org.onosproject.net.flowobjective.DefaultNextTreatment;
-import org.onosproject.net.flowobjective.DefaultObjectiveContext;
-import org.onosproject.net.flowobjective.FilteringObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.IdNextTreatment;
-import org.onosproject.net.flowobjective.NextObjective;
-import org.onosproject.net.flowobjective.NextTreatment;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveContext;
-import org.onosproject.net.flowobjective.ObjectiveError;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostListener;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.intf.InterfaceService;
-import org.onosproject.segmentrouting.SegmentRoutingService;
-import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
-import org.onosproject.segmentrouting.storekey.VlanNextObjectiveStoreKey;
-import org.onosproject.segmentrouting.xconnect.api.XconnectCodec;
-import org.onosproject.segmentrouting.xconnect.api.XconnectDesc;
-import org.onosproject.segmentrouting.xconnect.api.XconnectEndpoint;
-import org.onosproject.segmentrouting.xconnect.api.XconnectKey;
-import org.onosproject.segmentrouting.xconnect.api.XconnectLoadBalancerEndpoint;
-import org.onosproject.segmentrouting.xconnect.api.XconnectPortEndpoint;
-import org.onosproject.segmentrouting.xconnect.api.XconnectService;
-import org.onosproject.store.serializers.KryoNamespaces;
-import org.onosproject.store.service.ConsistentMap;
-import org.onosproject.store.service.MapEvent;
-import org.onosproject.store.service.MapEventListener;
-import org.onosproject.store.service.Serializer;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.Versioned;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Deactivate;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferenceCardinality;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.Serializable;
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.Iterator;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
-import static java.util.concurrent.Executors.newScheduledThreadPool;
-import static org.onlab.util.Tools.groupedThreads;
-
-@Component(immediate = true, service = XconnectService.class)
-public class XconnectManager implements XconnectService {
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private CodecService codecService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private StorageService storageService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public NetworkConfigService netCfgService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private LeadershipService leadershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private ClusterService clusterService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.OPTIONAL)
-    public SegmentRoutingService srService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public InterfaceService interfaceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    private PortLoadBalancerService portLoadBalancerService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY)
-    public NetworkConfigRegistry cfgService;
-
-    private static final String APP_NAME = "org.onosproject.xconnect";
-    private static final String ERROR_NOT_LEADER = "Not leader controller";
-    private static final String ERROR_NEXT_OBJ_BUILDER = "Unable to construct next objective builder";
-    private static final String ERROR_NEXT_ID = "Unable to get next id";
-    private static final String ERROR_NOT_EDGE_ROUTER = "Device is not Edge Router";
-    private static final String ERROR_PORT_NOT_RANGE = "Ports for the device are not in the range";
-
-    private static Logger log = LoggerFactory.getLogger(XconnectManager.class);
-
-    private ApplicationId appId;
-    private ConsistentMap<XconnectKey, Set<XconnectEndpoint>> xconnectStore;
-    private ConsistentMap<XconnectKey, Integer> xconnectNextObjStore;
-
-    private ConsistentMap<VlanNextObjectiveStoreKey, Integer> xconnectMulticastNextStore;
-    private ConsistentMap<VlanNextObjectiveStoreKey, List<PortNumber>> xconnectMulticastPortsStore;
-
-    private final MapEventListener<XconnectKey, Set<XconnectEndpoint>> xconnectListener = new XconnectMapListener();
-    private ExecutorService xConnectExecutor;
-
-    private final DeviceListener deviceListener = new InternalDeviceListener();
-    private ExecutorService deviceEventExecutor;
-
-    private final HostListener hostListener = new InternalHostListener();
-    private ExecutorService hostEventExecutor;
-
-    // Wait time for the cache
-    private static final int WAIT_TIME_MS = 15000;
-    //The cache is implemented as buffer for waiting the installation of PortLoadBalancer when present
-    private Cache<PortLoadBalancerId, XconnectKey> portLoadBalancerCache;
-    // Executor for the cache
-    private ScheduledExecutorService portLoadBalancerExecutor;
-    // We need to listen for some events to properly installed the xconnect with portloadbalancer
-    private final PortLoadBalancerListener portLoadBalancerListener = new InternalPortLoadBalancerListener();
-
-    @Activate
-    void activate() {
-        appId = coreService.registerApplication(APP_NAME);
-        codecService.registerCodec(XconnectDesc.class, new XconnectCodec());
-
-        KryoNamespace.Builder serializer = KryoNamespace.newBuilder()
-                .register(KryoNamespaces.API)
-                .register(XconnectManager.class)
-                .register(XconnectKey.class)
-                .register(XconnectEndpoint.class)
-                .register(XconnectPortEndpoint.class)
-                .register(XconnectLoadBalancerEndpoint.class)
-                .register(VlanNextObjectiveStoreKey.class);
-
-        xconnectStore = storageService.<XconnectKey, Set<XconnectEndpoint>>consistentMapBuilder()
-                .withName("onos-sr-xconnect")
-                .withRelaxedReadConsistency()
-                .withSerializer(Serializer.using(serializer.build()))
-                .build();
-        xConnectExecutor = Executors.newSingleThreadScheduledExecutor(
-                groupedThreads("sr-xconnect-event", "%d", log));
-        xconnectStore.addListener(xconnectListener, xConnectExecutor);
-
-        xconnectNextObjStore = storageService.<XconnectKey, Integer>consistentMapBuilder()
-                .withName("onos-sr-xconnect-next")
-                .withRelaxedReadConsistency()
-                .withSerializer(Serializer.using(serializer.build()))
-                .build();
-
-        xconnectMulticastNextStore = storageService.<VlanNextObjectiveStoreKey, Integer>consistentMapBuilder()
-                .withName("onos-sr-xconnect-l2multicast-next")
-                .withSerializer(Serializer.using(serializer.build()))
-                .build();
-        xconnectMulticastPortsStore = storageService.<VlanNextObjectiveStoreKey, List<PortNumber>>consistentMapBuilder()
-                .withName("onos-sr-xconnect-l2multicast-ports")
-                .withSerializer(Serializer.using(serializer.build()))
-                .build();
-
-        deviceEventExecutor = Executors.newSingleThreadScheduledExecutor(
-                groupedThreads("sr-xconnect-device-event", "%d", log));
-        deviceService.addListener(deviceListener);
-
-        hostEventExecutor = Executors.newSingleThreadExecutor(
-                groupedThreads("sr-xconnect-host-event", "%d", log));
-        hostService.addListener(hostListener);
-
-        portLoadBalancerCache = CacheBuilder.newBuilder()
-                .expireAfterWrite(WAIT_TIME_MS, TimeUnit.MILLISECONDS)
-                .removalListener((RemovalNotification<PortLoadBalancerId, XconnectKey> notification) ->
-                        log.debug("PortLoadBalancer cache removal event. portLoadBalancerId={}, xConnectKey={}",
-                                                   notification.getKey(), notification.getValue())).build();
-        portLoadBalancerExecutor = newScheduledThreadPool(1,
-                                              groupedThreads("portLoadBalancerCacheWorker", "-%d", log));
-        // Let's schedule the cleanup of the cache
-        portLoadBalancerExecutor.scheduleAtFixedRate(portLoadBalancerCache::cleanUp, 0,
-                                         WAIT_TIME_MS, TimeUnit.MILLISECONDS);
-        portLoadBalancerService.addListener(portLoadBalancerListener);
-
-        log.info("Started");
-    }
-
-    @Deactivate
-    void deactivate() {
-        xconnectStore.removeListener(xconnectListener);
-        deviceService.removeListener(deviceListener);
-        hostService.removeListener(hostListener);
-        portLoadBalancerService.removeListener(portLoadBalancerListener);
-        codecService.unregisterCodec(XconnectDesc.class);
-
-        deviceEventExecutor.shutdown();
-        hostEventExecutor.shutdown();
-        xConnectExecutor.shutdown();
-        portLoadBalancerExecutor.shutdown();
-
-        log.info("Stopped");
-    }
-
-    @Override
-    public void addOrUpdateXconnect(DeviceId deviceId, VlanId vlanId, Set<XconnectEndpoint> endpoints) {
-        log.info("Adding or updating xconnect. deviceId={}, vlanId={}, endpoints={}",
-                 deviceId, vlanId, endpoints);
-        SegmentRoutingDeviceConfig config = cfgService.getConfig(deviceId, SegmentRoutingDeviceConfig.class);
-
-        List<PortNumber> devicePorts = deviceService.getPorts(deviceId).stream()
-                .map(Port::number)
-                .collect(Collectors.toList());
-        if (!config.isEdgeRouter()) {
-            throw new IllegalArgumentException(ERROR_NOT_EDGE_ROUTER);
-        } else {
-                Iterator<XconnectEndpoint> itr = endpoints.iterator();
-                while (itr.hasNext()) {
-                    XconnectEndpoint ep = itr.next();
-                    // Note: we don't validate an endpoint with LOAD_BALANCER type
-                    if (ep.type() != XconnectEndpoint.Type.PORT) {
-                        continue;
-                    }
-                    if (!devicePorts.contains(((XconnectPortEndpoint) ep).port())) {
-                        throw new IllegalArgumentException(ERROR_PORT_NOT_RANGE);
-                    }
-                }
-        }
-        final XconnectKey key = new XconnectKey(deviceId, vlanId);
-        xconnectStore.put(key, endpoints);
-    }
-
-    @Override
-    public void removeXonnect(DeviceId deviceId, VlanId vlanId) {
-        log.info("Removing xconnect. deviceId={}, vlanId={}",
-                 deviceId, vlanId);
-        final XconnectKey key = new XconnectKey(deviceId, vlanId);
-        xconnectStore.remove(key);
-
-        // Cleanup multicasting support, if any.
-        srService.getPairDeviceId(deviceId).ifPresent(pairDeviceId ->
-            cleanupL2MulticastRule(pairDeviceId, srService.getPairLocalPort(pairDeviceId).get(), vlanId, true)
-        );
-
-    }
-
-    @Override
-    public Set<XconnectDesc> getXconnects() {
-        return xconnectStore.asJavaMap().entrySet().stream()
-                .map(e -> new XconnectDesc(e.getKey(), e.getValue()))
-                .collect(Collectors.toSet());
-    }
-
-    @Override
-    public boolean hasXconnect(ConnectPoint cp) {
-        return getXconnects().stream().anyMatch(desc ->
-                desc.key().deviceId().equals(cp.deviceId()) && desc.endpoints().stream().anyMatch(ep ->
-                        ep.type() == XconnectEndpoint.Type.PORT && ((XconnectPortEndpoint) ep).port().equals(cp.port())
-                )
-        );
-    }
-
-    @Override
-    public List<VlanId> getXconnectVlans(DeviceId deviceId, PortNumber port) {
-        return getXconnects().stream()
-                .filter(desc -> desc.key().deviceId().equals(deviceId) && desc.endpoints().stream().anyMatch(ep ->
-                        ep.type() == XconnectEndpoint.Type.PORT && ((XconnectPortEndpoint) ep).port().equals(port)))
-                .map(XconnectDesc::key)
-                .map(XconnectKey::vlanId)
-                .collect(Collectors.toList());
-    }
-
-    @Override
-    public boolean isXconnectVlan(DeviceId deviceId, VlanId vlanId) {
-        XconnectKey key = new XconnectKey(deviceId, vlanId);
-        return Versioned.valueOrNull(xconnectStore.get(key)) != null;
-    }
-
-    @Override
-    public ImmutableMap<XconnectKey, Integer> getNext() {
-        if (xconnectNextObjStore != null) {
-            return ImmutableMap.copyOf(xconnectNextObjStore.asJavaMap());
-        } else {
-            return ImmutableMap.of();
-        }
-    }
-
-    @Override
-    public int getNextId(DeviceId deviceId, VlanId vlanId) {
-        return Versioned.valueOrElse(xconnectNextObjStore.get(new XconnectKey(deviceId, vlanId)), -1);
-    }
-
-    @Override
-    public void removeNextId(int nextId) {
-        xconnectNextObjStore.entrySet().forEach(e -> {
-            if (e.getValue().value() == nextId) {
-                xconnectNextObjStore.remove(e.getKey());
-            }
-        });
-    }
-
-    private class XconnectMapListener implements MapEventListener<XconnectKey, Set<XconnectEndpoint>> {
-        @Override
-        public void event(MapEvent<XconnectKey, Set<XconnectEndpoint>> event) {
-            XconnectKey key = event.key();
-            Set<XconnectEndpoint> ports = Versioned.valueOrNull(event.newValue());
-            Set<XconnectEndpoint> oldPorts = Versioned.valueOrNull(event.oldValue());
-
-            switch (event.type()) {
-                case INSERT:
-                    populateXConnect(key, ports);
-                    break;
-                case UPDATE:
-                    updateXConnect(key, oldPorts, ports);
-                    break;
-                case REMOVE:
-                    revokeXConnect(key, oldPorts);
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private class InternalDeviceListener implements DeviceListener {
-        // Offload the execution to an executor and then process the event
-        // if this instance is the leader of the device
-        @Override
-        public void event(DeviceEvent event) {
-            deviceEventExecutor.execute(() -> {
-                DeviceId deviceId = event.subject().id();
-                // Just skip if we are not the leader
-                if (!isLocalLeader(deviceId)) {
-                    log.debug("Not the leader of {}. Skip event {}", deviceId, event);
-                    return;
-                }
-                // Populate or revoke according to the device availability
-                if (deviceService.isAvailable(deviceId)) {
-                    init(deviceId);
-                } else {
-                    cleanup(deviceId);
-                }
-            });
-        }
-        // We want to manage only a subset of events and if we are the leader
-        @Override
-        public boolean isRelevant(DeviceEvent event) {
-            return event.type() == DeviceEvent.Type.DEVICE_ADDED ||
-                    event.type() == DeviceEvent.Type.DEVICE_AVAILABILITY_CHANGED ||
-                    event.type() == DeviceEvent.Type.DEVICE_UPDATED;
-        }
-    }
-
-    private class InternalHostListener implements HostListener {
-        @Override
-        public void event(HostEvent event) {
-            hostEventExecutor.execute(() -> {
-
-                switch (event.type()) {
-                    case HOST_ADDED:
-                    case HOST_REMOVED:
-                    case HOST_UPDATED:
-                        log.trace("Unhandled host event type: {} received. Ignoring.", event.type());
-                        break;
-                    case HOST_MOVED:
-                        log.trace("Processing host event {}", event);
-
-                        Host host = event.subject();
-                        Set<HostLocation> prevLocations = event.prevSubject().locations();
-                        Set<HostLocation> newLocations = host.locations();
-
-                        // Dual-home host port failure
-                        // For each old location, in failed and paired devices update L2 vlan groups
-                        Sets.difference(prevLocations, newLocations).forEach(prevLocation -> {
-
-                            Optional<DeviceId> pairDeviceId = srService.getPairDeviceId(prevLocation.deviceId());
-                            Optional<PortNumber> pairLocalPort = srService.getPairLocalPort(prevLocation.deviceId());
-
-                            if (pairDeviceId.isPresent() && pairLocalPort.isPresent() && newLocations.stream()
-                                    .anyMatch(location -> location.deviceId().equals(pairDeviceId.get())) &&
-                                    hasXconnect(new ConnectPoint(prevLocation.deviceId(), prevLocation.port()))) {
-
-                                List<VlanId> xconnectVlans = getXconnectVlans(prevLocation.deviceId(),
-                                                                              prevLocation.port());
-                                xconnectVlans.forEach(xconnectVlan -> {
-                                    // Add single-home host into L2 multicast group at paired device side.
-                                    // Also append ACL rule to forward traffic from paired port to L2 multicast group.
-                                    newLocations.stream()
-                                            .filter(location -> location.deviceId().equals(pairDeviceId.get()))
-                                            .forEach(location -> populateL2Multicast(location.deviceId(),
-                                                                                     srService.getPairLocalPort(
-                                                                                             location.deviceId()).get(),
-                                                                                     xconnectVlan,
-                                                                                     Collections.singletonList(
-                                                                                             location.port())));
-                                    // Ensure pair-port attached to xconnect vlan flooding group
-                                    // at dual home failed device.
-                                    updateL2Flooding(prevLocation.deviceId(), pairLocalPort.get(), xconnectVlan, true);
-                                });
-                            }
-                        });
-
-                        // Dual-home host port restoration
-                        // For each new location, reverse xconnect loop prevention groups.
-                        Sets.difference(newLocations, prevLocations).forEach(newLocation -> {
-                            final Optional<DeviceId> pairDeviceId = srService.getPairDeviceId(newLocation.deviceId());
-                            Optional<PortNumber> pairLocalPort = srService.getPairLocalPort(newLocation.deviceId());
-                            if (pairDeviceId.isPresent() && pairLocalPort.isPresent() &&
-                                    hasXconnect((new ConnectPoint(newLocation.deviceId(), newLocation.port())))) {
-
-                                List<VlanId> xconnectVlans = getXconnectVlans(newLocation.deviceId(),
-                                                                              newLocation.port());
-                                xconnectVlans.forEach(xconnectVlan -> {
-                                    // Remove recovered dual homed port from vlan L2 multicast group
-                                    prevLocations.stream()
-                                            .filter(prevLocation -> prevLocation.deviceId().equals(pairDeviceId.get()))
-                                            .forEach(prevLocation -> revokeL2Multicast(
-                                                    prevLocation.deviceId(),
-                                                    xconnectVlan,
-                                                    Collections.singletonList(newLocation.port()))
-                                            );
-
-                                    // Remove pair-port from vlan's flooding group at dual home
-                                    // restored device, if needed.
-                                    if (!hasAccessPortInMulticastGroup(new VlanNextObjectiveStoreKey(
-                                            newLocation.deviceId(), xconnectVlan), pairLocalPort.get())) {
-                                        updateL2Flooding(newLocation.deviceId(),
-                                                         pairLocalPort.get(),
-                                                         xconnectVlan,
-                                                         false);
-
-                                        // Clean L2 multicast group at pair-device; also update store.
-                                        cleanupL2MulticastRule(pairDeviceId.get(),
-                                                               srService.getPairLocalPort(pairDeviceId.get()).get(),
-                                                               xconnectVlan,
-                                                               false);
-                                    }
-                                });
-                            }
-                        });
-                        break;
-
-                    default:
-                        log.warn("Unsupported host event type: {} received. Ignoring.", event.type());
-                        break;
-                }
-            });
-        }
-    }
-
-    private void init(DeviceId deviceId) {
-        getXconnects().stream()
-                .filter(desc -> desc.key().deviceId().equals(deviceId))
-                .forEach(desc -> populateXConnect(desc.key(), desc.endpoints()));
-    }
-
-    private void cleanup(DeviceId deviceId) {
-        xconnectNextObjStore.entrySet().stream()
-                .filter(entry -> entry.getKey().deviceId().equals(deviceId))
-                .forEach(entry -> xconnectNextObjStore.remove(entry.getKey()));
-        log.debug("{} is removed from xConnectNextObjStore", deviceId);
-    }
-
-    /**
-     * Populates XConnect groups and flows for given key.
-     *
-     * @param key       XConnect key
-     * @param endpoints a set of endpoints to be cross-connected
-     */
-    private void populateXConnect(XconnectKey key, Set<XconnectEndpoint> endpoints) {
-        if (!isLocalLeader(key.deviceId())) {
-            log.debug("Abort populating XConnect {}: {}", key, ERROR_NOT_LEADER);
-            return;
-        }
-
-        int nextId = populateNext(key, endpoints);
-        if (nextId == -1) {
-            log.warn("Fail to populateXConnect {}: {}", key, ERROR_NEXT_ID);
-            return;
-        }
-        populateFilter(key, endpoints);
-        populateFwd(key, nextId);
-        populateAcl(key);
-    }
-
-    /**
-     * Populates filtering objectives for given XConnect.
-     *
-     * @param key       XConnect store key
-     * @param endpoints XConnect endpoints
-     */
-    private void populateFilter(XconnectKey key, Set<XconnectEndpoint> endpoints) {
-        // FIXME Improve the logic
-        //       If port load balancer is not involved, use filtered port. Otherwise, use unfiltered port.
-        //       The purpose is to make sure existing XConnect logic can still work on a configured port.
-        boolean filtered = endpoints.stream()
-                .map(ep -> getNextTreatment(key.deviceId(), ep, false))
-                .allMatch(t -> t.type().equals(NextTreatment.Type.TREATMENT));
-
-        endpoints.stream()
-                .map(ep -> getPhysicalPorts(key.deviceId(), ep))
-                .flatMap(Set::stream).forEach(port -> {
-                    FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port, filtered);
-                    ObjectiveContext context = new DefaultObjectiveContext(
-                            (objective) -> log.debug("XConnect FilterObj for {} on port {} populated",
-                                    key, port),
-                            (objective, error) ->
-                                    log.warn("Failed to populate XConnect FilterObj for {} on port {}: {}",
-                                            key, port, error));
-                    flowObjectiveService.filter(key.deviceId(), filtObjBuilder.add(context));
-                });
-    }
-
-    /**
-     * Populates next objectives for given XConnect.
-     *
-     * @param key       XConnect store key
-     * @param endpoints XConnect endpoints
-     * @return next id
-     */
-    private int populateNext(XconnectKey key, Set<XconnectEndpoint> endpoints) {
-        int nextId = Versioned.valueOrElse(xconnectNextObjStore.get(key), -1);
-        if (nextId != -1) {
-            log.debug("NextObj for {} found, id={}", key, nextId);
-            return nextId;
-        } else {
-            NextObjective.Builder nextObjBuilder = nextObjBuilder(key, endpoints);
-            if (nextObjBuilder == null) {
-                log.warn("Fail to populate {}: {}", key, ERROR_NEXT_OBJ_BUILDER);
-                return -1;
-            }
-            ObjectiveContext nextContext = new DefaultObjectiveContext(
-                    // To serialize this with kryo
-                    (Serializable & Consumer<Objective>) (objective) ->
-                            log.debug("XConnect NextObj for {} added", key),
-                    (Serializable & BiConsumer<Objective, ObjectiveError>) (objective, error) -> {
-                        log.warn("Failed to add XConnect NextObj for {}: {}", key, error);
-                        srService.invalidateNextObj(objective.id());
-                    });
-            NextObjective nextObj = nextObjBuilder.add(nextContext);
-            flowObjectiveService.next(key.deviceId(), nextObj);
-            xconnectNextObjStore.put(key, nextObj.id());
-            log.debug("NextObj for {} not found. Creating new NextObj with id={}", key, nextObj.id());
-            return nextObj.id();
-        }
-    }
-
-    /**
-     * Populates bridging forwarding objectives for given XConnect.
-     *
-     * @param key    XConnect store key
-     * @param nextId next objective id
-     */
-    private void populateFwd(XconnectKey key, int nextId) {
-        ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextId);
-        ObjectiveContext fwdContext = new DefaultObjectiveContext(
-                (objective) -> log.debug("XConnect FwdObj for {} populated", key),
-                (objective, error) ->
-                        log.warn("Failed to populate XConnect FwdObj for {}: {}", key, error));
-        flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.add(fwdContext));
-    }
-
-    /**
-     * Populates ACL forwarding objectives for given XConnect.
-     *
-     * @param key XConnect store key
-     */
-    private void populateAcl(XconnectKey key) {
-        ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
-        ObjectiveContext aclContext = new DefaultObjectiveContext(
-                (objective) -> log.debug("XConnect AclObj for {} populated", key),
-                (objective, error) ->
-                        log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
-        flowObjectiveService.forward(key.deviceId(), aclObjBuilder.add(aclContext));
-    }
-
-    /**
-     * Revokes XConnect groups and flows for given key.
-     *
-     * @param key       XConnect key
-     * @param endpoints XConnect endpoints
-     */
-    private void revokeXConnect(XconnectKey key, Set<XconnectEndpoint> endpoints) {
-        if (!isLocalLeader(key.deviceId())) {
-            log.debug("Abort revoking XConnect {}: {}", key, ERROR_NOT_LEADER);
-            return;
-        }
-
-        revokeFilter(key, endpoints);
-        int nextId = Versioned.valueOrElse(xconnectNextObjStore.get(key), -1);
-        if (nextId != -1) {
-            revokeFwd(key, nextId, null);
-            revokeNext(key, endpoints, nextId, null);
-        } else {
-            log.warn("NextObj for {} does not exist in the store.", key);
-        }
-        revokeFilter(key, endpoints);
-        revokeAcl(key);
-    }
-
-    /**
-     * Revokes filtering objectives for given XConnect.
-     *
-     * @param key       XConnect store key
-     * @param endpoints XConnect endpoints
-     */
-    private void revokeFilter(XconnectKey key, Set<XconnectEndpoint> endpoints) {
-        // FIXME Improve the logic
-        //       If port load balancer is not involved, use filtered port. Otherwise, use unfiltered port.
-        //       The purpose is to make sure existing XConnect logic can still work on a configured port.
-        boolean filtered = endpoints.stream()
-                .map(ep -> getNextTreatment(key.deviceId(), ep, false))
-                .allMatch(t -> t.type().equals(NextTreatment.Type.TREATMENT));
-
-        endpoints.stream()
-                .map(ep -> getPhysicalPorts(key.deviceId(), ep)).
-                flatMap(Set::stream).forEach(port -> {
-                    FilteringObjective.Builder filtObjBuilder = filterObjBuilder(key, port, filtered);
-                    ObjectiveContext context = new DefaultObjectiveContext(
-                            (objective) -> log.debug("XConnect FilterObj for {} on port {} revoked",
-                                                     key, port),
-                            (objective, error) ->
-                                    log.warn("Failed to revoke XConnect FilterObj for {} on port {}: {}",
-                                             key, port, error));
-                    flowObjectiveService.filter(key.deviceId(), filtObjBuilder.remove(context));
-                });
-    }
-
-    /**
-     * Revokes next objectives for given XConnect.
-     *
-     * @param key        XConnect store key
-     * @param endpoints  XConnect endpoints
-     * @param nextId     next objective id
-     * @param nextFuture completable future for this next objective operation
-     */
-    private void revokeNext(XconnectKey key, Set<XconnectEndpoint> endpoints, int nextId,
-                            CompletableFuture<ObjectiveError> nextFuture) {
-        ObjectiveContext context = new ObjectiveContext() {
-            @Override
-            public void onSuccess(Objective objective) {
-                log.debug("Previous NextObj for {} removed", key);
-                if (nextFuture != null) {
-                    nextFuture.complete(null);
-                }
-            }
-
-            @Override
-            public void onError(Objective objective, ObjectiveError error) {
-                log.warn("Failed to remove previous NextObj for {}: {}", key, error);
-                if (nextFuture != null) {
-                    nextFuture.complete(error);
-                }
-                srService.invalidateNextObj(objective.id());
-            }
-        };
-
-        NextObjective.Builder nextObjBuilder = nextObjBuilder(key, endpoints, nextId);
-        if (nextObjBuilder == null) {
-            log.warn("Fail to revokeNext {}: {}", key, ERROR_NEXT_OBJ_BUILDER);
-            return;
-        }
-        // Release the port load balancer if present
-        endpoints.stream()
-                .filter(endpoint -> endpoint.type() == XconnectEndpoint.Type.LOAD_BALANCER)
-                .forEach(endpoint -> {
-                    String portLoadBalancerKey = String.valueOf(((XconnectLoadBalancerEndpoint) endpoint).key());
-                    portLoadBalancerService.release(new PortLoadBalancerId(key.deviceId(),
-                            Integer.parseInt(portLoadBalancerKey)), appId);
-                });
-        flowObjectiveService.next(key.deviceId(), nextObjBuilder.remove(context));
-        xconnectNextObjStore.remove(key);
-    }
-
-    /**
-     * Revokes bridging forwarding objectives for given XConnect.
-     *
-     * @param key       XConnect store key
-     * @param nextId    next objective id
-     * @param fwdFuture completable future for this forwarding objective operation
-     */
-    private void revokeFwd(XconnectKey key, int nextId, CompletableFuture<ObjectiveError> fwdFuture) {
-        ForwardingObjective.Builder fwdObjBuilder = fwdObjBuilder(key, nextId);
-        ObjectiveContext context = new ObjectiveContext() {
-            @Override
-            public void onSuccess(Objective objective) {
-                log.debug("Previous FwdObj for {} removed", key);
-                if (fwdFuture != null) {
-                    fwdFuture.complete(null);
-                }
-            }
-
-            @Override
-            public void onError(Objective objective, ObjectiveError error) {
-                log.warn("Failed to remove previous FwdObj for {}: {}", key, error);
-                if (fwdFuture != null) {
-                    fwdFuture.complete(error);
-                }
-            }
-        };
-        flowObjectiveService.forward(key.deviceId(), fwdObjBuilder.remove(context));
-    }
-
-    /**
-     * Revokes ACL forwarding objectives for given XConnect.
-     *
-     * @param key XConnect store key
-     */
-    private void revokeAcl(XconnectKey key) {
-        ForwardingObjective.Builder aclObjBuilder = aclObjBuilder(key.vlanId());
-        ObjectiveContext aclContext = new DefaultObjectiveContext(
-                (objective) -> log.debug("XConnect AclObj for {} populated", key),
-                (objective, error) ->
-                        log.warn("Failed to populate XConnect AclObj for {}: {}", key, error));
-        flowObjectiveService.forward(key.deviceId(), aclObjBuilder.remove(aclContext));
-    }
-
-    /**
-     * Updates XConnect groups and flows for given key.
-     *
-     * @param key           XConnect key
-     * @param prevEndpoints previous XConnect endpoints
-     * @param endpoints     new XConnect endpoints
-     */
-    private void updateXConnect(XconnectKey key, Set<XconnectEndpoint> prevEndpoints,
-                                Set<XconnectEndpoint> endpoints) {
-        if (!isLocalLeader(key.deviceId())) {
-            log.debug("Abort updating XConnect {}: {}", key, ERROR_NOT_LEADER);
-            return;
-        }
-        // NOTE: ACL flow doesn't include port information. No need to update it.
-        //       Pair port is built-in and thus not going to change. No need to update it.
-
-        // remove old filter
-        prevEndpoints.stream().filter(prevEndpoint -> !endpoints.contains(prevEndpoint)).forEach(prevEndpoint ->
-                revokeFilter(key, ImmutableSet.of(prevEndpoint)));
-        // install new filter
-        endpoints.stream().filter(endpoint -> !prevEndpoints.contains(endpoint)).forEach(endpoint ->
-                populateFilter(key, ImmutableSet.of(endpoint)));
-
-        CompletableFuture<ObjectiveError> fwdFuture = new CompletableFuture<>();
-        CompletableFuture<ObjectiveError> nextFuture = new CompletableFuture<>();
-
-        int nextId = Versioned.valueOrElse(xconnectNextObjStore.get(key), -1);
-        if (nextId != -1) {
-            revokeFwd(key, nextId, fwdFuture);
-
-            fwdFuture.thenAcceptAsync(fwdStatus -> {
-                if (fwdStatus == null) {
-                    log.debug("Fwd removed. Now remove group {}", key);
-                    revokeNext(key, prevEndpoints, nextId, nextFuture);
-                }
-            });
-
-            nextFuture.thenAcceptAsync(nextStatus -> {
-                if (nextStatus == null) {
-                    log.debug("Installing new group and flow for {}", key);
-                    int newNextId = populateNext(key, endpoints);
-                    if (newNextId == -1) {
-                        log.warn("Fail to updateXConnect {}: {}", key, ERROR_NEXT_ID);
-                        return;
-                    }
-                    populateFwd(key, newNextId);
-                }
-            });
-        } else {
-            log.warn("NextObj for {} does not exist in the store.", key);
-        }
-    }
-
-    /**
-     * Creates a next objective builder for XConnect with given nextId.
-     *
-     * @param key       XConnect key
-     * @param endpoints XConnect endpoints
-     * @param nextId  next objective id
-     * @return next objective builder
-     */
-    private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<XconnectEndpoint> endpoints, int nextId) {
-        TrafficSelector metadata =
-                DefaultTrafficSelector.builder().matchVlanId(key.vlanId()).build();
-        NextObjective.Builder nextObjBuilder = DefaultNextObjective
-                .builder().withId(nextId)
-                .withType(NextObjective.Type.BROADCAST).fromApp(appId)
-                .withMeta(metadata);
-
-        for (XconnectEndpoint endpoint : endpoints) {
-            NextTreatment nextTreatment = getNextTreatment(key.deviceId(), endpoint, true);
-            if (nextTreatment == null) {
-                // If a PortLoadBalancer is used in the XConnect - putting on hold
-                if (endpoint.type() == XconnectEndpoint.Type.LOAD_BALANCER) {
-                    log.warn("Unable to create nextObj. PortLoadBalancer not ready");
-                    String portLoadBalancerKey = String.valueOf(((XconnectLoadBalancerEndpoint) endpoint).key());
-                    portLoadBalancerCache.asMap().putIfAbsent(new PortLoadBalancerId(key.deviceId(),
-                                    Integer.parseInt(portLoadBalancerKey)), key);
-                } else {
-                    log.warn("Unable to create nextObj. Null NextTreatment");
-                }
-                return null;
-            }
-            nextObjBuilder.addTreatment(nextTreatment);
-        }
-
-        return nextObjBuilder;
-    }
-
-    /**
-     * Creates a next objective builder for XConnect.
-     *
-     * @param key       XConnect key
-     * @param endpoints Xconnect endpoints
-     * @return next objective builder
-     */
-    private NextObjective.Builder nextObjBuilder(XconnectKey key, Set<XconnectEndpoint> endpoints) {
-        int nextId = flowObjectiveService.allocateNextId();
-        return nextObjBuilder(key, endpoints, nextId);
-    }
-
-
-    /**
-     * Creates a bridging forwarding objective builder for XConnect.
-     *
-     * @param key    XConnect key
-     * @param nextId next ID of the broadcast group for this XConnect key
-     * @return forwarding objective builder
-     */
-    private ForwardingObjective.Builder fwdObjBuilder(XconnectKey key, int nextId) {
-        /*
-         * Driver should treat objectives with MacAddress.NONE and !VlanId.NONE
-         * as the VLAN cross-connect broadcast rules
-         */
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        sbuilder.matchVlanId(key.vlanId());
-        sbuilder.matchEthDst(MacAddress.NONE);
-
-        ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
-        fob.withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .withSelector(sbuilder.build())
-                .nextStep(nextId)
-                .withPriority(XCONNECT_PRIORITY)
-                .fromApp(appId)
-                .makePermanent();
-        return fob;
-    }
-
-    /**
-     * Creates an ACL forwarding objective builder for XConnect.
-     *
-     * @param vlanId cross connect VLAN id
-     * @return forwarding objective builder
-     */
-    private ForwardingObjective.Builder aclObjBuilder(VlanId vlanId) {
-        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
-        sbuilder.matchVlanId(vlanId);
-
-        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
-
-        ForwardingObjective.Builder fob = DefaultForwardingObjective.builder();
-        fob.withFlag(ForwardingObjective.Flag.VERSATILE)
-                .withSelector(sbuilder.build())
-                .withTreatment(tbuilder.build())
-                .withPriority(XCONNECT_ACL_PRIORITY)
-                .fromApp(appId)
-                .makePermanent();
-        return fob;
-    }
-
-    /**
-     * Creates a filtering objective builder for XConnect.
-     *
-     * @param key  XConnect key
-     * @param port XConnect ports
-     * @param filtered true if this is a filtered port
-     * @return next objective builder
-     */
-    private FilteringObjective.Builder filterObjBuilder(XconnectKey key, PortNumber port, boolean filtered) {
-        FilteringObjective.Builder fob = DefaultFilteringObjective.builder();
-        fob.withKey(Criteria.matchInPort(port))
-                .addCondition(Criteria.matchEthDst(MacAddress.NONE))
-                .withPriority(XCONNECT_PRIORITY);
-        if (filtered) {
-            fob.addCondition(Criteria.matchVlanId(key.vlanId()));
-        } else {
-            fob.addCondition(Criteria.matchVlanId(VlanId.ANY));
-        }
-        return fob.permit().fromApp(appId);
-    }
-
-    /**
-     * Updates L2 flooding groups; add pair link into L2 flooding group of given xconnect vlan.
-     *
-     * @param deviceId Device ID
-     * @param port     Port details
-     * @param vlanId   VLAN ID
-     * @param install  Whether to add or revoke pair link addition to flooding group
-     */
-    private void updateL2Flooding(DeviceId deviceId, PortNumber port, VlanId vlanId, boolean install) {
-        XconnectKey key = new XconnectKey(deviceId, vlanId);
-        // Ensure leadership on device
-        if (!isLocalLeader(deviceId)) {
-            log.debug("Abort updating L2Flood {}: {}", key, ERROR_NOT_LEADER);
-            return;
-        }
-
-        // Locate L2 flooding group details for given xconnect vlan
-        int nextId = Versioned.valueOrElse(xconnectNextObjStore.get(key), -1);
-        if (nextId == -1) {
-            log.debug("XConnect vlan {} broadcast group for device {} doesn't exists. " +
-                              "Aborting pair group linking.", vlanId, deviceId);
-            return;
-        }
-
-        // Add pairing-port group to flooding group
-        TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
-        // treatment.popVlan();
-        treatment.setOutput(port);
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) ->
-                        log.debug("Pair port added/removed to vlan {} next objective {} on {}",
-                                  vlanId, nextId, deviceId),
-                (objective, error) ->
-                        log.warn("Failed adding/removing pair port to vlan {} next objective {} on {}." +
-                                         "Error : {}", vlanId, nextId, deviceId, error)
-        );
-        NextObjective.Builder vlanNextObjectiveBuilder = DefaultNextObjective.builder()
-                .withId(nextId)
-                .withType(NextObjective.Type.BROADCAST)
-                .fromApp(srService.appId())
-                .withMeta(DefaultTrafficSelector.builder().matchVlanId(vlanId).build())
-                .addTreatment(treatment.build());
-        if (install) {
-            flowObjectiveService.next(deviceId, vlanNextObjectiveBuilder.addToExisting(context));
-        } else {
-            flowObjectiveService.next(deviceId, vlanNextObjectiveBuilder.removeFromExisting(context));
-        }
-        log.debug("Submitted next objective {} for vlan: {} in device {}",
-                  nextId, vlanId, deviceId);
-    }
-
-    /**
-     * Populate L2 multicast rule on given deviceId that matches given mac, given vlan and
-     * output to given port's L2 mulitcast group.
-     *
-     * @param deviceId    Device ID
-     * @param pairPort    Pair port number
-     * @param vlanId      VLAN ID
-     * @param accessPorts List of access ports to be added into L2 multicast group
-     */
-    private void populateL2Multicast(DeviceId deviceId, PortNumber pairPort,
-                                     VlanId vlanId, List<PortNumber> accessPorts) {
-        // Ensure enough rights to program pair device
-        if (!srService.shouldProgram(deviceId)) {
-            log.debug("Abort populate L2Multicast {}-{}: {}", deviceId, vlanId, ERROR_NOT_LEADER);
-            return;
-        }
-
-        boolean multicastGroupExists = true;
-        int vlanMulticastNextId;
-        VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
-
-        // Step 1 : Populate single homed access ports into vlan's L2 multicast group
-        NextObjective.Builder vlanMulticastNextObjBuilder = DefaultNextObjective
-                .builder()
-                .withType(NextObjective.Type.BROADCAST)
-                .fromApp(srService.appId())
-            .withMeta(DefaultTrafficSelector.builder().matchVlanId(vlanId)
-                          .matchEthDst(MacAddress.IPV4_MULTICAST).build());
-        vlanMulticastNextId = getMulticastGroupNextObjectiveId(key);
-        if (vlanMulticastNextId == -1) {
-            // Vlan's L2 multicast group doesn't exist; create it, update store and add pair port as sub-group
-            multicastGroupExists = false;
-            vlanMulticastNextId = flowObjectiveService.allocateNextId();
-            addMulticastGroupNextObjectiveId(key, vlanMulticastNextId);
-            vlanMulticastNextObjBuilder.addTreatment(
-                    DefaultTrafficTreatment.builder().setOutput(pairPort).build()
-            );
-        }
-        vlanMulticastNextObjBuilder.withId(vlanMulticastNextId);
-        int nextId = vlanMulticastNextId;
-        accessPorts.forEach(p -> {
-            TrafficTreatment.Builder egressAction = DefaultTrafficTreatment.builder();
-            // Do vlan popup action based on interface configuration
-            if (interfaceService.getInterfacesByPort(new ConnectPoint(deviceId, p))
-                    .stream().noneMatch(i -> i.vlanTagged().contains(vlanId))) {
-                egressAction.popVlan();
-            }
-            egressAction.setOutput(p);
-            vlanMulticastNextObjBuilder.addTreatment(egressAction.build());
-            addMulticastGroupPort(key, p);
-        });
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) ->
-                        log.debug("L2 multicast group installed/updated. "
-                                          + "NextObject Id {} on {} for subnet {} ",
-                                  nextId, deviceId, vlanId),
-                (objective, error) ->
-                        log.warn("L2 multicast group failed to install/update. "
-                                         + " NextObject Id {} on {} for subnet {} : {}",
-                                 nextId, deviceId, vlanId, error)
-        );
-        if (!multicastGroupExists) {
-            flowObjectiveService.next(deviceId, vlanMulticastNextObjBuilder.add(context));
-
-            // Step 2 : Populate ACL rule; selector = vlan + pair-port, output = vlan L2 multicast group
-            TrafficSelector.Builder multicastSelector = DefaultTrafficSelector.builder();
-            multicastSelector.matchEthType(Ethernet.TYPE_VLAN);
-            multicastSelector.matchInPort(pairPort);
-            multicastSelector.matchVlanId(vlanId);
-            ForwardingObjective.Builder vlanMulticastForwardingObj = DefaultForwardingObjective.builder()
-                    .withFlag(ForwardingObjective.Flag.VERSATILE)
-                    .nextStep(vlanMulticastNextId)
-                    .withSelector(multicastSelector.build())
-                    .withPriority(100)
-                    .fromApp(srService.appId())
-                    .makePermanent();
-            context = new DefaultObjectiveContext(
-                    (objective) -> log.debug("L2 multicasting versatile rule for device {}, port/vlan {}/{} populated",
-                                             deviceId,
-                                             pairPort,
-                                             vlanId),
-                    (objective, error) -> log.warn("Failed to populate L2 multicasting versatile rule for device {}, " +
-                                                           "ports/vlan {}/{}: {}", deviceId, pairPort, vlanId, error));
-            flowObjectiveService.forward(deviceId, vlanMulticastForwardingObj.add(context));
-        } else {
-            // L2_MULTICAST & BROADCAST are similar structure in subgroups; so going with BROADCAST type.
-            vlanMulticastNextObjBuilder.withType(NextObjective.Type.BROADCAST);
-            flowObjectiveService.next(deviceId, vlanMulticastNextObjBuilder.addToExisting(context));
-        }
-    }
-
-    /**
-     * Removes access ports from VLAN L2 multicast group on given deviceId.
-     *
-     * @param deviceId    Device ID
-     * @param vlanId      VLAN ID
-     * @param accessPorts List of access ports to be added into L2 multicast group
-     */
-    private void revokeL2Multicast(DeviceId deviceId, VlanId vlanId, List<PortNumber> accessPorts) {
-        // Ensure enough rights to program pair device
-        if (!srService.shouldProgram(deviceId)) {
-            log.debug("Abort revoke L2Multicast {}-{}: {}", deviceId, vlanId, ERROR_NOT_LEADER);
-            return;
-        }
-
-        VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
-
-        int vlanMulticastNextId = getMulticastGroupNextObjectiveId(key);
-        if (vlanMulticastNextId == -1) {
-            return;
-        }
-        NextObjective.Builder vlanMulticastNextObjBuilder = DefaultNextObjective
-                .builder()
-                .withType(NextObjective.Type.BROADCAST)
-                .fromApp(srService.appId())
-                .withMeta(DefaultTrafficSelector.builder().matchVlanId(vlanId).build())
-                .withId(vlanMulticastNextId);
-        accessPorts.forEach(p -> {
-            TrafficTreatment.Builder egressAction = DefaultTrafficTreatment.builder();
-            // Do vlan popup action based on interface configuration
-            if (interfaceService.getInterfacesByPort(new ConnectPoint(deviceId, p))
-                    .stream().noneMatch(i -> i.vlanTagged().contains(vlanId))) {
-                egressAction.popVlan();
-            }
-            egressAction.setOutput(p);
-            vlanMulticastNextObjBuilder.addTreatment(egressAction.build());
-            removeMulticastGroupPort(key, p);
-        });
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) ->
-                        log.debug("L2 multicast group installed/updated. "
-                                          + "NextObject Id {} on {} for subnet {} ",
-                                  vlanMulticastNextId, deviceId, vlanId),
-                (objective, error) ->
-                        log.warn("L2 multicast group failed to install/update. "
-                                         + " NextObject Id {} on {} for subnet {} : {}",
-                                 vlanMulticastNextId, deviceId, vlanId, error)
-        );
-        flowObjectiveService.next(deviceId, vlanMulticastNextObjBuilder.removeFromExisting(context));
-    }
-
-    /**
-     * Cleans up VLAN L2 multicast group on given deviceId. ACL rules for the group will also be deleted.
-     * Normally multicast group is not removed if it contains access ports; which can be forced
-     * by "force" flag
-     *
-     * @param deviceId Device ID
-     * @param pairPort Pair port number
-     * @param vlanId   VLAN ID
-     * @param force    Forceful removal
-     */
-    private void cleanupL2MulticastRule(DeviceId deviceId, PortNumber pairPort, VlanId vlanId, boolean force) {
-
-        // Ensure enough rights to program pair device
-        if (!srService.shouldProgram(deviceId)) {
-            log.debug("Abort cleanup L2Multicast {}-{}: {}", deviceId, vlanId, ERROR_NOT_LEADER);
-            return;
-        }
-
-        VlanNextObjectiveStoreKey key = new VlanNextObjectiveStoreKey(deviceId, vlanId);
-
-        // Ensure L2 multicast group doesn't contain access ports
-        if (hasAccessPortInMulticastGroup(key, pairPort) && !force) {
-            return;
-        }
-
-        // Load L2 multicast group details
-        int vlanMulticastNextId = getMulticastGroupNextObjectiveId(key);
-        if (vlanMulticastNextId == -1) {
-            return;
-        }
-
-        // Step 1 : Clear ACL rule; selector = vlan + pair-port, output = vlan L2 multicast group
-        TrafficSelector.Builder l2MulticastSelector = DefaultTrafficSelector.builder();
-        l2MulticastSelector.matchEthType(Ethernet.TYPE_VLAN);
-        l2MulticastSelector.matchInPort(pairPort);
-        l2MulticastSelector.matchVlanId(vlanId);
-        ForwardingObjective.Builder vlanMulticastForwardingObj = DefaultForwardingObjective.builder()
-                .withFlag(ForwardingObjective.Flag.VERSATILE)
-                .nextStep(vlanMulticastNextId)
-                .withSelector(l2MulticastSelector.build())
-                .withPriority(100)
-                .fromApp(srService.appId())
-                .makePermanent();
-        ObjectiveContext context = new DefaultObjectiveContext(
-                (objective) -> log.debug("L2 multicasting rule for device {}, port/vlan {}/{} deleted", deviceId,
-                                         pairPort, vlanId),
-                (objective, error) -> log.warn("Failed to delete L2 multicasting rule for device {}, " +
-                                                       "ports/vlan {}/{}: {}", deviceId, pairPort, vlanId, error));
-        flowObjectiveService.forward(deviceId, vlanMulticastForwardingObj.remove(context));
-
-        // Step 2 : Clear L2 multicast group associated with vlan
-        NextObjective.Builder l2MulticastGroupBuilder = DefaultNextObjective
-                .builder()
-                .withId(vlanMulticastNextId)
-                .withType(NextObjective.Type.BROADCAST)
-                .fromApp(srService.appId())
-            .withMeta(DefaultTrafficSelector.builder()
-                          .matchVlanId(vlanId)
-                          .matchEthDst(MacAddress.IPV4_MULTICAST).build())
-                .addTreatment(DefaultTrafficTreatment.builder().popVlan().setOutput(pairPort).build());
-        context = new DefaultObjectiveContext(
-                (objective) ->
-                        log.debug("L2 multicast group with NextObject Id {} deleted on {} for subnet {} ",
-                                  vlanMulticastNextId, deviceId, vlanId),
-                (objective, error) ->
-                        log.warn("L2 multicast group with NextObject Id {} failed to delete on {} for subnet {} : {}",
-                                 vlanMulticastNextId, deviceId, vlanId, error)
-        );
-        flowObjectiveService.next(deviceId, l2MulticastGroupBuilder.remove(context));
-
-        // Finally clear store.
-        removeMulticastGroup(key);
-    }
-
-    private int getMulticastGroupNextObjectiveId(VlanNextObjectiveStoreKey key) {
-        return Versioned.valueOrElse(xconnectMulticastNextStore.get(key), -1);
-    }
-
-    private void addMulticastGroupNextObjectiveId(VlanNextObjectiveStoreKey key, int nextId) {
-        if (nextId == -1) {
-            return;
-        }
-        xconnectMulticastNextStore.put(key, nextId);
-    }
-
-    private void addMulticastGroupPort(VlanNextObjectiveStoreKey groupKey, PortNumber port) {
-        xconnectMulticastPortsStore.compute(groupKey, (key, ports) -> {
-            if (ports == null) {
-                ports = Lists.newArrayList();
-            }
-            ports.add(port);
-            return ports;
-        });
-    }
-
-    private void removeMulticastGroupPort(VlanNextObjectiveStoreKey groupKey, PortNumber port) {
-        xconnectMulticastPortsStore.compute(groupKey, (key, ports) -> {
-            if (ports != null && !ports.isEmpty()) {
-                ports.remove(port);
-            }
-            return ports;
-        });
-    }
-
-    private void removeMulticastGroup(VlanNextObjectiveStoreKey groupKey) {
-        xconnectMulticastPortsStore.remove(groupKey);
-        xconnectMulticastNextStore.remove(groupKey);
-    }
-
-    private boolean hasAccessPortInMulticastGroup(VlanNextObjectiveStoreKey groupKey, PortNumber pairPort) {
-        List<PortNumber> ports = Versioned.valueOrElse(xconnectMulticastPortsStore.get(groupKey), ImmutableList.of());
-        return ports.stream().anyMatch(p -> !p.equals(pairPort));
-    }
-
-    // Custom-built function, when the device is not available we need a fallback mechanism
-    private boolean isLocalLeader(DeviceId deviceId) {
-        if (!mastershipService.isLocalMaster(deviceId)) {
-            // When the device is available we just check the mastership
-            if (deviceService.isAvailable(deviceId)) {
-                return false;
-            }
-            // Fallback with Leadership service - device id is used as topic
-            NodeId leader = leadershipService.runForLeadership(
-                    deviceId.toString()).leaderNodeId();
-            // Verify if this node is the leader
-            return clusterService.getLocalNode().id().equals(leader);
-        }
-        return true;
-    }
-
-    private Set<PortNumber> getPhysicalPorts(DeviceId deviceId, XconnectEndpoint endpoint) {
-        if (endpoint.type() == XconnectEndpoint.Type.PORT) {
-            PortNumber port = ((XconnectPortEndpoint) endpoint).port();
-            return Sets.newHashSet(port);
-        }
-        if (endpoint.type() == XconnectEndpoint.Type.LOAD_BALANCER) {
-            PortLoadBalancerId portLoadBalancerId = new PortLoadBalancerId(deviceId,
-                    ((XconnectLoadBalancerEndpoint) endpoint).key());
-            Set<PortNumber> ports = portLoadBalancerService.getPortLoadBalancer(portLoadBalancerId).ports();
-            return Sets.newHashSet(ports);
-        }
-        return Sets.newHashSet();
-    }
-
-    private NextTreatment getNextTreatment(DeviceId deviceId, XconnectEndpoint endpoint, boolean reserve) {
-        if (endpoint.type() == XconnectEndpoint.Type.PORT) {
-            PortNumber port = ((XconnectPortEndpoint) endpoint).port();
-            return DefaultNextTreatment.of(DefaultTrafficTreatment.builder().setOutput(port).build());
-        }
-        if (endpoint.type() == XconnectEndpoint.Type.LOAD_BALANCER) {
-            PortLoadBalancerId portLoadBalancerId = new PortLoadBalancerId(deviceId,
-                    ((XconnectLoadBalancerEndpoint) endpoint).key());
-            NextTreatment idNextTreatment =  IdNextTreatment.of(portLoadBalancerService
-                    .getPortLoadBalancerNext(portLoadBalancerId));
-            // Reserve only one time during next objective creation
-            if (reserve) {
-                if (!portLoadBalancerService.reserve(portLoadBalancerId, appId)) {
-                    log.warn("Reservation failed for {}", portLoadBalancerId);
-                    idNextTreatment = null;
-                }
-            }
-            return idNextTreatment;
-        }
-        return null;
-    }
-
-    private class InternalPortLoadBalancerListener implements PortLoadBalancerListener {
-        // Populate xconnect once portloadbalancer is available
-        @Override
-        public void event(PortLoadBalancerEvent event) {
-            portLoadBalancerExecutor.execute(() -> dequeue(event.subject().portLoadBalancerId()));
-        }
-        // When we receive INSTALLED port load balancing is ready
-        @Override
-        public boolean isRelevant(PortLoadBalancerEvent event) {
-            return event.type() == PortLoadBalancerEvent.Type.INSTALLED;
-        }
-    }
-
-    // Invalidate the cache and re-start the xconnect installation
-    private void dequeue(PortLoadBalancerId portLoadBalancerId) {
-        XconnectKey xconnectKey = portLoadBalancerCache.getIfPresent(portLoadBalancerId);
-        if (xconnectKey == null) {
-            log.trace("{} not present in the cache", portLoadBalancerId);
-            return;
-        }
-        log.debug("Dequeue {}", portLoadBalancerId);
-        portLoadBalancerCache.invalidate(portLoadBalancerId);
-        Set<XconnectEndpoint> endpoints = Versioned.valueOrNull(xconnectStore.get(xconnectKey));
-        if (endpoints == null || endpoints.isEmpty()) {
-            log.warn("Endpoints not found for XConnect {}", xconnectKey);
-            return;
-        }
-        populateXConnect(xconnectKey, endpoints);
-        log.trace("PortLoadBalancer cache size {}", portLoadBalancerCache.size());
-    }
-
-}
diff --git a/app/src/main/java/org/onosproject/segmentrouting/xconnect/impl/package-info.java b/app/src/main/java/org/onosproject/segmentrouting/xconnect/impl/package-info.java
deleted file mode 100644
index 547e3bc..0000000
--- a/app/src/main/java/org/onosproject/segmentrouting/xconnect/impl/package-info.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.
- */
-
-/**
- * VLAN cross connect implementation.
- */
-package org.onosproject.segmentrouting.xconnect.impl;
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/AugmentedPortAuthTracker.java b/app/src/test/java/org/onosproject/segmentrouting/AugmentedPortAuthTracker.java
deleted file mode 100644
index cc214e3..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/AugmentedPortAuthTracker.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * An augmented implementation of {@link PortAuthTracker}, so that we can
- * instrument its behavior for unit test assertions.
- */
-class AugmentedPortAuthTracker extends PortAuthTracker {
-
-    // instrument blocking flow activity, so we can see when we get hits
-    final List<ConnectPoint> installed = new ArrayList<>();
-    final List<ConnectPoint> cleared = new ArrayList<>();
-
-
-    void resetMetrics() {
-        installed.clear();
-        cleared.clear();
-    }
-
-    @Override
-    void installBlockingFlow(DeviceId d, PortNumber p) {
-        super.installBlockingFlow(d, p);
-        installed.add(new ConnectPoint(d, p));
-    }
-
-    @Override
-    void clearBlockingFlow(DeviceId d, PortNumber p) {
-        super.clearBlockingFlow(d, p);
-        cleared.add(new ConnectPoint(d, p));
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/DefaultRoutingHandlerTest.java b/app/src/test/java/org/onosproject/segmentrouting/DefaultRoutingHandlerTest.java
deleted file mode 100644
index a6278b2..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/DefaultRoutingHandlerTest.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Sets;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.IpAddress;
-import org.onosproject.cluster.ClusterService;
-import org.onosproject.cluster.DefaultControllerNode;
-import org.onosproject.cluster.NodeId;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.TestConsistentMap;
-import org.onosproject.store.service.TestConsistentMultimap;
-
-import java.util.Optional;
-
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
-import static org.junit.Assert.*;
-
-public class DefaultRoutingHandlerTest {
-    private SegmentRoutingManager srManager;
-    private DefaultRoutingHandler dfh;
-
-    private static final DeviceId DEV1A = DeviceId.deviceId("of:1a");
-    private static final DeviceId DEV1B = DeviceId.deviceId("of:1b");
-    private static final DeviceId DEV2 = DeviceId.deviceId("of:2");
-
-    private static final NodeId NODE1 = NodeId.nodeId("192.168.1.1");
-    private static final NodeId NODE2 = NodeId.nodeId("192.168.1.2");
-    private static final NodeId NODE3 = NodeId.nodeId("192.168.1.3");
-    private static final IpAddress IP1 = IpAddress.valueOf("192.168.1.1");
-    private static final IpAddress IP2 = IpAddress.valueOf("192.168.1.2");
-    private static final IpAddress IP3 = IpAddress.valueOf("192.168.1.3");
-
-    @Before
-    public void setUp() {
-        srManager = createMock(SegmentRoutingManager.class);
-        srManager.storageService = createMock(StorageService.class);
-        expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
-        expect(srManager.storageService.consistentMultimapBuilder()).andReturn(
-                new TestConsistentMultimap.Builder<>()).anyTimes();
-        replay(srManager.storageService);
-        srManager.routingRulePopulator = createMock(RoutingRulePopulator.class);
-        srManager.deviceService = createMock(DeviceService.class);
-        srManager.deviceConfiguration = createMock(DeviceConfiguration.class);
-        srManager.mastershipService = createMock(MastershipService.class);
-        srManager.clusterService = createMock(ClusterService.class);
-        dfh = new DefaultRoutingHandler(srManager);
-    }
-
-    private void clearCache() {
-        dfh.invalidateShouldProgramCache(DEV1A);
-        dfh.invalidateShouldProgramCache(DEV1B);
-        dfh.invalidateShouldProgramCache(DEV2);
-    }
-
-    // Node 1 is the master of switch 1A, 1B, and 2
-    @Test
-    public void testShouldHandleRoutingCase1() {
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE1).anyTimes();
-        replay(srManager.mastershipService);
-
-        expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
-        replay(srManager);
-
-        // Node 1 should program every device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertTrue(dfh.shouldProgram(DEV1A));
-        assertTrue(dfh.shouldProgram(DEV1B));
-        assertTrue(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 3 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-    }
-
-    // Node 1 is the master of switch 1A, 1B
-    // Node 2 is the master of switch 2
-    @Test
-    public void testShouldHandleRoutingCase2() {
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE2).anyTimes();
-        replay(srManager.mastershipService);
-
-        expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
-        replay(srManager);
-
-        // Node 1 should program 1A, 1B
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertTrue(dfh.shouldProgram(DEV1A));
-        assertTrue(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program 2
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertTrue(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 3 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-    }
-
-    // Node 1 is the master of switch 1A
-    // Node 2 is the master of switch 1B
-    // Node 3 is the master of switch 2
-    @Test
-    public void testShouldHandleRoutingCase3() {
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE2).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE3).anyTimes();
-        replay(srManager.mastershipService);
-
-        expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
-        replay(srManager);
-
-        // Node 1 should program 1A, 1B
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertTrue(dfh.shouldProgram(DEV1A));
-        assertTrue(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 3 should program 2
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertTrue(dfh.shouldProgram(DEV2));
-    }
-
-    // Node 3 is the master of switch 1A, 1B, 2
-    // Later on, node 1 becomes the master of 1A; Node 2 becomes the master of 1B.
-    @Test
-    public void testShouldHandleRoutingCase4() {
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE3).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE3).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE3).anyTimes();
-        replay(srManager.mastershipService);
-
-        expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV2)).andReturn(Optional.empty()).anyTimes();
-        replay(srManager);
-
-        // Node 1 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 3 should program 1A, 1B and 2
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
-        replay(srManager.clusterService);
-        assertTrue(dfh.shouldProgram(DEV1A));
-        assertTrue(dfh.shouldProgram(DEV1B));
-        assertTrue(dfh.shouldProgram(DEV2));
-
-        // Mastership of switch 1A moves to Node 1
-        reset(srManager.mastershipService);
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE2).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV2)).andReturn(NODE3).anyTimes();
-        replay(srManager.mastershipService);
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 1 should program 1A, 1B
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertTrue(dfh.shouldProgram(DEV1A));
-        assertTrue(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertFalse(dfh.shouldProgram(DEV2));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 3 should program 2
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE3, IP3)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-        assertTrue(dfh.shouldProgram(DEV2));
-    }
-
-    // Node 1 is the master of 1A. 1B has no master
-    // Node 2 becomes the master of 1B later
-    @Test
-    public void testShouldHandleRoutingCase5() {
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(null).anyTimes();
-        replay(srManager.mastershipService);
-
-        expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
-        replay(srManager);
-
-        // Node 1 should program both 1A and 1B
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertTrue(dfh.shouldProgram(DEV1A));
-        assertTrue(dfh.shouldProgram(DEV1B));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-
-        // Mastership of switch 1B moves to Node 2
-        reset(srManager.mastershipService);
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(NODE1).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(NODE2).anyTimes();
-        replay(srManager.mastershipService);
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 1 should program 1A, 1B
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertTrue(dfh.shouldProgram(DEV1A));
-        assertTrue(dfh.shouldProgram(DEV1B));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-    }
-
-    // Neither 1A or 1B has master
-    @Test
-    public void testShouldHandleRoutingCase6() {
-        expect(srManager.mastershipService.getMasterFor(DEV1A)).andReturn(null).anyTimes();
-        expect(srManager.mastershipService.getMasterFor(DEV1B)).andReturn(null).anyTimes();
-        replay(srManager.mastershipService);
-
-        expect(srManager.getPairDeviceId(DEV1A)).andReturn(Optional.of(DEV1B)).anyTimes();
-        expect(srManager.getPairDeviceId(DEV1B)).andReturn(Optional.of(DEV1A)).anyTimes();
-        replay(srManager);
-
-        // Node 1 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE1, IP1)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-
-        reset(srManager.clusterService);
-        clearCache();
-
-        // Node 2 should program no device
-        expect(srManager.clusterService.getLocalNode()).andReturn(new DefaultControllerNode(NODE2, IP2)).anyTimes();
-        replay(srManager.clusterService);
-        assertFalse(dfh.shouldProgram(DEV1A));
-        assertFalse(dfh.shouldProgram(DEV1B));
-
-        assertFalse(dfh.shouldProgram.containsKey(Sets.newHashSet(DEV1A, DEV1B)));
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java b/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
deleted file mode 100644
index d67cb07..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/HostHandlerTest.java
+++ /dev/null
@@ -1,1020 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.easymock.EasyMock;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.EthType;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.util.PredictableExecutor;
-import org.onosproject.net.config.ConfigApplyDelegate;
-import org.onosproject.net.host.HostProbingService;
-import org.onosproject.net.host.ProbeMode;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultHost;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.config.NetworkConfigRegistryAdapter;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.Route;
-import org.onosproject.routeservice.RouteInfo;
-import org.onosproject.routeservice.RouteService;
-import org.onosproject.routeservice.RouteTableId;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.TestConsistentMap;
-import org.onosproject.store.service.TestConsistentMultimap;
-
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.reset;
-import static org.easymock.EasyMock.verify;
-import static org.junit.Assert.*;
-import static org.onlab.util.Tools.groupedThreads;
-
-/**
- * Unit test for {@link HostHandler}.
- */
-public class HostHandlerTest {
-    private HostHandler hostHandler;
-
-    // Mocked routing and bridging tables
-    private static final Map<MockBridgingTableKey, MockBridgingTableValue> BRIDGING_TABLE =
-            Maps.newConcurrentMap();
-    private static final Map<MockRoutingTableKey, MockRoutingTableValue> ROUTING_TABLE =
-            Maps.newConcurrentMap();
-    private static final Map<ConnectPoint, Set<IpPrefix>> SUBNET_TABLE = Maps.newConcurrentMap();
-    // Mocked Next Id
-    private static final Map<Integer, TrafficTreatment> NEXT_TABLE = Maps.newConcurrentMap();
-
-    // Host Mac, VLAN
-    private static final ProviderId PROVIDER_ID = ProviderId.NONE;
-    private static final MacAddress HOST_MAC = MacAddress.valueOf("00:00:00:00:00:01");
-    private static final VlanId HOST_VLAN_UNTAGGED = VlanId.NONE;
-    private static final HostId HOST_ID_UNTAGGED = HostId.hostId(HOST_MAC, HOST_VLAN_UNTAGGED);
-    private static final VlanId HOST_VLAN_TAGGED = VlanId.vlanId((short) 20);
-    private static final HostId HOST_ID_TAGGED = HostId.hostId(HOST_MAC, HOST_VLAN_TAGGED);
-    // Host IP
-    private static final IpAddress HOST_IP11 = IpAddress.valueOf("10.0.1.1");
-    private static final IpAddress HOST_IP21 = IpAddress.valueOf("10.0.2.1");
-    private static final IpAddress HOST_IP12 = IpAddress.valueOf("10.0.1.2");
-    private static final IpAddress HOST_IP13 = IpAddress.valueOf("10.0.1.3");
-    private static final IpAddress HOST_IP14 = IpAddress.valueOf("10.0.1.4");
-    private static final IpAddress HOST_IP33 = IpAddress.valueOf("10.0.3.3");
-    // Device
-    private static final DeviceId DEV1 = DeviceId.deviceId("of:0000000000000001");
-    private static final DeviceId DEV2 = DeviceId.deviceId("of:0000000000000002");
-    private static final DeviceId DEV3 = DeviceId.deviceId("of:0000000000000003");
-    private static final DeviceId DEV4 = DeviceId.deviceId("of:0000000000000004");
-    private static final DeviceId DEV5 = DeviceId.deviceId("of:0000000000000005");
-    private static final DeviceId DEV6 = DeviceId.deviceId("of:0000000000000006");
-    // Port
-    private static final PortNumber P1 = PortNumber.portNumber(1);
-    private static final PortNumber P2 = PortNumber.portNumber(2);
-    private static final PortNumber P3 = PortNumber.portNumber(3);
-    private static final PortNumber P9 = PortNumber.portNumber(9);
-    // Connect Point
-    private static final ConnectPoint CP11 = new ConnectPoint(DEV1, P1);
-    private static final HostLocation HOST_LOC11 = new HostLocation(CP11, 0);
-    private static final ConnectPoint CP12 = new ConnectPoint(DEV1, P2);
-    private static final HostLocation HOST_LOC12 = new HostLocation(CP12, 0);
-    private static final ConnectPoint CP13 = new ConnectPoint(DEV1, P3);
-    private static final HostLocation HOST_LOC13 = new HostLocation(CP13, 0);
-    private static final ConnectPoint CP21 = new ConnectPoint(DEV2, P1);
-    private static final HostLocation HOST_LOC21 = new HostLocation(CP21, 0);
-    private static final ConnectPoint CP22 = new ConnectPoint(DEV2, P2);
-    private static final HostLocation HOST_LOC22 = new HostLocation(CP22, 0);
-    // Connect Point for dual-homed host failover
-    private static final ConnectPoint CP31 = new ConnectPoint(DEV3, P1);
-    private static final HostLocation HOST_LOC31 = new HostLocation(CP31, 0);
-    private static final ConnectPoint CP32 = new ConnectPoint(DEV3, P2);
-    private static final HostLocation HOST_LOC32 = new HostLocation(CP32, 0);
-    private static final ConnectPoint CP33 = new ConnectPoint(DEV3, P3);
-    private static final HostLocation HOST_LOC33 = new HostLocation(CP33, 0);
-    private static final ConnectPoint CP41 = new ConnectPoint(DEV4, P1);
-    private static final HostLocation HOST_LOC41 = new HostLocation(CP41, 0);
-    private static final ConnectPoint CP42 = new ConnectPoint(DEV4, P2);
-    private static final HostLocation HOST_LOC42 = new HostLocation(CP42, 0);
-    private static final ConnectPoint CP39 = new ConnectPoint(DEV3, P9);
-    private static final ConnectPoint CP49 = new ConnectPoint(DEV4, P9);
-    // Conenct Point for mastership test
-    private static final ConnectPoint CP51 = new ConnectPoint(DEV5, P1);
-    private static final HostLocation HOST_LOC51 = new HostLocation(CP51, 0);
-    private static final ConnectPoint CP61 = new ConnectPoint(DEV6, P1);
-    private static final HostLocation HOST_LOC61 = new HostLocation(CP61, 0);
-    // Interface VLAN
-    private static final VlanId INTF_VLAN_UNTAGGED = VlanId.vlanId((short) 10);
-    private static final VlanId INTF_VLAN_TAGGED_1 = VlanId.vlanId((short) 20);
-    private static final Set<VlanId> INTF_VLAN_TAGGED = Sets.newHashSet(INTF_VLAN_TAGGED_1);
-    private static final VlanId INTF_VLAN_NATIVE = VlanId.vlanId((short) 30);
-    private static final Set<VlanId> INTF_VLAN_PAIR = Sets.newHashSet(VlanId.vlanId((short) 10),
-            VlanId.vlanId((short) 20), VlanId.vlanId((short) 30));
-    private static final VlanId INTF_VLAN_OTHER = VlanId.vlanId((short) 40);
-    // Interface subnet
-    private static final IpPrefix INTF_PREFIX1 = IpPrefix.valueOf("10.0.1.254/24");
-    private static final IpPrefix INTF_PREFIX2 = IpPrefix.valueOf("10.0.2.254/24");
-    private static final IpPrefix INTF_PREFIX3 = IpPrefix.valueOf("10.0.3.254/24");
-    private static final InterfaceIpAddress INTF_IP1 =
-            new InterfaceIpAddress(INTF_PREFIX1.address(), INTF_PREFIX1);
-    private static final InterfaceIpAddress INTF_IP2 =
-            new InterfaceIpAddress(INTF_PREFIX2.address(), INTF_PREFIX2);
-    private static final InterfaceIpAddress INTF_IP3 =
-            new InterfaceIpAddress(INTF_PREFIX3.address(), INTF_PREFIX3);
-    // Interfaces
-    private static final Interface INTF11 =
-            new Interface(null, CP11, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF12 =
-            new Interface(null, CP12, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF13 =
-            new Interface(null, CP13, Lists.newArrayList(INTF_IP2), MacAddress.NONE, null,
-                    null, INTF_VLAN_TAGGED, INTF_VLAN_NATIVE);
-    private static final Interface INTF21 =
-            new Interface(null, CP21, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF22 =
-            new Interface(null, CP22, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF31 =
-            new Interface(null, CP31, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF32 =
-            new Interface(null, CP32, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF33 =
-            new Interface(null, CP33, Lists.newArrayList(INTF_IP3), MacAddress.NONE, null,
-                    INTF_VLAN_OTHER, null, null);
-    private static final Interface INTF39 =
-            new Interface(null, CP39, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    null, INTF_VLAN_PAIR, null);
-    private static final Interface INTF41 =
-            new Interface(null, CP41, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF42 =
-            new Interface(null, CP42, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    INTF_VLAN_UNTAGGED, null, null);
-    private static final Interface INTF49 =
-            new Interface(null, CP49, Lists.newArrayList(INTF_IP1), MacAddress.NONE, null,
-                    null, INTF_VLAN_PAIR, null);
-    // Host
-    private static final Host HOST1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC,
-            HOST_VLAN_UNTAGGED, Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11),
-            false);
-
-    // A set of hosts
-    private static final Set<Host> HOSTS = Sets.newHashSet(HOST1);
-    // A set of devices of which we have mastership
-    private static final Set<DeviceId> LOCAL_DEVICES = Sets.newHashSet(DEV1, DEV2, DEV3, DEV4);
-    // A set of interfaces
-    private static final Set<Interface> INTERFACES = Sets.newHashSet(INTF11, INTF12, INTF13, INTF21,
-            INTF22, INTF31, INTF32, INTF33, INTF39, INTF41, INTF42, INTF49);
-
-    private MockHostProbingService mockLocationProbingService;
-
-    @Before
-    public void setUp() {
-        // Initialize pairDevice and pairLocalPort config
-        ObjectMapper mapper = new ObjectMapper();
-        ConfigApplyDelegate delegate = config -> { };
-
-        SegmentRoutingDeviceConfig dev3Config = new SegmentRoutingDeviceConfig();
-        JsonNode dev3Tree = mapper.createObjectNode();
-        dev3Config.init(DEV3, "host-handler-test", dev3Tree, mapper, delegate);
-        dev3Config.setPairDeviceId(DEV4).setPairLocalPort(P9);
-
-        SegmentRoutingDeviceConfig dev4Config = new SegmentRoutingDeviceConfig();
-        JsonNode dev4Tree = mapper.createObjectNode();
-        dev4Config.init(DEV4, "host-handler-test", dev4Tree, mapper, delegate);
-        dev4Config.setPairDeviceId(DEV3).setPairLocalPort(P9);
-
-        MockNetworkConfigRegistry mockNetworkConfigRegistry = new MockNetworkConfigRegistry();
-        mockNetworkConfigRegistry.applyConfig(dev3Config);
-        mockNetworkConfigRegistry.applyConfig(dev4Config);
-
-        // Initialize Segment Routing Manager
-        SegmentRoutingManager srManager = new MockSegmentRoutingManager(NEXT_TABLE);
-        srManager.storageService = createMock(StorageService.class);
-        expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
-        expect(srManager.storageService.consistentMultimapBuilder()).andReturn(
-                new TestConsistentMultimap.Builder<>()).anyTimes();
-        replay(srManager.storageService);
-        srManager.cfgService = new NetworkConfigRegistryAdapter();
-        srManager.deviceConfiguration = new DeviceConfiguration(srManager);
-        srManager.flowObjectiveService = new MockFlowObjectiveService(BRIDGING_TABLE, NEXT_TABLE);
-        srManager.routingRulePopulator = new MockRoutingRulePopulator(srManager, ROUTING_TABLE);
-        srManager.defaultRoutingHandler = new MockDefaultRoutingHandler(srManager, SUBNET_TABLE, ROUTING_TABLE);
-        srManager.interfaceService = new MockInterfaceService(INTERFACES);
-        srManager.mastershipService = new MockMastershipService(LOCAL_DEVICES);
-        srManager.hostService = new MockHostService(HOSTS);
-        srManager.cfgService = mockNetworkConfigRegistry;
-        mockLocationProbingService = new MockHostProbingService();
-        srManager.probingService = mockLocationProbingService;
-        srManager.linkHandler = new MockLinkHandler(srManager);
-
-        // Not important for most of the HostHandler test case. Simply return an empty set here
-        srManager.routeService = createNiceMock(RouteService.class);
-        expect(srManager.routeService.getRouteTables()).andReturn(Sets.newHashSet()).anyTimes();
-        replay(srManager.routeService);
-
-        hostHandler = new HostHandler(srManager);
-        hostHandler.hostWorkers = new PredictableExecutor(
-                0, groupedThreads("onos/sr", "h-worker-%d"), true);
-
-        ROUTING_TABLE.clear();
-        BRIDGING_TABLE.clear();
-    }
-
-    @Test
-    public void init() {
-        hostHandler.init(DEV1);
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        hostHandler.init(DEV2);
-        assertEquals(2, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-    }
-
-    @Test(expected = IllegalArgumentException.class)
-    public void testHostAddedAtWrongLocation() {
-        hostHandler.processHostAddedAtLocation(HOST1, HOST_LOC13);
-    }
-
-
-    @Test()
-    public void testHostAddedAtCorrectLocation() {
-        hostHandler.processHostAddedAtLocation(HOST1, HOST_LOC11);
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-    }
-
-    @Test
-    public void testHostAdded() {
-        Host subject;
-
-        // Untagged host discovered on untagged port
-        // Expect: add one routing rule and one bridging rule
-        subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Untagged host discovered on tagged/native port
-        // Expect: add one routing rule and one bridging rule
-        subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC13), Sets.newHashSet(HOST_IP21), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_NATIVE)));
-
-        // Tagged host discovered on untagged port
-        // Expect: ignore the host. No rule is added.
-        subject = new DefaultHost(PROVIDER_ID, HOST_ID_TAGGED, HOST_MAC, HOST_VLAN_TAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(2, BRIDGING_TABLE.size());
-
-        // Tagged host discovered on tagged port with the same IP
-        // Expect: update existing route, add one bridging rule
-        subject = new DefaultHost(PROVIDER_ID, HOST_ID_TAGGED, HOST_MAC, HOST_VLAN_TAGGED,
-                Sets.newHashSet(HOST_LOC13), Sets.newHashSet(HOST_IP21), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertEquals(HOST_VLAN_TAGGED, ROUTING_TABLE.get(new MockRoutingTableKey(HOST_LOC13.deviceId(),
-                HOST_IP21.toIpPrefix())).vlanId);
-        assertEquals(3, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, HOST_VLAN_TAGGED)));
-    }
-
-    @Test
-    public void testDualHomedHostAdded() {
-        // Add a dual-homed host that has 2 locations
-        // Expect: add two routing rules and two bridging rules
-        Host subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-    }
-
-    @Test
-    public void testSingleHomedHostAddedOnPairLeaf() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC33), Sets.newHashSet(HOST_IP33), false);
-
-        // Add a single-homed host with one location
-        // Expect: the pair link should not be utilized
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertEquals(P3, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP33.toIpPrefix())).portNumber);
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertEquals(P3, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_OTHER)).portNumber);
-    }
-
-    @Test
-    public void testDualHomedHostAddedOneByOne() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31), Sets.newHashSet(HOST_IP11), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a dual-homed host with one location
-        // Expect: the pair link is utilized temporarily before the second location is discovered
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        // Expect probe to be sent out on pair device
-        assertTrue(mockLocationProbingService.verifyProbe(host1, CP41, ProbeMode.DISCOVER));
-
-        // Add the second location of dual-homed host
-        // Expect: no longer use the pair link
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-    }
-
-    @Test
-    public void testHostRemoved() {
-        Host subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a host
-        // Expect: add one routing rule and one bridging rule
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Remove the host
-        // Expect: add the routing rule and the bridging rule
-        hostHandler.processHostRemovedEvent(new HostEvent(HostEvent.Type.HOST_REMOVED, subject));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, BRIDGING_TABLE.size());
-    }
-
-    @Test
-    public void testDualHomedHostRemoved() {
-        // Add a dual-homed host that has 2 locations
-        // Expect: add two routing rules and two bridging rules
-        Host subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Remove a dual-homed host that has 2 locations
-        // Expect: all routing and bridging rules are removed
-        hostHandler.processHostRemovedEvent(new HostEvent(HostEvent.Type.HOST_REMOVED, subject));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, BRIDGING_TABLE.size());
-    }
-
-    @Test
-    public void testHostMoved() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC21), Sets.newHashSet(HOST_IP11), false);
-        Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC13), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a host
-        // Expect: add one new routing rule, one new bridging rule
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP13.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Move the host to CP13, which has different subnet setting
-        // Expect: remove routing rule. Change vlan in bridging rule.
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host1));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_NATIVE)));
-        assertNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Move the host to CP21, which has same subnet setting
-        // Expect: add a new routing rule. Change vlan in bridging rule.
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host3));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-    }
-
-    @Test
-    public void testDualHomedHostMoved() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC12, HOST_LOC22), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-        Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP13, HOST_IP14), false);
-        Host host4 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC22), Sets.newHashSet(HOST_IP12, HOST_IP13), false);
-        Host host5 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC12, HOST_LOC21), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-
-
-        // Add a host with IP11, IP12 and LOC11, LOC21
-        // Expect: 4 routing rules and 2 bridging rules
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Move the host to LOC12, LOC21 and keep the IP
-        // Expect: 4 routing rules and 2 bridging rules all at the new location
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host5, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P2, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Move the host to LOC12, LOC22 and keep the IP
-        // Expect: 4 routing rules and 2 bridging rules all at the new location
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host5));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P2, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P2, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Move the host to LOC11, LOC21 and change the IP to IP13, IP14 at the same time
-        // Expect: 4 routing rules and 2 bridging rules all at the new location
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP13.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP14.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP13.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP14.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Move the host to LOC11, LOC22 and change the IP to IP12, IP13 at the same time
-        // Expect: 4 routing rules and 2 bridging rules all at the new location
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host4, host3));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP13.toIpPrefix())).portNumber);
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P2, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP13.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P2, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-    }
-
-    @Test
-    public void testDualHomedHostMoveTransient() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(HOST_IP11), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC32, HOST_LOC41), Sets.newHashSet(HOST_IP11), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-
-        // Mock DefaultRoutingHandler
-        DefaultRoutingHandler mockDefaultRoutingHandler = createMock(DefaultRoutingHandler.class);
-        hostHandler.srManager.defaultRoutingHandler = mockDefaultRoutingHandler;
-
-        // Host moved from [1A/1, 1B/1] to [1A/2, 1B/1]
-        // We should expect only one bridging flow and one routing flow programmed on 1A
-
-        expect(mockDefaultRoutingHandler.populateBridging(DEV3, P2, HOST_MAC, HOST_VLAN_UNTAGGED))
-                .andReturn(CompletableFuture.completedFuture(null)).once();
-        expect(mockDefaultRoutingHandler.populateRoute(DEV3, HOST_IP11.toIpPrefix(),
-                HOST_MAC, HOST_VLAN_UNTAGGED, P2, true))
-                .andReturn(CompletableFuture.completedFuture(null)).once();
-        replay(mockDefaultRoutingHandler);
-
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-        verify(mockDefaultRoutingHandler);
-    }
-
-    @Test
-    public void testHostMoveToInvalidLocation() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC51), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a host
-        // Expect: add one new routing rule, one new bridging rule
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Move the host to an invalid location
-        // Expect: Old flow is removed. New flow is not created
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, BRIDGING_TABLE.size());
-
-        // Move the host to a valid location
-        // Expect: add one new routing rule, one new bridging rule
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host1, host2));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-    }
-
-    @Test
-    public void testDualHomedHostMoveToInvalidLocation() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC51), Sets.newHashSet(HOST_IP11), false);
-        Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC61, HOST_LOC51), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a host
-        // Expect: add two new routing rules, two new bridging rules
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Move first host location to an invalid location
-        // Expect: One routing and one bridging flow
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Move second host location to an invalid location
-        // Expect: No routing or bridging rule
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, BRIDGING_TABLE.size());
-
-        // Move second host location back to a valid location
-        // Expect: One routing and one bridging flow
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host3));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Move first host location back to a valid location
-        // Expect: Two routing and two bridging flow
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host1, host2));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-    }
-
-    @Test
-    public void testDualHomingSingleLocationFail() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-
-        // Add a host
-        // Expect: add four new routing rules, two new bridging rules
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Host becomes single-homed
-        // Expect: redirect flows from host location to pair link
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Host becomes dual-homed again
-        // Expect: Redirect flows from pair link back to host location
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host1, host2));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-    }
-
-    @Test
-    public void testDualHomingBothLocationFail() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-
-        // Add a host
-        // Expect: add four new routing rules, two new bridging rules
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Host becomes single-homed
-        // Expect: redirect flows from host location to pair link
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP12.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Host loses both locations
-        // Expect: Remove last location and all previous redirection flows
-        hostHandler.processHostRemovedEvent(new HostEvent(HostEvent.Type.HOST_REMOVED, host2));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, BRIDGING_TABLE.size());
-    }
-
-    @Test
-    public void testHostUpdated() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP21), false);
-        Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP12), false);
-
-        // Add a host
-        // Expect: add one new routing rule. Add one new bridging rule.
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(HOST_LOC11.deviceId(), HOST_MAC,
-                INTF_VLAN_UNTAGGED)));
-
-        // Update the host IP to same subnet
-        // Expect: update routing rule with new IP. No change to bridging rule.
-        hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host3, host1));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Update the host IP to different subnet
-        // Expect: Remove routing rule. No change to bridging rule.
-        hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host3));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-    }
-
-    @Test
-    public void testDelayedIpAndLocation() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31), Sets.newHashSet(), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31), Sets.newHashSet(HOST_IP11), false);
-        Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a dual-home host with only one location and no IP
-        // Expect: only bridging redirection works
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Discover IP
-        // Expect: routing redirection should also work
-        hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host1));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P9, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        // Expect probe to be sent out on pair device
-        assertTrue(mockLocationProbingService.verifyProbe(host2, CP41, ProbeMode.DISCOVER));
-
-        // Discover location
-        // Expect: cancel all redirections
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host3, host2));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-    }
-
-    @Test
-    public void testDelayedIpAndLocation2() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31), Sets.newHashSet(), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(), false);
-        Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a dual-home host with only one location and no IP
-        // Expect: only bridging redirection works
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P9, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Discover Location
-        // Expect: cancel bridging redirections
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-
-        // Discover IP
-        // Expect: add IP rules to both location
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host3, host2));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV3, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(P1, ROUTING_TABLE.get(new MockRoutingTableKey(DEV4, HOST_IP11.toIpPrefix())).portNumber);
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV3, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-        assertEquals(P1, BRIDGING_TABLE.get(new MockBridgingTableKey(DEV4, HOST_MAC, INTF_VLAN_UNTAGGED)).portNumber);
-    }
-
-    @Test
-    public void testDualHomedHostUpdated() {
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11, HOST_IP12), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP11, HOST_IP21), false);
-        Host host3 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC21), Sets.newHashSet(HOST_IP13, HOST_IP14), false);
-
-        // Add a dual-homed host with two locations and two IPs
-        // Expect: add four new routing rules. Add two new bridging rules
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP12.toIpPrefix())));
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Update both host IPs
-        // Expect: update routing rules with new IP. No change to bridging rule.
-        hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host3, host1));
-        assertEquals(4, ROUTING_TABLE.size());
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP13.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP14.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP12.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP13.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP14.toIpPrefix())));
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        // Update one of the host IP to different subnet
-        // Expect: update routing rule with new IP. No change to bridging rule.
-        hostHandler.processHostUpdatedEvent(new HostEvent(HostEvent.Type.HOST_UPDATED, host2, host3));
-        assertEquals(2, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP21.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP12.toIpPrefix())));
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP11.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP21.toIpPrefix())));
-        assertNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV2, HOST_IP12.toIpPrefix())));
-        assertEquals(2, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV2, HOST_MAC, INTF_VLAN_UNTAGGED)));
-    }
-
-    @Test
-    public void testVlanForPairPort() {
-        assertEquals(INTF_VLAN_UNTAGGED, hostHandler.vlanForPairPort(VlanId.NONE, CP11));
-        assertEquals(INTF_VLAN_NATIVE, hostHandler.vlanForPairPort(VlanId.NONE, CP13));
-        assertEquals(INTF_VLAN_TAGGED_1, hostHandler.vlanForPairPort(INTF_VLAN_TAGGED_1, CP13));
-        assertNull(hostHandler.vlanForPairPort(INTF_VLAN_UNTAGGED, CP11));
-        assertNull(hostHandler.vlanForPairPort(INTF_VLAN_UNTAGGED, CP13));
-        assertNull(hostHandler.vlanForPairPort(VlanId.NONE, CP51));
-        assertNull(hostHandler.vlanForPairPort(INTF_VLAN_UNTAGGED, CP51));
-    }
-
-    @Test
-    public void testHostRemovedWithRouteRemoved() {
-        Host subject = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC11), Sets.newHashSet(HOST_IP11), false);
-
-        // Add a host
-        // Expect: add one routing rule and one bridging rule
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, subject));
-        assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(DEV1, HOST_IP11.toIpPrefix())));
-        assertEquals(1, BRIDGING_TABLE.size());
-        assertNotNull(BRIDGING_TABLE.get(new MockBridgingTableKey(DEV1, HOST_MAC, INTF_VLAN_UNTAGGED)));
-
-        IpPrefix prefix = IpPrefix.valueOf("55.55.55.0/24");
-
-        // Setting up mock route service
-        RouteService routeService = hostHandler.srManager.routeService;
-        reset(routeService);
-
-        IpAddress nextHopIp2 = IpAddress.valueOf("20.0.0.1");
-        MacAddress nextHopMac2 = MacAddress.valueOf("00:22:33:44:55:66");
-        VlanId nextHopVlan2 = VlanId.NONE;
-
-        Route r1 = new Route(Route.Source.STATIC, prefix, HOST_IP11);
-        ResolvedRoute rr1 = new ResolvedRoute(r1, HOST_MAC, VlanId.NONE);
-        Route r2 = new Route(Route.Source.STATIC, prefix, nextHopIp2);
-        ResolvedRoute rr2 = new ResolvedRoute(r2, nextHopMac2, nextHopVlan2);
-        RouteInfo routeInfo = new RouteInfo(prefix, rr1, Sets.newHashSet(rr1, rr2));
-        RouteTableId routeTableId = new RouteTableId("ipv4");
-
-        expect(routeService.getRouteTables()).andReturn(Sets.newHashSet(routeTableId));
-        expect(routeService.getRoutes(routeTableId)).andReturn(Sets.newHashSet(routeInfo));
-        replay(routeService);
-
-        // Setting up mock device configuration
-        hostHandler.srManager.deviceConfiguration = EasyMock.createNiceMock(DeviceConfiguration.class);
-        DeviceConfiguration deviceConfiguration = hostHandler.srManager.deviceConfiguration;
-        expect(deviceConfiguration.inSameSubnet(CP11, HOST_IP11)).andReturn(true);
-        deviceConfiguration.removeSubnet(CP11, prefix);
-        expectLastCall();
-        replay(deviceConfiguration);
-
-        // Remove the host
-        // Expect: add the routing rule and the bridging rule
-        hostHandler.processHostRemovedEvent(new HostEvent(HostEvent.Type.HOST_REMOVED, subject));
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, BRIDGING_TABLE.size());
-
-        // Expect: subnet is removed from device config
-        verify(deviceConfiguration);
-    }
-
-    @Test
-    public void testHostProbing() {
-        // Case: [1A/1, 1B/1] -> [1A/2, 1B/1]
-        // Expect: DISCOVER probe should be sent to every port on 1B that has the same VLAN as 1A/2
-        //         VERIFY probe should be sent to 1B/1
-        Host host1 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC31, HOST_LOC41), Sets.newHashSet(HOST_IP11), false);
-        Host host2 = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_UNTAGGED,
-                Sets.newHashSet(HOST_LOC32, HOST_LOC41), Sets.newHashSet(HOST_IP11), false);
-        hostHandler.processHostAddedEvent(new HostEvent(HostEvent.Type.HOST_ADDED, host1));
-
-        hostHandler.srManager.probingService = createMock(HostProbingService.class);
-        hostHandler.srManager.probingService.probeHost(host2, CP41, ProbeMode.DISCOVER);
-        expectLastCall();
-        hostHandler.srManager.probingService.probeHost(host2, CP42, ProbeMode.DISCOVER);
-        expectLastCall();
-        hostHandler.srManager.probingService.probeHost(host2, CP41, ProbeMode.VERIFY);
-        expectLastCall();
-        replay(hostHandler.srManager.probingService);
-
-        hostHandler.processHostMovedEvent(new HostEvent(HostEvent.Type.HOST_MOVED, host2, host1));
-
-        verify(hostHandler.srManager.probingService);
-    }
-
-    @Test
-    public void testEffectiveLocations() {
-        Host regularHost = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_TAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC12), Sets.newHashSet(HOST_IP11), false);
-        Host auxHost = new DefaultHost(PROVIDER_ID, HOST_ID_UNTAGGED, HOST_MAC, HOST_VLAN_TAGGED,
-                Sets.newHashSet(HOST_LOC11, HOST_LOC12), Sets.newHashSet(HOST_LOC21, HOST_LOC22),
-                Sets.newHashSet(HOST_IP11), VlanId.NONE, EthType.EtherType.UNKNOWN.ethType(), false, false);
-
-        assertEquals(Sets.newHashSet(HOST_LOC11, HOST_LOC12), hostHandler.effectiveLocations(regularHost));
-        assertEquals(Sets.newHashSet(HOST_LOC21, HOST_LOC22), hostHandler.effectiveLocations(auxHost));
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/IcmpHandlerTest.java b/app/src/test/java/org/onosproject/segmentrouting/IcmpHandlerTest.java
deleted file mode 100644
index 68472de..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/IcmpHandlerTest.java
+++ /dev/null
@@ -1,795 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-import com.google.common.collect.ImmutableSet;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.ICMP;
-import org.onlab.packet.ICMP6;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.IPv6;
-import org.onlab.packet.MPLS;
-import org.onosproject.TestApplicationId;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.config.ConfigApplyDelegate;
-import org.onosproject.net.config.basics.InterfaceConfig;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.config.SegmentRoutingAppConfig;
-import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
-
-import java.util.Collections;
-
-import static org.easymock.EasyMock.*;
-import static org.hamcrest.core.Is.is;
-import static org.junit.Assert.*;
-import static org.onlab.packet.ICMP.*;
-import static org.onlab.packet.ICMP6.ECHO_REPLY;
-import static org.onosproject.segmentrouting.TestUtils.*;
-
-/**
- * Tests for IcmpHandler.
- */
-public class IcmpHandlerTest {
-
-    private IcmpHandler icmpHandler;
-    private MockPacketService packetService;
-    private SegmentRoutingManager segmentRoutingManager;
-    private ApplicationId testApplicationId = TestApplicationId.create("test");
-
-    @Before
-    public void setUp() {
-
-        // Init
-        ObjectMapper mapper = new ObjectMapper();
-        ConfigApplyDelegate delegate = config -> { };
-
-        // Setup configuration for app
-        SegmentRoutingAppConfig appConfig = new SegmentRoutingAppConfig();
-        JsonNode appTree = mapper.createObjectNode();
-        appConfig.init(testApplicationId, "icmp-handler-test", appTree, mapper, delegate);
-        appConfig.setSuppressSubnet(Collections.emptySet());
-
-        // Setup configuration for the devices
-        SegmentRoutingDeviceConfig remoteLeafConfig = new SegmentRoutingDeviceConfig();
-        JsonNode remoteLeafTree = mapper.createObjectNode();
-        remoteLeafConfig.init(REMOTE_LEAF, "icmp-handler-test", remoteLeafTree, mapper, delegate);
-        remoteLeafConfig.setNodeSidIPv4(REMOTE_LEAF_SID4)
-                .setNodeSidIPv6(REMOTE_LEAF_SID6)
-                .setRouterIpv4(REMOTE_LEAF_LB4)
-                .setRouterIpv6(REMOTE_LEAF_LB6)
-                .setIsEdgeRouter(true)
-                .setRouterMac(REMOTE_MAC.toString());
-        SegmentRoutingDeviceConfig localLeafConfig = new SegmentRoutingDeviceConfig();
-        JsonNode localLeafTree = mapper.createObjectNode();
-        localLeafConfig.init(LOCAL_LEAF, "icmp-handler-test", localLeafTree, mapper, delegate);
-        localLeafConfig.setNodeSidIPv4(LOCAL_LEAF_SID4)
-                .setRouterIpv4(LOCAL_LEAF_LB4)
-                .setNodeSidIPv6(LOCAL_LEAF_SID6)
-                .setRouterIpv6(LOCAL_LEAF_LB6)
-                .setIsEdgeRouter(true)
-                .setRouterMac(LOCAL_MAC.toString());
-        SegmentRoutingDeviceConfig localLeaf1Config = new SegmentRoutingDeviceConfig();
-        JsonNode localLeaf1Tree = mapper.createObjectNode();
-        localLeaf1Config.init(LOCAL_LEAF1, "icmp-handler-test", localLeaf1Tree, mapper, delegate);
-        localLeaf1Config.setNodeSidIPv4(LOCAL_LEAF1_SID4)
-                .setRouterIpv4(LOCAL_LEAF1_LB4)
-                .setNodeSidIPv6(LOCAL_LEAF1_SID6)
-                .setRouterIpv6(LOCAL_LEAF1_LB6)
-                .setIsEdgeRouter(true)
-                .setRouterMac(LOCAL_MAC1.toString())
-                .setPairDeviceId(LOCAL_LEAF2)
-                .setPairLocalPort(P3);
-        SegmentRoutingDeviceConfig localLeaf2Config = new SegmentRoutingDeviceConfig();
-        JsonNode localLeaf2Tree = mapper.createObjectNode();
-        localLeaf2Config.init(LOCAL_LEAF2, "icmp-handler-test", localLeaf2Tree, mapper, delegate);
-        localLeaf2Config.setNodeSidIPv4(LOCAL_LEAF2_SID4)
-                .setRouterIpv4(LOCAL_LEAF2_LB4)
-                .setNodeSidIPv6(LOCAL_LEAF2_SID6)
-                .setRouterIpv6(LOCAL_LEAF2_LB6)
-                .setIsEdgeRouter(true)
-                .setRouterMac(LOCAL_MAC2.toString())
-                .setPairDeviceId(LOCAL_LEAF1)
-                .setPairLocalPort(P3);
-
-        // Setup configuration for ports
-        InterfaceConfig remoteLeafPorts1Config = new InterfaceConfig();
-        ArrayNode remoteLeafPorts1Tree = mapper.createArrayNode();
-        remoteLeafPorts1Config.init(CP12, "icmp-handler-test", remoteLeafPorts1Tree, mapper, delegate);
-        remoteLeafPorts1Config.addInterface(INTF1);
-        InterfaceConfig remoteLeafPorts2Config = new InterfaceConfig();
-        ArrayNode remoteLeafPorts2Tree = mapper.createArrayNode();
-        remoteLeafPorts2Config.init(CP13, "icmp-handler-test", remoteLeafPorts2Tree, mapper, delegate);
-        remoteLeafPorts2Config.addInterface(INTF2);
-        InterfaceConfig localLeafPortsConfig = new InterfaceConfig();
-        ArrayNode localLeafPortsTree = mapper.createArrayNode();
-        localLeafPortsConfig.init(CP1011, "icmp-handler-test", localLeafPortsTree, mapper, delegate);
-        localLeafPortsConfig.addInterface(INTF111);
-        InterfaceConfig localLeaf1PortsConfig = new InterfaceConfig();
-        ArrayNode localLeaf1PortsTree = mapper.createArrayNode();
-        localLeaf1PortsConfig.init(CP2011, "icmp-handler-test", localLeaf1PortsTree, mapper, delegate);
-        localLeaf1PortsConfig.addInterface(INTF211);
-        InterfaceConfig localLeaf2Ports1Config = new InterfaceConfig();
-        ArrayNode localLeaf2Ports1Tree = mapper.createArrayNode();
-        localLeaf2Ports1Config.init(CP2021, "icmp-handler-test", localLeaf2Ports1Tree, mapper, delegate);
-        localLeaf2Ports1Config.addInterface(INTF212);
-        InterfaceConfig localLeaf2Ports2Config = new InterfaceConfig();
-        ArrayNode localLeaf2Ports2Tree = mapper.createArrayNode();
-        localLeaf2Ports2Config.init(CP2024, "icmp-handler-test", localLeaf2Ports2Tree, mapper, delegate);
-        localLeaf2Ports2Config.addInterface(INTF213);
-
-        // Apply config
-        MockNetworkConfigRegistry mockNetworkConfigRegistry = new MockNetworkConfigRegistry();
-
-        mockNetworkConfigRegistry.applyConfig(remoteLeafConfig);
-        mockNetworkConfigRegistry.applyConfig(remoteLeafPorts1Config);
-        mockNetworkConfigRegistry.applyConfig(remoteLeafPorts2Config);
-        mockNetworkConfigRegistry.applyConfig(localLeafConfig);
-        mockNetworkConfigRegistry.applyConfig(localLeafPortsConfig);
-        mockNetworkConfigRegistry.applyConfig(localLeaf1Config);
-        mockNetworkConfigRegistry.applyConfig(localLeaf1PortsConfig);
-        mockNetworkConfigRegistry.applyConfig(localLeaf2Config);
-        mockNetworkConfigRegistry.applyConfig(localLeaf2Ports1Config);
-        mockNetworkConfigRegistry.applyConfig(localLeaf2Ports2Config);
-
-        segmentRoutingManager = new SegmentRoutingManager();
-        segmentRoutingManager.appId = testApplicationId;
-        packetService = new MockPacketService();
-        segmentRoutingManager.packetService = packetService;
-        segmentRoutingManager.cfgService = mockNetworkConfigRegistry;
-        segmentRoutingManager.neighbourResolutionService = new MockNeighbourResolutionService();
-        segmentRoutingManager.interfaceService = new MockInterfaceService(ImmutableSet.of(
-                INTF1, INTF2, INTF111, INTF211, INTF212, INTF213));
-        segmentRoutingManager.deviceConfiguration = new DeviceConfiguration(segmentRoutingManager);
-        segmentRoutingManager.ipHandler = new IpHandler(segmentRoutingManager);
-        segmentRoutingManager.deviceService = createMock(DeviceService.class);
-        segmentRoutingManager.routeService = new MockRouteService(ROUTE_STORE);
-        segmentRoutingManager.hostService = new MockHostService(Collections.emptySet());
-        icmpHandler = new IcmpHandler(segmentRoutingManager);
-    }
-
-    // Ping to our gateway
-    @Test
-    public void testPing4MyGateway() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(REMOTE_LEAF))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_MY, CP12);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_MY.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_MY.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_MY.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV4_MY.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to our gateway
-    @Test
-    public void testPing6MyGateway() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(REMOTE_LEAF))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_MY, CP12);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_MY.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_MY.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_MY.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV6_MY.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping to a gateway attached to our leaf
-    @Test
-    public void testPing4LocalGateway() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(REMOTE_LEAF))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_LOCAL, CP12);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_LOCAL.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_LOCAL.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_LOCAL.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4_LOCAL.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV4_MY.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to a gateway attached to our leaf
-    @Test
-    public void testPing6LocalGateway() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(REMOTE_LEAF))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_LOCAL, CP12);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_LOCAL.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_LOCAL.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_LOCAL.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_LOCAL.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV6_MY.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping to a gateway attached only to the pair leaf (routing through spine)
-    @Test
-    public void testPing4RemoteGatewaySamePair() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_SAME, CP2025);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_SAME.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_SAME.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_SAME.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof MPLS);
-        MPLS mpls = (MPLS) ethernet.getPayload();
-        assertThat(mpls.getLabel(), is(LOCAL_LEAF1_SID4));
-        assertTrue(mpls.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) mpls.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4_SAME.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV41.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to a gateway attached only to the pair leaf (routing through spine)
-    @Test
-    public void testPing6RemoteGatewaySamePair() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_SAME, CP2025);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_SAME.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_SAME.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_SAME.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof MPLS);
-        MPLS mpls = (MPLS) ethernet.getPayload();
-        assertThat(mpls.getLabel(), is(LOCAL_LEAF1_SID6));
-        assertTrue(mpls.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) mpls.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_SAME.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV61.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping to a gateway but destination leaf is down
-    @Test
-    public void testPing4RemoteGatewayLeafDown() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF))
-                .andReturn(false)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4, CP11);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4.getSourceMAC());
-        assertNull(ethernet);
-
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to a gateway but destination leaf is down
-    @Test
-    public void testPing6RemoteGatewayLeafDown() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF))
-                .andReturn(false)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6, CP11);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6.getSourceMAC());
-        assertNull(ethernet);
-
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping to a gateway but one of the destination leaf is down
-    @Test
-    public void testPing4RemoteGatewayLeaf1Down() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(false)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV41, CP11);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV41.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV41.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV41.getSourceMAC()));
-        assertThat(ethernet.getEtherType(), is(Ethernet.MPLS_UNICAST));
-        assertTrue(ethernet.getPayload() instanceof MPLS);
-        MPLS mpls = (MPLS) ethernet.getPayload();
-        assertThat(mpls.getLabel(), is(LOCAL_LEAF2_SID4));
-        assertTrue(mpls.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) mpls.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV41.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to a gateway but one of the destination leaf is down
-    @Test
-    public void testPing6RemoteGatewayLeaf2Down() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(false)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV61, CP11);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV61.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV61.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV61.getSourceMAC()));
-        assertThat(ethernet.getEtherType(), is(Ethernet.MPLS_UNICAST));
-        assertTrue(ethernet.getPayload() instanceof MPLS);
-        MPLS mpls = (MPLS) ethernet.getPayload();
-        assertThat(mpls.getLabel(), is(LOCAL_LEAF1_SID6));
-        assertTrue(mpls.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) mpls.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV61.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to a link local address
-    @Test
-    public void testPing6LinkLocalAddress() {
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_LL, CP12);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_LL.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_LL.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_LL.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_LL.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV6_LL.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-    }
-
-    // Ping to the looback of our leaf
-    @Test
-    public void testPing4Loopback() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(REMOTE_LEAF))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_LOOPBACK, CP12);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_LOOPBACK.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_LOOPBACK.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_LOOPBACK.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4_LOOPBACK.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV4_MY.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to the looback of our leaf
-    @Test
-    public void testPing6Loopback() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(REMOTE_LEAF))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_LOOPBACK, CP12);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_LOOPBACK.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_LOOPBACK.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_LOOPBACK.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_LOOPBACK.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV6_MY.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping to the looback of our leaf (pair)
-    @Test
-    public void testPing4LoopbackPair() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_LOOPBACK_PAIR, CP2011);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_LOOPBACK_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_LOOPBACK_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_LOOPBACK_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4_LOOPBACK_PAIR.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV41.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to the looback of our leaf (pair)
-    @Test
-    public void testPing6LoopbackPair() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_LOOPBACK_PAIR, CP2021);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_LOOPBACK_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_LOOPBACK_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_LOOPBACK_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_LOOPBACK_PAIR.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV61.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping to the loopback of the leaf but hashing of the bond interfaces sends to wrong leaf
-    @Test
-    public void testPing4LoopbackPairDifferentLeaf() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_LOOPBACK_PAIR, CP2021);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_LOOPBACK_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_LOOPBACK_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_LOOPBACK_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4_LOOPBACK_PAIR.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV41.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to the loopback of the leaf but hashing of the bond interfaces sends to wrong leaf
-    @Test
-    public void testPing6LoopbackPairDifferentLeaf() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_LOOPBACK_PAIR, CP2011);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_LOOPBACK_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_LOOPBACK_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_LOOPBACK_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_LOOPBACK_PAIR.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV61.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping loopback of a destination that is down but
-    // hashing of the bond interfaces sends to other leaf
-    @Test
-    public void testPing4LoopbackPairDifferentLeafDown() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(false)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_LOOPBACK_PAIR, CP2021);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_LOOPBACK_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_LOOPBACK_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_LOOPBACK_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4_LOOPBACK_PAIR.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV41.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 loopback of a destination that is down but
-    // hashing of the bond interfaces sends to other leaf
-    @Test
-    public void testPing6LoopbackPairDifferentLeafDown() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(false)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_LOOPBACK_PAIR, CP2011);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_LOOPBACK_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_LOOPBACK_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_LOOPBACK_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_LOOPBACK_PAIR.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV61.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping to a dh gateway
-    @Test
-    public void testPing4GatewayPair() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmp(ETH_REQ_IPV4_GATEWAY_PAIR, CP2011);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV4_GATEWAY_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV4_GATEWAY_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV4_GATEWAY_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv4);
-        IPv4 ip = (IPv4) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV4_GATEWAY_PAIR.toInt()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV41.toInt()));
-        assertTrue(ip.getPayload() instanceof ICMP);
-        ICMP icmp = (ICMP) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(TYPE_ECHO_REPLY));
-        assertThat(icmp.getIcmpCode(), is(CODE_ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-    // Ping6 to a dh gateway
-    @Test
-    public void testPing6GatewayPair() {
-        // Expected behavior
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF1))
-                .andReturn(true)
-                .times(1);
-        expect(segmentRoutingManager.deviceService.isAvailable(LOCAL_LEAF2))
-                .andReturn(true)
-                .times(1);
-        replay(segmentRoutingManager.deviceService);
-
-        // Process
-        icmpHandler.processIcmpv6(ETH_REQ_IPV6_GATEWAY_PAIR, CP2021);
-
-        // Verify packet-out
-        Ethernet ethernet = packetService.getEthernetPacket(ETH_REQ_IPV6_GATEWAY_PAIR.getSourceMAC());
-        assertNotNull(ethernet);
-        assertThat(ethernet.getSourceMAC(), is(ETH_REQ_IPV6_GATEWAY_PAIR.getDestinationMAC()));
-        assertThat(ethernet.getDestinationMAC(), is(ETH_REQ_IPV6_GATEWAY_PAIR.getSourceMAC()));
-        assertTrue(ethernet.getPayload() instanceof IPv6);
-        IPv6 ip = (IPv6) ethernet.getPayload();
-        assertThat(ip.getSourceAddress(), is(DST_IPV6_GATEWAY_PAIR.toOctets()));
-        assertThat(ip.getDestinationAddress(), is(SRC_IPV61.toOctets()));
-        assertTrue(ip.getPayload() instanceof ICMP6);
-        ICMP6 icmp = (ICMP6) ip.getPayload();
-        assertThat(icmp.getIcmpType(), is(ECHO_REPLY));
-        // Verify behavior
-        verify(segmentRoutingManager.deviceService);
-    }
-
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockBridgingTableKey.java b/app/src/test/java/org/onosproject/segmentrouting/MockBridgingTableKey.java
deleted file mode 100644
index a90b1cf..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockBridgingTableKey.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-/**
- * Mock Bridging Table Key.
- */
-class MockBridgingTableKey {
-    DeviceId deviceId;
-    MacAddress macAddress;
-    VlanId vlanId;
-
-    MockBridgingTableKey(DeviceId deviceId, MacAddress macAddress, VlanId vlanId) {
-        this.deviceId = deviceId;
-        this.macAddress = macAddress;
-        this.vlanId = vlanId;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof MockBridgingTableKey)) {
-            return false;
-        }
-        final MockBridgingTableKey other = (MockBridgingTableKey) obj;
-        return Objects.equals(this.macAddress, other.macAddress) &&
-                Objects.equals(this.deviceId, other.deviceId) &&
-                Objects.equals(this.vlanId, other.vlanId);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(macAddress, vlanId);
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockBridgingTableValue.java b/app/src/test/java/org/onosproject/segmentrouting/MockBridgingTableValue.java
deleted file mode 100644
index 68b0db8..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockBridgingTableValue.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.net.PortNumber;
-
-import java.util.Objects;
-
-/**
- * Mock Bridging Table Value.
- */
-class MockBridgingTableValue {
-    boolean popVlan;
-    PortNumber portNumber;
-
-    MockBridgingTableValue(boolean popVlan, PortNumber portNumber) {
-        this.popVlan = popVlan;
-        this.portNumber = portNumber;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof MockBridgingTableValue)) {
-            return false;
-        }
-        final MockBridgingTableValue other = (MockBridgingTableValue) obj;
-        return Objects.equals(this.popVlan, other.popVlan) &&
-                Objects.equals(this.portNumber, other.portNumber);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(popVlan, portNumber);
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java b/app/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
deleted file mode 100644
index 0002948..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockDefaultRoutingHandler.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.IpPrefix;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Mock Default Routing Handler.
- */
-public class MockDefaultRoutingHandler extends DefaultRoutingHandler {
-    private Map<ConnectPoint, Set<IpPrefix>> subnetTable;
-    private Map<MockRoutingTableKey, MockRoutingTableValue> routingTable;
-
-    MockDefaultRoutingHandler(SegmentRoutingManager srManager,
-                              Map<ConnectPoint, Set<IpPrefix>> subnetTable,
-                              Map<MockRoutingTableKey, MockRoutingTableValue> routingTable) {
-        super(srManager);
-        this.subnetTable = subnetTable;
-        this.routingTable = routingTable;
-    }
-
-    @Override
-    protected void populateSubnet(Set<ConnectPoint> cpts, Set<IpPrefix> subnets) {
-        subnetTable.forEach((k, v) -> {
-            if (!cpts.contains(k)) {
-                subnetTable.get(k).removeAll(subnets);
-                if (subnetTable.get(k).isEmpty()) {
-                    subnetTable.remove(k);
-                }
-            }
-        });
-
-        cpts.forEach(cpt -> subnetTable.put(cpt, subnets));
-    }
-
-    @Override
-    protected boolean revokeSubnet(Set<IpPrefix> subnets) {
-        for (Map.Entry<ConnectPoint, Set<IpPrefix>> entry : subnetTable.entrySet()) {
-            entry.getValue().removeAll(subnets);
-            if (entry.getValue().isEmpty()) {
-                subnetTable.remove(entry.getKey());
-            }
-        }
-        routingTable.entrySet().removeIf(e -> subnets.contains(e.getKey().ipPrefix));
-        return true;
-    }
-
-    @Override
-    protected boolean shouldProgram(DeviceId deviceId) {
-        return true;
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockDevice.java b/app/src/test/java/org/onosproject/segmentrouting/MockDevice.java
deleted file mode 100644
index 0abbf2a..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockDevice.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.net.Annotations;
-import org.onosproject.net.DefaultDevice;
-import org.onosproject.net.DeviceId;
-/**
- * Test fixture for the device service.
- */
-public class MockDevice extends DefaultDevice {
-
-        public MockDevice(DeviceId id, Annotations annotations) {
-            super(null, id, null, null, null, null, null, null, annotations);
-        }
- }
-
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockDeviceService.java b/app/src/test/java/org/onosproject/segmentrouting/MockDeviceService.java
deleted file mode 100644
index 1d173f6..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockDeviceService.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright 2016-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Set;
-
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultPort;
-import org.onosproject.net.Port;
-import org.onosproject.net.device.DeviceListener;
-import org.onosproject.net.device.DeviceServiceAdapter;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-
-/**
- * Test fixture for the device service.
- */
-public class MockDeviceService extends DeviceServiceAdapter {
-    private List<Device> devices = new LinkedList<>();
-    private DeviceListener listener;
-
-    public void addDevice(Device dev) {
-        devices.add(dev);
-    }
-
-    public void addMultipleDevices(Set<Device> devicesToAdd) {
-        devicesToAdd.forEach(dev -> devices.add(dev));
-    }
-
-    @Override
-    public Device getDevice(DeviceId deviceId) {
-        for (Device dev : devices) {
-            if (dev.id().equals(deviceId)) {
-                return dev;
-            }
-        }
-        return null;
-    }
-
-    @Override
-    public Port getPort(ConnectPoint cp) {
-        return new DefaultPort(null, null, false);
-    }
-
-    @Override
-    public Iterable<Device> getAvailableDevices() {
-        return devices;
-    }
-
-    @Override
-    public void addListener(DeviceListener listener) {
-        this.listener = listener;
-    }
-
-    /**
-     * Get the listener.
-     */
-    public DeviceListener getListener() {
-        return listener;
-    }
-
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockFlowObjectiveService.java b/app/src/test/java/org/onosproject/segmentrouting/MockFlowObjectiveService.java
deleted file mode 100644
index 0f2d7a9..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockFlowObjectiveService.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.flow.criteria.Criterion;
-import org.onosproject.net.flow.criteria.EthCriterion;
-import org.onosproject.net.flow.criteria.VlanIdCriterion;
-import org.onosproject.net.flow.instructions.Instruction;
-import org.onosproject.net.flow.instructions.Instructions;
-import org.onosproject.net.flow.instructions.L2ModificationInstruction;
-import org.onosproject.net.flowobjective.FlowObjectiveServiceAdapter;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.net.flowobjective.Objective;
-import org.onosproject.net.flowobjective.ObjectiveError;
-
-import java.util.Map;
-
-/**
- * Mock Flow Objective Service.
- */
-public class MockFlowObjectiveService extends FlowObjectiveServiceAdapter {
-    private Map<MockBridgingTableKey, MockBridgingTableValue> bridgingTable;
-    private Map<Integer, TrafficTreatment> nextTable;
-
-    MockFlowObjectiveService(Map<MockBridgingTableKey, MockBridgingTableValue> bridgingTable,
-                             Map<Integer, TrafficTreatment> nextTable) {
-        this.bridgingTable = bridgingTable;
-        this.nextTable = nextTable;
-    }
-
-    @Override
-    public void forward(DeviceId deviceId, ForwardingObjective forwardingObjective) {
-        TrafficSelector selector = forwardingObjective.selector();
-        TrafficTreatment treatment = nextTable.get(forwardingObjective.nextId());
-        MacAddress macAddress = ((EthCriterion) selector.getCriterion(Criterion.Type.ETH_DST)).mac();
-        VlanId vlanId = ((VlanIdCriterion) selector.getCriterion(Criterion.Type.VLAN_VID)).vlanId();
-
-        boolean popVlan = treatment.allInstructions().stream()
-                .filter(instruction -> instruction.type().equals(Instruction.Type.L2MODIFICATION))
-                .anyMatch(instruction -> ((L2ModificationInstruction) instruction).subtype()
-                        .equals(L2ModificationInstruction.L2SubType.VLAN_POP));
-        PortNumber portNumber = treatment.allInstructions().stream()
-                .filter(instruction -> instruction.type().equals(Instruction.Type.OUTPUT))
-                .map(instruction -> ((Instructions.OutputInstruction) instruction).port()).findFirst().orElse(null);
-        if (portNumber == null) {
-            throw new IllegalArgumentException();
-        }
-
-        Objective.Operation op = forwardingObjective.op();
-
-        MockBridgingTableKey btKey = new MockBridgingTableKey(deviceId, macAddress, vlanId);
-        MockBridgingTableValue btValue = new MockBridgingTableValue(popVlan, portNumber);
-
-        if (op.equals(Objective.Operation.ADD)) {
-            bridgingTable.put(btKey, btValue);
-            forwardingObjective.context().ifPresent(context -> context.onSuccess(forwardingObjective));
-        } else if (op.equals(Objective.Operation.REMOVE)) {
-            bridgingTable.remove(btKey, btValue);
-            forwardingObjective.context().ifPresent(context -> context.onSuccess(forwardingObjective));
-        } else {
-            forwardingObjective.context().ifPresent(context ->
-                    context.onError(forwardingObjective, ObjectiveError.UNKNOWN));
-            throw new IllegalArgumentException();
-        }
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockHostProbingService.java b/app/src/test/java/org/onosproject/segmentrouting/MockHostProbingService.java
deleted file mode 100644
index 807db0c..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockHostProbingService.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Lists;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.Host;
-import org.onosproject.net.host.HostProbingService;
-import org.onosproject.net.host.ProbeMode;
-
-import java.util.List;
-import java.util.Objects;
-
-public class MockHostProbingService implements HostProbingService {
-    List<Probe> probes;
-
-    private class Probe {
-        private Host host;
-        private ConnectPoint connectPoint;
-        private ProbeMode probeMode;
-
-        Probe(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
-            this.host = host;
-            this.connectPoint = connectPoint;
-            this.probeMode = probeMode;
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            if (this == o) {
-                return true;
-            }
-            if (!(o instanceof Probe)) {
-                return false;
-            }
-            Probe that = (Probe) o;
-            return (Objects.equals(this.host, that.host) &&
-                    Objects.equals(this.connectPoint, that.connectPoint) &&
-                    Objects.equals(this.probeMode, that.probeMode));
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(host, connectPoint, probeMode);
-        }
-    }
-
-    MockHostProbingService() {
-        probes = Lists.newArrayList();
-    }
-
-    boolean verifyProbe(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
-        Probe probe = new Probe(host, connectPoint, probeMode);
-        return probes.contains(probe);
-    }
-
-    @Override
-    public void probeHost(Host host, ConnectPoint connectPoint, ProbeMode probeMode) {
-        probes.add(new Probe(host, connectPoint, probeMode));
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockHostService.java b/app/src/test/java/org/onosproject/segmentrouting/MockHostService.java
deleted file mode 100644
index e8c4701..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockHostService.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.ImmutableSet;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.host.HostServiceAdapter;
-
-import java.util.Set;
-
-/**
- * Mock Host Service.
- */
-public class MockHostService extends HostServiceAdapter {
-    private Set<Host> hosts;
-
-    MockHostService(Set<Host> hosts) {
-        this.hosts = ImmutableSet.copyOf(hosts);
-    }
-
-    @Override
-    public Set<Host> getHosts() {
-        return hosts;
-    }
-
-    @Override
-    public Host getHost(HostId hostId) {
-        return hosts.stream().filter(host -> hostId.equals(host.id())).findFirst().orElse(null);
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java b/app/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
deleted file mode 100644
index 6e170b0..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockInterfaceService.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.ImmutableSet;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.intf.impl.InterfaceManager;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-import static java.util.stream.Collectors.toSet;
-
-/**
- * Mock Interface Service.
- */
-public class MockInterfaceService extends InterfaceManager {
-    private Set<Interface> interfaces;
-
-    MockInterfaceService(Set<Interface> interfaces) {
-        this.interfaces = ImmutableSet.copyOf(interfaces);
-    }
-
-    @Override
-    public Set<Interface> getInterfacesByPort(ConnectPoint cp) {
-        return interfaces.stream().filter(intf -> cp.equals(intf.connectPoint()))
-                .collect(Collectors.toSet());
-    }
-
-    @Override
-    public Set<Interface> getInterfaces() {
-        return interfaces;
-    }
-
-    @Override
-    public Interface getMatchingInterface(IpAddress ip) {
-        return getMatchingInterfacesStream(ip).findFirst().orElse(null);
-    }
-
-    @Override
-    public Set<Interface> getMatchingInterfaces(IpAddress ip) {
-        return getMatchingInterfacesStream(ip).collect(toSet());
-    }
-
-    private Stream<Interface> getMatchingInterfacesStream(IpAddress ip) {
-        return interfaces.stream()
-                .filter(intf -> intf.ipAddressesList().stream()
-                        .anyMatch(intfIp -> intfIp.subnetAddress().contains(ip)));
-    }
-
-    @Override
-    public boolean isConfigured(ConnectPoint connectPoint) {
-        Set<Interface> intfs = getInterfacesByPort(connectPoint);
-        if (intfs == null) {
-            return false;
-        }
-        for (Interface intf : intfs) {
-            if (!intf.ipAddressesList().isEmpty() || intf.vlan() != VlanId.NONE
-                    || intf.vlanNative() != VlanId.NONE
-                    || intf.vlanUntagged() != VlanId.NONE
-                    || !intf.vlanTagged().isEmpty()) {
-                return true;
-            }
-        }
-        return false;
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockLinkHandler.java b/app/src/test/java/org/onosproject/segmentrouting/MockLinkHandler.java
deleted file mode 100644
index fe5df5d..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockLinkHandler.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.net.HostLocation;
-
-/**
- * Mocks the LinkHandler in SR.
- *
- */
-public class MockLinkHandler extends LinkHandler {
-
-    MockLinkHandler(SegmentRoutingManager srManager) {
-        super(srManager, null);
-    }
-
-    @Override
-    void checkUplinksForHost(HostLocation loc) {
-        // currently does nothing - can be extended to be a useful mock when
-        // implementing unit tests for link handling
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockMastershipService.java b/app/src/test/java/org/onosproject/segmentrouting/MockMastershipService.java
deleted file mode 100644
index 5494e27..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockMastershipService.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.ImmutableSet;
-import org.onosproject.mastership.MastershipServiceAdapter;
-import org.onosproject.net.DeviceId;
-
-import java.util.Set;
-
-/**
- * Mock Mastership Service.
- */
-public class MockMastershipService extends MastershipServiceAdapter {
-    // A set of devices of which we have mastership.
-    private Set<DeviceId> localDevices;
-
-    MockMastershipService(Set<DeviceId> localDevices) {
-        this.localDevices = ImmutableSet.copyOf(localDevices);
-    }
-
-    @Override
-    public boolean isLocalMaster(DeviceId deviceId) {
-        return localDevices.contains(deviceId);
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockNeighbourResolutionService.java b/app/src/test/java/org/onosproject/segmentrouting/MockNeighbourResolutionService.java
deleted file mode 100644
index 5a41149..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockNeighbourResolutionService.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.neighbour.NeighbourHandlerRegistration;
-import org.onosproject.net.neighbour.NeighbourMessageHandler;
-import org.onosproject.net.neighbour.NeighbourResolutionService;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Mock Neighbour Resolution Service.
- */
-public class MockNeighbourResolutionService implements NeighbourResolutionService {
-    @Override
-    public void registerNeighbourHandler(ConnectPoint connectPoint,
-                                         NeighbourMessageHandler handler, ApplicationId appId) {
-
-    }
-
-    @Override
-    public void registerNeighbourHandler(Interface intf,
-                                         NeighbourMessageHandler handler, ApplicationId appId) {
-
-    }
-
-    @Override
-    public void unregisterNeighbourHandler(ConnectPoint connectPoint,
-                                           NeighbourMessageHandler handler, ApplicationId appId) {
-
-    }
-
-    @Override
-    public void unregisterNeighbourHandler(Interface intf,
-                                           NeighbourMessageHandler handler, ApplicationId appId) {
-
-    }
-
-    @Override
-    public void unregisterNeighbourHandlers(ApplicationId appId) {
-
-    }
-
-    @Override
-    public Map<ConnectPoint, Collection<NeighbourHandlerRegistration>> getHandlerRegistrations() {
-        return null;
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockNetworkConfigRegistry.java b/app/src/test/java/org/onosproject/segmentrouting/MockNetworkConfigRegistry.java
deleted file mode 100644
index 12d57ea..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockNetworkConfigRegistry.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Sets;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.config.Config;
-import org.onosproject.net.config.NetworkConfigRegistryAdapter;
-import org.onosproject.net.config.basics.BasicDeviceConfig;
-
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Mock Network Config Registry.
- */
-class MockNetworkConfigRegistry extends NetworkConfigRegistryAdapter {
-    private Set<Config> configs = Sets.newHashSet();
-
-    void applyConfig(Config config) {
-        configs.add(config);
-    }
-
-    @Override
-    public <S, C extends Config<S>> C getConfig(S subject, Class<C> configClass) {
-        Config c = configs.stream()
-                .filter(config -> subject.equals(config.subject()))
-                .filter(config -> configClass.equals(config.getClass()))
-                .findFirst().orElse(null);
-        return (C) c;
-    }
-
-    @Override
-    public <S, C extends Config<S>> C addConfig(S subject, Class<C> configClass) {
-        Config c = configs.stream()
-                .filter(config -> subject.equals(config.subject()))
-                .filter(config -> configClass.equals(config.getClass()))
-                .findFirst().orElseGet(() -> {
-                    if (configClass.equals(BasicDeviceConfig.class)) {
-                        BasicDeviceConfig deviceConfig = new BasicDeviceConfig();
-                        ObjectMapper mapper = new ObjectMapper();
-                        deviceConfig.init((DeviceId) subject, ((DeviceId) subject).toString(),
-                                          JsonNodeFactory.instance.objectNode(), mapper, config -> {
-                                });
-                        return deviceConfig;
-                    }
-                    return null;
-                });
-        return (C) c;
-    }
-
-    @Override
-    public <S, C extends Config<S>> Set<S> getSubjects(Class<S> subject, Class<C> configClass) {
-        ImmutableSet.Builder<S> builder = ImmutableSet.builder();
-        String cName = configClass.getName();
-        configs.forEach(k -> {
-            if (subject.isInstance(k.subject()) && Objects.equals(cName, k.getClass().getName())) {
-                builder.add((S) k.subject());
-            }
-        });
-        return builder.build();
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockPacketService.java b/app/src/test/java/org/onosproject/segmentrouting/MockPacketService.java
deleted file mode 100644
index 73a8b65..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockPacketService.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Maps;
-import org.apache.commons.lang3.tuple.Pair;
-import org.onlab.packet.DeserializationException;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.MacAddress;
-import org.onosproject.net.packet.OutboundPacket;
-import org.onosproject.net.packet.PacketServiceAdapter;
-
-import java.util.Map;
-
-/**
- * Mock Packet Service.
- * It is used for tests related to packet-ins management.
- */
-public class MockPacketService extends PacketServiceAdapter {
-
-    private final Map<MacAddress, Pair<OutboundPacket, Ethernet>> outBoundPackets = Maps.newHashMap();
-
-    @Override
-    public void emit(OutboundPacket packet) {
-        try {
-            Ethernet ethernetPacket = Ethernet.deserializer().deserialize(packet.data().array(),
-                                                                          packet.data().arrayOffset(),
-                                                                          packet.data().array().length);
-            outBoundPackets.put(ethernetPacket.getDestinationMAC(), Pair.of(packet, ethernetPacket));
-        } catch (DeserializationException e) {
-
-        }
-    }
-
-    Ethernet getEthernetPacket(MacAddress key) {
-        Pair<OutboundPacket, Ethernet> pair = outBoundPackets.get(key);
-        return pair != null ? pair.getRight() : null;
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockRouteService.java b/app/src/test/java/org/onosproject/segmentrouting/MockRouteService.java
deleted file mode 100644
index c3730dd..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockRouteService.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Sets;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.RouteInfo;
-import org.onosproject.routeservice.RouteServiceAdapter;
-import org.onosproject.routeservice.RouteTableId;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Mock Route Service.
- * We assume there is only one routing table named "default".
- */
-public class MockRouteService extends RouteServiceAdapter {
-    private Map<IpPrefix, Set<ResolvedRoute>> routeStore;
-
-    MockRouteService(Map<IpPrefix, Set<ResolvedRoute>> routeStore) {
-        this.routeStore = routeStore;
-    }
-
-    @Override
-    public Collection<RouteInfo> getRoutes(RouteTableId id) {
-        return routeStore.entrySet().stream().map(e -> {
-            IpPrefix prefix = e.getKey();
-            Set<ResolvedRoute> resolvedRoutes = e.getValue();
-            ResolvedRoute bestRoute =  resolvedRoutes.stream().findFirst().orElse(null);
-            return new RouteInfo(prefix, bestRoute, resolvedRoutes);
-        }).collect(Collectors.toSet());
-    }
-
-    @Override
-    public Collection<RouteTableId> getRouteTables() {
-        return Sets.newHashSet(new RouteTableId("default"));
-    }
-
-    @Override
-    public Optional<ResolvedRoute> longestPrefixLookup(IpAddress ip) {
-        return this.routeStore.get(ip.toIpPrefix()).stream()
-                .findFirst();
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockRoutingRulePopulator.java b/app/src/test/java/org/onosproject/segmentrouting/MockRoutingRulePopulator.java
deleted file mode 100644
index 4d222d8..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockRoutingRulePopulator.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flowobjective.Objective;
-
-import java.util.Map;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * Mock Routing Rule Populator.
- */
-public class MockRoutingRulePopulator extends RoutingRulePopulator {
-    private Map<MockRoutingTableKey, MockRoutingTableValue> routingTable;
-
-    MockRoutingRulePopulator(SegmentRoutingManager srManager,
-                             Map<MockRoutingTableKey, MockRoutingTableValue> routingTable) {
-        super(srManager);
-        this.routingTable = routingTable;
-    }
-
-    @Override
-    public CompletableFuture<Objective> populateRoute(DeviceId deviceId, IpPrefix prefix, MacAddress hostMac,
-                                                      VlanId hostVlanId, PortNumber outPort, boolean directHost) {
-        MockRoutingTableKey rtKey = new MockRoutingTableKey(deviceId, prefix);
-        MockRoutingTableValue rtValue = new MockRoutingTableValue(outPort, hostMac, hostVlanId);
-        routingTable.put(rtKey, rtValue);
-        return CompletableFuture.completedFuture(null);
-    }
-
-    @Override
-    public CompletableFuture<Objective> revokeRoute(DeviceId deviceId, IpPrefix prefix,
-                            MacAddress hostMac, VlanId hostVlanId, PortNumber outPort, boolean directHost) {
-        MockRoutingTableKey rtKey = new MockRoutingTableKey(deviceId, prefix);
-        MockRoutingTableValue rtValue = new MockRoutingTableValue(outPort, hostMac, hostVlanId);
-        routingTable.remove(rtKey, rtValue);
-        return CompletableFuture.completedFuture(null);
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockRoutingTableKey.java b/app/src/test/java/org/onosproject/segmentrouting/MockRoutingTableKey.java
deleted file mode 100644
index 4f09c2a..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockRoutingTableKey.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.IpPrefix;
-import org.onosproject.net.DeviceId;
-
-import java.util.Objects;
-
-/**
- * Mock Routing Table Key.
- */
-class MockRoutingTableKey {
-    DeviceId deviceId;
-    IpPrefix ipPrefix;
-
-    MockRoutingTableKey(DeviceId deviceId, IpPrefix ipPrefix) {
-        this.deviceId = deviceId;
-        this.ipPrefix = ipPrefix;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof MockRoutingTableKey)) {
-            return false;
-        }
-        final MockRoutingTableKey other = (MockRoutingTableKey) obj;
-        return Objects.equals(this.deviceId, other.deviceId) &&
-                Objects.equals(this.ipPrefix, other.ipPrefix);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(deviceId, ipPrefix);
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockRoutingTableValue.java b/app/src/test/java/org/onosproject/segmentrouting/MockRoutingTableValue.java
deleted file mode 100644
index 5842d2d..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockRoutingTableValue.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.PortNumber;
-
-import java.util.Objects;
-
-/**
- * Mock Routing Table Value.
- */
-class MockRoutingTableValue {
-    PortNumber portNumber;
-    MacAddress macAddress;
-    VlanId vlanId;
-
-    MockRoutingTableValue(PortNumber portNumber, MacAddress macAddress, VlanId vlanId) {
-        this.portNumber = portNumber;
-        this.macAddress = macAddress;
-        this.vlanId = vlanId;
-    }
-
-    @Override
-    public boolean equals(final Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (!(obj instanceof MockRoutingTableValue)) {
-            return false;
-        }
-        final MockRoutingTableValue other = (MockRoutingTableValue) obj;
-        return Objects.equals(this.portNumber, other.portNumber) &&
-                Objects.equals(this.macAddress, other.macAddress) &&
-                Objects.equals(this.vlanId, other.vlanId);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(portNumber, macAddress, vlanId);
-    }
-}
-
diff --git a/app/src/test/java/org/onosproject/segmentrouting/MockSegmentRoutingManager.java b/app/src/test/java/org/onosproject/segmentrouting/MockSegmentRoutingManager.java
deleted file mode 100644
index ad098b4..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/MockSegmentRoutingManager.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.flow.TrafficSelector;
-import org.onosproject.net.flow.TrafficTreatment;
-
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Mock Segment Routing Manager.
- */
-public class MockSegmentRoutingManager extends SegmentRoutingManager {
-    private Map<Integer, TrafficTreatment> nextTable;
-    private AtomicInteger atomicNextId = new AtomicInteger();
-
-    MockSegmentRoutingManager(Map<Integer, TrafficTreatment> nextTable) {
-        appId = new DefaultApplicationId(1, SegmentRoutingManager.APP_NAME);
-        this.nextTable = nextTable;
-    }
-
-    @Override
-    public int getPortNextObjectiveId(DeviceId deviceId, PortNumber portNum,
-                                      TrafficTreatment treatment,
-                                      TrafficSelector meta,
-                                      boolean createIfMissing) {
-        int nextId = atomicNextId.incrementAndGet();
-        nextTable.put(nextId, treatment);
-        return nextId;
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/PortAuthTrackerTest.java b/app/src/test/java/org/onosproject/segmentrouting/PortAuthTrackerTest.java
deleted file mode 100644
index 6ab4bad..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/PortAuthTrackerTest.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.Before;
-import org.junit.Test;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.core.DefaultApplicationId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.segmentrouting.PortAuthTracker.BlockState;
-import org.onosproject.segmentrouting.config.BlockedPortsConfig;
-import org.onosproject.segmentrouting.config.BlockedPortsConfigTest;
-
-import java.io.IOException;
-import java.io.InputStream;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.onosproject.net.ConnectPoint.deviceConnectPoint;
-import static org.onosproject.net.DeviceId.deviceId;
-import static org.onosproject.net.PortNumber.portNumber;
-import static org.onosproject.segmentrouting.PortAuthTracker.BlockState.AUTHENTICATED;
-import static org.onosproject.segmentrouting.PortAuthTracker.BlockState.BLOCKED;
-import static org.onosproject.segmentrouting.PortAuthTracker.BlockState.UNCHECKED;
-
-/**
- * Unit Tests for {@link PortAuthTracker}.
- */
-public class PortAuthTrackerTest {
-    private static final ApplicationId APP_ID = new DefaultApplicationId(1, "foo");
-    private static final String KEY = "blocked";
-    private static final ObjectMapper MAPPER = new ObjectMapper();
-    private static final String PATH_CFG = "/blocked-ports.json";
-    private static final String PATH_CFG_ALT = "/blocked-ports-alt.json";
-
-    private static final String DEV1 = "of:0000000000000001";
-    private static final String DEV3 = "of:0000000000000003";
-    private static final String DEV4 = "of:0000000000000004";
-
-    private BlockedPortsConfig cfg;
-    private AugmentedPortAuthTracker tracker;
-
-    private void print(String s) {
-        System.out.println(s);
-    }
-
-    private void print(Object o) {
-        print(o.toString());
-    }
-
-    private void print(String fmt, Object... params) {
-        print(String.format(fmt, params));
-    }
-
-    private void title(String s) {
-        print("=== %s ===", s);
-    }
-
-    private BlockedPortsConfig makeConfig(String path) throws IOException {
-        InputStream blockedPortsJson = BlockedPortsConfigTest.class
-                .getResourceAsStream(path);
-        JsonNode node = MAPPER.readTree(blockedPortsJson);
-        BlockedPortsConfig cfg = new BlockedPortsConfig();
-        cfg.init(APP_ID, KEY, node, MAPPER, null);
-        return cfg;
-    }
-
-    ConnectPoint cp(String devId, int port) {
-        return ConnectPoint.deviceConnectPoint(devId + "/" + port);
-    }
-
-    @Before
-    public void setUp() throws IOException {
-        cfg = makeConfig(PATH_CFG);
-        tracker = new AugmentedPortAuthTracker();
-    }
-
-    private void verifyPortState(String devId, int first, BlockState... states) {
-        DeviceId dev = deviceId(devId);
-        int last = first + states.length;
-        int pn = first;
-        int i = 0;
-        while (pn < last) {
-            PortNumber pnum = portNumber(pn);
-            BlockState actual = tracker.currentState(dev, pnum);
-            print("%s/%s [%s]  --> %s", devId, pn, states[i], actual);
-            assertEquals("oops: " + devId + "/" + pn + "~" + actual,
-                         states[i], actual);
-            pn++;
-            i++;
-        }
-    }
-
-    @Test
-    public void basic() {
-        title("basic");
-        print(tracker);
-        print(cfg);
-
-        assertEquals("wrong entry count", 0, tracker.entryCount());
-
-        // let's assume that the net config just got loaded..
-        tracker.configurePortBlocking(cfg);
-        assertEquals("wrong entry count", 13, tracker.entryCount());
-
-        verifyPortState(DEV1, 1, BLOCKED, BLOCKED, BLOCKED, BLOCKED, UNCHECKED);
-        verifyPortState(DEV1, 6, UNCHECKED, BLOCKED, BLOCKED, BLOCKED, UNCHECKED);
-
-        verifyPortState(DEV3, 1, UNCHECKED, UNCHECKED, UNCHECKED);
-        verifyPortState(DEV3, 6, UNCHECKED, BLOCKED, BLOCKED, BLOCKED, UNCHECKED);
-
-        verifyPortState(DEV4, 1, BLOCKED, UNCHECKED, UNCHECKED, UNCHECKED, BLOCKED);
-    }
-
-    @Test
-    public void logonLogoff() {
-        title("logonLogoff");
-
-        tracker.configurePortBlocking(cfg);
-        assertEquals("wrong entry count", 13, tracker.entryCount());
-        verifyPortState(DEV1, 1, BLOCKED, BLOCKED, BLOCKED);
-
-        ConnectPoint cp = deviceConnectPoint(DEV1 + "/2");
-        tracker.radiusAuthorize(cp);
-        print("");
-        verifyPortState(DEV1, 1, BLOCKED, AUTHENTICATED, BLOCKED);
-
-        tracker.radiusLogoff(cp);
-        print("");
-        verifyPortState(DEV1, 1, BLOCKED, BLOCKED, BLOCKED);
-    }
-
-    @Test
-    public void installedFlows() {
-        title("installed flows");
-
-        assertEquals(0, tracker.installed.size());
-        tracker.configurePortBlocking(cfg);
-        assertEquals(13, tracker.installed.size());
-
-        assertTrue(tracker.installed.contains(cp(DEV1, 1)));
-        assertTrue(tracker.installed.contains(cp(DEV3, 7)));
-        assertTrue(tracker.installed.contains(cp(DEV4, 5)));
-    }
-
-    @Test
-    public void flowsLogonLogoff() {
-        title("flows logon logoff");
-
-        tracker.configurePortBlocking(cfg);
-
-        // let's pick a connect point from the configuration
-        ConnectPoint cp = cp(DEV4, 5);
-
-        assertTrue(tracker.installed.contains(cp));
-        assertEquals(0, tracker.cleared.size());
-
-        tracker.resetMetrics();
-        tracker.radiusAuthorize(cp);
-        // verify we requested the blocking flow to be cleared
-        assertTrue(tracker.cleared.contains(cp));
-
-        tracker.resetMetrics();
-        assertEquals(0, tracker.installed.size());
-        tracker.radiusLogoff(cp);
-        // verify we requested the blocking flow to be reinstated
-        assertTrue(tracker.installed.contains(cp));
-    }
-
-    @Test
-    public void uncheckedPortIgnored() {
-        title("unchecked port ignored");
-
-        tracker.configurePortBlocking(cfg);
-        tracker.resetMetrics();
-
-        // let's pick a connect point NOT in the configuration
-        ConnectPoint cp = cp(DEV4, 2);
-        assertEquals(BlockState.UNCHECKED, tracker.currentState(cp));
-
-        assertEquals(0, tracker.installed.size());
-        assertEquals(0, tracker.cleared.size());
-        tracker.radiusAuthorize(cp);
-        assertEquals(0, tracker.installed.size());
-        assertEquals(0, tracker.cleared.size());
-        tracker.radiusLogoff(cp);
-        assertEquals(0, tracker.installed.size());
-        assertEquals(0, tracker.cleared.size());
-    }
-
-    @Test
-    public void reconfiguration() throws IOException {
-        title("reconfiguration");
-
-        /* see 'blocked-ports.json' and 'blocked-ports-alt.json'
-
-          cfg:  "1": ["1-4", "7-9"],
-                "3": ["7-9"],
-                "4": ["1", "5", "9"]
-
-          alt:  "1": ["1-9"],
-                "3": ["7"],
-                "4": ["1"]
-         */
-        tracker.configurePortBlocking(cfg);
-        // dev1: ports 5 and 6 are NOT configured in the original CFG
-        assertFalse(tracker.installed.contains(cp(DEV1, 5)));
-        assertFalse(tracker.installed.contains(cp(DEV1, 6)));
-
-        tracker.resetMetrics();
-        assertEquals(0, tracker.installed.size());
-        assertEquals(0, tracker.cleared.size());
-
-        BlockedPortsConfig alt = makeConfig(PATH_CFG_ALT);
-        tracker.configurePortBlocking(alt);
-
-        // dev1: ports 5 and 6 ARE configured in the alternate CFG
-        assertTrue(tracker.installed.contains(cp(DEV1, 5)));
-        assertTrue(tracker.installed.contains(cp(DEV1, 6)));
-
-        // also, check for the ports that were decommissioned
-        assertTrue(tracker.cleared.contains(cp(DEV3, 8)));
-        assertTrue(tracker.cleared.contains(cp(DEV3, 9)));
-        assertTrue(tracker.cleared.contains(cp(DEV4, 5)));
-        assertTrue(tracker.cleared.contains(cp(DEV4, 9)));
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java b/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
deleted file mode 100644
index f2a439b..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
+++ /dev/null
@@ -1,660 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.config.ConfigApplyDelegate;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultHost;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.config.NetworkConfigRegistryAdapter;
-import org.onosproject.net.flow.TrafficTreatment;
-import org.onosproject.net.host.HostEvent;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.Route;
-import org.onosproject.routeservice.RouteEvent;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-import org.onosproject.segmentrouting.config.SegmentRoutingDeviceConfig;
-import org.onosproject.segmentrouting.phasedrecovery.api.PhasedRecoveryService;
-import org.onosproject.store.service.StorageService;
-import org.onosproject.store.service.TestConsistentMap;
-import org.onosproject.store.service.TestConsistentMultimap;
-
-import java.util.Map;
-import java.util.Set;
-
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.reset;
-import static org.junit.Assert.*;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.easymock.EasyMock.verify;
-
-/**
- * Unit test for {@link RouteHandler}.
- */
-public class RouteHandlerTest {
-    private SegmentRoutingManager srManager;
-    private RouteHandler routeHandler;
-    private HostService hostService;
-
-    // Mocked routing and bridging tables
-    private static final Map<MockBridgingTableKey, MockBridgingTableValue> BRIDGING_TABLE =
-            Maps.newConcurrentMap();
-    private static final Map<MockRoutingTableKey, MockRoutingTableValue> ROUTING_TABLE =
-            Maps.newConcurrentMap();
-    private static final Map<ConnectPoint, Set<IpPrefix>> SUBNET_TABLE = Maps.newConcurrentMap();
-    // Mocked Next Id
-    private static final Map<Integer, TrafficTreatment> NEXT_TABLE = Maps.newConcurrentMap();
-    private static final Map<IpPrefix, Set<ResolvedRoute>> ROUTE_STORE = Maps.newConcurrentMap();
-
-    private static final IpPrefix P1 = IpPrefix.valueOf("10.0.0.0/24");
-
-    // Single homed router 1
-    private static final IpAddress N1 = IpAddress.valueOf("10.0.1.1");
-    private static final MacAddress M1 = MacAddress.valueOf("00:00:00:00:00:01");
-    private static final VlanId V1 = VlanId.vlanId((short) 1);
-    private static final ConnectPoint CP1 = ConnectPoint.deviceConnectPoint("of:0000000000000001/1");
-    private static final Route R1 = new Route(Route.Source.STATIC, P1, N1);
-    private static final ResolvedRoute RR1 = new ResolvedRoute(R1, M1, V1);
-    private static final Route DHCP_R1 = new Route(Route.Source.DHCP, P1, N1);
-    private static final ResolvedRoute DHCP_RR1 = new ResolvedRoute(DHCP_R1, M1, V1);
-
-    // Single homed router 2
-    private static final IpAddress N2 = IpAddress.valueOf("10.0.2.1");
-    private static final MacAddress M2 = MacAddress.valueOf("00:00:00:00:00:02");
-    private static final VlanId V2 = VlanId.vlanId((short) 2);
-    private static final ConnectPoint CP2 = ConnectPoint.deviceConnectPoint("of:0000000000000002/2");
-    private static final Route R2 = new Route(Route.Source.STATIC, P1, N2);
-    private static final ResolvedRoute RR2 = new ResolvedRoute(R2, M2, V2);
-
-    // Dual homed router 1
-    private static final IpAddress N3 = IpAddress.valueOf("10.0.3.1");
-    private static final MacAddress M3 = MacAddress.valueOf("00:00:00:00:00:03");
-    private static final VlanId V3 = VlanId.vlanId((short) 3);
-    private static final Route R3 = new Route(Route.Source.STATIC, P1, N3);
-    private static final ResolvedRoute RR3 = new ResolvedRoute(R3, M3, V3);
-    private static final Route DHCP_R3 = new Route(Route.Source.DHCP, P1, N3);
-    private static final ResolvedRoute DHCP_RR3 = new ResolvedRoute(DHCP_R3, M3, V3);
-
-    // Single homed router 3
-    private static final IpAddress N4 = IpAddress.valueOf("10.0.4.1");
-    private static final MacAddress M4 = MacAddress.valueOf("00:00:00:00:00:04");
-    private static final VlanId V4 = VlanId.vlanId((short) 4);
-    private static final ConnectPoint CP4 = ConnectPoint.deviceConnectPoint("of:0000000000000004/4");
-    private static final Route R4 = new Route(Route.Source.STATIC, P1, N4);
-    private static final ResolvedRoute RR4 = new ResolvedRoute(R4, M4, V4);
-
-    // Hosts
-    private static final Host H1 = new DefaultHost(ProviderId.NONE, HostId.hostId(M1, V1), M1, V1,
-            Sets.newHashSet(new HostLocation(CP1, 0)), Sets.newHashSet(N1), false);
-    private static final Host H2 = new DefaultHost(ProviderId.NONE, HostId.hostId(M2, V2), M2, V2,
-            Sets.newHashSet(new HostLocation(CP2, 0)), Sets.newHashSet(N2), false);
-    private static final Host H3D = new DefaultHost(ProviderId.NONE, HostId.hostId(M3, V3), M3, V3,
-            Sets.newHashSet(new HostLocation(CP1, 0), new HostLocation(CP2, 0)), Sets.newHashSet(N3), false);
-    private static final Host H3S = new DefaultHost(ProviderId.NONE, HostId.hostId(M3, V3), M3, V3,
-            Sets.newHashSet(new HostLocation(CP1, 0)), Sets.newHashSet(N3), false);
-    private static final Host H4 = new DefaultHost(ProviderId.NONE, HostId.hostId(M4, V4), M4, V4,
-            Sets.newHashSet(new HostLocation(CP4, 0)), Sets.newHashSet(N4), false);
-
-    // Pair Local Port
-    private static final PortNumber P9 = PortNumber.portNumber(9);
-
-    // A set of hosts
-    private static final Set<Host> HOSTS = Sets.newHashSet(H1, H2, H3D, H4);
-    private static final Set<Host> HOSTS_ONE_FAIL = Sets.newHashSet(H1, H2, H3S);
-    private static final Set<Host> HOSTS_BOTH_FAIL = Sets.newHashSet(H1, H2);
-    // A set of devices of which we have mastership
-    private static final Set<DeviceId> LOCAL_DEVICES = Sets.newHashSet(CP1.deviceId(), CP2.deviceId());
-    // A set of interfaces
-    private static final InterfaceIpAddress IF_IP1 =
-            new InterfaceIpAddress(IpAddress.valueOf("10.0.1.254"), IpPrefix.valueOf("10.0.1.254/24"));
-    private static final InterfaceIpAddress IF_IP3 =
-            new InterfaceIpAddress(IpAddress.valueOf("10.0.3.254"), IpPrefix.valueOf("10.0.3.254/24"));
-    private static final Interface IF_CP1 = new Interface("if-cp1", CP1, Lists.newArrayList(IF_IP1, IF_IP3),
-            null, null, null, null, null);
-    private static final Interface IF_CP2 = new Interface("if-cp2", CP2, Lists.newArrayList(IF_IP1, IF_IP3),
-            null, null, null, null, null);
-    private static final Set<Interface> INTERFACES = Sets.newHashSet(IF_CP1, IF_CP2);
-
-    @Before
-    public void setUp() {
-        ObjectMapper mapper = new ObjectMapper();
-        ConfigApplyDelegate delegate = config -> { };
-
-        SegmentRoutingDeviceConfig dev1Config = new SegmentRoutingDeviceConfig();
-        JsonNode dev1Tree = mapper.createObjectNode();
-        dev1Config.init(CP1.deviceId(), "host-handler-test", dev1Tree, mapper, delegate);
-        dev1Config.setPairDeviceId(CP2.deviceId()).setPairLocalPort(P9);
-
-        SegmentRoutingDeviceConfig dev2Config = new SegmentRoutingDeviceConfig();
-        JsonNode dev2Tree = mapper.createObjectNode();
-        dev2Config.init(CP2.deviceId(), "host-handler-test", dev2Tree, mapper, delegate);
-        dev2Config.setPairDeviceId(CP1.deviceId()).setPairLocalPort(P9);
-
-        MockNetworkConfigRegistry mockNetworkConfigRegistry = new MockNetworkConfigRegistry();
-        mockNetworkConfigRegistry.applyConfig(dev1Config);
-        mockNetworkConfigRegistry.applyConfig(dev2Config);
-
-        // Initialize Segment Routing Manager
-        srManager = new MockSegmentRoutingManager(NEXT_TABLE);
-        srManager.storageService = createMock(StorageService.class);
-        expect(srManager.storageService.consistentMapBuilder()).andReturn(new TestConsistentMap.Builder<>()).anyTimes();
-        expect(srManager.storageService.consistentMultimapBuilder()).andReturn(
-                new TestConsistentMultimap.Builder<>()).anyTimes();
-        replay(srManager.storageService);
-        srManager.cfgService = new NetworkConfigRegistryAdapter();
-        srManager.deviceConfiguration = createMock(DeviceConfiguration.class);
-        srManager.flowObjectiveService = new MockFlowObjectiveService(BRIDGING_TABLE, NEXT_TABLE);
-        srManager.routingRulePopulator = new MockRoutingRulePopulator(srManager, ROUTING_TABLE);
-        srManager.defaultRoutingHandler = new MockDefaultRoutingHandler(srManager, SUBNET_TABLE, ROUTING_TABLE);
-        srManager.interfaceService = new MockInterfaceService(INTERFACES);
-        srManager.mastershipService = new MockMastershipService(LOCAL_DEVICES);
-        hostService = new MockHostService(HOSTS);
-        srManager.hostService = hostService;
-        srManager.cfgService = mockNetworkConfigRegistry;
-        srManager.routeService = new MockRouteService(ROUTE_STORE);
-        srManager.phasedRecoveryService = createMock(PhasedRecoveryService.class);
-        expect(srManager.phasedRecoveryService.isEnabled()).andReturn(true).anyTimes();
-        replay(srManager.phasedRecoveryService);
-
-        routeHandler = new RouteHandler(srManager);
-
-        ROUTING_TABLE.clear();
-        BRIDGING_TABLE.clear();
-        SUBNET_TABLE.clear();
-    }
-
-    @Test
-    public void init() {
-        ROUTE_STORE.put(P1, Sets.newHashSet(RR1));
-
-        routeHandler.init(CP1.deviceId());
-
-        assertEquals(1, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-
-        assertEquals(1, SUBNET_TABLE.size());
-    }
-
-    @Test
-    public void initTwoNextHops() {
-        ROUTE_STORE.put(P1, Sets.newHashSet(RR1, RR2));
-
-        routeHandler.init(CP1.deviceId());
-
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M2, rtv2.macAddress);
-        assertEquals(V2, rtv2.vlanId);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-    }
-
-    // Only one of two dual-homed next hops present.
-    // Expect one routing table to be programmed with direct flow.
-    // The other is not programmed, not even for subnet
-    @Test
-    public void initDhcpRouteSingleDualHomeNextHop() {
-        ROUTE_STORE.put(P1, Sets.newHashSet(DHCP_RR1));
-
-        routeHandler.init(CP1.deviceId());
-
-        assertEquals(1, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-
-        assertEquals(1, SUBNET_TABLE.size());
-    }
-
-    // Both dual-homed next hops present.
-    // Expect both routing table to be programmed with direct flow
-    @Test
-    public void initDhcpRouteBothDualHomeNextHop() {
-        ROUTE_STORE.put(P1, Sets.newHashSet(DHCP_RR3));
-
-        routeHandler.init(CP1.deviceId());
-
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M3, rtv1.macAddress);
-        assertEquals(V3, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M3, rtv2.macAddress);
-        assertEquals(V3, rtv2.vlanId);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-    }
-
-    @Test
-    public void processRouteAdded() {
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.addSubnet(CP1, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR1, Sets.newHashSet(RR1));
-        routeHandler.processRouteAdded(re);
-
-        assertEquals(1, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-
-        assertEquals(1, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void processRouteUpdated() {
-        processRouteAdded();
-
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.removeSubnet(CP1, P1);
-        expectLastCall().once();
-        srManager.deviceConfiguration.addSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_UPDATED, RR2, RR1, Sets.newHashSet(RR2),
-                Sets.newHashSet(RR1));
-        routeHandler.processRouteUpdated(re);
-
-        // Note: We shouldn't remove the old nexthop during the occasion of route update
-        //       since the populate subnet will take care of it and point it to an ECMP group
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M2, rtv2.macAddress);
-        assertEquals(V2, rtv2.vlanId);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(1, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void processRouteRemoved() {
-        processRouteAdded();
-
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.removeSubnet(CP1, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR1, Sets.newHashSet(RR1));
-        routeHandler.processRouteRemoved(re);
-
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, SUBNET_TABLE.size());
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testTwoSingleHomedAdded() {
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.addSubnet(CP1, P1);
-        expectLastCall().once();
-        srManager.deviceConfiguration.addSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR1, Sets.newHashSet(RR1, RR2));
-        routeHandler.processRouteAdded(re);
-
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(M2, rtv2.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(V2, rtv2.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testOneDualHomedAdded() {
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.addSubnet(CP1, P1);
-        expectLastCall().once();
-        srManager.deviceConfiguration.addSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR3, Sets.newHashSet(RR3));
-        routeHandler.processRouteAdded(re);
-
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M3, rtv1.macAddress);
-        assertEquals(M3, rtv2.macAddress);
-        assertEquals(V3, rtv1.vlanId);
-        assertEquals(V3, rtv2.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testOneSingleHomedToTwoSingleHomed() {
-        processRouteAdded();
-
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.addSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED, RR1, null,
-                Sets.newHashSet(RR1, RR2), Sets.newHashSet(RR1));
-        routeHandler.processAlternativeRoutesChanged(re);
-
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(M2, rtv2.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(V2, rtv2.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testTwoSingleHomedToOneSingleHomed() {
-        testTwoSingleHomedAdded();
-
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.removeSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED, RR1, null,
-                Sets.newHashSet(RR1), Sets.newHashSet(RR1, RR2));
-        routeHandler.processAlternativeRoutesChanged(re);
-
-        // Note: We shouldn't remove the old nexthop during the occasion of route update
-        //       since the populate subnet will take care of it and point it to an ECMP group
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-
-        assertEquals(1, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    // TODO Add test cases for two single homed next hop at same location
-
-    @Test
-    public void testDualHomedSingleLocationFail() {
-        testOneDualHomedAdded();
-
-        ROUTE_STORE.put(P1, Sets.newHashSet(RR3));
-
-        reset(srManager.deviceConfiguration);
-        expect(srManager.deviceConfiguration.getBatchedSubnets(H3D.id()))
-                .andReturn(Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(P1)));
-        srManager.deviceConfiguration.removeSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        HostEvent he = new HostEvent(HostEvent.Type.HOST_MOVED, H3S, H3D);
-        routeHandler.processHostMovedEvent(he);
-
-        // We do not remove the route on CP2. Instead, we let the subnet population overrides it
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M3, rtv1.macAddress);
-        assertEquals(V3, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-
-        // ECMP route table hasn't changed
-        assertEquals(1, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testDualHomedBothLocationFail() {
-        testDualHomedSingleLocationFail();
-
-        hostService = new MockHostService(HOSTS_ONE_FAIL);
-
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.removeSubnet(CP1, P1);
-        expectLastCall().once();
-        srManager.deviceConfiguration.removeSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR3, Sets.newHashSet(RR3));
-        routeHandler.processRouteRemoved(re);
-
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, SUBNET_TABLE.size());
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testSingleHomedToDualHomed() {
-        testDualHomedSingleLocationFail();
-
-        reset(srManager.deviceConfiguration);
-        expect(srManager.deviceConfiguration.getBatchedSubnets(H3S.id()))
-                .andReturn(Lists.<Set<IpPrefix>>newArrayList(Sets.newHashSet(P1)));
-        srManager.deviceConfiguration.addSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        HostEvent he = new HostEvent(HostEvent.Type.HOST_MOVED, H3D, H3S);
-        routeHandler.processHostMovedEvent(he);
-
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M3, rtv1.macAddress);
-        assertEquals(M3, rtv2.macAddress);
-        assertEquals(V3, rtv1.vlanId);
-        assertEquals(V3, rtv2.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testTwoSingleHomedRemoved() {
-        testTwoSingleHomedAdded();
-
-        hostService = new MockHostService(HOSTS_BOTH_FAIL);
-
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.removeSubnet(CP1, P1);
-        expectLastCall().once();
-        srManager.deviceConfiguration.removeSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR1, Sets.newHashSet(RR1, RR2));
-        routeHandler.processRouteRemoved(re);
-
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, SUBNET_TABLE.size());
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testOneDualHomeRemoved() {
-        testOneDualHomedAdded();
-
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.removeSubnet(CP1, P1);
-        expectLastCall().once();
-        srManager.deviceConfiguration.removeSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_REMOVED, RR3, Sets.newHashSet(RR3));
-        routeHandler.processRouteRemoved(re);
-
-        assertEquals(0, ROUTING_TABLE.size());
-        assertEquals(0, SUBNET_TABLE.size());
-
-        verify(srManager.deviceConfiguration);
-    }
-
-    @Test
-    public void testMoreThanTwoNextHop() {
-        // next hop = CP1, CP2
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.addSubnet(CP1, P1);
-        srManager.deviceConfiguration.addSubnet(CP2, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        RouteEvent re = new RouteEvent(RouteEvent.Type.ROUTE_ADDED, RR1, Sets.newHashSet(RR1, RR2));
-        routeHandler.processRouteAdded(re);
-
-        assertEquals(2, ROUTING_TABLE.size());
-        MockRoutingTableValue rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M2, rtv2.macAddress);
-        assertEquals(V2, rtv2.vlanId);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-
-        // next hop = CP1, CP2, CP4 (invalid)
-        re = new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED, RR1, null,
-                Sets.newHashSet(RR1, RR2, RR4), Sets.newHashSet(RR1, RR2));
-        routeHandler.processAlternativeRoutesChanged(re);
-
-        assertEquals(2, ROUTING_TABLE.size());
-        rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1));
-        assertEquals(M1, rtv1.macAddress);
-        assertEquals(V1, rtv1.vlanId);
-        assertEquals(CP1.port(), rtv1.portNumber);
-        rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M2, rtv2.macAddress);
-        assertEquals(V2, rtv2.vlanId);
-        assertEquals(CP2.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP1).contains(P1));
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-
-        // next hop = CP2, CP4
-        reset(srManager.deviceConfiguration);
-        srManager.deviceConfiguration.addSubnet(CP2, P1);
-        srManager.deviceConfiguration.addSubnet(CP4, P1);
-        expectLastCall().once();
-        replay(srManager.deviceConfiguration);
-
-        re = new RouteEvent(RouteEvent.Type.ALTERNATIVE_ROUTES_CHANGED, RR1, null,
-                Sets.newHashSet(RR2, RR4), Sets.newHashSet(RR1, RR2, RR4));
-        routeHandler.processAlternativeRoutesChanged(re);
-
-        assertEquals(2, ROUTING_TABLE.size());
-        rtv1 = ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1));
-        assertEquals(M2, rtv1.macAddress);
-        assertEquals(V2, rtv1.vlanId);
-        assertEquals(CP2.port(), rtv1.portNumber);
-        rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP4.deviceId(), P1));
-        assertEquals(M4, rtv2.macAddress);
-        assertEquals(V4, rtv2.vlanId);
-        assertEquals(CP4.port(), rtv2.portNumber);
-
-        assertEquals(2, SUBNET_TABLE.size());
-        assertTrue(SUBNET_TABLE.get(CP2).contains(P1));
-        assertTrue(SUBNET_TABLE.get(CP4).contains(P1));
-
-        verify(srManager.deviceConfiguration);
-    }
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java b/app/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
deleted file mode 100644
index c8afeef..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
-import org.easymock.EasyMock;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultDevice;
-import org.onosproject.net.DefaultPort;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.intf.InterfaceService;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.segmentrouting.config.DeviceConfiguration;
-
-import java.util.List;
-import java.util.Set;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-import static org.junit.Assert.*;
-
-public class RoutingRulePopulatorTest {
-    private RoutingRulePopulator rrp;
-    private SegmentRoutingManager srManager;
-    private InterfaceService interfaceService;
-    private DeviceService deviceService;
-
-    private final DeviceId devId1 = DeviceId.deviceId("of:1");
-    private final Device dev1 = new DefaultDevice(ProviderId.NONE, devId1, Device.Type.SWITCH,
-            null, null, null, null, null);
-
-    private final PortNumber p1 = PortNumber.portNumber(1);
-    private final PortNumber p2 = PortNumber.portNumber(2);
-    private final PortNumber p3 = PortNumber.portNumber(3);
-    private final PortNumber p4 = PortNumber.portNumber(4);
-    private final PortNumber p5 = PortNumber.portNumber(5);
-
-    private final VlanId v10 = VlanId.vlanId((short) 10);
-    private final VlanId v20 = VlanId.vlanId((short) 20);
-    private VlanId vInt;
-
-    private final Interface u10 = new Interface(null, new ConnectPoint(devId1, p1),
-            null, null, null, v10, null, null);
-    private final Interface t10 = new Interface(null, new ConnectPoint(devId1, p2),
-            null, null, null, null, Sets.newHashSet(v10), null);
-    private final Interface t10n20 = new Interface(null, new ConnectPoint(devId1, p3),
-            null, null, null, null, Sets.newHashSet(v10), v20);
-
-    @Before
-    public void setUp() throws Exception {
-        Set<Interface> interfaces = Sets.newHashSet(u10, t10, t10n20);
-        interfaceService = new MockInterfaceService(interfaces);
-        deviceService = EasyMock.createMock(DeviceService.class);
-        srManager = new MockSegmentRoutingManager(Maps.newHashMap());
-        srManager.deviceConfiguration =  EasyMock.createMock(DeviceConfiguration.class);
-        srManager.interfaceService = interfaceService;
-        srManager.deviceService = deviceService;
-        vInt = srManager.getDefaultInternalVlan();
-        rrp = new RoutingRulePopulator(srManager);
-    }
-
-    // All ports are enabled
-    @Test
-    public void testNoMoreEnabledPortCase1() throws Exception {
-        Port port1 = new DefaultPort(dev1, p1, true);
-        Port port2 = new DefaultPort(dev1, p2, true);
-        Port port3 = new DefaultPort(dev1, p3, true);
-        Port port4 = new DefaultPort(dev1, p4, true);
-        Port port5 = new DefaultPort(dev1, p5, true);
-        List<Port> ports = Lists.newArrayList(port1, port2, port3, port4, port5);
-
-        expect(deviceService.getPorts(anyObject(DeviceId.class))).andReturn(ports).anyTimes();
-        replay(deviceService);
-        assertFalse(rrp.noMoreEnabledPort(devId1, v10));
-        assertFalse(rrp.noMoreEnabledPort(devId1, v20));
-        assertFalse(rrp.noMoreEnabledPort(devId1, vInt));
-    }
-
-    // Disable port 1
-    @Test
-    public void testNoMoreEnabledPortCase2() throws Exception {
-        Port port1 = new DefaultPort(dev1, p1, false);
-        Port port2 = new DefaultPort(dev1, p2, true);
-        Port port3 = new DefaultPort(dev1, p3, true);
-        Port port4 = new DefaultPort(dev1, p4, true);
-        Port port5 = new DefaultPort(dev1, p5, true);
-        List<Port> ports = Lists.newArrayList(port1, port2, port3, port4, port5);
-
-        expect(deviceService.getPorts(anyObject(DeviceId.class))).andReturn(ports).anyTimes();
-        replay(deviceService);
-        assertFalse(rrp.noMoreEnabledPort(devId1, v10));
-        assertFalse(rrp.noMoreEnabledPort(devId1, v20));
-        assertFalse(rrp.noMoreEnabledPort(devId1, vInt));
-    }
-
-    // Disable port 1 and 3
-    @Test
-    public void testNoMoreEnabledPortCase3() throws Exception {
-        Port port1 = new DefaultPort(dev1, p1, false);
-        Port port2 = new DefaultPort(dev1, p2, true);
-        Port port3 = new DefaultPort(dev1, p3, false);
-        Port port4 = new DefaultPort(dev1, p4, true);
-        Port port5 = new DefaultPort(dev1, p5, true);
-        List<Port> ports = Lists.newArrayList(port1, port2, port3, port4, port5);
-
-        expect(deviceService.getPorts(anyObject(DeviceId.class))).andReturn(ports).anyTimes();
-        replay(deviceService);
-        assertFalse(rrp.noMoreEnabledPort(devId1, v10));
-        assertTrue(rrp.noMoreEnabledPort(devId1, v20));
-        assertFalse(rrp.noMoreEnabledPort(devId1, vInt));
-    }
-
-    // Disable port 1 to 3
-    @Test
-    public void testNoMoreEnabledPortCase4() throws Exception {
-        Port port1 = new DefaultPort(dev1, p1, false);
-        Port port2 = new DefaultPort(dev1, p2, false);
-        Port port3 = new DefaultPort(dev1, p3, false);
-        Port port4 = new DefaultPort(dev1, p4, true);
-        Port port5 = new DefaultPort(dev1, p5, true);
-        List<Port> ports = Lists.newArrayList(port1, port2, port3, port4, port5);
-
-        expect(deviceService.getPorts(anyObject(DeviceId.class))).andReturn(ports).anyTimes();
-        replay(deviceService);
-        assertTrue(rrp.noMoreEnabledPort(devId1, v10));
-        assertTrue(rrp.noMoreEnabledPort(devId1, v20));
-        assertFalse(rrp.noMoreEnabledPort(devId1, vInt));
-    }
-
-    // Disable port 1 to 4
-    @Test
-    public void testNoMoreEnabledPortCase5() throws Exception {
-        Port port1 = new DefaultPort(dev1, p1, false);
-        Port port2 = new DefaultPort(dev1, p2, false);
-        Port port3 = new DefaultPort(dev1, p3, false);
-        Port port4 = new DefaultPort(dev1, p4, false);
-        Port port5 = new DefaultPort(dev1, p5, true);
-        List<Port> ports = Lists.newArrayList(port1, port2, port3, port4, port5);
-
-        expect(deviceService.getPorts(anyObject(DeviceId.class))).andReturn(ports).anyTimes();
-        replay(deviceService);
-        assertTrue(rrp.noMoreEnabledPort(devId1, v10));
-        assertTrue(rrp.noMoreEnabledPort(devId1, v20));
-        assertFalse(rrp.noMoreEnabledPort(devId1, vInt));
-    }
-
-    // Disable all ports
-    @Test
-    public void testNoMoreEnabledPortCase6() throws Exception {
-        Port port1 = new DefaultPort(dev1, p1, false);
-        Port port2 = new DefaultPort(dev1, p2, false);
-        Port port3 = new DefaultPort(dev1, p3, false);
-        Port port4 = new DefaultPort(dev1, p4, false);
-        Port port5 = new DefaultPort(dev1, p5, false);
-        List<Port> ports = Lists.newArrayList(port1, port2, port3, port4, port5);
-
-        expect(deviceService.getPorts(anyObject(DeviceId.class))).andReturn(ports).anyTimes();
-        replay(deviceService);
-        assertTrue(rrp.noMoreEnabledPort(devId1, v10));
-        assertTrue(rrp.noMoreEnabledPort(devId1, v20));
-        assertTrue(rrp.noMoreEnabledPort(devId1, vInt));
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/TestUtils.java b/app/src/test/java/org/onosproject/segmentrouting/TestUtils.java
deleted file mode 100644
index 339b2c8..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/TestUtils.java
+++ /dev/null
@@ -1,460 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.routeservice.ResolvedRoute;
-import org.onosproject.routeservice.Route;
-import org.onlab.packet.Ethernet;
-import org.onlab.packet.ICMP;
-import org.onlab.packet.ICMP6;
-import org.onlab.packet.IPv4;
-import org.onlab.packet.IPv6;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.Ip6Address;
-import org.onlab.packet.IpAddress;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onlab.packet.ICMPEcho;
-
-import java.util.Map;
-import java.util.Set;
-
-import static org.onlab.packet.ICMP.TYPE_ECHO_REQUEST;
-import static org.onlab.packet.ICMP6.ECHO_REQUEST;
-import static org.onlab.packet.IPv4.PROTOCOL_ICMP;
-import static org.onosproject.routeservice.Route.Source.STATIC;
-
-/**
- * Utilities class for unit tests.
- */
-public final class TestUtils {
-
-    private TestUtils() {
-
-    }
-
-    // Device configuration section
-    static final DeviceId REMOTE_LEAF = DeviceId.deviceId("of:0000000000000001");
-    static final int REMOTE_LEAF_SID4 = 1;
-    static final String REMOTE_LEAF_LB4 = "192.168.0.1";
-    static final int REMOTE_LEAF_SID6 = 10;
-    static final String REMOTE_LEAF_LB6 = "2000::c0a8:1";
-    private static final PortNumber P1 = PortNumber.portNumber(1);
-    static final MacAddress REMOTE_MAC = MacAddress.valueOf("00:00:00:00:00:02");
-
-    static final DeviceId LOCAL_LEAF = DeviceId.deviceId("of:0000000000000101");
-    static final int LOCAL_LEAF_SID4 = 101;
-    static final String LOCAL_LEAF_LB4 = "192.168.0.101";
-    static final int LOCAL_LEAF_SID6 = 111;
-    static final String LOCAL_LEAF_LB6 = "2000::c0a8:101";
-    static final MacAddress LOCAL_MAC = MacAddress.valueOf("00:00:00:00:01:01");
-
-    // Configure a pair
-    static final DeviceId LOCAL_LEAF1 = DeviceId.deviceId("of:0000000000000201");
-    static final int LOCAL_LEAF1_SID4 = 201;
-    static final String LOCAL_LEAF1_LB4 = "192.168.0.201";
-    static final int LOCAL_LEAF1_SID6 = 211;
-    static final String LOCAL_LEAF1_LB6 = "2000::c0a8:201";
-    static final MacAddress LOCAL_MAC1 = MacAddress.valueOf("00:00:00:00:02:01");
-
-    static final DeviceId LOCAL_LEAF2 = DeviceId.deviceId("of:0000000000000202");
-    static final int LOCAL_LEAF2_SID4 = 202;
-    static final String LOCAL_LEAF2_LB4 = "192.168.0.202";
-    static final int LOCAL_LEAF2_SID6 = 212;
-    static final String LOCAL_LEAF2_LB6 = "2000::c0a8:202";
-    static final MacAddress LOCAL_MAC2 = MacAddress.valueOf("00:00:00:00:02:02");
-
-    // Pair port
-    static final PortNumber P3 = PortNumber.portNumber(3);
-
-    // Ports configuration section
-    static final ConnectPoint CP11 = new ConnectPoint(REMOTE_LEAF, P1);
-    private static final PortNumber P2 = PortNumber.portNumber(2);
-    static final ConnectPoint CP12 = new ConnectPoint(REMOTE_LEAF, P2);
-    private static final IpAddress IP4_1 = IpAddress.valueOf("10.0.0.254");
-    private static final IpPrefix PREFIX4_1 = IpPrefix.valueOf("10.0.0.254/24");
-    private static final IpAddress IP6_1 = IpAddress.valueOf("2000::ff");
-    private static final IpPrefix PREFIX6_1 = IpPrefix.valueOf("2000::ff/120");
-    private static final InterfaceIpAddress INTF_IP4_1 = new InterfaceIpAddress(
-            IP4_1, PREFIX4_1);
-    private static final InterfaceIpAddress INTF_IP6_1 = new InterfaceIpAddress(
-            IP6_1, PREFIX6_1);
-    private static final VlanId INTF_VLAN_UNTAGGED = VlanId.vlanId((short) 10);
-    static final Interface INTF1 = new Interface(
-            "INTF1", CP12, Lists.newArrayList(INTF_IP4_1, INTF_IP6_1), MacAddress.NONE,
-            null, INTF_VLAN_UNTAGGED, null, null);
-    static final ConnectPoint CP13 = new ConnectPoint(REMOTE_LEAF, P3);
-    private static final IpAddress IP4_2 = IpAddress.valueOf("10.0.3.254");
-    private static final IpPrefix PREFIX4_2 = IpPrefix.valueOf("10.0.3.254/24");
-    private static final IpAddress IP6_2 = IpAddress.valueOf("2000::3ff");
-    private static final IpPrefix PREFIX6_2 = IpPrefix.valueOf("2000::3ff/120");
-    private static final InterfaceIpAddress INTF_IP4_2 = new InterfaceIpAddress(
-            IP4_2, PREFIX4_2);
-    private static final InterfaceIpAddress INTF_IP6_2 = new InterfaceIpAddress(
-            IP6_2, PREFIX6_2);
-    static final Interface INTF2 = new Interface(
-            "INTF2", CP13, Lists.newArrayList(INTF_IP4_2, INTF_IP6_2), MacAddress.NONE,
-            null, INTF_VLAN_UNTAGGED, null, null);
-
-    static final ConnectPoint CP1011 = new ConnectPoint(LOCAL_LEAF, P1);
-    private static final IpAddress IP4_11 = IpAddress.valueOf("10.0.1.254");
-    private static final IpPrefix PREFIX4_11 = IpPrefix.valueOf("10.0.1.254/24");
-    private static final InterfaceIpAddress INTF_IP4_11 = new InterfaceIpAddress(
-            IP4_11, PREFIX4_11);
-    private static final IpAddress IP6_11 = IpAddress.valueOf("2000::1ff");
-    private static final IpPrefix PREFIX6_11 = IpPrefix.valueOf("2000::1ff/120");
-    private static final InterfaceIpAddress INTF_IP6_11 = new InterfaceIpAddress(
-            IP6_11, PREFIX6_11);
-    static final Interface INTF111 = new Interface(
-            "INTF111", CP1011, Lists.newArrayList(INTF_IP4_11, INTF_IP6_11), MacAddress.NONE, null,
-            INTF_VLAN_UNTAGGED, null, null);
-
-    static final ConnectPoint CP2011 = new ConnectPoint(LOCAL_LEAF1, P1);
-    private static final IpAddress IP4_21 = IpAddress.valueOf("10.0.2.254");
-    private static final IpPrefix PREFIX4_21 = IpPrefix.valueOf("10.0.2.254/24");
-    private static final InterfaceIpAddress INTF_IP4_21 = new InterfaceIpAddress(
-            IP4_21, PREFIX4_21);
-    private static final IpAddress IP6_21 = IpAddress.valueOf("2000::2ff");
-    private static final IpPrefix PREFIX6_21 = IpPrefix.valueOf("2000::2ff/120");
-    private static final InterfaceIpAddress INTF_IP6_21 = new InterfaceIpAddress(
-            IP6_21, PREFIX6_21);
-    static final Interface INTF211 = new Interface(
-            "INTF211", CP2011, Lists.newArrayList(INTF_IP4_21, INTF_IP6_21), MacAddress.NONE, null,
-            INTF_VLAN_UNTAGGED, null, null);
-
-    static final ConnectPoint CP2021 = new ConnectPoint(LOCAL_LEAF2, P1);
-    private static final IpAddress IP4_22 = IpAddress.valueOf("10.0.2.254");
-    private static final IpPrefix PREFIX4_22 = IpPrefix.valueOf("10.0.2.254/24");
-    private static final InterfaceIpAddress INTF_IP4_22 = new InterfaceIpAddress(
-            IP4_22, PREFIX4_22);
-    private static final IpAddress IP6_22 = IpAddress.valueOf("2000::2ff");
-    private static final IpPrefix PREFIX6_22 = IpPrefix.valueOf("2000::2ff/120");
-    private static final InterfaceIpAddress INTF_IP6_22 = new InterfaceIpAddress(
-            IP6_22, PREFIX6_22);
-    static final Interface INTF212 = new Interface(
-            "INTF212", CP2021, Lists.newArrayList(INTF_IP4_22, INTF_IP6_22), MacAddress.NONE, null,
-            INTF_VLAN_UNTAGGED, null, null);
-    private static final PortNumber P4 = PortNumber.portNumber(4);
-    static final ConnectPoint CP2024 = new ConnectPoint(LOCAL_LEAF2, P4);
-    private static final PortNumber P5 = PortNumber.portNumber(5);
-    static final ConnectPoint CP2025 = new ConnectPoint(LOCAL_LEAF2, P5);
-    private static final IpAddress IP4_23 = IpAddress.valueOf("10.0.4.254");
-    private static final IpPrefix PREFIX4_23 = IpPrefix.valueOf("10.0.4.254/24");
-    private static final InterfaceIpAddress INTF_IP4_23 = new InterfaceIpAddress(
-            IP4_23, PREFIX4_23);
-    private static final IpAddress IP6_23 = IpAddress.valueOf("2000::4ff");
-    private static final IpPrefix PREFIX6_23 = IpPrefix.valueOf("2000::4ff/120");
-    private static final InterfaceIpAddress INTF_IP6_23 = new InterfaceIpAddress(
-            IP6_23, PREFIX6_23);
-    static final Interface INTF213 = new Interface(
-            "INTF212", CP2024, Lists.newArrayList(INTF_IP4_23, INTF_IP6_23), MacAddress.NONE, null,
-            INTF_VLAN_UNTAGGED, null, null);
-
-    // Packet-ins section
-    private static final MacAddress SRC_MAC = MacAddress.valueOf("00:00:00:00:00:01");
-
-    private static final ICMPEcho ICMP_ECHO = new ICMPEcho()
-            .setIdentifier((short) 0)
-            .setSequenceNum((short) 0);
-
-    private static final ICMP ICMP_REQUEST = (ICMP) new ICMP()
-            .setIcmpType(TYPE_ECHO_REQUEST)
-            .setPayload(ICMP_ECHO);
-
-    private static final Ip4Address SRC_IPV4 = Ip4Address.valueOf("10.0.1.1");
-    static final Ip4Address DST_IPV4 = Ip4Address.valueOf("10.0.0.254");
-
-    private static final IPv4 IPV4_REQUEST = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4.toInt())
-            .setSourceAddress(SRC_IPV4.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV4 = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV4_REQUEST);
-
-    private static final ICMP6 ICMP6_REQUEST = new ICMP6()
-            .setIcmpType(ECHO_REQUEST);
-
-    private static final Ip6Address SRC_IPV6 = Ip6Address.valueOf("2000::101");
-    static final Ip6Address DST_IPV6 = Ip6Address.valueOf("2000::ff");
-
-    private static final IPv6 IPV6_REQUEST = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6.toOctets())
-            .setSourceAddress(SRC_IPV6.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6 = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV6_REQUEST);
-
-    static final Ip4Address SRC_IPV41 = Ip4Address.valueOf("10.0.2.1");
-
-    private static final IPv4 IPV41_REQUEST = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4.toInt())
-            .setSourceAddress(SRC_IPV41.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV41 = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV41_REQUEST);
-
-    static final Ip6Address SRC_IPV61 = Ip6Address.valueOf("2000::201");
-
-    private static final IPv6 IPV61_REQUEST = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6.toOctets())
-            .setSourceAddress(SRC_IPV61.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV61 = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV61_REQUEST);
-
-    private static final MacAddress SRC_MAC_MY = MacAddress.valueOf("00:01:00:00:00:01");
-    static final Ip4Address SRC_IPV4_MY = Ip4Address.valueOf("10.0.0.1");
-
-    private static final IPv4 IPV4_REQUEST_MY = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4.toInt())
-            .setSourceAddress(SRC_IPV4_MY.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV4_MY = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC_MY)
-            .setPayload(IPV4_REQUEST_MY);
-
-    static final Ip6Address SRC_IPV6_MY = Ip6Address.valueOf("2000::1");
-
-    private static final IPv6 IPV6_REQUEST_MY = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6.toOctets())
-            .setSourceAddress(SRC_IPV6_MY.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6_MY = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC_MY)
-            .setPayload(IPV6_REQUEST_MY);
-
-    static final Ip4Address DST_IPV4_LOCAL = Ip4Address.valueOf("10.0.3.254");
-
-    private static final IPv4 IPV4_REQUEST_LOCAL = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4_LOCAL.toInt())
-            .setSourceAddress(SRC_IPV4_MY.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV4_LOCAL = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC_MY)
-            .setPayload(IPV4_REQUEST_LOCAL);
-
-    static final Ip6Address DST_IPV6_LOCAL = Ip6Address.valueOf("2000::3ff");
-
-    private static final IPv6 IPV6_REQUEST_LOCAL = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6_LOCAL.toOctets())
-            .setSourceAddress(SRC_IPV6_MY.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6_LOCAL = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC_MY)
-            .setPayload(IPV6_REQUEST_LOCAL);
-
-    static final Ip4Address DST_IPV4_SAME = Ip4Address.valueOf("10.0.4.254");
-
-    private static final IPv4 IPV4_REQUEST_SAME = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4_SAME.toInt())
-            .setSourceAddress(SRC_IPV41.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV4_SAME = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(LOCAL_MAC2)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV4_REQUEST_SAME);
-
-    static final Ip6Address DST_IPV6_SAME = Ip6Address.valueOf("2000::4ff");
-
-    private static final IPv6 IPV6_REQUEST_SAME = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6_SAME.toOctets())
-            .setSourceAddress(SRC_IPV61.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6_SAME = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(LOCAL_MAC2)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV6_REQUEST_SAME);
-
-    static final Ip6Address DST_IPV6_LL = Ip6Address.valueOf(
-            IPv6.getLinkLocalAddress(MacAddress.NONE.toBytes()));
-    static final Ip6Address SRC_IPV6_LL = Ip6Address.valueOf(
-            IPv6.getLinkLocalAddress(SRC_MAC_MY.toBytes()));
-
-    private static final IPv6 IPV6_REQUEST_LL = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6_LL.toOctets())
-            .setSourceAddress(SRC_IPV6_LL.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6_LL = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(MacAddress.NONE)
-            .setSourceMACAddress(SRC_MAC_MY)
-            .setPayload(IPV6_REQUEST_LL);
-
-    static final Ip4Address DST_IPV4_LOOPBACK = Ip4Address.valueOf(REMOTE_LEAF_LB4);
-
-    private static final IPv4 IPV4_REQUEST_LOOPBACK = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4_LOOPBACK.toInt())
-            .setSourceAddress(SRC_IPV4_MY.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV4_LOOPBACK = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC_MY)
-            .setPayload(IPV4_REQUEST_LOOPBACK);
-
-    static final Ip6Address DST_IPV6_LOOPBACK = Ip6Address.valueOf(REMOTE_LEAF_LB6);
-
-    private static final IPv6 IPV6_REQUEST_LOOPBACK = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6_LOOPBACK.toOctets())
-            .setSourceAddress(SRC_IPV6_MY.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6_LOOPBACK = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(REMOTE_MAC)
-            .setSourceMACAddress(SRC_MAC_MY)
-            .setPayload(IPV6_REQUEST_LOOPBACK);
-
-    static final Ip4Address DST_IPV4_LOOPBACK_PAIR = Ip4Address.valueOf(LOCAL_LEAF1_LB4);
-
-    private static final IPv4 IPV4_REQUEST_LOOPBACK_PAIR = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4_LOOPBACK_PAIR.toInt())
-            .setSourceAddress(SRC_IPV41.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV4_LOOPBACK_PAIR = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(LOCAL_MAC1)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV4_REQUEST_LOOPBACK_PAIR);
-
-    static final Ip6Address DST_IPV6_LOOPBACK_PAIR = Ip6Address.valueOf(LOCAL_LEAF2_LB6);
-
-    private static final IPv6 IPV6_REQUEST_LOOPBACK_PAIR = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6_LOOPBACK_PAIR.toOctets())
-            .setSourceAddress(SRC_IPV61.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6_LOOPBACK_PAIR = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(LOCAL_MAC2)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV6_REQUEST_LOOPBACK_PAIR);
-
-    static final Ip4Address DST_IPV4_GATEWAY_PAIR = Ip4Address.valueOf("10.0.2.254");
-
-    private static final IPv4 IPV4_REQUEST_GATEWAY_PAIR = (IPv4) new IPv4()
-            .setDestinationAddress(DST_IPV4_GATEWAY_PAIR.toInt())
-            .setSourceAddress(SRC_IPV41.toInt())
-            .setTtl((byte) 64)
-            .setProtocol(PROTOCOL_ICMP)
-            .setPayload(ICMP_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV4_GATEWAY_PAIR = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV4)
-            .setDestinationMACAddress(LOCAL_MAC1)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV4_REQUEST_GATEWAY_PAIR);
-
-    static final Ip6Address DST_IPV6_GATEWAY_PAIR = Ip6Address.valueOf("2000::2ff");
-
-    private static final IPv6 IPV6_REQUEST_GATEWAY_PAIR = (IPv6) new IPv6()
-            .setDestinationAddress(DST_IPV6_GATEWAY_PAIR.toOctets())
-            .setSourceAddress(SRC_IPV61.toOctets())
-            .setHopLimit((byte) 255)
-            .setNextHeader(IPv6.PROTOCOL_ICMP6)
-            .setPayload(ICMP6_REQUEST);
-
-    static final Ethernet ETH_REQ_IPV6_GATEWAY_PAIR = (Ethernet) new Ethernet()
-            .setEtherType(Ethernet.TYPE_IPV6)
-            .setDestinationMACAddress(LOCAL_MAC2)
-            .setSourceMACAddress(SRC_MAC)
-            .setPayload(IPV6_REQUEST_GATEWAY_PAIR);
-
-    // Resolved route
-    private static final ResolvedRoute IPV4_ROUTE = new ResolvedRoute(
-            new Route(STATIC, SRC_IPV4.toIpPrefix(), SRC_IPV4), MacAddress.NONE);
-    private static final ResolvedRoute IPV6_ROUTE = new ResolvedRoute(
-            new Route(STATIC, SRC_IPV6.toIpPrefix(), SRC_IPV6), MacAddress.NONE);
-    static final Map<IpPrefix, Set<ResolvedRoute>> ROUTE_STORE = ImmutableMap.of(SRC_IPV4.toIpPrefix(),
-                                                                                 Sets.newHashSet(IPV4_ROUTE),
-                                                                                 SRC_IPV6.toIpPrefix(),
-                                                                                 Sets.newHashSet(IPV6_ROUTE));
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/config/DeviceConfigurationTest.java b/app/src/test/java/org/onosproject/segmentrouting/config/DeviceConfigurationTest.java
deleted file mode 100644
index 1674f6c..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/config/DeviceConfigurationTest.java
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting.config;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.JsonNodeFactory;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.basics.BasicDeviceConfig;
-import org.onosproject.net.config.basics.InterfaceConfig;
-import org.onosproject.net.host.InterfaceIpAddress;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.intf.InterfaceService;
-import org.onosproject.net.neighbour.NeighbourResolutionService;
-import org.onosproject.segmentrouting.SegmentRoutingManager;
-
-import java.io.InputStream;
-import java.util.List;
-import java.util.Set;
-
-import static org.easymock.EasyMock.anyObject;
-import static org.easymock.EasyMock.createMock;
-import static org.easymock.EasyMock.eq;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.expectLastCall;
-import static org.easymock.EasyMock.replay;
-import static org.junit.Assert.*;
-
-public class DeviceConfigurationTest {
-    private static final DeviceId DEV1 = DeviceId.deviceId("of:1");
-    private static final String CONFIG_KEY = "segmentrouting";
-    private static final PortNumber PORT1 = PortNumber.portNumber(1);
-    private static final PortNumber PORT2 = PortNumber.portNumber(2);
-    private static final ConnectPoint CP1 = new ConnectPoint(DEV1, PORT1);
-    private static final ConnectPoint CP2 = new ConnectPoint(DEV1, PORT2);
-    private static final MacAddress MAC1 = MacAddress.valueOf("00:11:22:33:44:55");
-    private static final VlanId VLAN1 = VlanId.vlanId((short) 10);
-    private static final VlanId VLAN2 = VlanId.vlanId((short) 20);
-    private static final IpPrefix PREFIX1 = IpPrefix.valueOf("10.0.1.254/24");
-    private static final IpPrefix PREFIX2 = IpPrefix.valueOf("10.0.2.254/24");
-    private static final IpPrefix ROUTE1 = IpPrefix.valueOf("20.0.1.254/24");
-    private static final InterfaceIpAddress INTF1_IP = new InterfaceIpAddress(PREFIX1.address(), PREFIX1);
-    private static final InterfaceIpAddress INTF2_IP = new InterfaceIpAddress(PREFIX2.address(), PREFIX2);
-    private static final List<InterfaceIpAddress> IP_LIST1 = Lists.newArrayList(INTF1_IP);
-    private static final List<InterfaceIpAddress> IP_LIST2 = Lists.newArrayList(INTF2_IP);
-    private static final Interface INTF1 = new Interface("mock-intf1", CP1, IP_LIST1, MAC1,
-            null, VLAN1, null, null);
-    private static final Interface INTF2 = new Interface("mock-intf2", CP2, IP_LIST2, MAC1,
-            null, VLAN2, null, null);
-    private static final Set<Interface> INTERFACES = Sets.newHashSet(INTF1, INTF2);
-
-    private DeviceConfiguration devConfig;
-
-    private NetworkConfigRegistry networkConfigService;
-
-    @Before
-    public void setUp() throws Exception {
-        InterfaceService interfaceService;
-        networkConfigService = null;
-        NeighbourResolutionService neighbourResolutionService;
-        SegmentRoutingManager srManager;
-
-        // Mock device netcfg
-        InputStream jsonStream = SegmentRoutingDeviceConfigTest.class.getResourceAsStream("/device.json");
-        ObjectMapper mapper = new ObjectMapper();
-        JsonNode jsonNode = mapper.readTree(jsonStream);
-        SegmentRoutingDeviceConfig srDevConfig = new SegmentRoutingDeviceConfig();
-        srDevConfig.init(DEV1, CONFIG_KEY, jsonNode, mapper, config -> { });
-        BasicDeviceConfig basicDeviceConfig = new BasicDeviceConfig();
-        basicDeviceConfig.init(DEV1, DEV1.toString(), JsonNodeFactory.instance.objectNode(), mapper, config -> { });
-        BasicDeviceConfig purgeOnDisconnectConfig = basicDeviceConfig.purgeOnDisconnection(true);
-
-
-        // Mock interface netcfg
-        jsonStream = InterfaceConfig.class.getResourceAsStream("/interface1.json");
-        jsonNode = mapper.readTree(jsonStream);
-        InterfaceConfig interfaceConfig1 = new InterfaceConfig();
-        interfaceConfig1.init(CP1, CONFIG_KEY, jsonNode, mapper, config -> { });
-        jsonStream = InterfaceConfig.class.getResourceAsStream("/interface2.json");
-        jsonNode = mapper.readTree(jsonStream);
-        InterfaceConfig interfaceConfig2 = new InterfaceConfig();
-        interfaceConfig2.init(CP1, CONFIG_KEY, jsonNode, mapper, config -> { });
-
-        networkConfigService = createMock(NetworkConfigRegistry.class);
-        expect(networkConfigService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class))
-                .andReturn(Sets.newHashSet(DEV1)).anyTimes();
-        expect(networkConfigService.getConfig(DEV1, SegmentRoutingDeviceConfig.class))
-                .andReturn(srDevConfig).anyTimes();
-        expect(networkConfigService.addConfig(DEV1, BasicDeviceConfig.class))
-                .andReturn(basicDeviceConfig).anyTimes();
-        expect(networkConfigService.getConfig(DEV1, BasicDeviceConfig.class))
-                .andReturn(basicDeviceConfig).anyTimes();
-        expect(networkConfigService.applyConfig(DEV1, BasicDeviceConfig.class, purgeOnDisconnectConfig.node()))
-                .andReturn(purgeOnDisconnectConfig).anyTimes();
-        expect(networkConfigService.getSubjects(ConnectPoint.class, InterfaceConfig.class))
-                .andReturn(Sets.newHashSet(CP1, CP2)).anyTimes();
-        expect(networkConfigService.getConfig(CP1, InterfaceConfig.class)).andReturn(interfaceConfig1).anyTimes();
-        expect(networkConfigService.getConfig(CP2, InterfaceConfig.class)).andReturn(interfaceConfig2).anyTimes();
-        expect(networkConfigService.applyConfig(eq(CP1), eq(InterfaceConfig.class), anyObject()))
-                .andReturn(interfaceConfig1).anyTimes();
-        expect(networkConfigService.applyConfig(eq(CP2), eq(InterfaceConfig.class), anyObject()))
-                .andReturn(interfaceConfig2).anyTimes();
-        expect(networkConfigService.getConfig(null, SegmentRoutingAppConfig.class)).andReturn(null).anyTimes();
-        replay(networkConfigService);
-
-        interfaceService = createMock(InterfaceService.class);
-        expect(interfaceService.getInterfaces()).andReturn(INTERFACES).anyTimes();
-        expect(interfaceService.getInterfacesByPort(CP1)).andReturn(Sets.newHashSet(INTF1)).anyTimes();
-        expect(interfaceService.getInterfacesByPort(CP2)).andReturn(Sets.newHashSet(INTF2)).anyTimes();
-        replay(interfaceService);
-
-        neighbourResolutionService = createMock(NeighbourResolutionService.class);
-        neighbourResolutionService.registerNeighbourHandler(anyObject(ConnectPoint.class), anyObject(), anyObject());
-        expectLastCall().anyTimes();
-        replay(neighbourResolutionService);
-
-        srManager = new SegmentRoutingManager();
-        srManager.interfaceService = interfaceService;
-        srManager.cfgService = networkConfigService;
-        srManager.neighbourResolutionService = neighbourResolutionService;
-
-        devConfig = new DeviceConfiguration(srManager);
-        devConfig.addSubnet(CP2, ROUTE1);
-    }
-
-    @Test
-    public void getConfiguredSubnets() {
-        Set<IpPrefix> expect = Sets.newHashSet(PREFIX1, PREFIX2);
-        assertEquals(expect, devConfig.getConfiguredSubnets(DEV1));
-    }
-
-    @Test
-    public void getSubnets() {
-        Set<IpPrefix> expect = Sets.newHashSet(PREFIX1, PREFIX2, ROUTE1);
-        assertEquals(expect, devConfig.getSubnets(DEV1));
-    }
-
-    @Test
-    public void getPortSubnets() {
-        assertEquals(Sets.newHashSet(PREFIX1), devConfig.getPortSubnets(DEV1, PORT1));
-        assertEquals(Sets.newHashSet(PREFIX2), devConfig.getPortSubnets(DEV1, PORT2));
-    }
-
-    @Test
-    public void inSameSubnet() {
-        assertTrue(devConfig.inSameSubnet(DEV1, PREFIX1.address()));
-        assertTrue(devConfig.inSameSubnet(DEV1, PREFIX2.address()));
-        assertFalse(devConfig.inSameSubnet(DEV1, ROUTE1.address()));
-    }
-
-    @Test
-    public void getPurgeOnDisconnect() {
-        assertNotNull(networkConfigService.getConfig(DEV1, BasicDeviceConfig.class));
-        assertTrue(networkConfigService.getConfig(DEV1, BasicDeviceConfig.class).purgeOnDisconnection());
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/grouphandler/DestinationSetTest.java b/app/src/test/java/org/onosproject/segmentrouting/grouphandler/DestinationSetTest.java
deleted file mode 100644
index b41fe07..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/grouphandler/DestinationSetTest.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Copyright 2017-present Open Networking Foundation
- *
- * 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.segmentrouting.grouphandler;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.onosproject.net.DeviceId;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-public class DestinationSetTest {
-    DestinationSet ds1, ds2, ds3, ds4, ds5, ds6;
-    DeviceId d201, d202;
-    int el201, el202;
-
-    @Before
-    public void setUp() {
-        d201 = DeviceId.deviceId("of:0000000000000201");
-        d202 = DeviceId.deviceId("of:0000000000000202");
-        el201 = 201;
-        el202 = 202;
-        ds1 = DestinationSet.createTypePushNone(d201);
-        ds2 = DestinationSet.createTypePushBos(el201, d201);
-        ds3 = DestinationSet.createTypePushBos(el201, d201, el202, d202);
-        ds4 = DestinationSet.createTypePushBos(DestinationSet.NO_EDGE_LABEL, d201,
-                                               DestinationSet.NO_EDGE_LABEL, d202);
-        ds5 = DestinationSet.createTypePopNotBos(d201);
-        ds6 = DestinationSet.createTypeSwapBos(el201, d201);
-    }
-
-    @Test
-    public void testIsValid() {
-        assertTrue(!ds1.notBos());
-        assertTrue(!ds1.swap());
-        assertTrue(ds1.getEdgeLabel(d201) == DestinationSet.NO_EDGE_LABEL);
-        assertTrue(ds1.getDestinationSwitches().size() == 1);
-
-        assertTrue(!ds2.notBos());
-        assertTrue(!ds2.swap());
-        assertTrue(ds2.getEdgeLabel(d201) == el201);
-        assertTrue(ds2.getDestinationSwitches().size() == 1);
-
-        assertTrue(!ds3.notBos());
-        assertTrue(!ds3.swap());
-        assertTrue(ds3.getEdgeLabel(d201) == el201);
-        assertTrue(ds3.getEdgeLabel(d202) == el202);
-        assertTrue(ds3.getDestinationSwitches().size() == 2);
-
-        assertTrue(!ds4.notBos());
-        assertTrue(!ds4.swap());
-        assertTrue(ds4.getEdgeLabel(d201) == DestinationSet.NO_EDGE_LABEL);
-        assertTrue(ds4.getEdgeLabel(d202) == DestinationSet.NO_EDGE_LABEL);
-        assertTrue(ds4.getDestinationSwitches().size() == 2);
-
-        assertFalse(ds1.equals(ds2));
-        assertFalse(ds1.equals(ds4));
-        assertFalse(ds3.equals(ds4));
-        assertFalse(ds2.equals(ds3));
-        assertFalse(ds1.equals(ds3));
-
-        assertFalse(ds1.equals(ds5));
-        assertFalse(ds2.equals(ds6));
-    }
-
-
-    @Test
-    public void testOneDestinationWithoutLabel() {
-        DestinationSet testds = DestinationSet.createTypePushNone(d201);
-        assertTrue(testds.equals(ds1)); // match
-
-        testds = DestinationSet.createTypePopNotBos(d201);
-        assertFalse(testds.equals(ds1)); // wrong notBos
-        assertTrue(testds.equals(ds5)); // correct notBos
-
-        testds = DestinationSet.createTypePushNone(d202);
-        assertFalse(testds.equals(ds1)); //wrong device
-
-        testds = DestinationSet.createTypePushBos(el201, d201);
-        assertFalse(testds.equals(ds1)); // wrong label
-
-        testds = DestinationSet.createTypePushBos(-1, d201, -1, d202);
-        assertFalse(testds.equals(ds1)); // 2-devs should not match
-
-        testds = DestinationSet.createTypeSwapBos(el201, d201);
-        assertFalse(testds.equals(ds1)); // wrong type and label
-        assertTrue(testds.equals(ds6)); // correct swap
-
-        testds = DestinationSet.createTypeSwapNotBos(el201, d201);
-        assertFalse(testds.equals(ds6)); // wrong notbos
-    }
-
-
-
-    @Test
-    public void testOneDestinationWithLabel() {
-        DestinationSet testds = DestinationSet.createTypePushBos(203, d202);
-        assertFalse(testds.equals(ds2)); //wrong label
-
-        testds = DestinationSet.createTypePushBos(201, d202);
-        assertFalse(testds.equals(ds2)); //wrong device
-
-        testds = DestinationSet.createTypePushBos(201,  DeviceId.deviceId("of:0000000000000201"));
-        assertTrue(testds.equals(ds2)); // match
-
-        testds = DestinationSet.createTypePushNone(d201);
-        assertFalse(testds.equals(ds2)); // wrong label
-
-        testds = DestinationSet.createTypePushBos(el201, d201, el202, d202);
-        assertFalse(testds.equals(ds1)); // 2-devs should not match
-    }
-
-    @Test
-    public void testDestPairWithLabel() {
-        DestinationSet testds = DestinationSet.createTypePushBos(el201, d201, el202, d202);
-        assertTrue(testds.equals(ds3)); // match same switches, same order
-        assertTrue(testds.hashCode() == ds3.hashCode());
-
-        testds = DestinationSet.createTypePushBos(el202, d202, el201, d201);
-        assertTrue(testds.equals(ds3)); // match same switches, order reversed
-        assertTrue(testds.hashCode() == ds3.hashCode());
-
-        testds = DestinationSet.createTypePushBos(el202, d202);
-        assertFalse(testds.equals(ds3)); // one less switch should not match
-        assertFalse(testds.hashCode() == ds3.hashCode());
-
-        testds = DestinationSet.createTypePushBos(el201, d201);
-        assertFalse(testds.equals(ds3)); // one less switch should not match
-        assertFalse(testds.hashCode() == ds3.hashCode());
-
-        testds = DestinationSet.createTypePushBos(el201, d201, 0, DeviceId.NONE);
-        assertFalse(testds.equals(ds3)); // one less switch should not match
-        assertFalse(testds.hashCode() == ds3.hashCode());
-
-        testds = DestinationSet.createTypePushBos(el201, d202, el201, d201);
-        assertFalse(testds.equals(ds3)); // wrong labels
-        assertFalse(testds.hashCode() == ds3.hashCode());
-
-        testds = DestinationSet.createTypePushBos(el202, d202, el201, d202);
-        assertFalse(testds.equals(ds3)); // wrong device
-        assertFalse(testds.hashCode() == ds3.hashCode());
-
-        testds = DestinationSet.createTypePushBos(
-                                    el202, DeviceId.deviceId("of:0000000000000205"),
-                                    el201, d201);
-        assertFalse(testds.equals(ds3)); // wrong device
-        assertFalse(testds.hashCode() == ds3.hashCode());
-    }
-
-    @Test
-    public void testDestPairWithoutLabel() {
-        DestinationSet testds = DestinationSet.createTypePushBos(-1, d201, -1, d202);
-        assertTrue(testds.equals(ds4)); // match same switches, same order
-        assertTrue(testds.hashCode() == ds4.hashCode());
-
-        testds = DestinationSet.createTypePushBos(-1, d202, -1, d201);
-        assertTrue(testds.equals(ds4)); // match same switches, order reversed
-        assertTrue(testds.hashCode() == ds4.hashCode());
-
-        testds = DestinationSet.createTypePushBos(-1, d202);
-        assertFalse(testds.equals(ds4)); // one less switch should not match
-        assertFalse(testds.hashCode() == ds4.hashCode());
-
-        testds = DestinationSet.createTypePushBos(-1, d201);
-        assertFalse(testds.equals(ds4)); // one less switch should not match
-        assertFalse(testds.hashCode() == ds4.hashCode());
-
-        testds = DestinationSet.createTypePushBos(-1, d201, 0, DeviceId.NONE);
-        assertFalse(testds.equals(ds4)); // one less switch should not match
-        assertFalse(testds.hashCode() == ds4.hashCode());
-
-        testds = DestinationSet.createTypePushBos(el201, d201, -1, d202);
-        assertFalse(testds.equals(ds4)); // wrong labels
-        assertFalse(testds.hashCode() == ds4.hashCode());
-
-        testds = DestinationSet.createTypePushBos(-1, d202, -1, d202);
-        assertFalse(testds.equals(ds4)); // wrong device
-        assertFalse(testds.hashCode() == ds4.hashCode());
-
-        testds = DestinationSet.createTypePushBos(
-                                    -1, DeviceId.deviceId("of:0000000000000205"),
-                                    -1, d201);
-        assertFalse(testds.equals(ds4)); // wrong device
-        assertFalse(testds.hashCode() == ds4.hashCode());
-    }
-
-}
diff --git a/app/src/test/java/org/onosproject/segmentrouting/pwaas/PwaasUtilTest.java b/app/src/test/java/org/onosproject/segmentrouting/pwaas/PwaasUtilTest.java
deleted file mode 100644
index 1d9f07e..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/pwaas/PwaasUtilTest.java
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * Copyright 2018-present Open Networking Foundation
- *
- * 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.segmentrouting.pwaas;
-
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.onlab.junit.TestUtils;
-import org.onlab.packet.MacAddress;
-import org.onlab.packet.VlanId;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultPort;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Port;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.intf.Interface;
-import org.onosproject.net.intf.InterfaceService;
-import org.onosproject.segmentrouting.MockDevice;
-
-import static org.easymock.EasyMock.createNiceMock;
-import static org.easymock.EasyMock.expect;
-import static org.easymock.EasyMock.replay;
-
-public class PwaasUtilTest {
-    private static final DeviceId DID1 = DeviceId.deviceId("of:1");
-    private static final DeviceId DID2 = DeviceId.deviceId("of:2");
-    private static final DeviceId DID99 = DeviceId.deviceId("of:99");
-    private static final PortNumber PN1 = PortNumber.portNumber(1);
-    private static final PortNumber PN2 = PortNumber.portNumber(2);
-    private static final PortNumber PN99 = PortNumber.portNumber(99);
-    private static final ConnectPoint CP11 = new ConnectPoint(DID1, PN1);
-    private static final ConnectPoint CP12 = new ConnectPoint(DID1, PN2);
-    private static final ConnectPoint CP21 = new ConnectPoint(DID2, PN1);
-    private static final ConnectPoint CP22 = new ConnectPoint(DID2, PN2);
-    private static final VlanId V1 = VlanId.vlanId((short) 1);
-    private static final VlanId V2 = VlanId.vlanId((short) 2);
-    private static final Device D1 = new MockDevice(DID1, null);
-    private static final Device D2 = new MockDevice(DID2, null);
-    private static final Port P11 = new DefaultPort(D1, PN1, true);
-    private static final Port P12 = new DefaultPort(D1, PN2, true);
-    private static final Port P21 = new DefaultPort(D2, PN1, true);
-    private static final Port P22 = new DefaultPort(D2, PN2, true);
-    private static final Interface I11 = new Interface("I11", CP11, Lists.newArrayList(), MacAddress.NONE,
-            VlanId.NONE, VlanId.NONE, Sets.newHashSet(VlanId.NONE), VlanId.NONE);
-    private static final Interface I12 = new Interface("I12", CP12, Lists.newArrayList(), MacAddress.NONE,
-            VlanId.NONE, VlanId.NONE, Sets.newHashSet(VlanId.NONE), VlanId.NONE);
-    private static final Interface I21 = new Interface("I21", CP21, Lists.newArrayList(), MacAddress.NONE,
-            VlanId.NONE, VlanId.NONE, Sets.newHashSet(VlanId.NONE), VlanId.NONE);
-    private static final Interface I22 = new Interface("I22", CP22, Lists.newArrayList(), MacAddress.NONE,
-            VlanId.NONE, VlanId.NONE, Sets.newHashSet(VlanId.NONE), VlanId.NONE);
-
-    private ConnectPoint cp1;
-    private ConnectPoint cp2;
-    private VlanId ingressInner;
-    private VlanId ingressOuter;
-    private VlanId egressInner;
-    private VlanId egressOuter;
-    private static final Long TUNNEL_ID = (long) 1234;
-
-    @Before
-    public void setUp() {
-        DeviceService deviceService = createNiceMock(DeviceService.class);
-        InterfaceService intfService = createNiceMock(InterfaceService.class);
-        TestUtils.setField(PwaasUtil.class, "deviceService", deviceService);
-        TestUtils.setField(PwaasUtil.class, "intfService", intfService);
-
-        expect(deviceService.getDevice(DID1)).andReturn(D1).anyTimes();
-        expect(deviceService.getDevice(DID2)).andReturn(D2).anyTimes();
-        expect(deviceService.getPort(CP11)).andReturn(P11).anyTimes();
-        expect(deviceService.getPort(CP12)).andReturn(P12).anyTimes();
-        expect(deviceService.getPort(CP21)).andReturn(P21).anyTimes();
-        expect(deviceService.getPort(CP22)).andReturn(P22).anyTimes();
-        expect(intfService.getInterfacesByPort(CP11)).andReturn(Sets.newHashSet(I11)).anyTimes();
-        expect(intfService.getInterfacesByPort(CP12)).andReturn(Sets.newHashSet(I12)).anyTimes();
-        expect(intfService.getInterfacesByPort(CP21)).andReturn(Sets.newHashSet(I21)).anyTimes();
-        expect(intfService.getInterfacesByPort(CP22)).andReturn(Sets.newHashSet(I22)).anyTimes();
-        replay(deviceService);
-        replay(intfService);
-    }
-
-    @Rule
-    public final ExpectedException exception = ExpectedException.none();
-
-    @Test
-    public void verifyPolicy() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = V2;
-        egressInner = V1;
-        egressOuter = V2;
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-
-        ingressInner = VlanId.NONE;
-        ingressOuter = VlanId.NONE;
-        egressInner = VlanId.NONE;
-        egressOuter = VlanId.NONE;
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyOnSameDevice() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID1, PN2);
-        ingressInner = VlanId.NONE;
-        ingressOuter = VlanId.NONE;
-        egressInner = VlanId.NONE;
-        egressOuter = VlanId.NONE;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_SAME_DEV, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyEmptyInnerCp1() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = VlanId.NONE;
-        ingressOuter = V1;
-        egressInner = VlanId.NONE;
-        egressOuter = VlanId.NONE;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_EMPTY_INNER_WHEN_OUTER_PRESENT, TUNNEL_ID, "cp1"));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyEmptyInnerCp2() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = VlanId.NONE;
-        ingressOuter = VlanId.NONE;
-        egressInner = VlanId.NONE;
-        egressOuter = V1;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_EMPTY_INNER_WHEN_OUTER_PRESENT, TUNNEL_ID, "cp2"));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyVlanWildcard() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = VlanId.ANY;
-        ingressOuter = VlanId.NONE;
-        egressInner = VlanId.NONE;
-        egressOuter = VlanId.NONE;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_WILDCARD_VLAN, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-
-    @Test
-    public void verifyPolicyDeviceServiceNotAvailable() {
-        TestUtils.setField(PwaasUtil.class, "deviceService", null);
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = V2;
-        egressInner = V1;
-        egressOuter = V2;
-        exception.expect(IllegalStateException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_SERVICE_UNAVAIL, "DeviceService"));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyDoubleToUntagged() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = V2;
-        egressInner = VlanId.NONE;
-        egressOuter = VlanId.NONE;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_DOUBLE_TO_UNTAGGED, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyDoubleToSingle() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = V2;
-        egressInner = V1;
-        egressOuter = VlanId.NONE;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_DOUBLE_TO_SINGLE, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicySingleToUntagged() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = VlanId.NONE;
-        egressInner = VlanId.NONE;
-        egressOuter = VlanId.NONE;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_SINGLE_TO_UNTAGGED, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyVlanTranslation() {
-        cp1 = new ConnectPoint(DID1, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = VlanId.NONE;
-        egressInner = V2;
-        egressOuter = VlanId.NONE;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_VLAN_TRANSLATION, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyDeviceNotFound() {
-        cp1 = new ConnectPoint(DID99, PN1);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = V2;
-        egressInner = V1;
-        egressOuter = V2;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_DEV_NOT_FOUND, DID99, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-
-    @Test
-    public void verifyPolicyPortNotFound() {
-        cp1 = new ConnectPoint(DID1, PN99);
-        cp2 = new ConnectPoint(DID2, PN2);
-        ingressInner = V1;
-        ingressOuter = V2;
-        egressInner = V1;
-        egressOuter = V2;
-        exception.expect(IllegalArgumentException.class);
-        exception.expectMessage(String.format(PwaasUtil.ERR_PORT_NOT_FOUND, PN99, DID1, TUNNEL_ID));
-        PwaasUtil.verifyPolicy(cp1, cp2, ingressInner, ingressOuter, egressInner, egressOuter, TUNNEL_ID);
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/java/org/onosproject/segmentrouting/xconnect/api/XconnectCodecTest.java b/app/src/test/java/org/onosproject/segmentrouting/xconnect/api/XconnectCodecTest.java
deleted file mode 100644
index bac9ff4..0000000
--- a/app/src/test/java/org/onosproject/segmentrouting/xconnect/api/XconnectCodecTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-/*
- * Copyright 2019-present Open Networking Foundation
- *
- * 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.segmentrouting.xconnect.api;
-
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.common.collect.Sets;
-import org.junit.Before;
-import org.junit.Test;
-import org.onlab.packet.VlanId;
-import org.onosproject.codec.CodecContext;
-import org.onosproject.codec.JsonCodec;
-import org.onosproject.codec.impl.MockCodecContext;
-import org.onosproject.net.DeviceId;
-
-import java.io.InputStream;
-import java.util.Set;
-
-import static org.junit.Assert.*;
-
-public class XconnectCodecTest {
-    private static final DeviceId DEVICE_ID = DeviceId.deviceId("of:1");
-    private static final VlanId VLAN_VID = VlanId.vlanId((short) 10);
-    private static final XconnectKey KEY = new XconnectKey(DEVICE_ID, VLAN_VID);
-    private static final XconnectEndpoint EP1 = XconnectEndpoint.fromString("1");
-    private static final XconnectEndpoint EP2 = XconnectEndpoint.fromString("2");
-    private static final XconnectEndpoint EP3 = XconnectEndpoint.fromString("LB:5");
-
-    private CodecContext context;
-    private JsonCodec<XconnectDesc> codec;
-
-    @Before
-    public void setUp() throws Exception {
-        context = new MockCodecContext();
-        codec = new XconnectCodec();
-    }
-
-    @Test
-    public void testEncodePort() throws Exception {
-        Set<XconnectEndpoint> endpoints1 = Sets.newHashSet(EP1, EP2);
-        XconnectDesc desc1 = new XconnectDesc(KEY, endpoints1);
-
-        ObjectMapper mapper = new ObjectMapper();
-        InputStream jsonStream1 = XconnectCodecTest.class.getResourceAsStream("/xconnect1.json");
-        JsonNode expected = mapper.readTree(jsonStream1);
-
-        JsonNode actual = codec.encode(desc1, context);
-
-        assertEquals(expected.get(XconnectCodec.DEVICE_ID), actual.get(XconnectCodec.DEVICE_ID));
-        assertEquals(expected.get(XconnectCodec.VLAN_ID).asInt(), actual.get(XconnectCodec.VLAN_ID).asInt());
-        assertEquals(expected.get(XconnectCodec.ENDPOINTS), actual.get(XconnectCodec.ENDPOINTS));
-    }
-
-    @Test
-    public void testDecodePort() throws Exception {
-        Set<XconnectEndpoint> endpoints1 = Sets.newHashSet(EP1, EP2);
-        XconnectDesc expected = new XconnectDesc(KEY, endpoints1);
-
-        ObjectMapper mapper = new ObjectMapper();
-        InputStream jsonStream1 = XconnectCodecTest.class.getResourceAsStream("/xconnect1.json");
-        ObjectNode objectNode = mapper.readTree(jsonStream1).deepCopy();
-
-        XconnectDesc actual = codec.decode(objectNode, context);
-
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    public void testEncodeLb() throws Exception {
-        Set<XconnectEndpoint> endpoints1 = Sets.newHashSet(EP1, EP3);
-        XconnectDesc desc1 = new XconnectDesc(KEY, endpoints1);
-
-        ObjectMapper mapper = new ObjectMapper();
-        InputStream jsonStream1 = XconnectCodecTest.class.getResourceAsStream("/xconnect2.json");
-        JsonNode expected = mapper.readTree(jsonStream1);
-
-        JsonNode actual = codec.encode(desc1, context);
-
-        assertEquals(expected.get(XconnectCodec.DEVICE_ID), actual.get(XconnectCodec.DEVICE_ID));
-        assertEquals(expected.get(XconnectCodec.VLAN_ID).asInt(), actual.get(XconnectCodec.VLAN_ID).asInt());
-        assertEquals(expected.get(XconnectCodec.ENDPOINTS), actual.get(XconnectCodec.ENDPOINTS));
-    }
-
-    @Test
-    public void testDecodeLb() throws Exception {
-        Set<XconnectEndpoint> endpoints1 = Sets.newHashSet(EP1, EP3);
-        XconnectDesc expected = new XconnectDesc(KEY, endpoints1);
-
-        ObjectMapper mapper = new ObjectMapper();
-        InputStream jsonStream1 = XconnectCodecTest.class.getResourceAsStream("/xconnect2.json");
-        ObjectNode objectNode = mapper.readTree(jsonStream1).deepCopy();
-
-        XconnectDesc actual = codec.decode(objectNode, context);
-
-        assertEquals(expected, actual);
-    }
-}
\ No newline at end of file
diff --git a/app/src/test/resources/interface1.json b/app/src/test/resources/interface1.json
deleted file mode 100644
index 0f7123f..0000000
--- a/app/src/test/resources/interface1.json
+++ /dev/null
@@ -1,6 +0,0 @@
-[
-  {
-    "ips" : [ "10.0.1.254/24" ],
-    "vlan-untagged": 10
-  }
-]
\ No newline at end of file
diff --git a/app/src/test/resources/interface2.json b/app/src/test/resources/interface2.json
deleted file mode 100644
index 114fbf5..0000000
--- a/app/src/test/resources/interface2.json
+++ /dev/null
@@ -1,6 +0,0 @@
-[
-  {
-    "ips" : [ "10.0.2.254/24" ],
-    "vlan-untagged": 20
-  }
-]
\ No newline at end of file
diff --git a/app/src/test/resources/pwaas-conflicting-vlan.json b/app/src/test/resources/pwaas-conflicting-vlan.json
deleted file mode 100644
index fac162d..0000000
--- a/app/src/test/resources/pwaas-conflicting-vlan.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "1": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000002/2",
-    "cP1InnerTag": "10",
-    "cP1OuterTag": "20",
-    "cP2InnerTag": "11",
-    "cP2OuterTag": "21",
-    "mode": "RAW",
-    "sdTag": "",
-    "pwLabel": "255"
-  },
-  "20": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000002/2",
-    "cP1InnerTag": "100",
-    "cP1OuterTag": "",
-    "cP2InnerTag": "21",
-    "cP2OuterTag": "",
-    "mode": "RAW",
-    "sdTag": "",
-    "pwLabel": "1255"
-  }
-}
diff --git a/app/src/test/resources/pwaas-invalid-mode.json b/app/src/test/resources/pwaas-invalid-mode.json
deleted file mode 100644
index af0a9d1..0000000
--- a/app/src/test/resources/pwaas-invalid-mode.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "1": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000011/1",
-    "cP1InnerTag": "10",
-    "cP1OuterTag": "20",
-    "cP2InnerTag": "11",
-    "cP2OuterTag": "",
-    "mode": "UNDEFINED_MODED",
-    "sdTag": "40",
-    "pwLabel": "255"
-  }
-}
diff --git a/app/src/test/resources/pwaas-invalid-pwlabel.json b/app/src/test/resources/pwaas-invalid-pwlabel.json
deleted file mode 100644
index 66697f5..0000000
--- a/app/src/test/resources/pwaas-invalid-pwlabel.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "1": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000011/1",
-    "cP1InnerTag": "10",
-    "cP1OuterTag": "20",
-    "cP2InnerTag": "11",
-    "cP2OuterTag": "",
-    "mode": "1",
-    "sdTag": "40",
-    "pwLabel": "255.555551"
-  }
-}
diff --git a/app/src/test/resources/pwaas-invalid-vlan.json b/app/src/test/resources/pwaas-invalid-vlan.json
deleted file mode 100644
index b556e06..0000000
--- a/app/src/test/resources/pwaas-invalid-vlan.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "1": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000002/2",
-    "cP1InnerTag": "10",
-    "cP1OuterTag": "20",
-    "cP2InnerTag": "11",
-    "cP2OuterTag": "21",
-    "mode": "RAW",
-    "sdTag": "",
-    "pwLabel": "255"
-  },
-  "20": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000002/2",
-    "cP1InnerTag": "100",
-    "cP1OuterTag": "200.55",
-    "cP2InnerTag": "1100",
-    "cP2OuterTag": "210",
-    "mode": "RAW",
-    "sdTag": "",
-    "pwLabel": "1255"
-  }
-}
diff --git a/app/src/test/resources/pwaas.json b/app/src/test/resources/pwaas.json
deleted file mode 100644
index 2a58b4e..0000000
--- a/app/src/test/resources/pwaas.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "1": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000002/1",
-    "cP1InnerTag": "10",
-    "cP1OuterTag": "20",
-    "cP2InnerTag": "10",
-    "cP2OuterTag": "21",
-    "mode": "RAW",
-    "sdTag": "",
-    "pwLabel": "255"
-  },
-  "20": {
-    "cP1": "of:0000000000000001/1",
-    "cP2": "of:0000000000000002/1",
-    "cP1InnerTag": "100",
-    "cP1OuterTag": "200",
-    "cP2InnerTag": "100",
-    "cP2OuterTag": "210",
-    "mode": "RAW",
-    "sdTag": "",
-    "pwLabel": "1255"
-  }
-}
diff --git a/app/src/test/resources/xconnect1.json b/app/src/test/resources/xconnect1.json
deleted file mode 100644
index d8990d4..0000000
--- a/app/src/test/resources/xconnect1.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "deviceId": "of:1",
-  "vlanId": 10,
-  "endpoints": ["1", "2"]
-}
diff --git a/app/src/test/resources/xconnect2.json b/app/src/test/resources/xconnect2.json
deleted file mode 100644
index 61919da..0000000
--- a/app/src/test/resources/xconnect2.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
-  "deviceId": "of:1",
-  "vlanId": 10,
-  "endpoints": ["1", "LB:5"]
-}