[ONOS-7684] Support VM Live Migration (VxLAN + VLAN)
Change-Id: I4717f0af6731b41eaf3114994f2087af74c3e3f5
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
index 3976e5c..4ca4a6a 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingFloatingIpHandler.java
@@ -33,6 +33,7 @@
import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
+import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceService;
@@ -46,6 +47,8 @@
import org.onosproject.openstacknetworking.api.Constants;
import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
import org.onosproject.openstacknetworking.api.InstancePort;
+import org.onosproject.openstacknetworking.api.InstancePortEvent;
+import org.onosproject.openstacknetworking.api.InstancePortListener;
import org.onosproject.openstacknetworking.api.InstancePortService;
import org.onosproject.openstacknetworking.api.OpenstackFlowRuleService;
import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
@@ -83,7 +86,9 @@
import static org.onosproject.openstacknetworking.api.Constants.ROUTING_TABLE;
import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_NETWORK_ID;
import static org.onosproject.openstacknetworking.impl.HostBasedInstancePort.ANNOTATION_PORT_ID;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.associatedFloatingIp;
import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.getGwByComputeDevId;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
import static org.onosproject.openstacknetworking.util.RulePopulatorUtil.buildExtension;
import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
@@ -130,10 +135,12 @@
private final ExecutorService eventExecutor = newSingleThreadExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
- private final OpenstackRouterListener floatingIpLisener = new InternalFloatingIpListener();
+ private final OpenstackRouterListener floatingIpListener = new InternalFloatingIpListener();
+ private final InstancePortListener instancePortListener = new InternalInstancePortListener();
private final OpenstackNodeListener osNodeListener = new InternalNodeListener();
private final HostListener hostListener = new InternalHostListener();
private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
+ private Map<String, DeviceId> migrationPool = Maps.newConcurrentMap();
private ApplicationId appId;
private NodeId localNodeId;
@@ -144,17 +151,19 @@
localNodeId = clusterService.getLocalNode().id();
leadershipService.runForLeadership(appId.name());
hostService.addListener(hostListener);
- osRouterAdminService.addListener(floatingIpLisener);
+ osRouterAdminService.addListener(floatingIpListener);
osNodeService.addListener(osNodeListener);
+ instancePortService.addListener(instancePortListener);
log.info("Started");
}
@Deactivate
protected void deactivate() {
+ instancePortService.removeListener(instancePortListener);
hostService.removeListener(hostListener);
osNodeService.removeListener(osNodeListener);
- osRouterAdminService.removeListener(floatingIpLisener);
+ osRouterAdminService.removeListener(floatingIpListener);
leadershipService.withdraw(appId.name());
eventExecutor.shutdown();
@@ -584,7 +593,6 @@
return osNetworkService.externalPeerRouter(exGatewayInfo);
}
-
private void associateFloatingIp(NetFloatingIP osFip) {
Port osPort = osNetworkService.port(osFip.getPortId());
if (osPort == null) {
@@ -809,4 +817,95 @@
host.annotations().value(ANNOTATION_PORT_ID) != null;
}
}
+
+ private class InternalInstancePortListener implements InstancePortListener {
+
+ @Override
+ public boolean isRelevant(InstancePortEvent event) {
+ Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
+ NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
+
+ return fip != null && isAssociatedWithVM(osNetworkService, fip);
+ }
+
+ @Override
+ public void event(InstancePortEvent event) {
+
+ Set<NetFloatingIP> ips = osRouterAdminService.floatingIps();
+ NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
+ Port osPort = osNetworkService.port(fip.getPortId());
+ Network osNet = osNetworkService.network(osPort.getNetworkId());
+ Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
+
+ ExternalPeerRouter externalPeerRouter = externalPeerRouter(osNet);
+ if (externalPeerRouter == null) {
+ final String errorFormat = ERR_FLOW + "no external peer router found";
+ throw new IllegalStateException(errorFormat);
+ }
+
+ switch (event.type()) {
+ case OPENSTACK_INSTANCE_MIGRATION_STARTED:
+ eventExecutor.execute(() -> {
+
+ // since downstream internal rules are located in all gateway
+ // nodes, therefore, we simply update the rules with new compute node info
+ setDownstreamInternalRules(fip, osNet, event.subject(), true);
+
+ // since DownstreamExternal rules should only be placed in
+ // corresponding gateway node, we need to install new rule to
+ // the corresponding gateway node
+ setDownstreamExternalRulesHelper(fip, osNet,
+ event.subject(), externalPeerRouter, gateways, true);
+
+ // since ComputeNodeToGateway rules should only be placed in
+ // corresponding compute node, we need to install new rule to
+ // the target compute node, and remove rules from original node
+ setComputeNodeToGatewayHelper(event.subject(), osNet, gateways, true);
+
+ migrationPool.put(fip.getFloatingIpAddress(), event.subject().deviceId());
+
+ });
+ break;
+ case OPENSTACK_INSTANCE_MIGRATION_ENDED:
+
+ // if we only have one gateway, we simply do not remove any
+ // flow rules from either gateway or compute node
+ if (gateways.size() == 1) {
+ return;
+ }
+
+ // checks whether the destination compute node's device id
+ // has identical gateway hash or not
+ // if it is true, we simply do not remove the rules, as
+ // it has been overwritten at port detention event
+ // if it is false, we will remove the rules
+ DeviceId newDeviceId = migrationPool.get(fip.getFloatingIpAddress());
+ DeviceId oldDeviceId = event.subject().deviceId();
+ migrationPool.remove(fip.getFloatingIpAddress());
+
+ OpenstackNode oldGateway = getGwByComputeDevId(gateways, oldDeviceId);
+ OpenstackNode newGateway = getGwByComputeDevId(gateways, newDeviceId);
+
+ if (oldGateway != null && oldGateway.equals(newGateway)) {
+ return;
+ }
+
+ eventExecutor.execute(() -> {
+
+ // we need to remove the old ComputeNodeToGateway rules from
+ // original compute node
+ setComputeNodeToGatewayHelper(event.subject(), osNet, gateways, false);
+
+ // since DownstreamExternal rules should only be placed in
+ // corresponding gateway node, we need to remove old rule from
+ // the corresponding gateway node
+ setDownstreamExternalRulesHelper(fip, osNet,
+ event.subject(), externalPeerRouter, gateways, false);
+ });
+ break;
+ default:
+ break;
+ }
+ }
+ }
}