[ONOS-7684] Support VM Live Migration (VxLAN + VLAN)

Change-Id: I4717f0af6731b41eaf3114994f2087af74c3e3f5
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
index 62fd2b4..6094e72 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHostProvider.java
@@ -16,6 +16,8 @@
 package org.onosproject.openstacknetworking.impl;
 
 import com.google.common.base.Strings;
+import com.google.common.collect.Maps;
+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;
@@ -30,7 +32,9 @@
 import org.onosproject.mastership.MastershipService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.DefaultAnnotations;
+import org.onosproject.net.DefaultHost;
 import org.onosproject.net.Device;
+import org.onosproject.net.DeviceId;
 import org.onosproject.net.Host;
 import org.onosproject.net.HostId;
 import org.onosproject.net.HostLocation;
@@ -46,6 +50,8 @@
 import org.onosproject.net.host.HostService;
 import org.onosproject.net.provider.AbstractProvider;
 import org.onosproject.net.provider.ProviderId;
+import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortService;
 import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
 import org.onosproject.openstacknode.api.OpenstackNode;
 import org.onosproject.openstacknode.api.OpenstackNodeEvent;
@@ -56,6 +62,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -77,7 +84,6 @@
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private static final String PORT_NAME_PREFIX_VM = "tap";
-    private static final String PORT_NAME_PREFIX_CAVIUM = "enp";
     private static final String ERR_ADD_HOST = "Failed to add host: ";
     private static final String ANNOTATION_SEGMENT_ID = "segId";
     private static final String SONA_HOST_SCHEME = "sona";
@@ -103,6 +109,9 @@
     @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
     protected OpenstackNodeService osNodeService;
 
+    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+    protected InstancePortService instancePortService;
+
     private final ExecutorService deviceEventExecutor =
             Executors.newSingleThreadExecutor(groupedThreads("openstacknetworking", "device-event"));
     private final InternalDeviceListener internalDeviceListener = new InternalDeviceListener();
@@ -110,6 +119,9 @@
 
     private HostProviderService hostProvider;
 
+    private Map<HostId, Device> hostDeviceMap = Maps.newConcurrentMap();
+    private Set<Host> migratingHosts = Sets.newConcurrentHashSet();
+
     /**
      * Creates OpenStack switching host provider.
      */
@@ -151,7 +163,7 @@
      *
      * @param port port object used in ONOS
      */
-    private void processPortAdded(Port port) {
+    private void processPortAdded(Port port, Device device) {
         // TODO check the node state is COMPLETE
         org.openstack4j.model.network.Port osPort = osNetworkService.port(port);
         if (osPort == null) {
@@ -176,6 +188,24 @@
                 .map(ip -> IpAddress.valueOf(ip.getIpAddress()))
                 .collect(Collectors.toSet());
         ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
+        HostId oldHostId = HostId.hostId(macAddr);
+
+        // In VM migration case, a duplicated host port (port created in at new
+        // compute node) will be detected at OVS; in this case, we will store
+        // the old host instance into migration list, and overwrite old host
+        // with new host instance issue host creation event to ONOS core
+        Device oldDevice = hostDeviceMap.get(oldHostId);
+
+        if (device != null && oldDevice != null && !oldDevice.equals(device)) {
+            Host host = hostService.getHost(oldHostId);
+            if (host != null) {
+                migratingHosts.add(host);
+            }
+        }
+
+        if (device != null) {
+            hostDeviceMap.put(oldHostId, device);
+        }
 
         DefaultAnnotations.Builder annotations = DefaultAnnotations.builder()
                 .set(ANNOTATION_NETWORK_ID, osPort.getNetworkId())
@@ -187,15 +217,25 @@
 
         }
 
+        long currentTime = System.currentTimeMillis();
+
         HostDescription hostDesc = new DefaultHostDescription(
                 macAddr,
                 VlanId.NONE,
-                new HostLocation(connectPoint, System.currentTimeMillis()),
+                new HostLocation(connectPoint, currentTime),
                 fixedIps,
                 annotations.build());
 
         HostId hostId = HostId.hostId(macAddr);
         hostProvider.hostDetected(hostId, hostDesc, false);
+
+        if (device != null && oldDevice != null && !oldDevice.equals(device)) {
+            Host oldHost = hostService.getHost(oldHostId);
+            Host newHost = new DefaultHost(oldHost.providerId(), hostId, macAddr,
+                    VlanId.NONE, new HostLocation(connectPoint, currentTime),
+                    fixedIps, annotations.build());
+            instancePortService.migrationPortAdded(HostBasedInstancePort.of(newHost));
+        }
     }
 
     /**
@@ -204,12 +244,37 @@
      * instance through host provider by giving connect point information,
      * and vanishes it.
      *
-     * @param port port object used in ONOS
+     * @param event device event
      */
-    private void processPortRemoved(Port port) {
+    private void processPortRemoved(DeviceEvent event) {
+        Port port = event.port();
+        DeviceId deviceId = event.subject().id();
         ConnectPoint connectPoint = new ConnectPoint(port.element().id(), port.number());
-        hostService.getConnectedHosts(connectPoint)
-                    .forEach(host -> hostProvider.hostVanished(host.id()));
+
+        Set<Host> hostsToBeRemoved = hostService.getConnectedHosts(connectPoint);
+
+        if (hostsToBeRemoved.size() == 0) {
+
+            for (Host host : migratingHosts) {
+                if (host.location() == null) {
+                    continue;
+                }
+                String hostLocation = host.location().toString();
+                StringBuilder deviceIdWithPort = new StringBuilder();
+                deviceIdWithPort.append(deviceId.toString());
+                deviceIdWithPort.append("/");
+                deviceIdWithPort.append(port.number().toString());
+
+                if (hostLocation.equals(deviceIdWithPort.toString())) {
+                    InstancePort instPort = HostBasedInstancePort.of(host);
+                    instancePortService.migrationPortRemoved(instPort);
+                    migratingHosts.remove(host);
+                }
+            }
+
+        } else {
+            hostsToBeRemoved.forEach(host -> hostProvider.hostVanished(host.id()));
+        }
     }
 
     /**
@@ -274,7 +339,7 @@
             log.debug("Instance port {} is detected from {}",
                     event.port().annotations().value(PORT_NAME),
                     event.subject().id());
-            processPortAdded(event.port());
+            processPortAdded(event.port(), event.subject());
         });
     }
 
@@ -290,7 +355,7 @@
             log.debug("Instance port {} is removed from {}",
                     event.port().annotations().value(PORT_NAME),
                     event.subject().id());
-            processPortRemoved(event.port());
+            processPortRemoved(event);
         });
     }
 
@@ -343,7 +408,8 @@
                         log.debug("Instance port {} is detected from {}",
                                   port.annotations().value(PORT_NAME),
                                   osNode.hostname());
-                        processPortAdded(port);
+                        processPortAdded(port,
+                                deviceService.getDevice(osNode.intgBridge()));
                     });
 
             portNamePrefixMap().values().forEach(portNamePrefix -> {
@@ -355,7 +421,8 @@
                             log.debug("Instance port {} is detected from {}",
                                     port.annotations().value(portNamePrefix),
                                     osNode.hostname());
-                            processPortAdded(port);
+                            processPortAdded(port,
+                                    deviceService.getDevice(osNode.intgBridge()));
                         });
             });