Remove next hop location from resolved route

The next hop location should be obtained from host service

Change-Id: I8652e6b8b1367097ffbfcb1651538c34819f67d6
diff --git a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
index 2113e71..23bbf07 100644
--- a/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/IcmpHandler.java
@@ -29,7 +29,6 @@
 import org.onlab.packet.ndp.NeighborSolicitation;
 import org.onosproject.net.neighbour.NeighbourMessageContext;
 import org.onosproject.net.neighbour.NeighbourMessageType;
-import org.onosproject.routeservice.ResolvedRoute;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -165,12 +164,15 @@
         //       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<ResolvedRoute> nexthop = srManager.routeService.longestPrefixLookup(destIpAddress);
-            if (nexthop.isPresent()) {
+            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(nexthop.get().location().deviceId());
+                    destRouterAddress = config.getRouterIpv4(deviceId.get());
                 } catch (DeviceConfigNotFoundException e) {
-                    log.warn("Device config not found. Abort ICMP processing");
+                    log.warn("Device config for {} not found. Abort ICMP processing", deviceId);
                     return;
                 }
             }
@@ -240,12 +242,15 @@
         //       The source might be an indirect host behind a router.
         //       Lookup the route store for the nexthop instead.
         if (destRouterAddress == null) {
-            Optional<ResolvedRoute> nexthop = srManager.routeService.longestPrefixLookup(destIpAddress);
-            if (nexthop.isPresent()) {
+            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(nexthop.get().location().deviceId());
+                    destRouterAddress = config.getRouterIpv6(deviceId.get());
                 } catch (DeviceConfigNotFoundException e) {
-                    log.warn("Device config not found. Abort ICMPv6 processing");
+                    log.warn("Device config for {} not found. Abort ICMPv6 processing", deviceId);
                     return;
                 }
             }
diff --git a/src/main/java/org/onosproject/segmentrouting/RouteHandler.java b/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
index c82b39b..214ba6b 100644
--- a/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/RouteHandler.java
@@ -22,9 +22,9 @@
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.routeservice.ResolvedRoute;
 import org.onosproject.routeservice.RouteEvent;
-import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -38,22 +38,24 @@
     private static final Logger log = LoggerFactory.getLogger(RouteHandler.class);
     private final SegmentRoutingManager srManager;
 
-    public RouteHandler(SegmentRoutingManager srManager) {
+    RouteHandler(SegmentRoutingManager srManager) {
         this.srManager = srManager;
     }
 
     protected void init(DeviceId deviceId) {
-        srManager.routeService.getRouteTables().forEach(routeTableId -> {
-            srManager.routeService.getRoutes(routeTableId).forEach(routeInfo -> {
-                routeInfo.allRoutes().stream()
-                        .filter(resolvedRoute -> resolvedRoute.location() != null &&
-                                resolvedRoute.location().deviceId().equals(deviceId))
-                        .forEach(this::processRouteAddedInternal);
-            });
-        });
+        srManager.routeService.getRouteTables().forEach(routeTableId ->
+            srManager.routeService.getRoutes(routeTableId).forEach(routeInfo ->
+                routeInfo.allRoutes().forEach(resolvedRoute ->
+                    srManager.nextHopLocations(resolvedRoute).stream()
+                            .filter(location -> deviceId.equals(location.deviceId()))
+                            .forEach(location -> processRouteAddedInternal(resolvedRoute)
+                    )
+                )
+            )
+        );
     }
 
-    protected void processRouteAdded(RouteEvent event) {
+    void processRouteAdded(RouteEvent event) {
         log.info("processRouteAdded {}", event);
         processRouteAddedInternal(event.subject());
     }
@@ -67,7 +69,12 @@
         IpPrefix prefix = route.prefix();
         MacAddress nextHopMac = route.nextHopMac();
         VlanId nextHopVlan = route.nextHopVlan();
-        ConnectPoint location = route.location();
+        ConnectPoint location = srManager.nextHopLocations(route).stream().findFirst().orElse(null);
+
+        if (location == null) {
+            log.info("{} ignored. Cannot find nexthop location", prefix);
+            return;
+        }
 
         srManager.deviceConfiguration.addSubnet(location, prefix);
         // XXX need to handle the case where there are two connectpoints
@@ -77,13 +84,13 @@
                 nextHopMac, nextHopVlan, location.port());
     }
 
-    protected void processRouteUpdated(RouteEvent event) {
+    void processRouteUpdated(RouteEvent event) {
         log.info("processRouteUpdated {}", event);
         processRouteRemovedInternal(event.prevSubject());
         processRouteAddedInternal(event.subject());
     }
 
-    protected void processRouteRemoved(RouteEvent event) {
+    void processRouteRemoved(RouteEvent event) {
         log.info("processRouteRemoved {}", event);
         processRouteRemovedInternal(event.subject());
     }
@@ -97,7 +104,12 @@
         IpPrefix prefix = route.prefix();
         MacAddress nextHopMac = route.nextHopMac();
         VlanId nextHopVlan = route.nextHopVlan();
-        ConnectPoint location = route.location();
+        ConnectPoint location = srManager.nextHopLocations(route).stream().findFirst().orElse(null);
+
+        if (location == null) {
+            log.info("{} ignored. Cannot find nexthop location", prefix);
+            return;
+        }
 
         srManager.deviceConfiguration.removeSubnet(location, prefix);
         srManager.defaultRoutingHandler.revokeSubnet(ImmutableSet.of(prefix));
diff --git a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 39b901b..6be675a 100644
--- a/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -29,6 +29,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Collectors;
 
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -50,6 +51,8 @@
 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;
@@ -84,6 +87,7 @@
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.net.topology.PathService;
 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;
@@ -696,6 +700,19 @@
     }
 
     /**
+     * Returns locations of given resolved route.
+     *
+     * @param resolvedRoute resolved route
+     * @return locations of nexthop. Might be empty if next hop is not found
+     */
+    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
diff --git a/src/test/java/org/onosproject/segmentrouting/MockHostService.java b/src/test/java/org/onosproject/segmentrouting/MockHostService.java
index 1bd593c..e8c4701 100644
--- a/src/test/java/org/onosproject/segmentrouting/MockHostService.java
+++ b/src/test/java/org/onosproject/segmentrouting/MockHostService.java
@@ -18,6 +18,7 @@
 
 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;
@@ -36,4 +37,9 @@
     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/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java b/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
index fc8c294..383f800 100644
--- a/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
+++ b/src/test/java/org/onosproject/segmentrouting/RouteHandlerTest.java
@@ -25,11 +25,15 @@
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 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.config.NetworkConfigRegistryAdapter;
 import org.onosproject.net.flow.TrafficTreatment;
 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;
@@ -62,19 +66,24 @@
     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, CP1);
+    private static final ResolvedRoute RR1 = new ResolvedRoute(R1, M1, V1);
 
     private static final IpAddress N2 = IpAddress.valueOf("10.0.2.254");
     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:0000000000000001/2");
     private static final Route R2 = new Route(Route.Source.STATIC, P1, N2);
-    private static final ResolvedRoute RR2 = new ResolvedRoute(R2, M2, V2, CP2);
+    private static final ResolvedRoute RR2 = new ResolvedRoute(R2, M2, V2);
 
     private static final RouteInfo RI1 = new RouteInfo(P1, RR1, Sets.newHashSet(RR1));
 
+    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);
+
     // A set of hosts
-    private static final Set<Host> HOSTS = Sets.newHashSet();
+    private static final Set<Host> HOSTS = Sets.newHashSet(H1, H2);
     // A set of devices of which we have mastership
     private static final Set<DeviceId> LOCAL_DEVICES = Sets.newHashSet();
     // A set of interfaces
@@ -127,7 +136,11 @@
         routeHandler.init(CP1.deviceId());
 
         assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1)));
+        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));
     }
@@ -138,7 +151,11 @@
         routeHandler.processRouteAdded(re);
 
         assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(CP1.deviceId(), P1)));
+        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));
     }
@@ -151,7 +168,11 @@
         routeHandler.processRouteUpdated(re);
 
         assertEquals(1, ROUTING_TABLE.size());
-        assertNotNull(ROUTING_TABLE.get(new MockRoutingTableKey(CP2.deviceId(), P1)));
+        MockRoutingTableValue rtv2 = ROUTING_TABLE.get(new MockRoutingTableKey(CP1.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));
     }
@@ -166,5 +187,4 @@
         assertEquals(0, ROUTING_TABLE.size());
         assertEquals(0, SUBNET_TABLE.size());
     }
-
 }
\ No newline at end of file