Refactored OpenstackRouting to support multiple gateway nodes

Change-Id: I6870ca9a4fd6f6b1cf2d2be72f52ef87827e1d2c
diff --git a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java
index 2c9a304..e31f410 100644
--- a/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/routing/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingArpHandler.java
@@ -15,99 +15,74 @@
  */
 package org.onosproject.openstacknetworking.routing;
 
-import com.google.common.collect.Lists;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
 import org.onlab.packet.ARP;
-import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpAddress;
 import org.onlab.packet.MacAddress;
-import org.onosproject.core.ApplicationId;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
-import org.onosproject.net.flow.TrafficSelector;
 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.PacketPriority;
+import org.onosproject.net.packet.PacketProcessor;
 import org.onosproject.net.packet.PacketService;
 import org.onosproject.openstackinterface.OpenstackInterfaceService;
 import org.onosproject.openstackinterface.OpenstackPort;
 import org.onosproject.scalablegateway.api.ScalableGatewayService;
 import org.onosproject.openstacknetworking.Constants;
-import org.onosproject.openstacknode.OpenstackNodeService;
 import org.slf4j.Logger;
 
 import java.nio.ByteBuffer;
-import java.util.List;
-import java.util.Optional;
+import java.util.concurrent.ExecutorService;
 
-import static com.google.common.base.Preconditions.checkNotNull;
+import static java.util.concurrent.Executors.newSingleThreadExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.Constants.DEVICE_OWNER_FLOATING_IP;
+import static org.onosproject.openstacknetworking.Constants.DEVICE_OWNER_ROUTER_GATEWAY;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
- * Handle ARP packet sent from Openstack Gateway nodes.
+ * Handle ARP, ICMP and NAT packets from gateway nodes.
  */
+@Component(immediate = true)
 public class OpenstackRoutingArpHandler {
-    protected final Logger log = getLogger(getClass());
+    private final Logger log = getLogger(getClass());
 
-    private final PacketService packetService;
-    private final OpenstackInterfaceService openstackService;
-    private final ScalableGatewayService gatewayService;
-    private final OpenstackNodeService nodeService;
-    private static final String NETWORK_ROUTER_GATEWAY = "network:router_gateway";
-    private static final String NETWORK_FLOATING_IP = "network:floatingip";
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected PacketService packetService;
 
-    /**
-     * Default constructor.
-     *
-     * @param packetService packet service
-     * @param openstackService openstackInterface service
-     * @param gatewayService gateway service
-     * @param nodeService openstackNodeService
-     */
-    OpenstackRoutingArpHandler(PacketService packetService, OpenstackInterfaceService openstackService,
-                               OpenstackNodeService nodeService, ScalableGatewayService gatewayService) {
-        this.packetService = packetService;
-        this.openstackService = checkNotNull(openstackService);
-        this.nodeService = nodeService;
-        this.gatewayService = gatewayService;
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackInterfaceService openstackService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected ScalableGatewayService gatewayService;
+
+    private final ExecutorService executorService =
+            newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "packet-event", log));
+
+    private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
+
+    @Activate
+    protected void activate() {
+        packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
+        log.info("Started");
     }
 
-    /**
-     * Requests ARP packet to GatewayNode.
-     *
-     * @param appId application id
-     */
-    public void requestPacket(ApplicationId appId) {
-
-        TrafficSelector arpSelector = DefaultTrafficSelector.builder()
-                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
-                .build();
-
-        getExternalInfo().forEach(deviceId ->
-                packetService.requestPackets(arpSelector,
-                        PacketPriority.CONTROL,
-                        appId,
-                        Optional.of(deviceId))
-        );
+    @Deactivate
+    protected void deactivate() {
+        packetService.removeProcessor(packetProcessor);
+        log.info("Stopped");
     }
 
-    /**
-     * Handles ARP packet.
-     *
-     * @param context packet context
-     * @param ethernet ethernet
-     */
-    public void processArpPacketFromRouter(PacketContext context, Ethernet ethernet) {
-        checkNotNull(context, "context can not be null");
-        checkNotNull(ethernet, "ethernet can not be null");
-
-
+    private void processArpPacket(PacketContext context, Ethernet ethernet) {
         ARP arp = (ARP) ethernet.getPayload();
-
-        log.debug("arpEvent called from {} to {}",
+        log.trace("arpEvent called from {} to {}",
                 Ip4Address.valueOf(arp.getSenderProtocolAddress()).toString(),
                 Ip4Address.valueOf(arp.getTargetProtocolAddress()).toString());
 
@@ -116,13 +91,11 @@
         }
 
         IpAddress targetIp = Ip4Address.valueOf(arp.getTargetProtocolAddress());
-
         if (getTargetMacForTargetIp(targetIp.getIp4Address()) == MacAddress.NONE) {
-                return;
+            return;
         }
-        // FIXME: Set the correct gateway
-        MacAddress targetMac = Constants.GW_EXT_INT_MAC;
 
+        MacAddress targetMac = Constants.DEFAULT_EXTERNAL_ROUTER_MAC;
         Ethernet ethReply = ARP.buildArpReply(targetIp.getIp4Address(),
                 targetMac, ethernet);
 
@@ -136,22 +109,35 @@
                 ByteBuffer.wrap(ethReply.serialize())));
     }
 
+    private class InternalPacketProcessor implements PacketProcessor {
+
+        @Override
+        public void process(PacketContext context) {
+            if (context.isHandled()) {
+                return;
+            } else if (!gatewayService.getGatewayDeviceIds().contains(
+                    context.inPacket().receivedFrom().deviceId())) {
+                // return if the packet is not from gateway nodes
+                return;
+            }
+
+            InboundPacket pkt = context.inPacket();
+            Ethernet ethernet = pkt.parsed();
+            if (ethernet != null &&
+                    ethernet.getEtherType() == Ethernet.TYPE_ARP) {
+                executorService.execute(() -> processArpPacket(context, ethernet));
+            }
+        }
+    }
+
+    // TODO make a cache for the MAC, not a good idea to REST call every time it gets ARP request
     private MacAddress getTargetMacForTargetIp(Ip4Address targetIp) {
         OpenstackPort port = openstackService.ports().stream()
-                .filter(p -> p.deviceOwner().equals(NETWORK_ROUTER_GATEWAY) ||
-                             p.deviceOwner().equals(NETWORK_FLOATING_IP))
+                .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_GATEWAY) ||
+                        p.deviceOwner().equals(DEVICE_OWNER_FLOATING_IP))
                 .filter(p -> p.fixedIps().containsValue(targetIp.getIp4Address()))
                 .findAny().orElse(null);
 
-        if (port == null) {
-            return MacAddress.NONE;
-        }
-        return port.macAddress();
-    }
-
-    private List<DeviceId> getExternalInfo() {
-        List<DeviceId> externalInfoList = Lists.newArrayList();
-        gatewayService.getGatewayDeviceIds().forEach(externalInfoList::add);
-        return externalInfoList;
+        return port == null ? MacAddress.NONE : port.macAddress();
     }
 }