Send GARP packet in case floating ip is associated and gateway node is updated.

Change-Id: I9aa4172bebdc25de5998b3b3123639bce7d865a0
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index b755017..db15e15 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -42,11 +42,21 @@
 import org.apache.sshd.client.future.OpenFuture;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
+import org.onlab.packet.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
 import org.onosproject.cfg.ConfigProperty;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketService;
 import org.onosproject.openstacknetworking.api.Constants.VnicType;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
 import org.onosproject.openstacknetworking.api.InstancePort;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
@@ -63,10 +73,13 @@
 import org.openstack4j.core.transport.ObjectMapperSingleton;
 import org.openstack4j.model.ModelEntity;
 import org.openstack4j.model.common.Identifier;
+import org.openstack4j.model.network.ExternalGateway;
 import org.openstack4j.model.network.NetFloatingIP;
 import org.openstack4j.model.network.Network;
 import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
 import org.openstack4j.model.network.RouterInterface;
+import org.openstack4j.model.network.Subnet;
 import org.openstack4j.openstack.OSFactory;
 import org.openstack4j.openstack.networking.domain.NeutronRouterInterface;
 import org.slf4j.Logger;
@@ -84,6 +97,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.ByteBuffer;
 import java.security.cert.X509Certificate;
 import java.util.Collection;
 import java.util.HashMap;
@@ -846,11 +860,126 @@
         return traceResult;
     }
 
+    /**
+     * Returns the floating ip with supplied instance port.
+     *
+     * @param instancePort instance port
+     * @param osRouterAdminService openstack router admin service
+     * @return floating ip
+     */
+    public static NetFloatingIP floatingIpByInstancePort(InstancePort instancePort,
+                                                         OpenstackRouterAdminService osRouterAdminService) {
+        return osRouterAdminService.floatingIps().stream()
+                .filter(netFloatingIP -> netFloatingIP.getPortId() != null)
+                .filter(netFloatingIP -> netFloatingIP.getPortId().equals(instancePort.portId()))
+                .findAny().orElse(null);
+    }
+
+    /**
+     * Sends GARP packet with supplied floating ip information.
+     *
+     * @param floatingIP floating ip
+     * @param instancePort instance port
+     * @param vlanId vlain id
+     * @param gatewayNode gateway node
+     * @param packetService packet service
+     */
+    public static void processGratuitousArpPacketForFloatingIp(NetFloatingIP floatingIP,
+                                                               InstancePort instancePort,
+                                                               VlanId vlanId,
+                                                               OpenstackNode gatewayNode,
+                                                               PacketService packetService) {
+        Ethernet ethernet = buildGratuitousArpPacket(floatingIP, instancePort, vlanId);
+
+        TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                .setOutput(gatewayNode.uplinkPortNum()).build();
+
+        packetService.emit(new DefaultOutboundPacket(gatewayNode.intgBridge(), treatment,
+                ByteBuffer.wrap(ethernet.serialize())));
+    }
+
+    /**
+     * Returns the external peer router with supplied network information.
+     *
+     * @param network network
+     * @param osNetworkService openstack network service
+     * @param osRouterAdminService openstack router admin service
+     * @return external peer router
+     */
+    public static ExternalPeerRouter externalPeerRouterForNetwork(Network network,
+                                                                  OpenstackNetworkService osNetworkService,
+                                                                  OpenstackRouterAdminService osRouterAdminService) {
+        if (network == null) {
+            return null;
+        }
+
+        Subnet subnet = osNetworkService.subnets(network.getId()).stream().findAny().orElse(null);
+
+        if (subnet == null) {
+            return null;
+        }
+
+        RouterInterface osRouterIface = osRouterAdminService.routerInterfaces().stream()
+                .filter(i -> Objects.equals(i.getSubnetId(), subnet.getId()))
+                .findAny().orElse(null);
+        if (osRouterIface == null) {
+            return null;
+        }
+
+        Router osRouter = osRouterAdminService.router(osRouterIface.getId());
+        if (osRouter == null) {
+            return null;
+        }
+        if (osRouter.getExternalGatewayInfo() == null) {
+            return null;
+        }
+
+        ExternalGateway exGatewayInfo = osRouter.getExternalGatewayInfo();
+        return osNetworkService.externalPeerRouter(exGatewayInfo);
+
+    }
+
     private static boolean isDirectPort(String portName) {
         return portNamePrefixMap().values().stream().anyMatch(p -> portName.startsWith(p));
     }
 
     /**
+     * Returns GARP packet with supplied floating ip and instance port information.
+     *
+     * @param floatingIP floating ip
+     * @param instancePort instance port
+     * @param vlanId vlan id
+     * @return GARP packet
+     */
+    private static Ethernet buildGratuitousArpPacket(NetFloatingIP floatingIP,
+                                                     InstancePort instancePort,
+                                                     VlanId vlanId) {
+        Ethernet ethernet = new Ethernet();
+        ethernet.setDestinationMACAddress(MacAddress.BROADCAST);
+        ethernet.setSourceMACAddress(instancePort.macAddress());
+        ethernet.setEtherType(Ethernet.TYPE_ARP);
+        ethernet.setVlanID(vlanId.id());
+
+        ARP arp = new ARP();
+        arp.setOpCode(ARP.OP_REPLY);
+        arp.setProtocolType(ARP.PROTO_TYPE_IP);
+        arp.setHardwareType(ARP.HW_TYPE_ETHERNET);
+
+        arp.setProtocolAddressLength((byte) Ip4Address.BYTE_LENGTH);
+        arp.setHardwareAddressLength((byte) Ethernet.DATALAYER_ADDRESS_LENGTH);
+
+        arp.setSenderHardwareAddress(instancePort.macAddress().toBytes());
+        arp.setTargetHardwareAddress(MacAddress.BROADCAST.toBytes());
+
+        arp.setSenderProtocolAddress(Ip4Address.valueOf(floatingIP.getFloatingIpAddress()).toInt());
+        arp.setTargetProtocolAddress(Ip4Address.valueOf(floatingIP.getFloatingIpAddress()).toInt());
+
+        ethernet.setPayload(arp);
+
+        return ethernet;
+    }
+
+    /**
      * Builds up and a complete endpoint URL from gateway node.
      *
      * @param node gateway node