Refactored OpenstackRouting to support multiple gateway nodes

Change-Id: I6870ca9a4fd6f6b1cf2d2be72f52ef87827e1d2c
diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/AbstractVmHandler.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/AbstractVmHandler.java
deleted file mode 100644
index d7b7a8a..0000000
--- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/AbstractVmHandler.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.switching;
-
-import org.onlab.osgi.DefaultServiceDirectory;
-import org.onlab.osgi.ServiceDirectory;
-import org.onlab.packet.Ip4Address;
-import org.onlab.util.Tools;
-import org.onosproject.core.ApplicationId;
-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.onosproject.openstacknetworking.Constants;
-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 ApplicationId appId;
-    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);
-
-        appId = coreService.registerApplication(Constants.APP_ID);
-        hostService.addListener(hostListener);
-
-        log.info("Started");
-    }
-
-    protected void deactivate() {
-        hostService.removeListener(hostListener);
-        eventExecutor.shutdown();
-
-        log.info("Stopped");
-    }
-
-    abstract void hostDetected(Host host);
-
-    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/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupManager.java
similarity index 97%
rename from apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java
rename to apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupManager.java
index 82a5bac..e06b7fa 100644
--- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupRulePopulator.java
+++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSecurityGroupManager.java
@@ -30,6 +30,7 @@
 import org.onlab.packet.IpPrefix;
 import org.onlab.packet.TpPort;
 import org.onlab.util.Tools;
+import org.onosproject.core.ApplicationId;
 import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -43,6 +44,7 @@
 import org.onosproject.openstackinterface.OpenstackSecurityGroup;
 import org.onosproject.openstackinterface.OpenstackSecurityGroupRule;
 import org.onosproject.openstacknetworking.OpenstackSecurityGroupService;
+import org.onosproject.openstacknetworking.AbstractVmHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -60,7 +62,7 @@
  */
 @Component(immediate = true)
 @Service
-public class OpenstackSecurityGroupRulePopulator extends AbstractVmHandler
+public class OpenstackSecurityGroupManager extends AbstractVmHandler
         implements OpenstackSecurityGroupService {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
@@ -77,10 +79,12 @@
     private static final String ETHTYPE_IPV4 = "IPV4";
 
     private final Map<Host, Set<SecurityGroupRule>> securityGroupRuleMap = Maps.newConcurrentMap();
+    private ApplicationId appId;
 
     @Activate
     protected void activate() {
         super.activate();
+        appId = coreService.registerApplication(SWITCHING_APP_ID);
     }
 
     @Deactivate
@@ -96,7 +100,7 @@
 
         Optional<Host> host = getVmByPortId(osPort.id());
         if (!host.isPresent()) {
-            log.warn("No host found with {}", osPort);
+            log.debug("No host found with {}", osPort.id());
             return;
         }
         eventExecutor.execute(() -> updateSecurityGroupRules(host.get(), true));
diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackArpHandler.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingArpHandler.java
similarity index 96%
rename from apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackArpHandler.java
rename to apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingArpHandler.java
index c31bfd9..c35371f 100644
--- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackArpHandler.java
+++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingArpHandler.java
@@ -40,6 +40,7 @@
 import org.onosproject.openstackinterface.OpenstackInterfaceService;
 import org.onosproject.openstackinterface.OpenstackNetwork;
 import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstacknetworking.AbstractVmHandler;
 import org.osgi.service.component.ComponentContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -54,12 +55,11 @@
  * Handles ARP packet from VMs.
  */
 @Component(immediate = true)
-public final class OpenstackArpHandler extends AbstractVmHandler {
+public final class OpenstackSwitchingArpHandler extends AbstractVmHandler {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String GATEWAY_MAC = "gatewayMac";
-    private static final String DEFAULT_GATEWAY_MAC = "1f:1f:1f:1f:1f:1f";
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected PacketService packetService;
@@ -67,9 +67,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackInterfaceService openstackService;
 
-    @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC,
+    @Property(name = GATEWAY_MAC, value = DEFAULT_GATEWAY_MAC_STR,
             label = "Fake MAC address for virtual network subnet gateway")
-    private String gatewayMac = DEFAULT_GATEWAY_MAC;
+    private String gatewayMac = DEFAULT_GATEWAY_MAC_STR;
 
     private final InternalPacketProcessor packetProcessor = new InternalPacketProcessor();
     private final Set<Ip4Address> gateways = Sets.newConcurrentHashSet();
diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingHostManager.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingHostManager.java
new file mode 100644
index 0000000..2d6621a
--- /dev/null
+++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingHostManager.java
@@ -0,0 +1,318 @@
+/*
+ * 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.switching;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Sets;
+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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ip4Address;
+import org.onlab.packet.IpPrefix;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.CoreService;
+import org.onosproject.dhcp.DhcpService;
+import org.onosproject.dhcp.IpAssignment;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.Device;
+import org.onosproject.net.Host;
+import org.onosproject.net.HostId;
+import org.onosproject.net.HostLocation;
+import org.onosproject.net.Port;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.host.HostService;
+import org.onosproject.net.provider.AbstractProvider;
+import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openstackinterface.OpenstackInterfaceService;
+import org.onosproject.openstackinterface.OpenstackNetwork;
+import org.onosproject.openstackinterface.OpenstackPort;
+import org.onosproject.openstackinterface.OpenstackSubnet;
+import org.onosproject.openstacknode.OpenstackNode;
+import org.onosproject.openstacknode.OpenstackNodeEvent;
+import org.onosproject.openstacknode.OpenstackNodeListener;
+import org.onosproject.openstacknode.OpenstackNodeService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import static org.onlab.util.Tools.groupedThreads;
+import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
+import static org.onosproject.openstacknetworking.Constants.*;
+
+@Service
+@Component(immediate = true)
+public final class OpenstackSwitchingHostManager extends AbstractProvider
+        implements HostProvider {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DeviceService deviceService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostProviderRegistry hostProviderRegistry;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected DhcpService dhcpService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected HostService hostService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected MastershipService mastershipService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackInterfaceService openstackService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected OpenstackNodeService openstackNodeService;
+
+    private final ExecutorService deviceEventExecutor =
+            Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event"));
+    private final ExecutorService configEventExecutor =
+            Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "config-event"));
+    private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
+    private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
+
+    private HostProviderService hostProvider;
+
+    /**
+     * Creates OpenStack switching host provider.
+     */
+    public OpenstackSwitchingHostManager() {
+        super(new ProviderId("host", SWITCHING_APP_ID));
+    }
+
+    @Activate
+    protected void activate() {
+        coreService.registerApplication(SWITCHING_APP_ID);
+        deviceService.addListener(internalDeviceListener);
+        openstackNodeService.addListener(internalNodeListener);
+        hostProvider = hostProviderRegistry.register(this);
+
+        log.info("Started");
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        hostProviderRegistry.unregister(this);
+        deviceService.removeListener(internalDeviceListener);
+        openstackNodeService.removeListener(internalNodeListener);
+
+        deviceEventExecutor.shutdown();
+        configEventExecutor.shutdown();
+
+        log.info("Stopped");
+    }
+
+    @Override
+    public void triggerProbe(Host host) {
+        // no probe is required
+    }
+
+    private void processPortAdded(Port port) {
+        // TODO check the node state is COMPLETE
+        OpenstackPort osPort = openstackService.port(port);
+        if (osPort == null) {
+            log.warn("Failed to get OpenStack port for {}", port);
+            return;
+        }
+
+        OpenstackNetwork osNet = openstackService.network(osPort.networkId());
+        if (osNet == null) {
+            log.warn("Failed to get OpenStack network {}",
+                    osPort.networkId());
+            return;
+        }
+
+        registerDhcpInfo(osPort);
+        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+        // TODO remove gateway IP from host annotation
+        Map.Entry<String, Ip4Address> fixedIp = osPort.fixedIps().entrySet().stream().findFirst().get();
+
+        // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
+        // existing instances.
+        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
+                .set(NETWORK_ID, osPort.networkId())
+                .set(SUBNET_ID, fixedIp.getKey())
+                .set(PORT_ID, osPort.id())
+                .set(VXLAN_ID, osNet.segmentId())
+                .set(TENANT_ID, osNet.tenantId())
+                // TODO remove gateway IP from host annotation
+                .set(GATEWAY_IP, fixedIp.getValue().toString())
+                .set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
+
+        HostDescription hostDesc = new DefaultHostDescription(
+                osPort.macAddress(),
+                VlanId.NONE,
+                new HostLocation(connectPoint, System.currentTimeMillis()),
+                Sets.newHashSet(osPort.fixedIps().values()),
+                annotations.build());
+
+        HostId hostId = HostId.hostId(osPort.macAddress());
+        hostProvider.hostDetected(hostId, hostDesc, false);
+    }
+
+    private void processPortRemoved(Port port) {
+        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+        removeHosts(connectPoint);
+    }
+
+    private void removeHosts(ConnectPoint connectPoint) {
+        hostService.getConnectedHosts(connectPoint).stream()
+                .forEach(host -> {
+                    dhcpService.removeStaticMapping(host.mac());
+                    hostProvider.hostVanished(host.id());
+                });
+    }
+
+    private void registerDhcpInfo(OpenstackPort openstackPort) {
+        checkNotNull(openstackPort);
+        checkArgument(!openstackPort.fixedIps().isEmpty());
+
+        OpenstackSubnet openstackSubnet = openstackService.subnets().stream()
+                .filter(n -> n.networkId().equals(openstackPort.networkId()))
+                .findFirst().orElse(null);
+        if (openstackSubnet == null) {
+            log.warn("Failed to find subnet for {}", openstackPort);
+            return;
+        }
+
+        Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get();
+        IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr());
+        Ip4Address broadcast = Ip4Address.makeMaskedAddress(
+                ipAddress,
+                subnetPrefix.prefixLength());
+
+        // TODO: supports multiple DNS servers
+        Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ?
+                DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0);
+
+        IpAssignment ipAssignment = IpAssignment.builder()
+                .ipAddress(ipAddress)
+                .leasePeriod(DHCP_INFINITE_LEASE)
+                .timestamp(new Date())
+                .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength()))
+                .broadcast(broadcast)
+                .domainServer(domainServer)
+                .assignmentStatus(Option_RangeNotEnforced)
+                .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp()))
+                .build();
+
+        dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment);
+    }
+
+    private class InternalDeviceListener implements DeviceListener {
+
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            if (!mastershipService.isLocalMaster(device.id())) {
+                // do not allow to proceed without mastership
+                return;
+            }
+
+            Port port = event.port();
+            if (port == null) {
+                return;
+            }
+
+            String portName = port.annotations().value(PORT_NAME);
+            if (Strings.isNullOrEmpty(portName) ||
+                    !portName.startsWith(PORT_NAME_PREFIX_VM)) {
+                // handles VM connected port event only
+                return;
+            }
+
+            switch (event.type()) {
+                case PORT_UPDATED:
+                    if (!event.port().isEnabled()) {
+                        deviceEventExecutor.execute(() -> processPortRemoved(event.port()));
+                    }
+                    break;
+                case PORT_ADDED:
+                    deviceEventExecutor.execute(() -> processPortAdded(event.port()));
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
+
+        @Override
+        public void event(OpenstackNodeEvent event) {
+            OpenstackNode node = event.node();
+            // TODO check leadership of the node and make only the leader process
+
+            switch (event.type()) {
+                case COMPLETE:
+                    log.info("COMPLETE node {} detected", node.hostname());
+
+                    // adds existing VMs running on the complete state node
+                    deviceService.getPorts(node.intBridge()).stream()
+                            .filter(port -> port.annotations().value(PORT_NAME)
+                                    .startsWith(PORT_NAME_PREFIX_VM) &&
+                                    port.isEnabled())
+                            .forEach(port -> {
+                                deviceEventExecutor.execute(() -> processPortAdded(port));
+                                log.info("VM is detected on {}", port);
+                            });
+
+                    // removes stale VMs
+                    hostService.getHosts().forEach(host -> {
+                        if (deviceService.getPort(host.location().deviceId(),
+                                host.location().port()) == null) {
+                            deviceEventExecutor.execute(() -> removeHosts(host.location()));
+                            log.info("Removed stale VM {}", host.location());
+                        }
+                    });
+                    break;
+                case INCOMPLETE:
+                    log.warn("{} is changed to INCOMPLETE state", node);
+                    break;
+                case INIT:
+                case DEVICE_CREATED:
+                default:
+                    break;
+            }
+        }
+    }
+}
diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
index 55fce2c..59b41ff 100644
--- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
+++ b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingManager.java
@@ -1,319 +1,273 @@
 /*
- * 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.
- */
+* 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.switching;
 
-import com.google.common.base.Strings;
-import com.google.common.collect.Sets;
 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.apache.felix.scr.annotations.Service;
+import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpPrefix;
-import org.onlab.packet.VlanId;
-import org.onosproject.core.CoreService;
-import org.onosproject.dhcp.DhcpService;
-import org.onosproject.dhcp.IpAssignment;
-import org.onosproject.mastership.MastershipService;
-import org.onosproject.net.ConnectPoint;
-import org.onosproject.net.DefaultAnnotations;
-import org.onosproject.net.Device;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
-import org.onosproject.net.HostId;
-import org.onosproject.net.HostLocation;
-import org.onosproject.net.Port;
-import org.onosproject.net.device.DeviceEvent;
-import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.PortNumber;
 import org.onosproject.net.device.DeviceService;
-import org.onosproject.net.host.DefaultHostDescription;
-import org.onosproject.net.host.HostDescription;
-import org.onosproject.net.host.HostProvider;
-import org.onosproject.net.host.HostProviderRegistry;
-import org.onosproject.net.host.HostProviderService;
-import org.onosproject.net.host.HostService;
-import org.onosproject.net.provider.AbstractProvider;
-import org.onosproject.net.provider.ProviderId;
-import org.onosproject.openstackinterface.OpenstackInterfaceService;
-import org.onosproject.openstackinterface.OpenstackNetwork;
-import org.onosproject.openstackinterface.OpenstackPort;
-import org.onosproject.openstackinterface.OpenstackSubnet;
-import org.onosproject.openstacknode.OpenstackNode;
-import org.onosproject.openstacknode.OpenstackNodeEvent;
-import org.onosproject.openstacknode.OpenstackNodeListener;
+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.flowobjective.DefaultForwardingObjective;
+import org.onosproject.net.flowobjective.FlowObjectiveService;
+import org.onosproject.net.flowobjective.ForwardingObjective;
+import org.onosproject.openstacknetworking.AbstractVmHandler;
 import org.onosproject.openstacknode.OpenstackNodeService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.util.Date;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.Objects;
+import java.util.Optional;
 
-import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.dhcp.IpAssignment.AssignmentStatus.Option_RangeNotEnforced;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.onosproject.openstacknetworking.Constants.*;
+import static org.onosproject.openstacknetworking.RulePopulatorUtil.buildExtension;
 
-@Service
-@Component(immediate = true)
 /**
- * Populates forwarding rules for VMs created by Openstack.
+ * Populates switching flow rules.
  */
-public final class OpenstackSwitchingManager extends AbstractProvider
-        implements HostProvider {
+@Component(immediate = true)
+public final class OpenstackSwitchingManager extends AbstractVmHandler {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected CoreService coreService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected DeviceService deviceService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostProviderRegistry hostProviderRegistry;
+    protected FlowObjectiveService flowObjectiveService;
 
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DhcpService dhcpService;
+    protected OpenstackNodeService nodeService;
 
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected HostService hostService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected MastershipService mastershipService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackInterfaceService openstackService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService openstackNodeService;
-
-    private final ExecutorService deviceEventExecutor =
-            Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "device-event"));
-    private final ExecutorService configEventExecutor =
-            Executors.newSingleThreadExecutor(groupedThreads("onos/openstackswitching", "config-event"));
-    private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
-    private final InternalOpenstackNodeListener internalNodeListener = new InternalOpenstackNodeListener();
-
-    private HostProviderService hostProvider;
-
-    /**
-     * Creates OpenStack switching host provider.
-     */
-    public OpenstackSwitchingManager() {
-        super(new ProviderId("host", APP_ID));
-    }
+    private ApplicationId appId;
 
     @Activate
     protected void activate() {
-        coreService.registerApplication(APP_ID);
-        deviceService.addListener(internalDeviceListener);
-        openstackNodeService.addListener(internalNodeListener);
-        hostProvider = hostProviderRegistry.register(this);
-
-        log.info("Started");
+        super.activate();
+        appId = coreService.registerApplication(SWITCHING_APP_ID);
     }
 
     @Deactivate
     protected void deactivate() {
-        hostProviderRegistry.unregister(this);
-        deviceService.removeListener(internalDeviceListener);
-        openstackNodeService.removeListener(internalNodeListener);
+        super.deactivate();
+    }
 
-        deviceEventExecutor.shutdown();
-        configEventExecutor.shutdown();
+    private void populateSwitchingRules(Host host) {
+        populateFlowRulesForTunnelTag(host);
+        populateFlowRulesForTrafficToSameCnode(host);
+        populateFlowRulesForTrafficToDifferentCnode(host);
 
-        log.info("Stopped");
+        log.debug("Populated switching rule for {}", host);
+    }
+
+    private void populateFlowRulesForTunnelTag(Host host) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(host.location().port());
+
+        tBuilder.setTunnelId(Long.valueOf(getVni(host)));
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(tBuilder.build())
+                .withPriority(TUNNELTAG_RULE_PRIORITY)
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .fromApp(appId)
+                .add();
+
+        flowObjectiveService.forward(host.location().deviceId(), fo);
+    }
+
+    private void populateFlowRulesForTrafficToSameCnode(Host host) {
+        //For L2 Switching Case
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(getIp(host).toIpPrefix())
+                .matchTunnelId(Long.valueOf(getVni(host)));
+
+        tBuilder.setOutput(host.location().port());
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(tBuilder.build())
+                .withPriority(SWITCHING_RULE_PRIORITY)
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .fromApp(appId)
+                .add();
+
+        flowObjectiveService.forward(host.location().deviceId(), fo);
+    }
+
+    private void populateFlowRulesForTrafficToDifferentCnode(Host host) {
+        Ip4Address localVmIp = getIp(host);
+        DeviceId localDeviceId = host.location().deviceId();
+        Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
+
+        if (!localDataIp.isPresent()) {
+            log.debug("Failed to get data IP for device {}",
+                    host.location().deviceId());
+            return;
+        }
+
+        String vni = getVni(host);
+        getVmsInDifferentCnode(host).forEach(remoteVm -> {
+            Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
+            if (remoteDataIp.isPresent()) {
+                setVxLanFlowRule(vni,
+                        localDeviceId,
+                        remoteDataIp.get().getIp4Address(),
+                        getIp(remoteVm));
+
+                setVxLanFlowRule(vni,
+                        remoteVm.location().deviceId(),
+                        localDataIp.get().getIp4Address(),
+                        localVmIp);
+            }
+        });
+    }
+
+    private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
+                                  Ip4Address vmIp) {
+        Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
+        if (!tunnelPort.isPresent()) {
+            log.warn("Failed to get tunnel port from {}", deviceId);
+            return;
+        }
+
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(Long.parseLong(vni))
+                .matchIPDst(vmIp.toIpPrefix());
+        tBuilder.extension(buildExtension(deviceService, deviceId, remoteIp), deviceId)
+                .setOutput(tunnelPort.get());
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(tBuilder.build())
+                .withPriority(SWITCHING_RULE_PRIORITY)
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .fromApp(appId)
+                .add();
+
+        flowObjectiveService.forward(deviceId, fo);
+    }
+
+    private void removeSwitchingRules(Host host) {
+        removeFlowRuleForTunnelTag(host);
+        removeFlowRuleForVMsInSameCnode(host);
+        removeFlowRuleForVMsInDiffrentCnode(host);
+
+        log.debug("Removed switching rule for {}", host);
+    }
+
+    private void removeFlowRuleForTunnelTag(Host host) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchInPort(host.location().port());
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(tBuilder.build())
+                .withPriority(TUNNELTAG_RULE_PRIORITY)
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .fromApp(appId)
+                .remove();
+
+        flowObjectiveService.forward(host.location().deviceId(), fo);
+    }
+
+    private void removeFlowRuleForVMsInSameCnode(Host host) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchIPDst(getIp(host).toIpPrefix())
+                .matchTunnelId(Long.valueOf(getVni(host)));
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(DefaultTrafficTreatment.builder().build())
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .withPriority(SWITCHING_RULE_PRIORITY)
+                .fromApp(appId)
+                .remove();
+
+        flowObjectiveService.forward(host.location().deviceId(), fo);
+    }
+
+    private void removeFlowRuleForVMsInDiffrentCnode(Host host) {
+        DeviceId deviceId = host.location().deviceId();
+        final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId)
+                .stream()
+                .filter(this::isValidHost)
+                .anyMatch(h -> Objects.equals(getVni(h), getVni(host)));
+
+        getVmsInDifferentCnode(host).forEach(h -> {
+           removeVxLanFlowRule(h.location().deviceId(), getIp(host), getVni(host));
+           if (!anyPortRemainedInSameCnode) {
+               removeVxLanFlowRule(deviceId, getIp(h), getVni(host));
+           }
+       });
+    }
+
+    private void removeVxLanFlowRule(DeviceId deviceId, Ip4Address vmIp, String vni) {
+        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+
+        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
+                .matchTunnelId(Long.valueOf(vni))
+                .matchIPDst(vmIp.toIpPrefix());
+
+        ForwardingObjective fo = DefaultForwardingObjective.builder()
+                .withSelector(sBuilder.build())
+                .withTreatment(DefaultTrafficTreatment.builder().build())
+                .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                .withPriority(SWITCHING_RULE_PRIORITY)
+                .fromApp(appId)
+                .remove();
+
+        flowObjectiveService.forward(deviceId, fo);
     }
 
     @Override
-    public void triggerProbe(Host host) {
-        // no probe is required
+    protected void hostDetected(Host host) {
+        populateSwitchingRules(host);
+        log.info("Added new virtual machine to switching service {}", host);
     }
 
-    private void processPortAdded(Port port) {
-        // TODO check the node state is COMPLETE
-        OpenstackPort osPort = openstackService.port(port);
-        if (osPort == null) {
-            log.warn("Failed to get OpenStack port for {}", port);
-            return;
-        }
-
-        OpenstackNetwork osNet = openstackService.network(osPort.networkId());
-        if (osNet == null) {
-            log.warn("Failed to get OpenStack network {}",
-                    osPort.networkId());
-            return;
-        }
-
-        registerDhcpInfo(osPort);
-        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
-        // TODO remove this and openstackPortInfo
-        String gatewayIp = osNet.subnets().stream().findFirst().get().gatewayIp();
-
-        // Added CREATE_TIME intentionally to trigger HOST_UPDATED event for the
-        // existing instances.
-        DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
-                .set(NETWORK_ID, osPort.networkId())
-                .set(PORT_ID, osPort.id())
-                .set(VXLAN_ID, osNet.segmentId())
-                .set(TENANT_ID, osNet.tenantId())
-                // TODO remove this and openstackPortInfo
-                .set(GATEWAY_IP, gatewayIp)
-                .set(CREATE_TIME, String.valueOf(System.currentTimeMillis()));
-
-        HostDescription hostDesc = new DefaultHostDescription(
-                osPort.macAddress(),
-                VlanId.NONE,
-                new HostLocation(connectPoint, System.currentTimeMillis()),
-                Sets.newHashSet(osPort.fixedIps().values()),
-                annotations.build());
-
-        HostId hostId = HostId.hostId(osPort.macAddress());
-        hostProvider.hostDetected(hostId, hostDesc, false);
-    }
-
-    private void processPortRemoved(Port port) {
-        ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
-        removeHosts(connectPoint);
-    }
-
-    private void removeHosts(ConnectPoint connectPoint) {
-        hostService.getConnectedHosts(connectPoint).stream()
-                .forEach(host -> {
-                    dhcpService.removeStaticMapping(host.mac());
-                    hostProvider.hostVanished(host.id());
-                });
-    }
-
-    private void registerDhcpInfo(OpenstackPort openstackPort) {
-        checkNotNull(openstackPort);
-        checkArgument(!openstackPort.fixedIps().isEmpty());
-
-        OpenstackSubnet openstackSubnet = openstackService.subnets().stream()
-                .filter(n -> n.networkId().equals(openstackPort.networkId()))
-                .findFirst().orElse(null);
-        if (openstackSubnet == null) {
-            log.warn("Failed to find subnet for {}", openstackPort);
-            return;
-        }
-
-        Ip4Address ipAddress = openstackPort.fixedIps().values().stream().findFirst().get();
-        IpPrefix subnetPrefix = IpPrefix.valueOf(openstackSubnet.cidr());
-        Ip4Address broadcast = Ip4Address.makeMaskedAddress(
-                ipAddress,
-                subnetPrefix.prefixLength());
-
-        // TODO: supports multiple DNS servers
-        Ip4Address domainServer = openstackSubnet.dnsNameservers().isEmpty() ?
-                DNS_SERVER_IP : openstackSubnet.dnsNameservers().get(0);
-
-        IpAssignment ipAssignment = IpAssignment.builder()
-                .ipAddress(ipAddress)
-                .leasePeriod(DHCP_INFINITE_LEASE)
-                .timestamp(new Date())
-                .subnetMask(Ip4Address.makeMaskPrefix(subnetPrefix.prefixLength()))
-                .broadcast(broadcast)
-                .domainServer(domainServer)
-                .assignmentStatus(Option_RangeNotEnforced)
-                .routerAddress(Ip4Address.valueOf(openstackSubnet.gatewayIp()))
-                .build();
-
-        dhcpService.setStaticMapping(openstackPort.macAddress(), ipAssignment);
-    }
-
-    private class InternalDeviceListener implements DeviceListener {
-
-        @Override
-        public void event(DeviceEvent event) {
-            Device device = event.subject();
-            if (!mastershipService.isLocalMaster(device.id())) {
-                // do not allow to proceed without mastership
-                return;
-            }
-
-            Port port = event.port();
-            if (port == null) {
-                return;
-            }
-
-            String portName = port.annotations().value(PORT_NAME);
-            if (Strings.isNullOrEmpty(portName) ||
-                    !portName.startsWith(PORTNAME_PREFIX_VM)) {
-                // handles VM connected port event only
-                return;
-            }
-
-            switch (event.type()) {
-                case PORT_UPDATED:
-                    if (!event.port().isEnabled()) {
-                        deviceEventExecutor.execute(() -> processPortRemoved(event.port()));
-                    }
-                    break;
-                case PORT_ADDED:
-                    deviceEventExecutor.execute(() -> processPortAdded(event.port()));
-                    break;
-                default:
-                    break;
-            }
-        }
-    }
-
-    private class InternalOpenstackNodeListener implements OpenstackNodeListener {
-
-        @Override
-        public void event(OpenstackNodeEvent event) {
-            OpenstackNode node = event.node();
-            // TODO check leadership of the node and make only the leader process
-
-            switch (event.type()) {
-                case COMPLETE:
-                    log.info("COMPLETE node {} detected", node.hostname());
-
-                    // adds existing VMs running on the complete state node
-                    deviceService.getPorts(node.intBridge()).stream()
-                            .filter(port -> port.annotations().value(PORT_NAME)
-                                    .startsWith(PORTNAME_PREFIX_VM) &&
-                                    port.isEnabled())
-                            .forEach(port -> {
-                                deviceEventExecutor.execute(() -> processPortAdded(port));
-                                log.info("VM is detected on {}", port);
-                            });
-
-                    // removes stale VMs
-                    hostService.getHosts().forEach(host -> {
-                        if (deviceService.getPort(host.location().deviceId(),
-                                host.location().port()) == null) {
-                            deviceEventExecutor.execute(() -> removeHosts(host.location()));
-                            log.info("Removed stale VM {}", host.location());
-                        }
-                    });
-                    break;
-                case INCOMPLETE:
-                    log.warn("{} is changed to INCOMPLETE state", node);
-                    break;
-                case INIT:
-                case DEVICE_CREATED:
-                default:
-                    break;
-            }
-        }
+    @Override
+    protected void hostRemoved(Host host) {
+        removeSwitchingRules(host);
+        log.info("Removed virtual machine from switching service {}", host);
     }
 }
diff --git a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingRulePopulator.java b/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingRulePopulator.java
deleted file mode 100644
index b3707a8..0000000
--- a/apps/openstacknetworking/switching/src/main/java/org/onosproject/openstacknetworking/switching/OpenstackSwitchingRulePopulator.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
-* 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.switching;
-
-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.Ethernet;
-import org.onlab.packet.Ip4Address;
-import org.onlab.packet.IpAddress;
-import org.onosproject.net.Device;
-import org.onosproject.net.DeviceId;
-import org.onosproject.net.Host;
-import org.onosproject.net.PortNumber;
-import org.onosproject.net.behaviour.ExtensionTreatmentResolver;
-import org.onosproject.net.device.DeviceService;
-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.flow.instructions.ExtensionTreatment;
-import org.onosproject.net.flow.instructions.ExtensionPropertyException;
-import org.onosproject.net.flowobjective.DefaultForwardingObjective;
-import org.onosproject.net.flowobjective.FlowObjectiveService;
-import org.onosproject.net.flowobjective.ForwardingObjective;
-import org.onosproject.openstacknode.OpenstackNodeService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Objects;
-import java.util.Optional;
-
-import static org.onosproject.net.flow.instructions.ExtensionTreatmentType.ExtensionTreatmentTypes.NICIRA_SET_TUNNEL_DST;
-import static org.onosproject.openstacknetworking.Constants.*;
-
-/**
- * Populates switching flow rules.
- */
-@Component(immediate = true)
-public final class OpenstackSwitchingRulePopulator extends AbstractVmHandler {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected DeviceService deviceService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected FlowObjectiveService flowObjectiveService;
-
-    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
-    protected OpenstackNodeService nodeService;
-
-
-    private static final String TUNNEL_DST = "tunnelDst";
-
-    @Activate
-    protected void activate() {
-        super.activate();
-    }
-
-    @Deactivate
-    protected void deactivate() {
-        super.deactivate();
-    }
-
-    private void populateSwitchingRules(Host host) {
-        populateFlowRulesForTunnelTag(host);
-        populateFlowRulesForTrafficToSameCnode(host);
-        populateFlowRulesForTrafficToDifferentCnode(host);
-
-        log.debug("Populated switching rule for {}", host);
-    }
-
-    private void populateFlowRulesForTunnelTag(Host host) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchInPort(host.location().port());
-
-        tBuilder.setTunnelId(Long.valueOf(getVni(host)));
-
-        ForwardingObjective fo = DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(tBuilder.build())
-                .withPriority(TUNNELTAG_RULE_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .fromApp(appId)
-                .add();
-
-        flowObjectiveService.forward(host.location().deviceId(), fo);
-    }
-
-    private void populateFlowRulesForTrafficToSameCnode(Host host) {
-        //For L2 Switching Case
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(getIp(host).toIpPrefix())
-                .matchTunnelId(Long.valueOf(getVni(host)));
-
-        tBuilder.setOutput(host.location().port());
-
-        ForwardingObjective fo = DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(tBuilder.build())
-                .withPriority(SWITCHING_RULE_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .fromApp(appId)
-                .add();
-
-        flowObjectiveService.forward(host.location().deviceId(), fo);
-    }
-
-    private void populateFlowRulesForTrafficToDifferentCnode(Host host) {
-        Ip4Address localVmIp = getIp(host);
-        DeviceId localDeviceId = host.location().deviceId();
-        Optional<IpAddress> localDataIp = nodeService.dataIp(localDeviceId);
-
-        if (!localDataIp.isPresent()) {
-            log.debug("Failed to get data IP for device {}",
-                    host.location().deviceId());
-            return;
-        }
-
-        String vni = getVni(host);
-        getVmsInDifferentCnode(host).forEach(remoteVm -> {
-            Optional<IpAddress> remoteDataIp = nodeService.dataIp(remoteVm.location().deviceId());
-            if (remoteDataIp.isPresent()) {
-                setVxLanFlowRule(vni,
-                        localDeviceId,
-                        remoteDataIp.get().getIp4Address(),
-                        getIp(remoteVm));
-
-                setVxLanFlowRule(vni,
-                        remoteVm.location().deviceId(),
-                        localDataIp.get().getIp4Address(),
-                        localVmIp);
-            }
-        });
-    }
-
-    private void setVxLanFlowRule(String vni, DeviceId deviceId, Ip4Address remoteIp,
-                                  Ip4Address vmIp) {
-        Optional<PortNumber> tunnelPort = nodeService.tunnelPort(deviceId);
-        if (!tunnelPort.isPresent()) {
-            log.warn("Failed to get tunnel port from {}", deviceId);
-            return;
-        }
-
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(Long.parseLong(vni))
-                .matchIPDst(vmIp.toIpPrefix());
-        tBuilder.extension(buildNiciraExtenstion(deviceId, remoteIp), deviceId)
-                .setOutput(tunnelPort.get());
-
-        ForwardingObjective fo = DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(tBuilder.build())
-                .withPriority(SWITCHING_RULE_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .fromApp(appId)
-                .add();
-
-        flowObjectiveService.forward(deviceId, fo);
-    }
-
-    private void removeSwitchingRules(Host host) {
-        removeFlowRuleForTunnelTag(host);
-        removeFlowRuleForVMsInSameCnode(host);
-        removeFlowRuleForVMsInDiffrentCnode(host);
-
-        log.debug("Removed switching rule for {}", host);
-    }
-
-    private void removeFlowRuleForTunnelTag(Host host) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchInPort(host.location().port());
-
-        ForwardingObjective fo = DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(tBuilder.build())
-                .withPriority(TUNNELTAG_RULE_PRIORITY)
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .fromApp(appId)
-                .remove();
-
-        flowObjectiveService.forward(host.location().deviceId(), fo);
-    }
-
-    private void removeFlowRuleForVMsInSameCnode(Host host) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchIPDst(getIp(host).toIpPrefix())
-                .matchTunnelId(Long.valueOf(getVni(host)));
-
-        ForwardingObjective fo = DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(DefaultTrafficTreatment.builder().build())
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .withPriority(SWITCHING_RULE_PRIORITY)
-                .fromApp(appId)
-                .remove();
-
-        flowObjectiveService.forward(host.location().deviceId(), fo);
-    }
-
-    private void removeFlowRuleForVMsInDiffrentCnode(Host host) {
-        DeviceId deviceId = host.location().deviceId();
-        final boolean anyPortRemainedInSameCnode = hostService.getConnectedHosts(deviceId)
-                .stream()
-                .filter(this::isValidHost)
-                .anyMatch(h -> Objects.equals(getVni(h), getVni(host)));
-
-        getVmsInDifferentCnode(host).forEach(h -> {
-           removeVxLanFlowRule(h.location().deviceId(), getIp(host), getVni(host));
-           if (!anyPortRemainedInSameCnode) {
-               removeVxLanFlowRule(deviceId, getIp(h), getVni(host));
-           }
-       });
-    }
-
-    private void removeVxLanFlowRule(DeviceId deviceId, Ip4Address vmIp, String vni) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-
-        sBuilder.matchEthType(Ethernet.TYPE_IPV4)
-                .matchTunnelId(Long.valueOf(vni))
-                .matchIPDst(vmIp.toIpPrefix());
-
-        ForwardingObjective fo = DefaultForwardingObjective.builder()
-                .withSelector(sBuilder.build())
-                .withTreatment(DefaultTrafficTreatment.builder().build())
-                .withFlag(ForwardingObjective.Flag.SPECIFIC)
-                .withPriority(SWITCHING_RULE_PRIORITY)
-                .fromApp(appId)
-                .remove();
-
-        flowObjectiveService.forward(deviceId, fo);
-    }
-
-    private ExtensionTreatment buildNiciraExtenstion(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;
-        }
-    }
-
-    @Override
-    protected void hostDetected(Host host) {
-        populateSwitchingRules(host);
-        log.info("Added new virtual machine to switching service {}", host);
-    }
-
-    @Override
-    protected void hostRemoved(Host host) {
-        removeSwitchingRules(host);
-        log.info("Removed virtual machine from switching service {}", host);
-    }
-}