CORD-508 SegmentRouting / vRouter integration

- Added excludePorts config to SegmentRouting
    SR does not push VLAN filtering rule to excluded ports
    SR ignores hosts learned from excluded ports
- Use separate default route config
    Don't need to config 0/0 on the interface anymore

Change-Id: Iea75d60c2d5f5368e79652b1bf192a6ced49030d
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index 10ac6d6..271ffa0 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -471,8 +471,9 @@
         }
 
         for (Port port : srManager.deviceService.getPorts(deviceId)) {
-            if (port.number().toLong() > 0 &&
-                    port.number().toLong() < SegmentRoutingService.OFPP_MAX &&
+            ConnectPoint cp = new ConnectPoint(deviceId, port.number());
+            // TODO: Handles dynamic port events when we are ready for dynamic config
+            if (!srManager.deviceConfiguration.excludedPorts().contains(cp) &&
                     port.isEnabled()) {
                 Ip4Prefix portSubnet = config.getPortSubnet(deviceId, port.number());
                 VlanId assignedVlan = (portSubnet == null)
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 32e03de..03b4f40 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -814,7 +814,8 @@
          * Reads network config and initializes related data structure accordingly.
          */
         public void configureNetwork() {
-            deviceConfiguration = new DeviceConfiguration(segmentRoutingManager.cfgService);
+            deviceConfiguration = new DeviceConfiguration(appId,
+                    segmentRoutingManager.cfgService);
 
             arpHandler = new ArpHandler(segmentRoutingManager);
             icmpHandler = new IcmpHandler(segmentRoutingManager);
@@ -987,22 +988,25 @@
             Set<IpAddress> ips = event.subject().ipAddresses();
             log.info("Host {}/{} is added at {}:{}", mac, vlanId, deviceId, port);
 
-            // Populate bridging table entry
-            log.debug("Populate L2 table entry for host {} at {}:{}",
-                      mac, deviceId, port);
-            ForwardingObjective.Builder fob =
-                    getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
-            flowObjectiveService.forward(deviceId, fob.add(
-                    new BridgingTableObjectiveContext(mac, vlanId)
-            ));
+            if (!deviceConfiguration.excludedPorts()
+                    .contains(new ConnectPoint(deviceId, port))) {
+                // Populate bridging table entry
+                log.debug("Populate L2 table entry for host {} at {}:{}",
+                          mac, deviceId, port);
+                ForwardingObjective.Builder fob =
+                        getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
+                flowObjectiveService.forward(deviceId, fob.add(
+                        new BridgingTableObjectiveContext(mac, vlanId)
+                ));
 
-            // Populate IP table entry
-            ips.forEach(ip -> {
-                if (ip.isIp4()) {
-                    routingRulePopulator.populateIpRuleForHost(
-                            deviceId, ip.getIp4Address(), mac, port);
-                }
-            });
+                // Populate IP table entry
+                ips.forEach(ip -> {
+                    if (ip.isIp4()) {
+                        routingRulePopulator.populateIpRuleForHost(
+                                deviceId, ip.getIp4Address(), mac, port);
+                    }
+                });
+            }
         }
 
         private void processHostRemoveEvent(HostEvent event) {
@@ -1013,20 +1017,23 @@
             Set<IpAddress> ips = event.subject().ipAddresses();
             log.debug("Host {}/{} is removed from {}:{}", mac, vlanId, deviceId, port);
 
-            // Revoke bridging table entry
-            ForwardingObjective.Builder fob =
-                    getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
-            flowObjectiveService.forward(deviceId, fob.remove(
-                    new BridgingTableObjectiveContext(mac, vlanId)
-            ));
+            if (!deviceConfiguration.excludedPorts()
+                    .contains(new ConnectPoint(deviceId, port))) {
+                // Revoke bridging table entry
+                ForwardingObjective.Builder fob =
+                        getForwardingObjectiveBuilder(deviceId, mac, vlanId, port);
+                flowObjectiveService.forward(deviceId, fob.remove(
+                        new BridgingTableObjectiveContext(mac, vlanId)
+                ));
 
-            // Revoke IP table entry
-            ips.forEach(ip -> {
-                if (ip.isIp4()) {
-                    routingRulePopulator.revokeIpRuleForHost(
-                            deviceId, ip.getIp4Address(), mac, port);
-                }
-            });
+                // Revoke IP table entry
+                ips.forEach(ip -> {
+                    if (ip.isIp4()) {
+                        routingRulePopulator.revokeIpRuleForHost(
+                                deviceId, ip.getIp4Address(), mac, port);
+                    }
+                });
+            }
         }
 
         private void processHostMovedEvent(HostEvent event) {
@@ -1041,35 +1048,41 @@
             log.debug("Host {}/{} is moved from {}:{} to {}:{}",
                     mac, vlanId, prevDeviceId, prevPort, newDeviceId, newPort);
 
-            // Revoke previous bridging table entry
-            ForwardingObjective.Builder prevFob =
-                    getForwardingObjectiveBuilder(prevDeviceId, mac, vlanId, prevPort);
-            flowObjectiveService.forward(prevDeviceId, prevFob.remove(
-                    new BridgingTableObjectiveContext(mac, vlanId)
-            ));
+            if (!deviceConfiguration.excludedPorts()
+                    .contains(new ConnectPoint(prevDeviceId, prevPort))) {
+                // Revoke previous bridging table entry
+                ForwardingObjective.Builder prevFob =
+                        getForwardingObjectiveBuilder(prevDeviceId, mac, vlanId, prevPort);
+                flowObjectiveService.forward(prevDeviceId, prevFob.remove(
+                        new BridgingTableObjectiveContext(mac, vlanId)
+                ));
 
-            // Revoke previous IP table entry
-            prevIps.forEach(ip -> {
-                if (ip.isIp4()) {
-                    routingRulePopulator.revokeIpRuleForHost(
-                            prevDeviceId, ip.getIp4Address(), mac, prevPort);
-                }
-            });
+                // Revoke previous IP table entry
+                prevIps.forEach(ip -> {
+                    if (ip.isIp4()) {
+                        routingRulePopulator.revokeIpRuleForHost(
+                                prevDeviceId, ip.getIp4Address(), mac, prevPort);
+                    }
+                });
+            }
 
-            // Populate new bridging table entry
-            ForwardingObjective.Builder newFob =
-                    getForwardingObjectiveBuilder(newDeviceId, mac, vlanId, newPort);
-            flowObjectiveService.forward(newDeviceId, newFob.add(
-                    new BridgingTableObjectiveContext(mac, vlanId)
-            ));
+            if (!deviceConfiguration.excludedPorts()
+                    .contains(new ConnectPoint(newDeviceId, newPort))) {
+                // Populate new bridging table entry
+                ForwardingObjective.Builder newFob =
+                        getForwardingObjectiveBuilder(newDeviceId, mac, vlanId, newPort);
+                flowObjectiveService.forward(newDeviceId, newFob.add(
+                        new BridgingTableObjectiveContext(mac, vlanId)
+                ));
 
-            // Populate new IP table entry
-            newIps.forEach(ip -> {
-                if (ip.isIp4()) {
-                    routingRulePopulator.populateIpRuleForHost(
-                            newDeviceId, ip.getIp4Address(), mac, newPort);
-                }
-            });
+                // Populate new IP table entry
+                newIps.forEach(ip -> {
+                    if (ip.isIp4()) {
+                        routingRulePopulator.populateIpRuleForHost(
+                                newDeviceId, ip.getIp4Address(), mac, newPort);
+                    }
+                });
+            }
         }
 
         private void processHostUpdatedEvent(HostEvent event) {
@@ -1083,21 +1096,27 @@
             Set<IpAddress> newIps = event.subject().ipAddresses();
             log.debug("Host {}/{} is updated", mac, vlanId);
 
-            // Revoke previous IP table entry
-            prevIps.forEach(ip -> {
-                if (ip.isIp4()) {
-                    routingRulePopulator.revokeIpRuleForHost(
-                            prevDeviceId, ip.getIp4Address(), mac, prevPort);
-                }
-            });
+            if (!deviceConfiguration.excludedPorts()
+                    .contains(new ConnectPoint(prevDeviceId, prevPort))) {
+                // Revoke previous IP table entry
+                prevIps.forEach(ip -> {
+                    if (ip.isIp4()) {
+                        routingRulePopulator.revokeIpRuleForHost(
+                                prevDeviceId, ip.getIp4Address(), mac, prevPort);
+                    }
+                });
+            }
 
-            // Populate new IP table entry
-            newIps.forEach(ip -> {
-                if (ip.isIp4()) {
-                    routingRulePopulator.populateIpRuleForHost(
-                            newDeviceId, ip.getIp4Address(), mac, newPort);
-                }
-            });
+            if (!deviceConfiguration.excludedPorts()
+                    .contains(new ConnectPoint(newDeviceId, newPort))) {
+                // Populate new IP table entry
+                newIps.forEach(ip -> {
+                    if (ip.isIp4()) {
+                        routingRulePopulator.populateIpRuleForHost(
+                                newDeviceId, ip.getIp4Address(), mac, newPort);
+                    }
+                });
+            }
         }
 
         @Override
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index db4bc63..f222bb3 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -18,11 +18,13 @@
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.SetMultimap;
+import com.google.common.collect.Sets;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.incubator.net.config.basics.ConfigException;
 import org.onosproject.incubator.net.config.basics.InterfaceConfig;
 import org.onosproject.incubator.net.intf.Interface;
@@ -51,11 +53,12 @@
  */
 public class DeviceConfiguration implements DeviceProperties {
 
-    private static final Logger log = LoggerFactory
-            .getLogger(DeviceConfiguration.class);
+    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 final Map<VlanId, List<ConnectPoint>> xConnects = new ConcurrentHashMap<>();
+    private final Set<ConnectPoint> excludedPorts = Sets.newConcurrentHashSet();
+    private SegmentRoutingAppConfig appConfig;
 
     private class SegmentRouterInfo {
         int nodeSid;
@@ -77,9 +80,11 @@
      * Constructs device configuration for all Segment Router devices,
      * organizing the data into various maps for easier access.
      *
+     * @param appId application id
      * @param cfgService config service
      */
-    public DeviceConfiguration(NetworkConfigRegistry cfgService) {
+    public DeviceConfiguration(ApplicationId appId,
+            NetworkConfigRegistry cfgService) {
         // Read config from device subject, excluding gatewayIps and subnets.
         Set<DeviceId> deviceSubjects =
                 cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
@@ -98,6 +103,11 @@
             allSegmentIds.add(info.nodeSid);
         });
 
+        // Read excluded port names from config
+        appConfig = cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
+        Set<String> excludePorts = (appConfig != null) ?
+                appConfig.excludePorts() : ImmutableSet.of();
+
         // Read gatewayIps and subnets from port subject.
         Set<ConnectPoint> portSubjects =
             cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class);
@@ -112,6 +122,12 @@
                 return;
             }
             networkInterfaces.forEach(networkInterface -> {
+                // Do not process excluded ports
+                if (excludePorts.contains(networkInterface.name())) {
+                    excludedPorts.add(subject);
+                    return;
+                }
+
                 VlanId vlanId = networkInterface.vlan();
                 ConnectPoint connectPoint = networkInterface.connectPoint();
                 DeviceId dpid = connectPoint.deviceId();
@@ -343,7 +359,13 @@
         if (srinfo != null) {
             log.trace("getSubnets for device{} is {}", deviceId,
                       srinfo.subnets.values());
-            return ImmutableSet.copyOf(srinfo.subnets.values());
+
+            ImmutableSet.Builder<Ip4Prefix> builder = ImmutableSet.builder();
+            builder.addAll(srinfo.subnets.values());
+            if (deviceId.equals(appConfig.vRouterId())) {
+                builder.add(Ip4Prefix.valueOf("0.0.0.0/0"));
+            }
+            return builder.build();
         }
         return null;
     }
@@ -464,4 +486,13 @@
         SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
         return srinfo != null && srinfo.adjacencySids.containsKey(sid);
     }
+
+    /**
+     * Returns a set of excluded ports.
+     *
+     * @return excluded ports
+     */
+    public Set<ConnectPoint> excludedPorts() {
+        return excludedPorts;
+    }
 }
\ No newline at end of file
diff --git a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
index e39fb18..163cc1c 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfig.java
@@ -21,6 +21,7 @@
 import com.google.common.collect.ImmutableSet;
 import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.config.Config;
 import java.util.Set;
 
@@ -31,10 +32,14 @@
  */
 public class SegmentRoutingAppConfig extends Config<ApplicationId> {
     private static final String VROUTER_MACS = "vRouterMacs";
+    private static final String VROUTER_ID = "vRouterId";
+    private static final String EXCLUDE_PORTS = "excludePorts";
 
     @Override
     public boolean isValid() {
-        return hasOnlyFields(VROUTER_MACS) && vRouterMacs() != null;
+        return hasOnlyFields(VROUTER_MACS, VROUTER_ID, EXCLUDE_PORTS) &&
+                vRouterMacs() != null && vRouterId() != null &&
+                excludePorts() != null;
     }
 
     /**
@@ -88,10 +93,84 @@
         return this;
     }
 
+    /**
+     * Gets vRouter device ID.
+     *
+     * @return vRouter device ID, or null if not valid
+     */
+    public DeviceId vRouterId() {
+        if (!object.has(VROUTER_ID)) {
+            return null;
+        }
+
+        try {
+            return DeviceId.deviceId(object.path(VROUTER_ID).asText());
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Sets vRouter device ID.
+     *
+     * @param vRouterId vRouter device ID
+     * @return this {@link SegmentRoutingAppConfig}
+     */
+    public SegmentRoutingAppConfig setVRouterId(DeviceId vRouterId) {
+        if (vRouterId == null) {
+            object.remove(VROUTER_ID);
+        } else {
+            object.put(VROUTER_ID, vRouterId.toString());
+        }
+        return this;
+    }
+
+    /**
+     * Gets names of ports that are ignored by SegmentRouting.
+     *
+     * @return set of port names
+     */
+    public Set<String> excludePorts() {
+        if (!object.has(EXCLUDE_PORTS)) {
+            return null;
+        }
+
+        ImmutableSet.Builder<String> builder = ImmutableSet.builder();
+        ArrayNode arrayNode = (ArrayNode) object.path(EXCLUDE_PORTS);
+        for (JsonNode jsonNode : arrayNode) {
+            String portName = jsonNode.asText(null);
+            if (portName == null) {
+                return null;
+            }
+            builder.add(portName);
+        }
+        return builder.build();
+    }
+
+    /**
+     * Sets names of ports that are ignored by SegmentRouting.
+     *
+     * @paran excludePorts names of ports that are ignored by SegmentRouting
+     * @return this {@link SegmentRoutingAppConfig}
+     */
+    public SegmentRoutingAppConfig setExcludePorts(Set<String> excludePorts) {
+        if (excludePorts == null) {
+            object.remove(EXCLUDE_PORTS);
+        } else {
+            ArrayNode arrayNode = mapper.createArrayNode();
+            excludePorts.forEach(portName -> {
+                arrayNode.add(portName);
+            });
+            object.set(EXCLUDE_PORTS, arrayNode);
+        }
+        return this;
+    }
+
     @Override
     public String toString() {
         return toStringHelper(this)
                 .add("vRouterMacs", vRouterMacs())
+                .add("excludePorts", excludePorts())
                 .toString();
     }
 }