[ONOS-7444] Optimize SONA gw doesn't use vrouter app and quagga anymore
- Done: Deriving MAC address from external peer router, SNAT, Floating IP-based routing, SNAT with VLAN,
        Floating IP-based routing with VLAN
- Todo: GW loadbalancing

Change-Id: Ic3dee387e8b6215b5398691665135a00475a306c
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 657d7a0..e6be7ef 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -35,7 +35,7 @@
 import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.openstacknetworking.api.Constants;
-import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeService;
@@ -68,7 +68,7 @@
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNetworkService osNetworkService;
+    protected OpenstackNetworkAdminService osNetworkAdminService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackRouterService osRouterService;
@@ -114,7 +114,7 @@
 
             //In case target ip is for associated floating ip, sets target mac to vm's.
             if (floatingIP != null && floatingIP.getPortId() != null) {
-                targetMac = MacAddress.valueOf(osNetworkService.port(floatingIP.getPortId()).getMacAddress());
+                targetMac = MacAddress.valueOf(osNetworkAdminService.port(floatingIP.getPortId()).getMacAddress());
             }
 
             if (isExternalGatewaySourceIp(targetIp.getIp4Address())) {
@@ -147,7 +147,7 @@
             try {
                 if (receivedPortNum.equals(
                         osNodeService.node(context.inPacket().receivedFrom().deviceId()).uplinkPortNum())) {
-                    osNetworkService.updateExternalPeerRouterMac(
+                    osNetworkAdminService.updateExternalPeerRouterMac(
                             Ip4Address.valueOf(arp.getSenderProtocolAddress()),
                             MacAddress.valueOf(arp.getSenderHardwareAddress()));
                 }
@@ -185,7 +185,7 @@
     }
 
     private boolean isExternalGatewaySourceIp(IpAddress targetIp) {
-        return osNetworkService.ports().stream()
+        return osNetworkAdminService.ports().stream()
                 .filter(osPort -> Objects.equals(osPort.getDeviceOwner(),
                         DEVICE_OWNER_ROUTER_GW))
                 .flatMap(osPort -> osPort.getFixedIps().stream())
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
index 16607b1..fe41bdc 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -37,6 +37,7 @@
 import org.onosproject.net.flow.TrafficSelector;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
@@ -160,9 +161,15 @@
             throw new IllegalStateException(error);
         }
 
+
+        ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
+        if (externalPeerRouter == null) {
+            return;
+        }
+
         setComputeNodeToGateway(instPort, osNet, install);
-        setDownstreamRules(floatingIp, osNet, instPort, install);
-        setUpstreamRules(floatingIp, osNet, instPort, install);
+        setDownstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
+        setUpstreamRules(floatingIp, osNet, instPort, externalPeerRouter, install);
     }
 
     private void setComputeNodeToGateway(InstancePort instPort, Network osNet, boolean install) {
@@ -216,7 +223,8 @@
     }
 
     private void setDownstreamRules(NetFloatingIP floatingIp, Network osNet,
-                                    InstancePort instPort, boolean install) {
+                                    InstancePort instPort, ExternalPeerRouter externalPeerRouter,
+                                    boolean install) {
         OpenstackNode cNode = osNodeService.node(instPort.deviceId());
         if (cNode == null) {
             final String error = String.format("Cannot find openstack node for device %s",
@@ -235,16 +243,22 @@
         }
 
         IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
-        TrafficSelector externalSelector = DefaultTrafficSelector.builder()
+        TrafficSelector.Builder externalSelectorBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(floating.toIpPrefix())
-                .build();
+                .matchIPDst(floating.toIpPrefix());
+
+        TrafficTreatment.Builder externalBuilder = DefaultTrafficTreatment.builder()
+                .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
+                .setEthDst(instPort.macAddress())
+                .setIpDst(instPort.ipAddress().getIp4Address());
+
+        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
+            externalSelectorBuilder.matchVlanId(externalPeerRouter.externalPeerRouterVlanId()).build();
+            externalBuilder.popVlan();
+        }
 
         osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
-            TrafficTreatment.Builder externalBuilder = DefaultTrafficTreatment.builder()
-                    .setEthSrc(Constants.DEFAULT_GATEWAY_MAC)
-                    .setEthDst(instPort.macAddress())
-                    .setIpDst(instPort.ipAddress().getIp4Address());
+
 
             switch (osNet.getNetworkType()) {
                 case VXLAN:
@@ -270,7 +284,7 @@
             osFlowRuleService.setRule(
                     appId,
                     gNode.intgBridge(),
-                    externalSelector,
+                    externalSelectorBuilder.build(),
                     externalBuilder.build(),
                     PRIORITY_FLOATING_EXTERNAL,
                     GW_COMMON_TABLE,
@@ -321,7 +335,8 @@
     }
 
     private void setUpstreamRules(NetFloatingIP floatingIp, Network osNet,
-                                  InstancePort instPort, boolean install) {
+                                  InstancePort instPort, ExternalPeerRouter externalPeerRouter,
+                                  boolean install) {
         IpAddress floating = IpAddress.valueOf(floatingIp.getFloatingIpAddress());
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
                 .matchEthType(Ethernet.TYPE_IPV4)
@@ -340,21 +355,20 @@
                 throw new IllegalStateException(error);
         }
 
-        MacAddress externalPeerRouterMac = externalPeerRouterMac(osNet);
-        if (externalPeerRouterMac == null) {
-            return;
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                .setIpSrc(floating.getIp4Address())
+                .setEthSrc(instPort.macAddress())
+                .setEthDst(externalPeerRouter.externalPeerRouterMac());
+
+        if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
+            tBuilder.popVlan();
         }
 
+        if (!externalPeerRouter.externalPeerRouterVlanId().equals(VlanId.NONE)) {
+            tBuilder.pushVlan().setVlanId(externalPeerRouter.externalPeerRouterVlanId());
+        }
 
         osNodeService.completeNodes(GATEWAY).forEach(gNode -> {
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                    .setIpSrc(floating.getIp4Address())
-                    .setEthSrc(instPort.macAddress())
-                    .setEthDst(externalPeerRouterMac);
-
-            if (osNet.getNetworkType().equals(NetworkType.VLAN)) {
-                tBuilder.popVlan();
-            }
 
             osFlowRuleService.setRule(
                     appId,
@@ -367,6 +381,32 @@
         });
     }
 
+    private ExternalPeerRouter externalPeerRouter(Network network) {
+        Subnet subnet = osNetworkService.subnet(network.getId());
+
+        if (subnet == null) {
+            return null;
+        }
+
+        RouterInterface osRouterIface = osRouterService.routerInterfaces().stream()
+                .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
+                .findAny().orElse(null);
+        if (osRouterIface == null) {
+            return null;
+        }
+
+        Router osRouter = osRouterService.router(osRouterIface.getId());
+        if (osRouter == null) {
+            return null;
+        }
+        if (osRouter.getExternalGatewayInfo() == null) {
+            return null;
+        }
+
+        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
+        return osNetworkService.externalPeerRouter(exGatewayInfo);
+    }
+
     private MacAddress externalPeerRouterMac(Network network) {
         if (network == null) {
             return null;
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
index 42a3da4..94cdd42 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -55,6 +55,7 @@
 import org.onosproject.openstacknetworking.api.InstancePortListener;
 import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
 import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
@@ -132,6 +133,9 @@
     protected OpenstackNetworkService osNetworkService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkAdminService osNetworkAdminService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackRouterService osRouterService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
@@ -217,10 +221,10 @@
         VlanId vlanId = externalPeerRouter == null ? VlanId.NONE : externalPeerRouter.externalPeerRouterVlanId();
 
         if (exGateway == null) {
-            osNetworkService.deleteExternalPeerRouter(exGateway);
+            osNetworkAdminService.deleteExternalPeerRouter(exGateway);
             osRouterService.routerInterfaces(osRouter.getId()).forEach(iface -> setSourceNat(iface, false));
         } else {
-            osNetworkService.deriveExternalPeerRouterMac(exGateway, osRouter, vlanId);
+            osNetworkAdminService.deriveExternalPeerRouterMac(exGateway, osRouter, vlanId);
             osRouterService.routerInterfaces(osRouter.getId()).forEach(iface ->
                     setSourceNat(iface, exGateway.isEnableSnat()));
         }