CORD-349 Support VLAN cross-connect traffic

Change related to this topic:
- Support VLAN cross-connect traffic
    Utilize ports subjectClass to achieve. For non-xConnect port, set interface VLAN to -1
- Remove VLAN checking since we have multiple VLANs per port
- Hash the L2 interface group key generation to include VLAN as well
- Update the network-cfg.json sample

Other refactoring changes:
- Read next objective stores from srManager directly
- Use constant for flow priority
- CORD-267 Javadoc fix

Change-Id: I4ca8c2d9c8b3633a4a0101c5070d19343f7e5b90
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
index dbac596..3d2d337 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceConfiguration.java
@@ -19,6 +19,7 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.incubator.net.config.basics.ConfigException;
 import org.onosproject.incubator.net.config.basics.InterfaceConfig;
 import org.onosproject.incubator.net.intf.Interface;
@@ -33,6 +34,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -48,8 +50,8 @@
     private static final Logger log = LoggerFactory
             .getLogger(DeviceConfiguration.class);
     private final List<Integer> allSegmentIds = new ArrayList<>();
-    private final ConcurrentHashMap<DeviceId, SegmentRouterInfo> deviceConfigMap
-        = new ConcurrentHashMap<>();
+    private final Map<DeviceId, SegmentRouterInfo> deviceConfigMap = new ConcurrentHashMap<>();
+    private final Map<VlanId, List<ConnectPoint>> xConnects = new ConcurrentHashMap<>();
 
     private class SegmentRouterInfo {
         int nodeSid;
@@ -62,14 +64,14 @@
         Map<Integer, Set<Integer>> adjacencySids;
 
         public SegmentRouterInfo() {
-            this.gatewayIps = new HashMap<>();
-            this.subnets = new HashMap<>();
+            gatewayIps = new HashMap<>();
+            subnets = new HashMap<>();
         }
     }
 
     /**
-     * Constructor. Reads all the configuration for all devices of type
-     * Segment Router and organizes into various maps for easier access.
+     * Constructs device configuration for all Segment Router devices,
+     * organizing the data into various maps for easier access.
      *
      * @param cfgService config service
      */
@@ -88,8 +90,8 @@
             info.isEdge = config.isEdgeRouter();
             info.adjacencySids = config.adjacencySids();
 
-            this.deviceConfigMap.put(info.deviceId, info);
-            this.allSegmentIds.add(info.nodeSid);
+            deviceConfigMap.put(info.deviceId, info);
+            allSegmentIds.add(info.nodeSid);
         });
 
         // Read gatewayIps and subnets from port subject.
@@ -106,17 +108,42 @@
                 return;
             }
             networkInterfaces.forEach(networkInterface -> {
-                DeviceId dpid = networkInterface.connectPoint().deviceId();
-                PortNumber port = networkInterface.connectPoint().port();
-                SegmentRouterInfo info = this.deviceConfigMap.get(dpid);
+                VlanId vlanId = networkInterface.vlan();
+                ConnectPoint connectPoint = networkInterface.connectPoint();
+                DeviceId dpid = connectPoint.deviceId();
+                PortNumber port = connectPoint.port();
+                SegmentRouterInfo info = deviceConfigMap.get(dpid);
 
                 // skip if there is no corresponding device for this ConenctPoint
                 if (info != null) {
+                    // Extract subnet information
                     Set<InterfaceIpAddress> interfaceAddresses = networkInterface.ipAddresses();
                     interfaceAddresses.forEach(interfaceAddress -> {
                         info.gatewayIps.put(port, interfaceAddress.ipAddress().getIp4Address());
                         info.subnets.put(port, interfaceAddress.subnetAddress().getIp4Prefix());
                     });
+
+                    // Extract VLAN cross-connect information
+                    // Do not setup cross-connect if VLAN is NONE
+                    if (vlanId.equals(VlanId.NONE)) {
+                        return;
+                    }
+                    List<ConnectPoint> connectPoints = xConnects.get(vlanId);
+                    if (connectPoints != null) {
+                        if (connectPoints.size() != 1) {
+                            log.warn("Cross-connect should only have two endpoints. Aborting.");
+                            return;
+                        }
+                        if (!connectPoints.get(0).deviceId().equals(connectPoint.deviceId())) {
+                            log.warn("Cross-connect endpoints must be on the same switch. Aborting.");
+                            return;
+                        }
+                        connectPoints.add(connectPoint);
+                    } else {
+                        connectPoints = new LinkedList<>();
+                        connectPoints.add(connectPoint);
+                        xConnects.put(vlanId, connectPoints);
+                    }
                 }
             });
 
@@ -235,6 +262,11 @@
         return subnetPortMap;
     }
 
+    @Override
+    public Map<VlanId, List<ConnectPoint>> getXConnects() {
+        return xConnects;
+    }
+
     /**
      * Returns the device identifier or data plane identifier (dpid)
      * of a segment router given its segment id.
diff --git a/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java b/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
index a39c956..9d4d884 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/DeviceProperties.java
@@ -21,6 +21,8 @@
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.Ip4Prefix;
 import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.PortNumber;
 
@@ -93,4 +95,11 @@
      * @return a map that contains all subnet-to-ports mapping of given device
      */
     Map<Ip4Prefix, List<PortNumber>> getSubnetPortsMap(DeviceId deviceId);
+
+    /**
+     * Returns the VLAN cross-connect configuration.
+     *
+     * @return A map of that maps VLAN ID to a list of cross-connect endpoints
+     */
+    Map<VlanId, List<ConnectPoint>> getXConnects();
 }
diff --git a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
index f788925..64486c6 100644
--- a/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
+++ b/src/main/java/org/onosproject/segmentrouting/config/SegmentRoutingConfig.java
@@ -35,24 +35,24 @@
  * Configuration object for Segment Routing Application.
  */
 public class SegmentRoutingConfig extends Config<DeviceId> {
-    public static final String NAME = "name";
-    public static final String IP = "routerIp";
-    public static final String MAC = "routerMac";
-    public static final String SID = "nodeSid";
-    public static final String EDGE = "isEdgeRouter";
-    public static final String ADJSIDS = "adjacencySids";
-    public static final String ADJSID = "adjSid";
-    public static final String PORTS = "ports";
+    private static final String NAME = "name";
+    private static final String IP = "routerIp";
+    private static final String MAC = "routerMac";
+    private static final String SID = "nodeSid";
+    private static final String EDGE = "isEdgeRouter";
+    private static final String ADJSIDS = "adjacencySids";
+    private static final String ADJSID = "adjSid";
+    private static final String PORTS = "ports";
 
     @Override
     public boolean isValid() {
         return hasOnlyFields(NAME, IP, MAC, SID, EDGE, ADJSIDS, ADJSID, PORTS) &&
-                this.name() != null &&
-                this.routerIp() != null &&
-                this.routerMac() != null &&
-                this.nodeSid() != -1 &&
-                this.isEdgeRouter() != null &&
-                this.adjacencySids() != null;
+                name() != null &&
+                routerIp() != null &&
+                routerMac() != null &&
+                nodeSid() != -1 &&
+                isEdgeRouter() != null &&
+                adjacencySids() != null;
     }
 
     /**