Fix: configure SNAT to gateway rules on receiving router port events

Change-Id: I4c9c9cae5fef405b4b3ce0baaf2211ec6eee898e
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 211873e..b49ec74 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
@@ -57,6 +57,8 @@
 import org.onosproject.openstacknetworking.api.InstancePortListener;
 import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknetworking.api.OpenstackRouterEvent;
 import org.onosproject.openstacknetworking.api.OpenstackRouterListener;
@@ -69,6 +71,7 @@
 import org.openstack4j.model.network.ExternalGateway;
 import org.openstack4j.model.network.IP;
 import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Port;
 import org.openstack4j.model.network.Router;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
@@ -156,6 +159,7 @@
     private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
     private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
     private final InstancePortListener instPortListener = new InternalInstancePortListener();
+    private final OpenstackNetworkListener osNetworkListener = new InternalNetworkEventListener();
 
     private ApplicationId appId;
     private NodeId localNodeId;
@@ -172,6 +176,7 @@
         localNodeId = clusterService.getLocalNode().id();
         osRouterService.addListener(osRouterListener);
         osNodeService.addListener(osNodeListener);
+        osNetworkService.addListener(osNetworkListener);
         instancePortService.addListener(instPortListener);
         leadershipService.runForLeadership(appId.name());
         packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
@@ -184,6 +189,7 @@
         instancePortService.removeListener(instPortListener);
         osRouterService.removeListener(osRouterListener);
         osNodeService.removeListener(osNodeListener);
+        osNetworkService.removeListener(osNetworkListener);
         instancePortService.removeListener(instPortListener);
         leadershipService.withdraw(appId.name());
         eventExecutor.shutdown();
@@ -502,8 +508,8 @@
         }
     }
 
-    private void setFakeGatewayArpRule(Router router, boolean install) {
-        setFakeGatewayArpRule(router.getExternalGatewayInfo(), install);
+    private void setFakeGatewayArpRuleByRouter(Router router, boolean install) {
+        setFakeGatewayArpRuleByGateway(router.getExternalGatewayInfo(), install);
     }
 
     private Set<IP> getExternalGatewaySnatIps(ExternalGateway extGw) {
@@ -516,47 +522,89 @@
                 .collect(Collectors.toSet());
     }
 
-    private void setFakeGatewayArpRule(ExternalGateway extGw, boolean install) {
+    private void setFakeGatewayArpRuleByGateway(ExternalGateway extGw, boolean install) {
         if (ARP_BROADCAST_MODE.equals(getArpMode())) {
 
             if (extGw == null) {
                 return;
             }
 
-            Set<IP> ips = getExternalGatewaySnatIps(extGw);
+            setFakeGatewayArpRuleByIps(getExternalGatewaySnatIps(extGw), install);
+        }
+    }
 
-            ips.forEach(ip -> {
-                TrafficSelector selector = DefaultTrafficSelector.builder()
-                        .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                        .matchArpOp(ARP.OP_REQUEST)
-                        .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
-                        .build();
+    private void setFakeGatewayArpRuleByIps(Set<IP> ips, boolean install) {
+        ips.forEach(ip -> {
+            TrafficSelector selector = DefaultTrafficSelector.builder()
+                    .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                    .matchArpOp(ARP.OP_REQUEST)
+                    .matchArpTpa(Ip4Address.valueOf(ip.getIpAddress()))
+                    .build();
 
-                TrafficTreatment treatment = DefaultTrafficTreatment.builder()
-                        .setArpOp(ARP.OP_REPLY)
-                        .setArpSha(MacAddress.valueOf(gatewayMac))
-                        .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
-                        .setOutput(PortNumber.IN_PORT)
-                        .build();
+            TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+                    .setArpOp(ARP.OP_REPLY)
+                    .setArpSha(MacAddress.valueOf(gatewayMac))
+                    .setArpSpa(Ip4Address.valueOf(ip.getIpAddress()))
+                    .setOutput(PortNumber.IN_PORT)
+                    .build();
 
-                osNodeService.completeNodes(GATEWAY).forEach(n ->
-                        osFlowRuleService.setRule(
-                                appId,
-                                n.intgBridge(),
-                                selector,
-                                treatment,
-                                PRIORITY_ARP_GATEWAY_RULE,
-                                GW_COMMON_TABLE,
-                                install
-                        )
-                );
+            osNodeService.completeNodes(GATEWAY).forEach(n ->
+                    osFlowRuleService.setRule(
+                            appId,
+                            n.intgBridge(),
+                            selector,
+                            treatment,
+                            PRIORITY_ARP_GATEWAY_RULE,
+                            GW_COMMON_TABLE,
+                            install
+                    )
+            );
 
-                if (install) {
-                    log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
-                } else {
-                    log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
-                }
-            });
+            if (install) {
+                log.info("Install ARP Rule for Gateway Snat {}", ip.getIpAddress());
+            } else {
+                log.info("Uninstall ARP Rule for Gateway Snat {}", ip.getIpAddress());
+            }
+        });
+    }
+
+    /**
+     * An internal network event listener, intended to uninstall ARP rules for
+     * routing the packets destined to external gateway.
+     */
+    private class InternalNetworkEventListener implements OpenstackNetworkListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNetworkEvent event) {
+            Port osPort = event.port();
+            if (osPort == null || osPort.getFixedIps() == null) {
+                return false;
+            }
+
+            // do not allow to proceed without leadership
+            NodeId leader = leadershipService.getLeader(appId.name());
+            return Objects.equals(localNodeId, leader) &&
+                    DEVICE_OWNER_ROUTER_GW.equals(osPort.getDeviceOwner());
+        }
+
+        @Override
+        public void event(OpenstackNetworkEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_PORT_CREATED:
+                case OPENSTACK_PORT_UPDATED:
+                    eventExecutor.execute(() ->
+                        setFakeGatewayArpRuleByIps((Set<IP>) event.port().getFixedIps(), true)
+                    );
+                    break;
+                case OPENSTACK_PORT_REMOVED:
+                    eventExecutor.execute(() ->
+                        setFakeGatewayArpRuleByIps((Set<IP>) event.port().getFixedIps(), false)
+                    );
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
         }
     }
 
@@ -582,25 +630,25 @@
                 case OPENSTACK_ROUTER_CREATED:
                     eventExecutor.execute(() ->
                         // add a router with external gateway
-                        setFakeGatewayArpRule(event.subject(), true)
+                        setFakeGatewayArpRuleByRouter(event.subject(), true)
                     );
                     break;
                 case OPENSTACK_ROUTER_REMOVED:
                     eventExecutor.execute(() ->
                         // remove a router with external gateway
-                        setFakeGatewayArpRule(event.subject(), false)
+                        setFakeGatewayArpRuleByRouter(event.subject(), false)
                     );
                     break;
                 case OPENSTACK_ROUTER_GATEWAY_ADDED:
                     eventExecutor.execute(() ->
                         // add a gateway manually after adding a router
-                        setFakeGatewayArpRule(event.externalGateway(), true)
+                        setFakeGatewayArpRuleByGateway(event.externalGateway(), true)
                     );
                     break;
                 case OPENSTACK_ROUTER_GATEWAY_REMOVED:
                     eventExecutor.execute(() ->
                         // remove a gateway from an existing router
-                        setFakeGatewayArpRule(event.externalGateway(), false)
+                        setFakeGatewayArpRuleByGateway(event.externalGateway(), false)
                     );
                     break;
                 case OPENSTACK_FLOATING_IP_ASSOCIATED:
@@ -823,7 +871,7 @@
 
             osRouterService.routers().stream()
                     .filter(router -> router.getExternalGatewayInfo() != null)
-                    .forEach(router -> setFakeGatewayArpRule(router, install));
+                    .forEach(router -> setFakeGatewayArpRuleByRouter(router, install));
         }
     }
 }