ONOS-5182 Refactored SONA to cache network states

Change-Id: Ib316fa5fa5d36e9da370a1578ac55de4a8dd9b04
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index b4ae36c..2a980d3 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -25,64 +25,97 @@
 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.onlab.util.Tools;
-import org.onosproject.net.Host;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+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.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.OpenstackNetwork;
-import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
+import org.openstack4j.model.network.Subnet;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+
 import java.nio.ByteBuffer;
 import java.util.Dictionary;
 import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.openstacknetworking.api.Constants.*;
+import static org.onosproject.openstacknetworking.api.Constants.DEFAULT_GATEWAY_MAC_STR;
+import static org.onosproject.openstacknetworking.api.Constants.OPENSTACK_NETWORKING_APP_ID;
 
 /**
  * Handles ARP packet from VMs.
  */
 @Component(immediate = true)
-public final class OpenstackSwitchingArpHandler extends AbstractVmHandler {
+public final class OpenstackSwitchingArpHandler {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String GATEWAY_MAC = "gatewayMac";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
+    protected ComponentConfigService configService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNetworkService osNetworkService;
 
     @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
             label = "Fake MAC address for virtual network subnet gateway")
     private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
 
     private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
-    private final Set<Ip4Address> gateways = Sets.newConcurrentHashSet();
+    private final InternalOpenstackNetworkListener osNetworkListener =
+            new InternalOpenstackNetworkListener();
+    private final Set<IpAddress> gateways = Sets.newConcurrentHashSet();
+
+    private ApplicationId appId;
 
     @Activate
     protected void activate() {
+        appId = coreService.registerApplication(OPENSTACK_NETWORKING_APP_ID);
+        configService.registerProperties(getClass());
+        osNetworkService.addListener(osNetworkListener);
         packetService.addProcessor(packetProcessor, PacketProcessor.director(0));
-        super.activate();
+        osNetworkService.subnets().forEach(this::addSubnetGateway);
+        requestPacket();
+
+        log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
         packetService.removeProcessor(packetProcessor);
-        super.deactivate();
+        osNetworkService.removeListener(osNetworkListener);
+        configService.unregisterProperties(getClass(), false);
+
+        log.info("Stopped");
     }
 
     @Modified
@@ -98,12 +131,41 @@
         log.info("Modified");
     }
 
+    private void requestPacket() {
+        TrafficSelector selector = DefaultTrafficSelector.builder()
+                .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+                .build();
+
+        packetService.requestPackets(
+                selector,
+                PacketPriority.CONTROL,
+                appId);
+    }
+
+    private void addSubnetGateway(Subnet osSubnet) {
+        if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+            return;
+        }
+        IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+        gateways.add(gatewayIp);
+        log.debug("Added ARP proxy entry IP:{}", gatewayIp);
+    }
+
+    private void removeSubnetGateway(Subnet osSubnet) {
+        if (Strings.isNullOrEmpty(osSubnet.getGateway())) {
+            return;
+        }
+        IpAddress gatewayIp = IpAddress.valueOf(osSubnet.getGateway());
+        gateways.remove(gatewayIp);
+        log.debug("Removed ARP proxy entry IP:{}", gatewayIp);
+    }
+
     /**
      * Processes ARP request packets.
      * It checks if the target IP is owned by a known host first and then ask to
      * OpenStack if it's not. This ARP proxy does not support overlapping IP.
      *
-     * @param context packet context
+     * @param context   packet context
      * @param ethPacket ethernet packet
      */
     private void processPacketIn(PacketContext context, Ethernet ethPacket) {
@@ -112,15 +174,18 @@
             return;
         }
 
-        Ip4Address targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
-        MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
-                getMacFromHostService(targetIp);
-        if (replyMac.equals(MacAddress.NONE)) {
-            replyMac = getMacFromOpenstack(targetIp);
+        InstancePort srcInstPort = instancePortService.instancePort(ethPacket.getSourceMAC());
+        if (srcInstPort == null) {
+            log.trace("Failed to find source instance port(MAC:{})",
+                    ethPacket.getSourceMAC());
+            return;
         }
 
+        IpAddress targetIp = Ip4Address.valueOf(arpPacket.getTargetProtocolAddress());
+        MacAddress replyMac = gateways.contains(targetIp) ? MacAddress.valueOf(gatewayMac) :
+                getMacFromHostOpenstack(targetIp, srcInstPort.networkId());
         if (replyMac == MacAddress.NONE) {
-            log.debug("Failed to find MAC address for {}", targetIp.toString());
+            log.trace("Failed to find MAC address for {}", targetIp);
             return;
         }
 
@@ -141,66 +206,24 @@
 
     /**
      * Returns MAC address of a host with a given target IP address by asking to
-     * OpenStack. It does not support overlapping IP.
-     *
-     * @param targetIp target ip address
-     * @return mac address, or null if it fails to fetch the mac
-     */
-    private MacAddress getMacFromOpenstack(IpAddress targetIp) {
-        checkNotNull(targetIp);
-
-        OpenstackPort openstackPort = openstackService.ports()
-                .stream()
-                .filter(port -> port.fixedIps().containsValue(targetIp.getIp4Address()))
-                .findFirst()
-                .orElse(null);
-
-        if (openstackPort != null) {
-            log.debug("Found MAC from OpenStack for {}", targetIp.toString());
-            return openstackPort.macAddress();
-        } else {
-            return MacAddress.NONE;
-        }
-    }
-
-    /**
-     * Returns MAC address of a host with a given target IP address by asking to
-     * host service. It does not support overlapping IP.
+     * instance port service.
      *
      * @param targetIp target ip
-     * @return mac address, or null if it fails to find the mac
+     * @param osNetId  openstack network id of the source instance port
+     * @return mac address, or none mac address if it fails to find the mac
      */
-    private MacAddress getMacFromHostService(IpAddress targetIp) {
+    private MacAddress getMacFromHostOpenstack(IpAddress targetIp, String osNetId) {
         checkNotNull(targetIp);
 
-        Host host = hostService.getHostsByIp(targetIp)
-                .stream()
-                .findFirst()
-                .orElse(null);
-
-        if (host != null) {
-            log.debug("Found MAC from host service for {}", targetIp.toString());
-            return host.mac();
+        InstancePort instPort = instancePortService.instancePort(targetIp, osNetId);
+        if (instPort != null) {
+            log.trace("Found MAC from host service for {}", targetIp);
+            return instPort.macAddress();
         } else {
             return MacAddress.NONE;
         }
     }
 
-    @Override
-    protected void hostDetected(Host host) {
-        OpenstackNetwork osNet = openstackService.network(host.annotations().value(NETWORK_ID));
-        if (osNet == null) {
-            log.warn("Failed to get OpenStack network for {}", host);
-            return;
-        }
-        osNet.subnets().forEach(subnet -> gateways.add(Ip4Address.valueOf(subnet.gatewayIp())));
-    }
-
-    @Override
-    protected void hostRemoved(Host host) {
-        // TODO remove subnet gateway from gateways if no hosts exists on that subnet
-    }
-
     private class InternalPacketProcessor implements PacketProcessor {
 
         @Override
@@ -216,4 +239,38 @@
             processPacketIn(context, ethPacket);
         }
     }
+
+    private class InternalOpenstackNetworkListener implements OpenstackNetworkListener {
+
+        @Override
+        public boolean isRelevant(OpenstackNetworkEvent event) {
+            Subnet osSubnet = event.subnet();
+            if (osSubnet == null) {
+                return false;
+            }
+            return !Strings.isNullOrEmpty(osSubnet.getGateway());
+        }
+
+        @Override
+        public void event(OpenstackNetworkEvent event) {
+            switch (event.type()) {
+                case OPENSTACK_SUBNET_CREATED:
+                case OPENSTACK_SUBNET_UPDATED:
+                    addSubnetGateway(event.subnet());
+                    break;
+                case OPENSTACK_SUBNET_REMOVED:
+                    removeSubnetGateway(event.subnet());
+                    break;
+                case OPENSTACK_NETWORK_CREATED:
+                case OPENSTACK_NETWORK_UPDATED:
+                case OPENSTACK_NETWORK_REMOVED:
+                case OPENSTACK_PORT_CREATED:
+                case OPENSTACK_PORT_UPDATED:
+                case OPENSTACK_PORT_REMOVED:
+                default:
+                    // do nothing for the other events
+                    break;
+            }
+        }
+    }
 }