[ONOS-6168, ONOS-6436] Implement multiple gateway nodes support for VLAN mode and implement VLAN based Logical Routing

Change-Id: Ifd1c26375abdf84603f28184e9cb9ad6c88648dd
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
index b4074f7..8bb75a1 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/Constants.java
@@ -26,6 +26,7 @@
     public static final String INTEGRATION_BRIDGE = "br-int";
     public static final String ROUTER_BRIDGE = "br-router";
     public static final String DEFAULT_TUNNEL = "vxlan";
+    public static final String VLAN = "vlan";
     public static final String PATCH_INTG_BRIDGE = "patch-intg";
     public static final String PATCH_ROUT_BRIDGE = "patch-rout";
 
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
index 4f3e5e7..bea3a41 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
@@ -297,7 +297,7 @@
         process(new OpenstackNodeEvent(COMPLETE, node));
         switch (node.type()) {
             case COMPUTE:
-                selectGroupHandler.createGatewayGroup(node.intBridge(), gatewayNodes());
+                selectGroupHandler.createGatewayGroup(node, gatewayNodes());
                 break;
             case GATEWAY:
                 updateGatewayGroup(node, true);
@@ -386,12 +386,15 @@
     }
 
     @Override
-    public synchronized GroupId gatewayGroupId(DeviceId srcDeviceId) {
-        GroupKey groupKey = selectGroupHandler.getGroupKey(srcDeviceId);
+    public synchronized GroupId gatewayGroupId(DeviceId srcDeviceId, NetworkMode networkMode) {
+        GroupKey groupKey = selectGroupHandler.groupKey(srcDeviceId, networkMode);
         Group group = groupService.getGroup(srcDeviceId, groupKey);
+
         if (group == null) {
             log.info("Created gateway group for {}", srcDeviceId);
-            return selectGroupHandler.createGatewayGroup(srcDeviceId, gatewayNodes());
+            selectGroupHandler.createGatewayGroup(nodeByDeviceId(srcDeviceId), gatewayNodes());
+
+            return groupService.getGroup(srcDeviceId, selectGroupHandler.groupKey(srcDeviceId, networkMode)).id();
         } else {
             return group.id();
         }
@@ -425,13 +428,26 @@
                 .stream()
                 .map(Versioned::value)
                 .filter(node -> node.type().equals(NodeType.COMPUTE))
+                .filter(node -> node.dataIp().isPresent())
                 .filter(node -> node.state().equals(COMPLETE))
-                .forEach(node -> {
-                    selectGroupHandler.updateGatewayGroupBuckets(node.intBridge(),
-                            ImmutableList.of(gatewayNode),
-                            isInsert);
-                    log.trace("Updated gateway group on {}", node.intBridge());
+                .forEach(computeNode -> {
+                    selectGroupHandler.updateGatewayGroupBuckets(computeNode,
+                            ImmutableList.of(gatewayNode), NetworkMode.VXLAN, isInsert);
+                    log.trace("Updated gateway group on {} for vxlan mode", computeNode.intBridge());
                 });
+
+        nodeStore.values()
+                .stream()
+                .map(Versioned::value)
+                .filter(node -> node.type().equals(NodeType.COMPUTE))
+                .filter(node -> node.vlanPort().isPresent())
+                .filter(node -> node.state().equals(COMPLETE))
+                .forEach(computeNode -> {
+                    selectGroupHandler.updateGatewayGroupBuckets(computeNode,
+                            ImmutableList.of(gatewayNode), NetworkMode.VLAN, isInsert);
+                    log.trace("Updated gateway group on {} for vlan mode", computeNode.intBridge());
+                });
+
     }
 
     private void initNode(OpenstackNode node) {
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
index 7362893..9600280 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
@@ -39,6 +39,14 @@
         GATEWAY
     }
 
+    enum NetworkMode {
+        /**
+         * VxLAN or VLAN mode.
+         */
+        VXLAN,
+        VLAN
+    }
+
     /**
      * Adds or updates a new node to the service.
      *
@@ -150,9 +158,10 @@
      * If the group does not exist in the supplied source device, creates one.
      *
      * @param srcDeviceId source device id
-     * @return The group id
+     * @param networkMode network mode
+     * @return group id
      */
-    GroupId gatewayGroupId(DeviceId srcDeviceId);
+    GroupId gatewayGroupId(DeviceId srcDeviceId, NetworkMode networkMode);
 
     /**
      * Returns the list of gateway node information with the given device identifier.
diff --git a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java
index df963db..5f8e065 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/SelectGroupHandler.java
@@ -18,8 +18,10 @@
 
 import com.google.common.collect.Lists;
 import org.onlab.packet.Ip4Address;
+import org.onlab.packet.MacAddress;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.GroupId;
+import org.onosproject.openstacknode.OpenstackNodeService.NetworkMode;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
@@ -47,7 +49,9 @@
 
 import java.util.List;
 
+import static org.onosproject.net.AnnotationKeys.PORT_MAC;
 import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.openstacknode.Constants.*;
 import static org.onosproject.net.group.DefaultGroupBucket.createSelectGroupBucket;
 
 /**
@@ -57,7 +61,7 @@
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String TUNNEL_DESTINATION = "tunnelDst";
-    private static final String PORTNAME_PREFIX_TUNNEL = "vxlan";
+    private static final String ERR_UNSUPPORTED_NET_TYPE = "Unsupported network type";
 
     private final GroupService groupService;
     private final DeviceService deviceService;
@@ -83,81 +87,139 @@
     /**
      * Creates select type group description according to given deviceId.
      *
-     * @param srcDeviceId target device id for group description
+     * @param computeNode target device id for group description
      * @param gatewayNodeList gateway node list for bucket action
-     * @return created select type group description
      */
-    public GroupId createGatewayGroup(DeviceId srcDeviceId, List<OpenstackNode> gatewayNodeList) {
-        List<GroupBucket> bucketList = generateBucketsForSelectGroup(srcDeviceId, gatewayNodeList);
-        GroupId groupId = getGroupId(srcDeviceId);
-        GroupDescription groupDescription = new DefaultGroupDescription(
-                srcDeviceId,
-                GroupDescription.Type.SELECT,
-                new GroupBuckets(bucketList),
-                getGroupKey(srcDeviceId),
-                groupId.id(),
-                appId);
+    public void createGatewayGroup(OpenstackNode computeNode, List<OpenstackNode> gatewayNodeList) {
+        List<GroupBucket> bucketList;
+        GroupId groupId;
 
-        groupService.addGroup(groupDescription);
-        return groupId;
+        if (computeNode.dataIp().isPresent()) {
+            bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, NetworkMode.VXLAN);
+            groupId = groupId(computeNode.intBridge(), NetworkMode.VXLAN);
+
+            GroupDescription groupDescription = new DefaultGroupDescription(
+                    computeNode.intBridge(),
+                    GroupDescription.Type.SELECT,
+                    new GroupBuckets(bucketList),
+                    groupKey(computeNode.intBridge(), NetworkMode.VXLAN),
+                    groupId.id(),
+                    appId);
+
+            groupService.addGroup(groupDescription);
+        }
+
+        if (computeNode.vlanPort().isPresent()) {
+            bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, NetworkMode.VLAN);
+            groupId = groupId(computeNode.intBridge(), NetworkMode.VLAN);
+
+            GroupDescription groupDescription = new DefaultGroupDescription(
+                    computeNode.intBridge(),
+                    GroupDescription.Type.SELECT,
+                    new GroupBuckets(bucketList),
+                    groupKey(computeNode.intBridge(), NetworkMode.VLAN),
+                    groupId.id(),
+                    appId);
+
+            groupService.addGroup(groupDescription);
+        }
     }
 
     /**
-     * Returns unique group key with supplied source device ID as a hash.
-     *
+     * Returns unique group key with supplied source device ID and network mode as a hash.
      * @param srcDeviceId source device id
+     * @param networkMode network mode
      * @return group key
      */
-    public GroupKey getGroupKey(DeviceId srcDeviceId) {
-        return new DefaultGroupKey(srcDeviceId.toString().getBytes());
+    public GroupKey groupKey(DeviceId srcDeviceId, NetworkMode networkMode) {
+        if (networkMode.equals(NetworkMode.VXLAN)) {
+            return new DefaultGroupKey(srcDeviceId.toString().concat(DEFAULT_TUNNEL).getBytes());
+        } else {
+            return new DefaultGroupKey(srcDeviceId.toString().concat(VLAN).getBytes());
+        }
     }
 
-    private GroupId getGroupId(DeviceId srcDeviceId) {
-        return new GroupId(srcDeviceId.toString().hashCode());
+    private GroupId groupId(DeviceId srcDeviceId, NetworkMode networkMode) {
+        if (networkMode.equals(NetworkMode.VXLAN)) {
+            return new GroupId(srcDeviceId.toString().concat(DEFAULT_TUNNEL).hashCode());
+        } else {
+            return new GroupId(srcDeviceId.toString().concat(VLAN).hashCode());
+        }
     }
 
+
     /**
      * Updates groupBuckets in select type group.
      *
-     * @param deviceId target device id to update the group
+     * @param computeNode compute node
      * @param gatewayNodeList updated gateway node list for bucket action
+     * @param networkMode network mode
      * @param isInsert update type(add or remove)
      */
-    public void updateGatewayGroupBuckets(DeviceId deviceId,
+    public void updateGatewayGroupBuckets(OpenstackNode computeNode,
                                           List<OpenstackNode> gatewayNodeList,
+                                          NetworkMode networkMode,
                                           boolean isInsert) {
-        List<GroupBucket> bucketList = generateBucketsForSelectGroup(deviceId, gatewayNodeList);
-        GroupKey groupKey = getGroupKey(deviceId);
-        if (groupService.getGroup(deviceId, groupKey) == null) {
-            log.error("There's no group in compute node {}", deviceId);
+        List<GroupBucket> bucketList = generateBucketsForSelectGroup(computeNode, gatewayNodeList, networkMode);
+        GroupKey groupKey = groupKey(computeNode.intBridge(), networkMode);
+        if (groupService.getGroup(computeNode.intBridge(), groupKey) == null) {
+            log.error("There's no group in compute node {}", computeNode.intBridge());
             return;
         }
 
         if (isInsert) {
             groupService.addBucketsToGroup(
-                    deviceId,
+                    computeNode.intBridge(),
                     groupKey,
                     new GroupBuckets(bucketList),
                     groupKey, appId);
         } else {
             groupService.removeBucketsFromGroup(
-                    deviceId,
+                    computeNode.intBridge(),
                     groupKey,
                     new GroupBuckets(bucketList),
                     groupKey, appId);
         }
     }
 
-    private List<GroupBucket> generateBucketsForSelectGroup(DeviceId deviceId, List<OpenstackNode> gatewayNodeList) {
+
+
+    private List<GroupBucket> generateBucketsForSelectGroup(OpenstackNode computeNode,
+                                                         List<OpenstackNode> gatewayNodeList,
+                                                         NetworkMode networkMode) {
         List<GroupBucket> bucketList = Lists.newArrayList();
-        gatewayNodeList.forEach(node -> {
-            TrafficTreatment tBuilder = DefaultTrafficTreatment.builder()
-                    .extension(buildNiciraExtenstion(deviceId, node.dataIp().get().getIp4Address()), deviceId)
-                    .setOutput(getTunnelPort(deviceId))
-                    .build();
-            bucketList.add(createSelectGroupBucket(tBuilder));
-        });
-        return bucketList;
+
+        switch (networkMode) {
+            case VXLAN:
+                gatewayNodeList.stream()
+                        .filter(node -> node.dataIp().isPresent())
+                        .forEach(node -> {
+                            TrafficTreatment tBuilder = DefaultTrafficTreatment.builder()
+                                    .extension(buildNiciraExtenstion(computeNode.intBridge(),
+                                            node.dataIp().get().getIp4Address()),
+                                            computeNode.intBridge())
+                                    .setOutput(getTunnelPort(computeNode.intBridge()))
+                                    .build();
+                            bucketList.add(createSelectGroupBucket(tBuilder));
+                        });
+                return bucketList;
+            case VLAN:
+                gatewayNodeList.stream()
+                        .filter(node -> node.vlanPort().isPresent())
+                        .forEach(node -> {
+                            TrafficTreatment tBuilder = DefaultTrafficTreatment.builder()
+                                    .setEthDst(MacAddress.valueOf(vlanPortMac(node)))
+                                    .setOutput(vlanPortNum(computeNode))
+                                    .build();
+                            bucketList.add(createSelectGroupBucket(tBuilder));
+                        });
+                return bucketList;
+            default:
+                final String error = String.format(
+                        ERR_UNSUPPORTED_NET_TYPE + "%s",
+                        networkMode.toString());
+                throw new IllegalStateException(error);
+        }
     }
 
     /**
@@ -193,7 +255,7 @@
      */
     private PortNumber getTunnelPort(DeviceId deviceId) {
         Port port = deviceService.getPorts(deviceId).stream()
-                .filter(p -> p.annotations().value(PORT_NAME).equals(PORTNAME_PREFIX_TUNNEL))
+                .filter(p -> p.annotations().value(PORT_NAME).equals(DEFAULT_TUNNEL))
                 .findAny().orElse(null);
 
         if (port == null) {
@@ -201,6 +263,18 @@
             return null;
         }
         return port.number();
+    }
 
+    private PortNumber vlanPortNum(OpenstackNode node) {
+        return deviceService.getPorts(node.intBridge()).stream()
+                .filter(p -> p.annotations().value(PORT_NAME).equals(node.vlanPort().get()) &&
+                        p.isEnabled())
+                .map(Port::number).findFirst().get();
+
+    }
+    private String vlanPortMac(OpenstackNode node) {
+        return deviceService.getPorts(node.intBridge()).stream()
+                .filter(p -> p.annotations().value(PORT_NAME).equals(node.vlanPort().get()) && p.isEnabled())
+                .findFirst().get().annotations().value(PORT_MAC);
     }
 }