Carry next hop VLAN in the resolved route

Also improve routes command to better display IPv6 routes

Change-Id: I72e78fc2a7ed19322c3b4281c7b93e19484f551e
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 5ea316e..f32ecd5 100644
--- a/cli/src/main/java/org/onosproject/cli/net/RoutesListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/RoutesListCommand.java
@@ -39,10 +39,12 @@
         description = "Lists routes in the route store")
 public class RoutesListCommand extends AbstractShellCommand {
 
-    private static final String FORMAT_HEADER =
-        "    Network            Next Hop        Source";
-    private static final String FORMAT_ROUTE =
-            "%-1s   %-18s %-15s %-10s";
+    private static final String NETWORK = "Network";
+    private static final String NEXTHOP = "Next Hop";
+    private static final String SOURCE = "Source";
+
+    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_TABLE = "Table: %s";
     private static final String FORMAT_TOTAL = "   Total: %d";
@@ -59,13 +61,19 @@
             print("%s", result);
         } else {
             service.getRouteTables().forEach(id -> {
-                print(FORMAT_TABLE, id);
-                print(FORMAT_HEADER);
                 Collection<RouteInfo> tableRoutes = service.getRoutes(id);
 
+                String format = tableRoutes.stream().anyMatch(route -> route.prefix().isIp6()) ?
+                        FORMAT_ROUTE6 : FORMAT_ROUTE;
+
+                // Print header
+                print(FORMAT_TABLE, id);
+                print(format, "", NETWORK, NEXTHOP, SOURCE);
+
+                // Print routing entries
                 tableRoutes.stream()
                         .sorted(Comparator.comparing(r -> r.prefix().address()))
-                        .forEach(this::print);
+                        .forEach(route -> this.print(format, route));
 
                 print(FORMAT_TOTAL, tableRoutes.size());
                 print("");
@@ -73,9 +81,9 @@
         }
     }
 
-    private void print(RouteInfo routeInfo) {
+    private void print(String format, RouteInfo routeInfo) {
         routeInfo.allRoutes()
-                .forEach(r -> print(FORMAT_ROUTE, isBestRoute(routeInfo.bestRoute(), r) ? ">" : "",
+                .forEach(r -> print(format, isBestRoute(routeInfo.bestRoute(), r) ? ">" : "",
                         r.prefix(), r.nextHop(), Route.Source.UNDEFINED));
     }
 
diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/routing/ResolvedRoute.java b/incubator/api/src/main/java/org/onosproject/incubator/net/routing/ResolvedRoute.java
index d6cb4b4..02d4e95 100644
--- a/incubator/api/src/main/java/org/onosproject/incubator/net/routing/ResolvedRoute.java
+++ b/incubator/api/src/main/java/org/onosproject/incubator/net/routing/ResolvedRoute.java
@@ -19,6 +19,7 @@
 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 java.util.Objects;
@@ -33,6 +34,7 @@
     private final IpPrefix prefix;
     private final IpAddress nextHop;
     private final MacAddress nextHopMac;
+    private final VlanId nextHopVlan;
     private final ConnectPoint location;
 
     /**
@@ -43,10 +45,7 @@
      * @param location connect point where the next hop connects to
      */
     public ResolvedRoute(Route route, MacAddress nextHopMac, ConnectPoint location) {
-        this.prefix = route.prefix();
-        this.nextHop = route.nextHop();
-        this.nextHopMac = nextHopMac;
-        this.location = location;
+        this(route, nextHopMac, VlanId.NONE, location);
     }
 
     /**
@@ -59,9 +58,41 @@
      */
     public ResolvedRoute(IpPrefix prefix, IpAddress nextHop, MacAddress nextHopMac,
                          ConnectPoint location) {
+        this(prefix, nextHop, nextHopMac, VlanId.NONE, location);
+    }
+
+    /**
+     * Creates a new resolved route.
+     *
+     * @param route input route
+     * @param nextHopMac next hop MAC address
+     * @param nextHopVlan next hop VLAN ID
+     * @param location connect point where the next hop connects to
+     */
+    public ResolvedRoute(Route route, MacAddress nextHopMac, VlanId nextHopVlan,
+                         ConnectPoint location) {
+        this.prefix = route.prefix();
+        this.nextHop = route.nextHop();
+        this.nextHopMac = nextHopMac;
+        this.nextHopVlan = nextHopVlan;
+        this.location = location;
+    }
+
+    /**
+     * Creates a new resolved route.
+     *
+     * @param prefix route prefix
+     * @param nextHop route next hop IP address
+     * @param nextHopMac next hop MAC address
+     * @param nextHopVlan next hop VLAN address
+     * @param location connect point where the next hop connects to
+     */
+    public ResolvedRoute(IpPrefix prefix, IpAddress nextHop, MacAddress nextHopMac,
+                         VlanId nextHopVlan, ConnectPoint location) {
         this.prefix = prefix;
         this.nextHop = nextHop;
         this.nextHopMac = nextHopMac;
+        this.nextHopVlan = nextHopVlan;
         this.location = location;
     }
 
@@ -93,6 +124,15 @@
     }
 
     /**
+     * Returns the next hop VLAN ID.
+     *
+     * @return VLAN ID
+     */
+    public VlanId nextHopVlan() {
+        return nextHopVlan;
+    }
+
+    /**
      * Returns the next hop location.
      *
      * @return connect point where the next hop attaches to
@@ -103,7 +143,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(prefix, nextHop, nextHopMac, location);
+        return Objects.hash(prefix, nextHop, nextHopMac, nextHopVlan, location);
     }
 
     @Override
@@ -121,6 +161,7 @@
         return Objects.equals(this.prefix, that.prefix) &&
                 Objects.equals(this.nextHop, that.nextHop) &&
                 Objects.equals(this.nextHopMac, that.nextHopMac) &&
+                Objects.equals(this.nextHopVlan, that.nextHopVlan) &&
                 Objects.equals(this.location, that.location);
     }
 
@@ -130,6 +171,7 @@
                 .add("prefix", prefix)
                 .add("nextHop", nextHop)
                 .add("nextHopMac", nextHopMac)
+                .add("nextHopVlan", nextHopVlan)
                 .add("location", location)
                 .toString();
     }
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 dab0418..5b637b8 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
@@ -251,7 +251,8 @@
 
         Optional<Host> host = hosts.stream().findFirst();
         if (host.isPresent()) {
-            return new ResolvedRoute(route, host.get().mac(), host.get().location());
+            return new ResolvedRoute(route, host.get().mac(), host.get().vlan(),
+                    host.get().location());
         } else {
             return null;
         }