Refactored OpenstackRouting to support multiple gateway nodes

Change-Id: I6870ca9a4fd6f6b1cf2d2be72f52ef87827e1d2c
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/AbstractVmHandler.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/AbstractVmHandler.java
new file mode 100644
index 0000000..58b89a6
--- /dev/null
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/AbstractVmHandler.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking;
+
+import org.onlab.osgi.DefaultServiceDirectory;
+import org.onlab.osgi.ServiceDirectory;
+import org.onlab.packet.Ip4Address;
+import org.onlab.util.Tools;
+import org.onosproject.core.CoreService;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Host;
+import org.onosproject.net.host.HostEvent;
+import org.onosproject.net.host.HostListener;
+import org.onosproject.net.host.HostService;
+import org.slf4j.Logger;
+
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.stream.Collectors;
+
+import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.openstacknetworking.Constants.*;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides abstract virtual machine handler.
+ */
+public abstract class AbstractVmHandler {
+    protected final Logger log = getLogger(getClass());
+
+    protected final ExecutorService eventExecutor = newSingleThreadScheduledExecutor(
+            groupedThreads(this.getClass().getSimpleName(), "event-handler"));
+
+    protected CoreService coreService;
+    protected MastershipService mastershipService;
+    protected HostService hostService;
+
+    protected HostListener hostListener = new InternalHostListener();
+
+    protected void activate() {
+        ServiceDirectory services = new DefaultServiceDirectory();
+        coreService = services.get(CoreService.class);
+        mastershipService = services.get(MastershipService.class);
+        hostService = services.get(HostService.class);
+        hostService.addListener(hostListener);
+
+        log.info("Started");
+    }
+
+    protected void deactivate() {
+        hostService.removeListener(hostListener);
+        eventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    /**
+     * Performs any action when a host is detected.
+     *
+     * @param host detected host
+     */
+    protected abstract void hostDetected(Host host);
+
+    /**
+     * Performs any action when a host is removed.
+     *
+     * @param host removed host
+     */
+    protected abstract void hostRemoved(Host host);
+
+    protected boolean isValidHost(Host host) {
+        return !host.ipAddresses().isEmpty() &&
+                host.annotations().value(VXLAN_ID) != null &&
+                host.annotations().value(NETWORK_ID) != null &&
+                host.annotations().value(TENANT_ID) != null &&
+                host.annotations().value(PORT_ID) != null;
+    }
+
+    protected Set<Host> getVmsInDifferentCnode(Host host) {
+        return Tools.stream(hostService.getHosts())
+                .filter(h -> !h.location().deviceId().equals(host.location().deviceId()))
+                .filter(this::isValidHost)
+                .filter(h -> Objects.equals(getVni(h), getVni(host)))
+                .collect(Collectors.toSet());
+    }
+
+    protected Optional<Host> getVmByPortId(String portId) {
+        return Tools.stream(hostService.getHosts())
+                .filter(this::isValidHost)
+                .filter(host -> host.annotations().value(PORT_ID).equals(portId))
+                .findFirst();
+    }
+
+    protected Ip4Address getIp(Host host) {
+        return host.ipAddresses().stream().findFirst().get().getIp4Address();
+    }
+
+    protected String getVni(Host host) {
+        return host.annotations().value(VXLAN_ID);
+    }
+
+    protected String getTenantId(Host host) {
+        return host.annotations().value(TENANT_ID);
+    }
+
+    private class InternalHostListener implements HostListener {
+
+        @Override
+        public void event(HostEvent event) {
+            Host host = event.subject();
+            if (!mastershipService.isLocalMaster(host.location().deviceId())) {
+                // do not allow to proceed without mastership
+                return;
+            }
+
+            if (!isValidHost(host)) {
+                log.debug("Invalid host event, ignore it {}", host);
+                return;
+            }
+
+            switch (event.type()) {
+                case HOST_UPDATED:
+                case HOST_ADDED:
+                    eventExecutor.execute(() -> hostDetected(host));
+                    break;
+                case HOST_REMOVED:
+                    eventExecutor.execute(() -> hostRemoved(host));
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java
index 54334b9..dbcb521 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/Constants.java
@@ -28,23 +28,27 @@
     private Constants() {
     }
 
-    public static final String APP_ID = "org.onosproject.openstackswitching";
+    public static final String SWITCHING_APP_ID = "org.onosproject.openstackswitching";
+    public static final String ROUTING_APP_ID = "org.onosproject.openstackrouting";
 
-    public static final String PORTNAME_PREFIX_VM = "tap";
-    public static final String PORTNAME_PREFIX_ROUTER = "qr-";
-    public static final String PORTNAME_PREFIX_TUNNEL = "vxlan";
+    public static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
+    public static final String DEVICE_OWNER_ROUTER_GATEWAY = "network:router_gateway";
+    public static final String DEVICE_OWNER_FLOATING_IP = "network:floatingip";
 
-    public static final MacAddress GATEWAY_MAC = MacAddress.valueOf("1f:1f:1f:1f:1f:1f");
+    public static final String PORT_NAME_PREFIX_VM = "tap";
+    public static final String PORT_NAME_PREFIX_TUNNEL = "vxlan";
 
-    // TODO: Please change these valuses following the way vrouter is implemented
-    public static final MacAddress GW_EXT_INT_MAC = MacAddress.valueOf("56:e6:30:a6:8c:e5");
-    public static final MacAddress PHY_ROUTER_MAC = MacAddress.valueOf("00:00:00:00:01:01");
+    public static final String DEFAULT_GATEWAY_MAC_STR = "fe:00:00:00:00:02";
+    public static final MacAddress DEFAULT_GATEWAY_MAC = MacAddress.valueOf(DEFAULT_GATEWAY_MAC_STR);
+    // TODO make this configurable
+    public static final MacAddress DEFAULT_EXTERNAL_ROUTER_MAC = MacAddress.valueOf("fe:00:00:00:00:01");
 
     public static final Ip4Address DNS_SERVER_IP = Ip4Address.valueOf("8.8.8.8");
     public static final IpPrefix IP_PREFIX_ANY = Ip4Prefix.valueOf("0.0.0.0/0");
     public static final int DHCP_INFINITE_LEASE = -1;
 
     public static final String NETWORK_ID = "networkId";
+    public static final String SUBNET_ID = "subnetId";
     public static final String PORT_ID = "portId";
     public static final String VXLAN_ID = "vxlanId";
     public static final String TENANT_ID = "tenantId";
@@ -55,4 +59,8 @@
     public static final int TUNNELTAG_RULE_PRIORITY = 30000;
     public static final int ACL_RULE_PRIORITY = 30000;
 
+    public static final int ROUTING_RULE_PRIORITY = 25000;
+    public static final int FLOATING_RULE_PRIORITY = 42000;
+    public static final int PNAT_RULE_PRIORITY = 26000;
+    public static final int PNAT_TIMEOUT = 120;
 }
\ No newline at end of file
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackFloatingIpService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackFloatingIpService.java
new file mode 100644
index 0000000..5f9f6b3
--- /dev/null
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackFloatingIpService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking;
+
+import org.onosproject.openstackinterface.OpenstackFloatingIP;
+
+/**
+ * Handles floating IP update requests from OpenStack.
+ */
+public interface OpenstackFloatingIpService {
+
+    enum Action {
+        ASSOCIATE,
+        DISSASSOCIATE
+    }
+
+    /**
+     * Handles floating IP create request from OpenStack.
+     *
+     * @param floatingIp floating IP information
+     */
+    void createFloatingIp(OpenstackFloatingIP floatingIp);
+
+    /**
+     * Handles floating IP update request from OpenStack.
+     *
+     * @param floatingIp floating IP information
+     */
+    void updateFloatingIp(OpenstackFloatingIP floatingIp);
+
+    /**
+     * Handles floating IP remove request from OpenStack.
+     *
+     * @param floatingIpId floating ip identifier
+     */
+    void deleteFloatingIp(String floatingIpId);
+}
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java
index 327d9e4..9923e6f 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/OpenstackRoutingService.java
@@ -15,76 +15,49 @@
  */
 package org.onosproject.openstacknetworking;
 
-import org.onosproject.openstackinterface.OpenstackFloatingIP;
 import org.onosproject.openstackinterface.OpenstackRouter;
 import org.onosproject.openstackinterface.OpenstackRouterInterface;
 
 /**
- * Supports L3 management REST API for openstack.
+ * Handles router update requests from OpenStack.
  */
 public interface OpenstackRoutingService {
 
     /**
-     * Stores the floating IP information created by openstack.
+     * Handles the router create request from OpenStack.
      *
-     * @param openstackFloatingIp Floating IP information
+     * @param osRouter router information
      */
-    void createFloatingIP(OpenstackFloatingIP openstackFloatingIp);
+    void createRouter(OpenstackRouter osRouter);
 
     /**
-     * Updates flow rules corresponding to the floating IP information updated by openstack.
+     * Handles the router update request from OpenStack.
+     * Update router is called when the name, administrative state, or the external
+     * gateway setting is updated. The external gateway update is the only case
+     * that openstack routing service cares.
      *
-     * @param openstackFloatingIp Floating IP information
+     * @param osRouter router information
      */
-    void updateFloatingIP(OpenstackFloatingIP openstackFloatingIp);
+    void updateRouter(OpenstackRouter osRouter);
 
     /**
-     * Removes flow rules corresponding to floating IP information removed by openstack.
+     * Handles the router remove request from OpenStack.
      *
-     * @param id Deleted Floating IP`s ID
+     * @param osRouterId identifier of the router
      */
-    void deleteFloatingIP(String id);
+    void removeRouter(String osRouterId);
 
     /**
-     * Stores the router information created by openstack.
+     * Handles router interface add request from OpenStack.
      *
-     * @param openstackRouter Router information
+     * @param osInterface router interface information
      */
-    void createRouter(OpenstackRouter openstackRouter);
+    void addRouterInterface(OpenstackRouterInterface osInterface);
 
     /**
-     * Updates flow rules corresponding to the router information updated by openstack.
+     * Handles router interface remove request from OpenStack.
      *
-     * @param openstackRouter Router information
+     * @param osInterface router interface information
      */
-    void updateRouter(OpenstackRouter openstackRouter);
-
-    /**
-     * Removes flow rules corresponding to the router information removed by openstack.
-     *
-     * @param id Deleted router`s ID
-     */
-    void deleteRouter(String id);
-
-    /**
-     * Updates flow rules corresponding to the router information updated by openstack.
-     *
-     * @param openstackRouterInterface Router interface information
-     */
-    void updateRouterInterface(OpenstackRouterInterface openstackRouterInterface);
-
-    /**
-     * Removes flow rules corresponding to the router information removed by openstack.
-     *
-     * @param openstackRouterInterface Router interface information
-     */
-    void removeRouterInterface(OpenstackRouterInterface openstackRouterInterface);
-
-    /**
-     * Returns network id for routerInterface.
-     *
-     * @param portId routerInterface`s port id
-     * @return network id
-     */
-    String networkIdForRouterInterface(String portId);
+    void removeRouterInterface(OpenstackRouterInterface osInterface);
 }
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/RulePopulatorUtil.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/RulePopulatorUtil.java
new file mode 100644
index 0000000..d220b8c
--- /dev/null
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/RulePopulatorUtil.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.openstacknetworking;
+
+import org.onlab.packet.Ip4Address;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
+import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficSelector;
+import org.onosproject.net.flow.instructions.ExtensionPropertyException;
+import org.onosproject.net.flow.instructions.ExtensionTreatment;
+import org.onosproject.net.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.slf4j.Logger;
+
+import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Provides common methods to help populating flow rules for SONA applications.
+ */
+public final class RulePopulatorUtil {
+
+    protected static final Logger log = getLogger(RulePopulatorUtil.class);
+
+    private static final String TUNNEL_DST = "tunnelDst";
+
+    private RulePopulatorUtil() {
+    }
+
+    /**
+     * Returns tunnel destination extension treatment object.
+     *
+     * @param deviceService driver service
+     * @param deviceId device id to apply this treatment
+     * @param remoteIp tunnel destination ip address
+     * @return extension treatment
+     */
+    public static ExtensionTreatment buildExtension(DeviceService deviceService,
+                                                    DeviceId deviceId,
+                                                    Ip4Address remoteIp) {
+        Device device = deviceService.getDevice(deviceId);
+        if (device != null && !device.is(ExtensionTreatmentResolver.class)) {
+            log.error("The extension treatment is not supported");
+            return null;
+        }
+
+        ExtensionTreatmentResolver resolver = device.as(ExtensionTreatmentResolver.class);
+        ExtensionTreatment treatment = resolver.getExtensionInstruction(NICIRA_SET_TUNNEL_DST.type());
+        try {
+            treatment.setPropertyValue(TUNNEL_DST, remoteIp);
+            return treatment;
+        } catch (ExtensionPropertyException e) {
+            log.warn("Failed to get tunnelDst extension treatment for {}", deviceId);
+            return null;
+        }
+    }
+
+    /**
+     * Removes flow rules with the supplied information.
+     *
+     * @param flowObjectiveService flow objective service
+     * @param appId application id
+     * @param deviceId device id to remove this flow rule
+     * @param selector traffic selector
+     * @param flag flag
+     * @param priority priority
+     */
+    public static void removeRule(FlowObjectiveService flowObjectiveService,
+                           ApplicationId appId,
+                           DeviceId deviceId,
+                           TrafficSelector selector,
+                           ForwardingObjective.Flag flag,
+                           int priority) {
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(selector)
+                .withTreatment(DefaultTrafficTreatment.builder().build())
+                .withFlag(flag)
+                .withPriority(priority)
+                .fromApp(appId)
+                .remove();
+
+        flowObjectiveService.forward(deviceId, fo);
+    }
+}