[ONOS-4511] Fix the bug about deleting floatingip or
routerInterface:When deleting floatingip or routerInterface, some flows
are not deleted.

Change-Id: I9fc547e53be9bf2f32edf6f4eb3bc6428ca5d27c
diff --git a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
index 6e0a379..06c3769 100644
--- a/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
+++ b/apps/vtn/vtnmgr/src/main/java/org/onosproject/vtn/manager/impl/VtnManager.java
@@ -103,6 +103,7 @@
 import org.onosproject.vtnrsc.DefaultVirtualPort;
 import org.onosproject.vtnrsc.FixedIp;
 import org.onosproject.vtnrsc.FloatingIp;
+import org.onosproject.vtnrsc.RouterId;
 import org.onosproject.vtnrsc.RouterInterface;
 import org.onosproject.vtnrsc.SecurityGroup;
 import org.onosproject.vtnrsc.SegmentationId;
@@ -246,7 +247,9 @@
                                 .register(BindingHostId.class)
                                 .register(SecurityGroup.class)
                                 .register(IpAddress.class)
-                                .register(DefaultVirtualPort.class);
+                                .register(DefaultVirtualPort.class)
+                                .register(RouterId.class)
+                                .register(TenantRouter.class);
 
         vPortStore = storageService
                 .<VirtualPortId, VirtualPort>eventuallyConsistentMapBuilder()
@@ -354,14 +357,48 @@
 
     @Override
     public void onOvsDetected(Device device) {
+        if (device == null) {
+            log.error("The device is null");
+            return;
+        }
+        if (!mastershipService.isLocalMaster(device.id())) {
+            return;
+        }
         // Create tunnel out flow rules
         applyTunnelOut(device, Objective.Operation.ADD);
+        // apply L3 arp flows
+        Iterable<RouterInterface> interfaces = routerInterfaceService
+                .getRouterInterfaces();
+        interfaces.forEach(routerInf -> {
+            VirtualPort gwPort = virtualPortService.getPort(routerInf.portId());
+            if (gwPort == null) {
+                gwPort = VtnData.getPort(vPortStore, routerInf.portId());
+            }
+            applyL3ArpFlows(device.id(), gwPort, Objective.Operation.ADD);
+        });
     }
 
     @Override
     public void onOvsVanished(Device device) {
+        if (device == null) {
+            log.error("The device is null");
+            return;
+        }
+        if (!mastershipService.isLocalMaster(device.id())) {
+            return;
+        }
         // Remove Tunnel out flow rules
         applyTunnelOut(device, Objective.Operation.REMOVE);
+        // apply L3 arp flows
+        Iterable<RouterInterface> interfaces = routerInterfaceService
+                .getRouterInterfaces();
+        interfaces.forEach(routerInf -> {
+            VirtualPort gwPort = virtualPortService.getPort(routerInf.portId());
+            if (gwPort == null) {
+                gwPort = VtnData.getPort(vPortStore, routerInf.portId());
+            }
+            applyL3ArpFlows(device.id(), gwPort, Objective.Operation.REMOVE);
+        });
     }
 
     @Override
@@ -411,13 +448,6 @@
     }
 
     private void applyTunnelOut(Device device, Objective.Operation type) {
-        if (device == null) {
-            log.error("The device is null");
-            return;
-        }
-        if (!mastershipService.isLocalMaster(device.id())) {
-            return;
-        }
         String controllerIp = VtnData.getControllerIpOfSwitch(device);
         if (controllerIp == null) {
             log.error("Can't find controller of device: {}",
@@ -802,6 +832,8 @@
                 programInterfacesSet(interfacesSet, operation);
             }
         }
+        // apply L3 arp flows
+        applyL3ArpFlows(null, gwPort, operation);
     }
 
     @Override
@@ -829,6 +861,8 @@
             gwPort = VtnData.getPort(vPortStore, routerInf.portId());
         }
         vPortStore.remove(gwPort.portId());
+        // apply L3 arp flows
+        applyL3ArpFlows(null, gwPort, operation);
     }
 
     @Override
@@ -874,6 +908,36 @@
         });
     }
 
+    private void applyL3ArpFlows(DeviceId deviceId, VirtualPort gwPort,
+                                 Objective.Operation operation) {
+        IpAddress ip = null;
+        Iterator<FixedIp> gwIps = gwPort.fixedIps().iterator();
+        if (gwIps.hasNext()) {
+            ip = gwIps.next().ip();
+        }
+        IpAddress gwIp = ip;
+        MacAddress gwMac = gwPort.macAddress();
+        TenantNetwork network = tenantNetworkService
+                .getNetwork(gwPort.networkId());
+        if (deviceId != null) {
+            // Arp rules
+            DriverHandler handler = driverService.createHandler(deviceId);
+            arpService.programArpRules(handler, deviceId, gwIp,
+                                       network.segmentationId(), gwMac,
+                                       operation);
+        } else {
+            Iterable<Device> devices = deviceService.getAvailableDevices();
+            Sets.newHashSet(devices).stream()
+            .filter(d -> Device.Type.SWITCH == d.type()).forEach(d -> {
+                // Arp rules
+                DriverHandler handler = driverService.createHandler(d.id());
+                arpService.programArpRules(handler, d.id(), gwIp,
+                                           network.segmentationId(), gwMac,
+                                           operation);
+            });
+        }
+    }
+
     private void applyEastWestL3Flows(Host h, SegmentationId l3vni,
                                       Objective.Operation operation) {
         if (!mastershipService.isLocalMaster(h.location().deviceId())) {
@@ -905,31 +969,53 @@
         }
         TenantNetwork network = tenantNetworkService
                 .getNetwork(hPort.networkId());
+        IpAddress dstVmIP = srcIp;
+        MacAddress dstVmGwMac = srcVmGwMac;
+        TenantId tenantId = hPort.tenantId();
         // Classifier rules
+        if (operation == Objective.Operation.ADD) {
+            sendEastWestL3Flows(h, srcVmGwMac, l3vni, srcGwIp, network,
+                                dstVmIP, dstVmGwMac, operation);
+        } else if (operation == Objective.Operation.REMOVE) {
+            FloatingIp floatingIp = null;
+            Iterable<FloatingIp> floatingIps = floatingIpService.getFloatingIps();
+            Set<FloatingIp> floatingIpSet = Sets.newHashSet(floatingIps).stream()
+                    .filter(f -> f.tenantId().equals(tenantId))
+                    .collect(Collectors.toSet());
+            for (FloatingIp f : floatingIpSet) {
+                IpAddress fixedIp = f.fixedIp();
+                if (fixedIp != null && fixedIp.equals(srcIp)) {
+                    floatingIp = f;
+                    break;
+                }
+            }
+            if (floatingIp == null) {
+                sendEastWestL3Flows(h, srcVmGwMac, l3vni, srcGwIp, network,
+                                    dstVmIP, dstVmGwMac, operation);
+            }
+        }
+    }
+
+    private void sendEastWestL3Flows(Host h, MacAddress srcVmGwMac,
+                                     SegmentationId l3vni, IpAddress srcGwIp,
+                                     TenantNetwork network, IpAddress dstVmIP,
+                                     MacAddress dstVmGwMac,
+                                     Objective.Operation operation) {
         classifierService
                 .programL3InPortClassifierRules(h.location().deviceId(),
                                                 h.location().port(), h.mac(),
                                                 srcVmGwMac, l3vni, operation);
-        classifierService.programArpClassifierRules(h.location().deviceId(),
-                                                    h.location().port(), srcGwIp,
-                                                    network.segmentationId(),
-                                                    operation);
-        // Arp rules
-        if (operation == Objective.Operation.ADD) {
-            DriverHandler handler = driverService.createHandler(h.location().deviceId());
-            arpService.programArpRules(handler, h.location().deviceId(), srcGwIp,
-                                       network.segmentationId(), srcVmGwMac,
-                                       operation);
-        }
+        classifierService
+                .programArpClassifierRules(h.location().deviceId(),
+                                           h.location().port(), srcGwIp,
+                                           network.segmentationId(), operation);
         Iterable<Device> devices = deviceService.getAvailableDevices();
-        IpAddress srcArpIp = srcIp;
-        MacAddress srcArpGwMac = srcVmGwMac;
         Sets.newHashSet(devices).stream()
                 .filter(d -> Device.Type.SWITCH == d.type()).forEach(d -> {
                     // L3FWD rules
-                    l3ForwardService.programRouteRules(d.id(), l3vni, srcArpIp,
+                    l3ForwardService.programRouteRules(d.id(), l3vni, dstVmIP,
                                                        network.segmentationId(),
-                                                       srcArpGwMac, h.mac(),
+                                                       dstVmGwMac, h.mac(),
                                                        operation);
                 });
     }
@@ -967,13 +1053,14 @@
                 // Floating ip BIND
                 if (type == VtnRscEvent.Type.FLOATINGIP_BIND) {
                     vPortStore.put(fipPort.portId(), fipPort);
-                    applyNorthSouthL3Flows(deviceId, host, vmPort, fipPort,
-                                           floaingIp, l3vni, exPort,
-                                           Objective.Operation.ADD);
+                    applyNorthSouthL3Flows(deviceId, false, tenantRouter, host,
+                                           vmPort, fipPort, floaingIp, l3vni,
+                                           exPort, Objective.Operation.ADD);
                 } else if (type == VtnRscEvent.Type.FLOATINGIP_UNBIND) {
                     // Floating ip UNBIND
-                    applyNorthSouthL3Flows(deviceId, host, vmPort, fipPort,
-                                           floaingIp, l3vni, exPort,
+                    applyNorthSouthL3Flows(deviceId, false, tenantRouter, host,
+                                           vmPort, fipPort, floaingIp, l3vni,
+                                           exPort,
                                            Objective.Operation.REMOVE);
                     vPortStore.remove(fipPort.portId());
                 }
@@ -981,7 +1068,27 @@
         }
     }
 
-    private void applyNorthSouthL3Flows(DeviceId deviceId, Host host,
+    private void sendNorthSouthL3Flows(DeviceId deviceId, FloatingIp floatingIp,
+                                       IpAddress dstVmGwIp,
+                                       MacAddress dstVmGwMac,
+                                       SegmentationId l3vni,
+                                       TenantNetwork vmNetwork,
+                                       VirtualPort vmPort, Host host,
+                                       Objective.Operation operation) {
+        l3ForwardService
+                .programRouteRules(deviceId, l3vni, floatingIp.fixedIp(),
+                                   vmNetwork.segmentationId(), dstVmGwMac,
+                                   vmPort.macAddress(), operation);
+        classifierService.programL3InPortClassifierRules(deviceId,
+                                                         host.location().port(),
+                                                         host.mac(), dstVmGwMac,
+                                                         l3vni, operation);
+        classifierService.programArpClassifierRules(deviceId, host.location()
+                .port(), dstVmGwIp, vmNetwork.segmentationId(), operation);
+    }
+
+    private void applyNorthSouthL3Flows(DeviceId deviceId, boolean hostFlag,
+                                        TenantRouter tenantRouter, Host host,
                                         VirtualPort vmPort, VirtualPort fipPort,
                                         FloatingIp floatingIp,
                                         SegmentationId l3vni, Port exPort,
@@ -1014,31 +1121,41 @@
         dnatService.programRules(deviceId, floatingIp.floatingIp(),
                                      fGwMac, floatingIp.fixedIp(),
                                      l3vni, operation);
-        l3ForwardService
-                .programRouteRules(deviceId, l3vni, floatingIp.fixedIp(),
-                                   vmNetwork.segmentationId(), dstVmGwMac,
-                                   vmPort.macAddress(), operation);
 
         // L3 uplink traffic flow
-        classifierService.programL3InPortClassifierRules(deviceId,
-                                                         host.location().port(),
-                                                         host.mac(), dstVmGwMac,
-                                                         l3vni, operation);
+        if (operation == Objective.Operation.ADD) {
+            sendNorthSouthL3Flows(deviceId, floatingIp, dstVmGwIp, dstVmGwMac,
+                                  l3vni, vmNetwork, vmPort, host, operation);
+            l2ForwardService.programLocalOut(deviceId,
+                                             fipNetwork.segmentationId(),
+                                             exPort.number(), fGwMac, operation);
+        } else if (operation == Objective.Operation.REMOVE) {
+            if (hostFlag || (!hostFlag
+                    && routerInfFlagOfTenantRouter.get(tenantRouter) == null)) {
+                sendNorthSouthL3Flows(deviceId, floatingIp, dstVmGwIp, dstVmGwMac,
+                                      l3vni, vmNetwork, vmPort, host, operation);
+            }
+            Iterable<FloatingIp> floatingIps = floatingIpService.getFloatingIps();
+            boolean exPortFlag = true;
+            if (floatingIps != null) {
+                Set<FloatingIp> floatingIpSet = Sets.newHashSet(floatingIps);
+                for (FloatingIp fip : floatingIpSet) {
+                    if (fip.fixedIp() != null) {
+                        exPortFlag = false;
+                        break;
+                    }
+                }
+            }
+            if (exPortFlag) {
+                l2ForwardService.programLocalOut(deviceId,
+                                                 fipNetwork.segmentationId(),
+                                                 exPort.number(), fGwMac, operation);
+            }
+        }
         snatService.programRules(deviceId, l3vni, floatingIp.fixedIp(),
                                      fGwMac, exPortMac,
                                      floatingIp.floatingIp(),
                                      fipNetwork.segmentationId(), operation);
-        classifierService.programArpClassifierRules(deviceId, host.location().port(),
-                                                    dstVmGwIp, vmNetwork.segmentationId(),
-                                                    operation);
-        if (operation == Objective.Operation.ADD) {
-            arpService.programArpRules(handler, deviceId, dstVmGwIp,
-                                       vmNetwork.segmentationId(), dstVmGwMac,
-                                       operation);
-            l2ForwardService.programLocalOut(deviceId,
-                                             fipNetwork.segmentationId(),
-                                             exPort.number(), fGwMac, operation);
-        }
     }
 
     private Port getExPort(DeviceId deviceId) {
@@ -1139,7 +1256,7 @@
                 .collect(Collectors.toSet());
         for (FloatingIp f : floatingIpSet) {
             IpAddress fixedIp = f.fixedIp();
-            if (fixedIp.equals(hostIp)) {
+            if (fixedIp != null && fixedIp.equals(hostIp)) {
                 floatingIp = f;
                 break;
             }
@@ -1154,8 +1271,9 @@
                 fipPort = VtnData.getPort(vPortStore, floatingIp.networkId(),
                                           floatingIp.floatingIp());
             }
-            applyNorthSouthL3Flows(deviceId, host, port, fipPort, floatingIp,
-                                   l3vni, exPort, operation);
+            applyNorthSouthL3Flows(deviceId, true, tenantRouter, host, port,
+                                   fipPort, floatingIp, l3vni, exPort,
+                                   operation);
         }
     }