[ONOS-3952] Implement FloatingIP Handler for OpenstackRoutingService

 - Implements floatingIp REST interfaces & event handler
 - Implements rulePopulate method for floatingIp handler
 - Fixes minor logics
 - Changes app structure
 - exports configuration
 - Implements case issue.
   when openstack deletes vm w/o deassociating floatingIp,
   openstack doesn`t send floatingIp deassociation event.

Change-Id: If4d8ac3fecfed1957d84139f94ae31f593a9097b
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
index ab1d360..1abcb63 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingRulePopulator.java
@@ -52,6 +52,8 @@
 import org.onosproject.openstackinterface.OpenstackRouter;
 import org.onosproject.openstackinterface.OpenstackRouterInterface;
 import org.onosproject.openstackinterface.OpenstackSubnet;
+import org.onosproject.openstackinterface.OpenstackFloatingIP;
+import org.onosproject.openstacknetworking.OpenstackPortInfo;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -73,16 +75,17 @@
     private final DriverService driverService;
     private final OpenstackRoutingConfig config;
 
-    private static final String PORTNAME_PREFIX_VM = "tap";
-    private static final String PORTNAME_PREFIX_ROUTER = "qr";
     private static final String PORTNAME_PREFIX_TUNNEL = "vxlan";
     private static final String PORTNAME = "portName";
+    private static final String PORTNAME_PREFIX_VM = "tap";
 
     private static final String PORTNOTNULL = "Port can not be null";
+    private static final String DEVICENOTNULL = "Device can not be null";
     private static final String TUNNEL_DESTINATION = "tunnelDst";
     private static final String DEVICE_ANNOTATION_CHANNELID = "channelId";
     private static final int ROUTING_RULE_PRIORITY = 25000;
-    private static final int PNAT_RULE_PRIORITY = 24000;
+    private static final int FLOATING_RULE_PRIORITY = 42000;
+    private static final int PNAT_RULE_PRIORITY = 26000;
     private static final int PNAT_TIMEOUT = 120;
     private static final MacAddress GATEWAYMAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
 
@@ -134,7 +137,7 @@
         this.externalInterface = externalInterfaceMacAddress;
         this.externalRouter = externalRouterMacAddress;
 
-        long vni = getVni(openstackPort);
+        long vni = getVni(openstackPort.networkId());
 
         populatePnatIncomingFlowRules(vni, externalIp);
         populatePnatOutgoingFlowRules(vni, externalIp);
@@ -173,7 +176,7 @@
                 break;
         }
 
-        Port port = checkNotNull(getPortNumOfExternalInterface(), PORTNOTNULL);
+        Port port = checkNotNull(getPortOfExternalInterface(), PORTNOTNULL);
         tBuilder.setOutput(port.number());
 
         ForwardingObjective fo = DefaultForwardingObjective.builder()
@@ -188,8 +191,8 @@
         flowObjectiveService.forward(inboundPacket.receivedFrom().deviceId(), fo);
     }
 
-    private Port getPortNumOfExternalInterface() {
-        return deviceService.getPorts(inboundPacket.receivedFrom().deviceId()).stream()
+    private Port getPortOfExternalInterface() {
+        return deviceService.getPorts(getGatewayNode().id()).stream()
                 .filter(p -> p.annotations().value(PORTNAME).equals(config.gatewayExternalInterfaceName()))
                 .findAny().orElse(null);
     }
@@ -207,6 +210,7 @@
 
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
         tBuilder.setTunnelId(vni)
+                .setEthDst(inboundPacket.parsed().getSourceMAC())
                 .setIpDst(IpAddress.valueOf(iPacket.getSourceAddress()));
 
         switch (iPacket.getProtocol()) {
@@ -226,7 +230,7 @@
                 break;
         }
 
-        tBuilder.extension(buildNiciraExtenstion(deviceId, Ip4Address.valueOf(iPacket.getSourceAddress())), deviceId)
+        tBuilder.extension(buildNiciraExtenstion(deviceId, getHostIpfromOpenstackPort(openstackPort)), deviceId)
                 .setOutput(getTunnelPort(deviceId));
 
         ForwardingObjective fo = DefaultForwardingObjective.builder()
@@ -241,14 +245,37 @@
         flowObjectiveService.forward(inboundPacket.receivedFrom().deviceId(), fo);
     }
 
-    /**
-     * Returns NiciraExtension treatment.
-     *
-     * @param id device id
-     * @param hostIp host ip
-     * @return NiciraExtension treatment
-     */
+    private Ip4Address getHostIpfromOpenstackPort(OpenstackPort openstackPort) {
+        Device device = getDevicefromOpenstackPort(openstackPort);
+        return getIPAddressforDevice(device);
+    }
 
+    private Device getDevicefromOpenstackPort(OpenstackPort openstackPort) {
+        String openstackPortName = PORTNAME_PREFIX_VM + openstackPort.id().substring(0, 11);
+        Device device = StreamSupport.stream(deviceService.getDevices().spliterator(), false)
+                .filter(d -> findPortinDevice(d, openstackPortName))
+                .findAny()
+                .orElse(null);
+        checkNotNull(device, DEVICENOTNULL);
+        return device;
+    }
+
+    private boolean findPortinDevice(Device d, String openstackPortName) {
+        Port port = deviceService.getPorts(d.id())
+                .stream()
+                .filter(p -> p.isEnabled() && p.annotations().value(PORTNAME).equals(openstackPortName))
+                .findAny()
+                .orElse(null);
+        return port != null ? true : false;
+    }
+
+    /**
+     * Builds NiciraExtension for tagging remoteIp of vxlan.
+     *
+     * @param id Device Id of vxlan source device
+     * @param hostIp Remote Ip of vxlan destination device
+     * @return NiciraExtension Treatment
+     */
     public ExtensionTreatment buildNiciraExtenstion(DeviceId id, Ip4Address hostIp) {
         Driver driver = driverService.getDriver(id);
         DriverHandler driverHandler = new DefaultDriverHandler(new DefaultDriverData(driver, id));
@@ -270,8 +297,8 @@
     /**
      * Returns port number of vxlan tunnel.
      *
-     * @param deviceId device id
-     * @return port number of vxlan tunnel
+     * @param deviceId Target Device Id
+     * @return PortNumber
      */
     public PortNumber getTunnelPort(DeviceId deviceId) {
         Port port = deviceService.getPorts(deviceId).stream()
@@ -328,7 +355,7 @@
     private void populateComputeNodeRules(long vni) {
         Device gatewayDevice = getGatewayNode();
 
-        StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
+        StreamSupport.stream(deviceService.getDevices().spliterator(), false)
                 .filter(d -> !checkGatewayNode(d.id()))
                 .forEach(d -> populateRuleToGateway(d, gatewayDevice, vni));
     }
@@ -366,12 +393,8 @@
         return deviceId.toString().equals(config.gatewayBridgeId());
     }
 
-    private long getVni(OpenstackPort openstackPort) {
-        return Long.parseLong(openstackService.network(openstackPort.networkId()).segmentId());
-    }
-
-    private long getVni(OpenstackSubnet openstackSubnet) {
-        return Long.parseLong(openstackService.network(openstackSubnet.networkId()).segmentId());
+    private long getVni(String netId) {
+        return Long.parseLong(openstackService.network(netId).segmentId());
     }
 
     /**
@@ -383,32 +406,122 @@
         OpenstackSubnet openstackSubnet = openstackService.subnet(routerInterface.subnetId());
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
         sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(getVni(openstackSubnet))
+                .matchTunnelId(getVni(openstackSubnet.networkId()))
                 .matchEthDst(GATEWAYMAC);
 
-        StreamSupport.stream(deviceService.getAvailableDevices().spliterator(), false)
+        StreamSupport.stream(deviceService.getDevices().spliterator(), false)
                 .forEach(d -> {
                     if (checkGatewayNode(d.id())) {
-                        removeExternalRule(d.id(), sBuilder, ForwardingObjective.Flag.VERSATILE);
+                        removeRule(d.id(), sBuilder, ForwardingObjective.Flag.VERSATILE, ROUTING_RULE_PRIORITY);
                     } else {
-                        removeExternalRule(d.id(), sBuilder, ForwardingObjective.Flag.SPECIFIC);
+                        removeRule(d.id(), sBuilder, ForwardingObjective.Flag.SPECIFIC, ROUTING_RULE_PRIORITY);
                     }
                 });
 
     }
 
-    private void removeExternalRule(DeviceId id, TrafficSelector.Builder sBuilder, ForwardingObjective.Flag flag) {
+    private void removeRule(DeviceId id, TrafficSelector.Builder sBuilder,
+                            ForwardingObjective.Flag flag, int priority) {
         TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
 
         ForwardingObjective fo = DefaultForwardingObjective.builder()
                 .withSelector(sBuilder.build())
                 .withTreatment(tBuilder.build())
                 .withFlag(flag)
-                .withPriority(ROUTING_RULE_PRIORITY)
+                .withPriority(priority)
                 .fromApp(appId)
                 .remove();
 
         flowObjectiveService.forward(id, fo);
     }
 
+    /**
+     * Populates flow rules for floatingIp configuration.
+     *
+     * @param floatingIP Corresponding floating ip information
+     */
+    public void populateFloatingIpRules(OpenstackFloatingIP floatingIP) {
+        OpenstackPort port = openstackService.port(floatingIP.portId());
+        //1. incoming rules
+        populateFloatingIpIncomingRules(floatingIP, port);
+        //2. outgoing rules
+        populateFloatingIpOutgoingRules(floatingIP, port);
+    }
+
+    private void populateFloatingIpIncomingRules(OpenstackFloatingIP floatingIP, OpenstackPort port) {
+        DeviceId portDeviceId = getDevicefromOpenstackPort(port).id();
+        Device gatewayNode = getGatewayNode();
+        Device portNode = deviceService.getDevice(portDeviceId);
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IpPrefix.valueOf(floatingIP.floatingIpAddress(), 32));
+
+        tBuilder.setEthSrc(MacAddress.valueOf(config.gatewayExternalInterfaceMac()))
+                .setEthDst(port.macAddress())
+                .setIpDst(floatingIP.fixedIpAddress())
+                .setTunnelId(getVni(port.networkId()))
+                .extension(buildNiciraExtenstion(gatewayNode.id(), getIPAddressforDevice(portNode)), gatewayNode.id())
+                .setOutput(getTunnelPort(gatewayNode.id()));
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(tBuilder.build())
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(FLOATING_RULE_PRIORITY)
+                .fromApp(appId)
+                .add();
+
+        flowObjectiveService.forward(getGatewayNode().id(), fo);
+
+    }
+
+    private void populateFloatingIpOutgoingRules(OpenstackFloatingIP floatingIP, OpenstackPort port) {
+        Port outputPort = checkNotNull(getPortOfExternalInterface(), PORTNOTNULL);
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(getVni(port.networkId()))
+                .matchIPSrc(IpPrefix.valueOf(floatingIP.fixedIpAddress(), 32));
+
+        tBuilder.setIpSrc(floatingIP.floatingIpAddress())
+                .setEthSrc(MacAddress.valueOf(config.gatewayExternalInterfaceMac()))
+                .setEthDst(MacAddress.valueOf(config.physicalRouterMac()))
+                .setOutput(outputPort.number());
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(tBuilder.build())
+                .withFlag(ForwardingObjective.Flag.VERSATILE)
+                .withPriority(FLOATING_RULE_PRIORITY)
+                .fromApp(appId)
+                .add();
+
+        flowObjectiveService.forward(getGatewayNode().id(), fo);
+    }
+
+    /**
+     * Removes flow rules for floating ip configuration.
+     *
+     * @param floatingIP Corresponding floating ip information
+     */
+    public void removeFloatingIpRules(OpenstackFloatingIP floatingIP, OpenstackPortInfo portInfo) {
+        TrafficSelector.Builder sOutgoingBuilder = DefaultTrafficSelector.builder();
+        TrafficSelector.Builder sIncomingBuilder = DefaultTrafficSelector.builder();
+
+        sOutgoingBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(portInfo.vni())
+                .matchIPSrc(IpPrefix.valueOf(portInfo.ip(), 32));
+
+        sIncomingBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(IpPrefix.valueOf(floatingIP.floatingIpAddress(), 32));
+
+        removeRule(getGatewayNode().id(), sOutgoingBuilder, ForwardingObjective.Flag.VERSATILE, FLOATING_RULE_PRIORITY);
+        removeRule(getGatewayNode().id(), sIncomingBuilder, ForwardingObjective.Flag.VERSATILE, FLOATING_RULE_PRIORITY);
+    }
+
 }