Carry information about source ONOS node in each route.

Change-Id: I4877a048771935e3dbe310272e0b82c77b8678af
diff --git a/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpRouteSelector.java b/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpRouteSelector.java
index 96f3a92..2c73dc0 100644
--- a/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpRouteSelector.java
+++ b/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpRouteSelector.java
@@ -17,6 +17,7 @@
 package org.onosproject.routing.bgp;
 
 import org.onlab.packet.IpPrefix;
+import org.onosproject.cluster.ClusterService;
 import org.onosproject.incubator.net.routing.Route;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,14 +33,17 @@
         LoggerFactory.getLogger(BgpRouteSelector.class);
 
     private BgpSessionManager bgpSessionManager;
+    private ClusterService clusterService;
 
     /**
      * Constructor.
      *
      * @param bgpSessionManager the BGP Session Manager to use
+     * @param clusterService the cluster service
      */
-    BgpRouteSelector(BgpSessionManager bgpSessionManager) {
+    BgpRouteSelector(BgpSessionManager bgpSessionManager, ClusterService clusterService) {
         this.bgpSessionManager = bgpSessionManager;
+        this.clusterService = clusterService;
     }
 
     /**
@@ -82,7 +86,7 @@
                                            Collection<Route> withdraws) {
         if (routeUpdate != null) {
             Route route = new Route(Route.Source.BGP, routeUpdate.routeEntry().prefix(),
-                    routeUpdate.routeEntry().nextHop());
+                    routeUpdate.routeEntry().nextHop(), clusterService.getLocalNode().id());
             if (routeUpdate.type().equals(RouteUpdate.Type.UPDATE)) {
                 updates.add(route);
             } else if (routeUpdate.type().equals(RouteUpdate.Type.DELETE)) {
diff --git a/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpSessionManager.java b/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpSessionManager.java
index a8cf5d8..e9e851b 100644
--- a/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpSessionManager.java
+++ b/apps/routing/common/src/main/java/org/onosproject/routing/bgp/BgpSessionManager.java
@@ -37,6 +37,7 @@
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.Ip6Prefix;
 import org.onlab.packet.IpPrefix;
+import org.onosproject.cluster.ClusterService;
 import org.onosproject.incubator.net.routing.Route;
 import org.onosproject.incubator.net.routing.RouteAdminService;
 import org.osgi.service.component.ComponentContext;
@@ -66,6 +67,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected RouteAdminService routeService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ClusterService clusterService;
+
     boolean isShutdown = true;
     private Channel serverChannel;     // Listener for incoming BGP connections
     private ServerBootstrap serverBootstrap;
@@ -74,7 +78,7 @@
             new ConcurrentHashMap<>();
     private Ip4Address myBgpId;        // Same BGP ID for all peers
 
-    private BgpRouteSelector bgpRouteSelector = new BgpRouteSelector(this);
+    private BgpRouteSelector bgpRouteSelector;
     private ConcurrentMap<Ip4Prefix, BgpRouteEntry> bgpRoutes4 =
             new ConcurrentHashMap<>();
     private ConcurrentMap<Ip6Prefix, BgpRouteEntry> bgpRoutes6 =
@@ -85,6 +89,7 @@
 
     @Activate
     protected void activate(ComponentContext context) {
+        bgpRouteSelector = new BgpRouteSelector(this, clusterService);
         readComponentConfiguration(context);
         start();
         log.info("BgpSessionManager started");
diff --git a/apps/routing/common/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java b/apps/routing/common/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java
index 9f9ad3c..3bbad0c 100644
--- a/apps/routing/common/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java
+++ b/apps/routing/common/src/test/java/org/onosproject/routing/bgp/BgpSessionManagerTest.java
@@ -34,6 +34,10 @@
 import org.onlab.junit.TestUtils.TestUtilsException;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
+import org.onlab.packet.IpAddress;
+import org.onosproject.cluster.ClusterService;
+import org.onosproject.cluster.DefaultControllerNode;
+import org.onosproject.cluster.NodeId;
 import org.onosproject.incubator.net.routing.RouteAdminService;
 import org.osgi.service.component.ComponentContext;
 
@@ -80,6 +84,9 @@
     private static final long DEFAULT_MULTI_EXIT_DISC = 20;
     private static final long BETTER_MULTI_EXIT_DISC = 30;
 
+    private static final NodeId NODE_ID = new NodeId("local");
+    private static final IpAddress LOCAL = IpAddress.valueOf("127.0.0.1");
+
     BgpRouteEntry.AsPath asPathShort;
     BgpRouteEntry.AsPath asPathLong;
 
@@ -255,6 +262,12 @@
         replay(routeService);
         bgpSessionManager.routeService = routeService;
 
+        ClusterService clusterService = createMock(ClusterService.class);
+        expect(clusterService.getLocalNode())
+                .andReturn(new DefaultControllerNode(NODE_ID, LOCAL)).anyTimes();
+        replay(clusterService);
+        bgpSessionManager.clusterService = clusterService;
+
         // NOTE: We use port 0 to bind on any available port
         ComponentContext componentContext = createMock(ComponentContext.class);
         getDictionaryMock(componentContext);
diff --git a/apps/routing/fibinstaller/src/main/java/org/onosproject/routing/fibinstaller/FibInstaller.java b/apps/routing/fibinstaller/src/main/java/org/onosproject/routing/fibinstaller/FibInstaller.java
index bbf2c36..18e28c3 100644
--- a/apps/routing/fibinstaller/src/main/java/org/onosproject/routing/fibinstaller/FibInstaller.java
+++ b/apps/routing/fibinstaller/src/main/java/org/onosproject/routing/fibinstaller/FibInstaller.java
@@ -267,7 +267,7 @@
     }
 
     private synchronized void deleteRoute(ResolvedRoute route) {
-        deleteRoute(new Route(Route.Source.UNDEFINED, route.prefix(), route.nextHop()));
+        deleteRoute(route.route());
     }
 
     private void deleteRoute(Route route) {
diff --git a/apps/routing/fpm/src/main/java/org/onosproject/routing/fpm/FpmManager.java b/apps/routing/fpm/src/main/java/org/onosproject/routing/fpm/FpmManager.java
index c85cd9c..4f7e488 100644
--- a/apps/routing/fpm/src/main/java/org/onosproject/routing/fpm/FpmManager.java
+++ b/apps/routing/fpm/src/main/java/org/onosproject/routing/fpm/FpmManager.java
@@ -244,7 +244,7 @@
                 // We ignore interface routes with no gateway for now.
                 return;
             }
-            route = new Route(Route.Source.FPM, prefix, gateway);
+            route = new Route(Route.Source.FPM, prefix, gateway, clusterService.getLocalNode().id());
 
 
             Route oldRoute = fpmRoutes.get(peer).put(prefix, route);
@@ -262,7 +262,7 @@
                 return;
             }
 
-            route = new Route(Route.Source.FPM, prefix, existing.nextHop());
+            route = new Route(Route.Source.FPM, prefix, existing.nextHop(), clusterService.getLocalNode().id());
 
             withdraws.add(route);
             break;
diff --git a/cli/src/main/java/org/onosproject/cli/net/RoutesListCommand.java b/cli/src/main/java/org/onosproject/cli/net/RoutesListCommand.java
index b24ee5d..afb405d 100644
--- a/cli/src/main/java/org/onosproject/cli/net/RoutesListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/RoutesListCommand.java
@@ -41,9 +41,10 @@
     private static final String NETWORK = "Network";
     private static final String NEXTHOP = "Next Hop";
     private static final String SOURCE = "Source";
+    private static final String NODE = "Node";
 
-    private static final String FORMAT_ROUTE = "%-1s   %-18s %-15s %-10s";
-    private static final String FORMAT_ROUTE6 = "%-1s   %-43s %-39s %-10s";
+    private static final String FORMAT_ROUTE = "%-1s   %-18s %-15s %s (%s)";
+    private static final String FORMAT_ROUTE6 = "%-1s   %-43s %-39s %s (%s)";
 
     private static final String FORMAT_TABLE = "Table: %s";
     private static final String FORMAT_TOTAL = "   Total: %d";
@@ -67,7 +68,7 @@
 
                 // Print header
                 print(FORMAT_TABLE, id);
-                print(format, "", NETWORK, NEXTHOP, SOURCE);
+                print(format, "", NETWORK, NEXTHOP, SOURCE, NODE);
 
                 // Print routing entries
                 tableRoutes.stream()
@@ -84,11 +85,9 @@
         routeInfo.allRoutes().stream()
                 .sorted(Comparator.comparing(r -> r.nextHop()))
                 .forEach(r -> print(format, isBestRoute(routeInfo.bestRoute(), r) ? ">" : "",
-                        r.prefix(), r.nextHop(), r.route().source()));
+                        r.prefix(), r.nextHop(), r.route().source(), r.route().sourceNode()));
     }
 
-
-
     private boolean isBestRoute(Optional<ResolvedRoute> bestRoute, ResolvedRoute route) {
         return Objects.equals(bestRoute.orElse(null), route);
     }
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/routing/Route.java b/incubator/api/src/main/java/org/onosproject/incubator/net/routing/Route.java
index 0ae4952..31fe8b7 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/routing/Route.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/routing/Route.java
@@ -18,6 +18,7 @@
 
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.IpPrefix;
+import org.onosproject.cluster.NodeId;
 
 import java.util.Objects;
 
@@ -33,6 +34,8 @@
     private static final String VERSION_MISMATCH =
             "Prefix and next hop must be in the same address family";
 
+    private static final NodeId UNDEFINED = new NodeId("-");
+
     /**
      * Source of the route.
      */
@@ -61,15 +64,28 @@
     private final Source source;
     private final IpPrefix prefix;
     private final IpAddress nextHop;
+    private final NodeId sourceNode;
 
     /**
      * Creates a route.
      *
      * @param source route source
      * @param prefix IP prefix
-     * @param nextHop net hop IP address
+     * @param nextHop next hop IP address
      */
     public Route(Source source, IpPrefix prefix, IpAddress nextHop) {
+        this(source, prefix, nextHop, UNDEFINED);
+    }
+
+    /**
+     * Creates a route.
+     *
+     * @param source route source
+     * @param prefix IP prefix
+     * @param nextHop next hop IP address
+     * @param sourceNode ONOS node the route was sourced from
+     */
+    public Route(Source source, IpPrefix prefix, IpAddress nextHop, NodeId sourceNode) {
         checkNotNull(prefix);
         checkNotNull(nextHop);
         checkArgument(prefix.version().equals(nextHop.version()), VERSION_MISMATCH);
@@ -77,6 +93,7 @@
         this.source = checkNotNull(source);
         this.prefix = prefix;
         this.nextHop = nextHop;
+        this.sourceNode = checkNotNull(sourceNode);
     }
 
     /**
@@ -106,6 +123,15 @@
         return nextHop;
     }
 
+    /**
+     * Returns the ONOS node the route was sourced from.
+     *
+     * @return ONOS node ID
+     */
+    public NodeId sourceNode() {
+        return sourceNode;
+    }
+
     @Override
     public int hashCode() {
         return Objects.hash(prefix, nextHop);
diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/routing/impl/RouteManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/routing/impl/RouteManager.java
index 539b2cb..6ab1f03 100644
--- a/incubator/net/src/main/java/org/onosproject/incubator/net/routing/impl/RouteManager.java
+++ b/incubator/net/src/main/java/org/onosproject/incubator/net/routing/impl/RouteManager.java
@@ -204,7 +204,7 @@
     @Override
     public Route longestPrefixMatch(IpAddress ip) {
         return longestPrefixLookup(ip)
-                .map(r -> new Route(Route.Source.STATIC, r.prefix(), r.nextHop()))
+                .map(ResolvedRoute::route)
                 .orElse(null);
     }