[ONOS-6186] Implement VLAN based virtual network provisioning and logical switching

Change-Id: I1dcee5377b3d4d9ec5fc3d6bc851a90a016074dc
diff --git a/apps/openstacknetworking/network-cfg.json b/apps/openstacknetworking/network-cfg.json
new file mode 100644
index 0000000..556de59
--- /dev/null
+++ b/apps/openstacknetworking/network-cfg.json
@@ -0,0 +1,51 @@
+{
+    "apps" : {
+        "org.onosproject.openstacknode" : {
+            "openstacknode" : {
+                 "nodes" : [
+                        {
+                                "hostname" : "compute-01",
+                                "type" : "COMPUTE",
+                                "managementIp" : "172.16.130.4",
+                                "vlanPort" : "eth2",
+                                "dataIp" : "172.16.130.4",
+                                "integrationBridge" : "of:00000000000000a1"
+                        },
+                        {
+                                "hostname" : "compute-02",
+                                "type" : "COMPUTE",
+                                "managementIp" : "172.16.130.6",
+                                "vlanPort" : "eth2",
+                                "dataIp" : "172.16.130.6",
+                                "integrationBridge" : "of:00000000000000a2"
+                        },
+                        {
+                                "hostname" : "gateway-01",
+                                "type" : "GATEWAY",
+                                "managementIp" : "172.16.130.3",
+                                "dataIp" : "172.16.130.3",
+                                "vlanPort" : "eth2",
+                                "integrationBridge" : "of:00000000000000a3",
+                                "routerBridge" : "of:00000000000000b1",
+                                "uplinkPort" : "quagga-router",
+                                "routerController" : "172.17.0.2"
+
+                        }
+                ]
+            }
+        }
+    },
+    "devices" : {
+        "of:00000000000000a1" : {
+            "basic" : {
+                "driver" : "sona"
+            }
+        },
+        "of:00000000000000a2" : {
+            "basic" : {
+                "driver" : "sona"
+            }
+        }
+    }
+}
+
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
index 8b489c9..a00eff9 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/cli/OpenstackNetworkListCommand.java
@@ -41,7 +41,7 @@
         description = "Lists all OpenStack networks")
 public class OpenstackNetworkListCommand extends AbstractShellCommand {
 
-    private static final String FORMAT = "%-40s%-20s%-20s%-8s";
+    private static final String FORMAT = "%-40s%-20s%-20s%-20s%-8s";
 
     @Override
     protected void execute() {
@@ -58,7 +58,7 @@
             return;
         }
 
-        print(FORMAT, "ID", "Name", "VNI", "Subnets");
+        print(FORMAT, "ID", "Name", "Network Mode", "VNI", "Subnets");
         for (Network net: networks) {
             List<String> subnets = service.subnets().stream()
                     .filter(subnet -> subnet.getNetworkId().equals(net.getId()))
@@ -66,6 +66,7 @@
                     .collect(Collectors.toList());
             print(FORMAT, net.getId(),
                     net.getName(),
+                    net.getNetworkType().toString(),
                     net.getProviderSegID(),
                     subnets.isEmpty() ? "" : subnets);
         }
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
index b399fdf..975096f 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
@@ -23,6 +23,7 @@
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.Ethernet;
+import org.onlab.packet.VlanId;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.mastership.MastershipService;
@@ -106,8 +107,18 @@
     }
 
     private void setNetworkRules(InstancePort instPort, boolean install) {
-        setTunnelTagFlowRules(instPort, install);
-        setForwardingRules(instPort, install);
+        switch (osNetworkService.network(instPort.networkId()).getNetworkType()) {
+            case VXLAN:
+                setTunnelTagFlowRules(instPort, install);
+                setForwardingRules(instPort, install);
+                break;
+            case VLAN:
+                setVlanTagFlowRules(instPort, install);
+                setForwardingRulesForVlan(instPort, install);
+                break;
+            default:
+                break;
+        }
     }
 
     private void setForwardingRules(InstancePort instPort, boolean install) {
@@ -159,6 +170,53 @@
                 });
     }
 
+    private void setForwardingRulesForVlan(InstancePort instPort, boolean install) {
+        // switching rules for the instPorts in the same node
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(instPort.ipAddress().toIpPrefix())
+                .matchVlanId(getVlanId(instPort))
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .popVlan()
+                .setEthDst(instPort.macAddress())
+                .setOutput(instPort.portNumber())
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                instPort.deviceId(),
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_SWITCHING_RULE,
+                install);
+
+        // switching rules for the instPorts in the remote node
+        osNodeService.completeNodes().stream()
+                .filter(osNode -> osNode.type() == COMPUTE)
+                .filter(osNode -> !osNode.intBridge().equals(instPort.deviceId()))
+                .filter(osNode -> osNode.vlanPort().isPresent())
+                .forEach(osNode -> {
+                    TrafficTreatment treatmentToRemote = DefaultTrafficTreatment.builder()
+                                    .setOutput(osNodeService.vlanPort(osNode.intBridge()).get())
+                                    .build();
+
+                    RulePopulatorUtil.setRule(
+                            flowObjectiveService,
+                            appId,
+                            osNode.intBridge(),
+                            selector,
+                            treatmentToRemote,
+                            ForwardingObjective.Flag.SPECIFIC,
+                            PRIORITY_SWITCHING_RULE,
+                            install);
+                });
+
+    }
+
     private void setTunnelTagFlowRules(InstancePort instPort, boolean install) {
         TrafficSelector selector = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
@@ -180,6 +238,43 @@
                 install);
     }
 
+    private void setVlanTagFlowRules(InstancePort instPort, boolean install) {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(instPort.portNumber())
+                .build();
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .pushVlan()
+                .setVlanId(getVlanId(instPort))
+                .build();
+
+        RulePopulatorUtil.setRule(
+                flowObjectiveService,
+                appId,
+                instPort.deviceId(),
+                selector,
+                treatment,
+                ForwardingObjective.Flag.SPECIFIC,
+                PRIORITY_TUNNEL_TAG_RULE,
+                install);
+
+    }
+
+    private VlanId getVlanId(InstancePort instPort) {
+        Network osNet = osNetworkService.network(instPort.networkId());
+
+        if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
+            final String error = String.format(
+                    ERR_SET_FLOWS + "Failed to get VNI for %s",
+                    instPort, osNet == null ? "<none>" : osNet.getName());
+            throw new IllegalStateException(error);
+        }
+
+        return VlanId.vlanId(osNet.getProviderSegID());
+    }
+
+
     private Long getVni(InstancePort instPort) {
         Network osNet = osNetworkService.network(instPort.networkId());
         if (osNet == null || Strings.isNullOrEmpty(osNet.getProviderSegID())) {
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 0bf78bf..643678a 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeManager.java
@@ -344,6 +344,17 @@
     }
 
     @Override
+    public Optional<PortNumber> vlanPort(DeviceId intBridgeId) {
+        Optional<String> vlanPortName = nodeByDeviceId(intBridgeId).vlanPort();
+
+        return deviceService.getPorts(intBridgeId).stream()
+                .filter(p -> p.annotations().value(PORT_NAME).equals(vlanPortName.get()) &&
+                        p.isEnabled())
+                .map(Port::number).findFirst();
+
+    }
+
+    @Override
     public Optional<DeviceId> routerBridge(DeviceId intBridgeId) {
         OpenstackNode node = nodeByDeviceId(intBridgeId);
         if (node == null || node.type().equals(NodeType.COMPUTE)) {
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 0fb6e77..7362893 100644
--- a/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
+++ b/apps/openstacknode/src/main/java/org/onosproject/openstacknode/OpenstackNodeService.java
@@ -112,6 +112,14 @@
     Optional<PortNumber> tunnelPort(DeviceId intBridgeId);
 
     /**
+     * Returns vlan port number of a given integration bridge device.
+     *
+     * @param intBridgeId integration bridge device id
+     * @return port number; or empty value
+     */
+    Optional<PortNumber> vlanPort(DeviceId intBridgeId);
+
+    /**
      * Returns router bridge device ID connected to a given integration bridge.
      * It returns valid value only if the node type is GATEWAY.
      *
@@ -128,6 +136,7 @@
      * @return port number; or empty value
      */
     Optional<PortNumber> externalPort(DeviceId intBridgeId);
+
     /**
      * Returns gateway node with the given device identifier.
      *