Determines the subnet of given port by checking the interface config
directly

Previously, SR checked the subnet data structure which contains
multiple subnets now in order to support generic routing

Change-Id: Ib6e4b107583d9fd1bca248b31a6c437236560199
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 6c25b7d..bd89102 100644
--- a/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -33,6 +33,7 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.event.Event;
 import org.onosproject.incubator.net.config.basics.McastConfig;
+import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
@@ -113,8 +114,7 @@
 @Component(immediate = true)
 public class SegmentRoutingManager implements SegmentRoutingService {
 
-    private static Logger log = LoggerFactory
-            .getLogger(SegmentRoutingManager.class);
+    private static Logger log = LoggerFactory.getLogger(SegmentRoutingManager.class);
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CoreService coreService;
@@ -141,7 +141,7 @@
     protected StorageService storageService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected NetworkConfigRegistry cfgService;
+    public NetworkConfigRegistry cfgService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected ComponentConfigService compCfgService;
@@ -155,11 +155,14 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected CordConfigService cordConfigService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    public InterfaceService interfaceService;
+
     protected ArpHandler arpHandler = null;
     protected IcmpHandler icmpHandler = null;
     protected IpHandler ipHandler = null;
     protected RoutingRulePopulator routingRulePopulator = null;
-    protected ApplicationId appId;
+    public ApplicationId appId;
     protected DeviceConfiguration deviceConfiguration = null;
 
     protected DefaultRoutingHandler defaultRoutingHandler = null;
@@ -256,7 +259,7 @@
     /**
      * Segment Routing App ID.
      */
-    public static final String SR_APP_ID = "org.onosproject.segmentrouting";
+    public static final String APP_NAME = "org.onosproject.segmentrouting";
     /**
      * The starting value of per-subnet VLAN ID assignment.
      */
@@ -268,7 +271,7 @@
 
     @Activate
     protected void activate() {
-        appId = coreService.registerApplication(SR_APP_ID);
+        appId = coreService.registerApplication(APP_NAME);
 
         log.debug("Creating EC map nsnextobjectivestore");
         EventuallyConsistentMapBuilder<NeighborSetNextObjectiveStoreKey, Integer>
@@ -894,29 +897,28 @@
     }
 
     private class InternalConfigListener implements NetworkConfigListener {
-        SegmentRoutingManager segmentRoutingManager;
+        SegmentRoutingManager srManager;
 
         /**
          * Constructs the internal network config listener.
          *
-         * @param srMgr segment routing manager
+         * @param srManager segment routing manager
          */
-        public InternalConfigListener(SegmentRoutingManager srMgr) {
-            this.segmentRoutingManager = srMgr;
+        public InternalConfigListener(SegmentRoutingManager srManager) {
+            this.srManager = srManager;
         }
 
         /**
          * Reads network config and initializes related data structure accordingly.
          */
         public void configureNetwork() {
-            deviceConfiguration = new DeviceConfiguration(appId,
-                    segmentRoutingManager.cfgService);
+            deviceConfiguration = new DeviceConfiguration(srManager);
 
-            arpHandler = new ArpHandler(segmentRoutingManager);
-            icmpHandler = new IcmpHandler(segmentRoutingManager);
-            ipHandler = new IpHandler(segmentRoutingManager);
-            routingRulePopulator = new RoutingRulePopulator(segmentRoutingManager);
-            defaultRoutingHandler = new DefaultRoutingHandler(segmentRoutingManager);
+            arpHandler = new ArpHandler(srManager);
+            icmpHandler = new IcmpHandler(srManager);
+            ipHandler = new IpHandler(srManager);
+            routingRulePopulator = new RoutingRulePopulator(srManager);
+            defaultRoutingHandler = new DefaultRoutingHandler(srManager);
 
             tunnelHandler = new TunnelHandler(linkService, deviceConfiguration,
                                               groupHandlerMap, tunnelStore);
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 c1af6f2..15e9fa7 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
@@ -23,16 +23,14 @@
 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;
 import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.config.NetworkConfigRegistry;
-import org.onosproject.net.config.NetworkConfigService;
 import org.onosproject.net.host.InterfaceIpAddress;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
+import org.onosproject.segmentrouting.SegmentRoutingManager;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -41,9 +39,9 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -54,11 +52,14 @@
  */
 public class DeviceConfiguration implements DeviceProperties {
 
+    private static final String ERROR_CONFIG = "Configuration error.";
+    private static final String TOO_MANY_SUBNET = ERROR_CONFIG + " Too many subnets configured on {}";
+    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 ApplicationId appId;
-    private NetworkConfigService cfgService;
+    private SegmentRoutingManager srManager;
 
     private class SegmentRouterInfo {
         int nodeSid;
@@ -80,20 +81,17 @@
      * 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
+     * @param srManager Segment Routing Manager
      */
-    public DeviceConfiguration(ApplicationId appId,
-            NetworkConfigRegistry cfgService) {
-        this.appId = appId;
-        this.cfgService = cfgService;
+    public DeviceConfiguration(SegmentRoutingManager srManager) {
+        this.srManager = srManager;
 
         // Read config from device subject, excluding gatewayIps and subnets.
         Set<DeviceId> deviceSubjects =
-                cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
+                srManager.cfgService.getSubjects(DeviceId.class, SegmentRoutingDeviceConfig.class);
         deviceSubjects.forEach(subject -> {
             SegmentRoutingDeviceConfig config =
-                cfgService.getConfig(subject, SegmentRoutingDeviceConfig.class);
+                    srManager.cfgService.getConfig(subject, SegmentRoutingDeviceConfig.class);
             SegmentRouterInfo info = new SegmentRouterInfo();
             info.deviceId = subject;
             info.nodeSid = config.nodeSid();
@@ -106,20 +104,12 @@
             allSegmentIds.add(info.nodeSid);
         });
 
-        // Read gatewayIps and subnets from port subject.
-        Set<ConnectPoint> portSubjects =
-            cfgService.getSubjects(ConnectPoint.class, InterfaceConfig.class);
-        portSubjects.forEach(subject -> {
-            // Do not process excluded ports
-            SegmentRoutingAppConfig appConfig =
-                    cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
-            if (appConfig != null && appConfig.suppressSubnet().contains(subject)) {
-                log.info("Ignore suppressed port {}", subject);
-                return;
-            }
-
+        // Read gatewayIps and subnets from port subject. Ignore suppressed ports.
+        Set<ConnectPoint> portSubjects = srManager.cfgService
+                .getSubjects(ConnectPoint.class, InterfaceConfig.class);
+        portSubjects.stream().filter(subject -> !isSuppressedPort(subject)).forEach(subject -> {
             InterfaceConfig config =
-                    cfgService.getConfig(subject, InterfaceConfig.class);
+                    srManager.cfgService.getConfig(subject, InterfaceConfig.class);
             Set<Interface> networkInterfaces;
             try {
                 networkInterfaces = config.getInterfaces();
@@ -343,7 +333,7 @@
             ImmutableSet.Builder<Ip4Prefix> builder = ImmutableSet.builder();
             builder.addAll(srinfo.subnets.values());
             SegmentRoutingAppConfig appConfig =
-                    cfgService.getConfig(appId, SegmentRoutingAppConfig.class);
+                    srManager.cfgService.getConfig(srManager.appId, SegmentRoutingAppConfig.class);
             if (appConfig != null) {
                 if (deviceId.equals(appConfig.vRouterId().orElse(null))) {
                     builder.add(Ip4Prefix.valueOf("0.0.0.0/0"));
@@ -354,25 +344,38 @@
         return null;
     }
 
+
     /**
-     *  Returns the configured non-/32 and non-/0 subnet on the given port,
-     *  or null if no subnet has been configured on the port.
+     * Returns the subnet configuration of given device and port.
      *
-     *  @param deviceId device identifier
-     *  @param pnum  port identifier
-     *  @return configured subnet on port, or null
+     * @param deviceId Device ID
+     * @param port Port number
+     * @return The subnet configured on given port or null if
+     *         the port is unconfigured, misconfigured or suppressed.
      */
-    public Ip4Prefix getPortSubnet(DeviceId deviceId, PortNumber pnum) {
-        SegmentRouterInfo srinfo = deviceConfigMap.get(deviceId);
-        if (srinfo != null) {
-            Optional<Ip4Prefix> result = srinfo.subnets.get(pnum).stream()
-                    .filter(subnet ->
-                            subnet.getIp4Prefix().prefixLength() != IpPrefix.MAX_INET_MASK_LENGTH &&
-                            subnet.getIp4Prefix().prefixLength() != 0)
-                    .findFirst();
-            return (result.isPresent()) ? result.get() : null;
+    public Ip4Prefix getPortSubnet(DeviceId deviceId, PortNumber port) {
+        ConnectPoint connectPoint = new ConnectPoint(deviceId, port);
+
+        if (isSuppressedPort(connectPoint)) {
+            return null;
         }
-        return null;
+
+        Set<Ip4Prefix> subnets =
+                srManager.interfaceService.getInterfacesByPort(connectPoint).stream()
+                        .flatMap(intf -> intf.ipAddressesList().stream())
+                        .map(InterfaceIpAddress::subnetAddress)
+                        .map(IpPrefix::getIp4Prefix)
+                        .collect(Collectors.toSet());
+
+        if (subnets.size() == 0) {
+            log.info(NO_SUBNET, connectPoint);
+            return null;
+        } else if (subnets.size() > 1) {
+            log.warn(TOO_MANY_SUBNET, connectPoint);
+            return null;
+        } else {
+            return subnets.stream().findFirst().orElse(null);
+        }
     }
 
     /**
@@ -504,4 +507,14 @@
         }
         srinfo.subnets.remove(cp.port(), ip4Prefix);
     }
+
+    private boolean isSuppressedPort(ConnectPoint connectPoint) {
+        SegmentRoutingAppConfig appConfig = srManager.cfgService
+                .getConfig(srManager.appId, SegmentRoutingAppConfig.class);
+        if (appConfig != null && appConfig.suppressSubnet().contains(connectPoint)) {
+            log.info("Ignore suppressed port {}", connectPoint);
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
index a5a7268..0f3a431 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/SegmentRoutingAppConfigTest.java
@@ -68,7 +68,7 @@
         InputStream invalidJsonStream = SegmentRoutingAppConfigTest.class
                 .getResourceAsStream("/app-invalid.json");
 
-        String key = SegmentRoutingManager.SR_APP_ID;
+        String key = SegmentRoutingManager.APP_NAME;
         ApplicationId subject = new TestApplicationId(key);
         ObjectMapper mapper = new ObjectMapper();
         JsonNode jsonNode = mapper.readTree(jsonStream);
diff --git a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/XConnectConfigTest.java b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/XConnectConfigTest.java
index f472a90..143bc29 100644
--- a/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/XConnectConfigTest.java
+++ b/apps/segmentrouting/src/test/java/org/onosproject/segmentrouting/config/XConnectConfigTest.java
@@ -62,7 +62,7 @@
         InputStream invalidJsonStream = SegmentRoutingAppConfigTest.class
                 .getResourceAsStream("/xconnect-invalid.json");
 
-        String key = SegmentRoutingManager.SR_APP_ID;
+        String key = SegmentRoutingManager.APP_NAME;
         ApplicationId subject = new TestApplicationId(key);
         ObjectMapper mapper = new ObjectMapper();
         JsonNode jsonNode = mapper.readTree(jsonStream);