[ONOS-3804] [ONOS-3805] Initial L3 flowrules setup for Compute/Gateway nodes and PNAT Handler for OpenstackRouting

 - Performs app refactoring (openstackrouting and openstackswitching)
 - Implements L3 REST call corresponding openstackRouter and openstackRouterInterface.
 - Implements initail L3 rules population to compute/gateway node.
 - Implements PNAT rules population corresponding packet-in event.
 - Fixs comments and javadocs.
 - Rebases on master.

Change-Id: I5ad68810f50dc977737d30c43150c892b978b7cb
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java
index 40eed7f..65b8a5f 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackPnatHandler.java
@@ -17,24 +17,29 @@
 
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.TCP;
 import org.onlab.packet.UDP;
 import org.onosproject.net.DeviceId;
-import org.onosproject.net.PortNumber;
+import org.onosproject.net.Port;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
 import org.onosproject.net.flow.TrafficTreatment;
 import org.onosproject.net.packet.DefaultOutboundPacket;
 import org.onosproject.net.packet.InboundPacket;
 import org.onosproject.net.packet.PacketContext;
 import org.onosproject.net.packet.PacketService;
+import org.onosproject.openstacknetworking.OpenstackNetwork;
+import org.onosproject.openstacknetworking.OpenstackNetworkingService;
 import org.onosproject.openstacknetworking.OpenstackPort;
+import org.onosproject.openstacknetworking.OpenstackRouter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.nio.ByteBuffer;
 
 import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.osgi.DefaultServiceDirectory.getService;
 
 
 /**
@@ -50,13 +55,20 @@
     private final OpenstackRoutingRulePopulator rulePopulator;
     private final int portNum;
     private final OpenstackPort openstackPort;
+    private final Port port;
+
+    private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
+    // TODO: This will be replaced to get the information from openstacknetworkingservice.
+    private static final MacAddress GATEWAYMAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
+    private static final MacAddress EXTERNAL_INTERFACE_MAC = MacAddress.valueOf("00:00:00:00:00:11");
 
     OpenstackPnatHandler(OpenstackRoutingRulePopulator rulePopulator, PacketContext context,
-                         int portNum, OpenstackPort openstackPort) {
+                         int portNum, OpenstackPort openstackPort, Port port) {
         this.rulePopulator = checkNotNull(rulePopulator);
         this.context = checkNotNull(context);
         this.portNum = checkNotNull(portNum);
         this.openstackPort = checkNotNull(openstackPort);
+        this.port = checkNotNull(port);
     }
 
     @Override
@@ -70,14 +82,41 @@
             return;
         }
 
-        packetOut(inboundPacket, portNum);
+        OpenstackRouter router = getOpenstackRouter(openstackPort);
 
         rulePopulator.populatePnatFlowRules(inboundPacket, openstackPort, portNum,
-                getExternalInterfaceMacAddress(), getExternalRouterMacAddress());
+                getExternalIp(router), getExternalInterfaceMacAddress(), getExternalRouterMacAddress());
+
+        packetOut((Ethernet) ethernet.clone(), inboundPacket.receivedFrom().deviceId(), portNum, router);
     }
 
-    private void packetOut(InboundPacket inboundPacket, int portNum) {
-        Ethernet ethernet = checkNotNull(inboundPacket.parsed());
+    private OpenstackRouter getOpenstackRouter(OpenstackPort openstackPort) {
+        OpenstackNetworkingService networkingService = getService(OpenstackNetworkingService.class);
+        OpenstackNetwork network = networkingService.network(openstackPort.networkId());
+
+        OpenstackPort port = networkingService.ports()
+                .stream()
+                .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
+                .filter(p -> checkSameSubnet(p, openstackPort))
+                .findAny()
+                .orElse(null);
+
+        return checkNotNull(networkingService.router(port.deviceId()));
+    }
+
+    private boolean checkSameSubnet(OpenstackPort p, OpenstackPort openstackPort) {
+        String key1 = checkNotNull(p.fixedIps().keySet().stream().findFirst().orElse(null)).toString();
+        String key2 = checkNotNull(openstackPort.fixedIps().keySet().stream().findFirst().orElse(null)).toString();
+        return key1.equals(key2) ? true : false;
+    }
+
+    private Ip4Address getExternalIp(OpenstackRouter router) {
+        return router.gatewayExternalInfo().externalFixedIps().values().stream().findAny().orElse(null);
+    }
+
+    private void packetOut(Ethernet ethernet, DeviceId deviceId, int portNum, OpenstackRouter router) {
+        PacketService packetService = getService(PacketService.class);
+
         IPv4 iPacket = (IPv4) ethernet.getPayload();
 
         TrafficTreatment.Builder treatment = DefaultTrafficTreatment.builder();
@@ -98,31 +137,27 @@
                 iPacket.setPayload(udpPacket);
                 break;
             default:
-                break;
+                log.error("Temporally, this method can process UDP and TCP protocol.");
+                return;
         }
 
+        iPacket.setSourceAddress(getExternalIp(router).toString());
         iPacket.resetChecksum();
-        iPacket.setPayload(ethernet);
+        iPacket.setParent(ethernet);
         ethernet.setSourceMACAddress(getExternalInterfaceMacAddress())
                 .setDestinationMACAddress(getExternalRouterMacAddress());
         ethernet.resetChecksum();
 
-        treatment.setOutput(getExternalPort(inboundPacket.receivedFrom().deviceId()));
+        treatment.setOutput(port.number());
 
-        packetService.emit(new DefaultOutboundPacket(inboundPacket.receivedFrom().deviceId(),
-                treatment.build(), ByteBuffer.wrap(ethernet.serialize())));
+        packetService.emit(new DefaultOutboundPacket(deviceId, treatment.build(),
+                ByteBuffer.wrap(ethernet.serialize())));
     }
 
-    private PortNumber getExternalPort(DeviceId deviceId) {
-        // TODO
-        return null;
-    }
     private MacAddress getExternalInterfaceMacAddress() {
-        // TODO
-        return null;
+        return EXTERNAL_INTERFACE_MAC;
     }
     private MacAddress getExternalRouterMacAddress() {
-        // TODO
-        return null;
+        return GATEWAYMAC;
     }
 }
\ No newline at end of file