[ONOS-7684] Support VM Live Migration (VxLAN + VLAN)
Change-Id: I4717f0af6731b41eaf3114994f2087af74c3e3f5
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java
index 00f15ed..4276cf2 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortEvent.java
@@ -40,7 +40,17 @@
/**
* Signifies that the instance port is disabled.
*/
- OPENSTACK_INSTANCE_PORT_VANISHED
+ OPENSTACK_INSTANCE_PORT_VANISHED,
+
+ /**
+ * Signifies that the instance migration is started.
+ */
+ OPENSTACK_INSTANCE_MIGRATION_STARTED,
+
+ /**
+ * Signifies that the instance is migration is ended.
+ */
+ OPENSTACK_INSTANCE_MIGRATION_ENDED
}
/**
diff --git a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
index f5ebe11..d462e1b 100644
--- a/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
+++ b/apps/openstacknetworking/api/src/main/java/org/onosproject/openstacknetworking/api/InstancePortService.java
@@ -66,4 +66,18 @@
* @return set of instance ports; empty list if no port exists
*/
Set<InstancePort> instancePorts(String osNetId);
+
+ /**
+ * Processes instance port addition event caused by VM migration.
+ *
+ * @param port instance port
+ */
+ void migrationPortAdded(InstancePort port);
+
+ /**
+ * Processes instance port removal event caused by VM migration.
+ *
+ * @param port instance port
+ */
+ void migrationPortRemoved(InstancePort port);
}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java
index d5d1aac..3378b82 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/HostBasedInstancePortManager.java
@@ -43,6 +43,8 @@
import java.util.Set;
import java.util.stream.Collectors;
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_ENDED;
+import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_MIGRATION_STARTED;
import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_DETECTED;
import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_UPDATED;
import static org.onosproject.openstacknetworking.api.InstancePortEvent.Type.OPENSTACK_INSTANCE_PORT_VANISHED;
@@ -65,7 +67,7 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected HostService hostService;
- private final HostListener hostListener = new InternalHostListener();
+ private final InternalHostListener hostListener = new InternalHostListener();
@Activate
protected void activate() {
@@ -124,6 +126,16 @@
return ImmutableSet.copyOf(instPors);
}
+ @Override
+ public void migrationPortAdded(InstancePort port) {
+ hostListener.processEvent(OPENSTACK_INSTANCE_MIGRATION_STARTED, port);
+ }
+
+ @Override
+ public void migrationPortRemoved(InstancePort port) {
+ hostListener.processEvent(OPENSTACK_INSTANCE_MIGRATION_ENDED, port);
+ }
+
private boolean isValidHost(Host host) {
return !host.ipAddresses().isEmpty() &&
host.annotations().value(ANNOTATION_NETWORK_ID) != null &&
@@ -171,6 +183,8 @@
eventMap.put(OPENSTACK_INSTANCE_PORT_UPDATED, "updated");
eventMap.put(OPENSTACK_INSTANCE_PORT_DETECTED, "detected");
eventMap.put(OPENSTACK_INSTANCE_PORT_VANISHED, "disabled");
+ eventMap.put(OPENSTACK_INSTANCE_MIGRATION_STARTED, "detected");
+ eventMap.put(OPENSTACK_INSTANCE_MIGRATION_ENDED, "disabled");
InstancePortEvent instPortEvent = new InstancePortEvent(type, port);
log.debug("Instance port is {}: {}", eventMap.get(type), port);
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
index 4cadb17..8c3c56d 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingArpHandler.java
@@ -57,6 +57,8 @@
import org.onosproject.net.packet.PacketService;
import org.onosproject.openstacknetworking.api.Constants;
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.OpenstackNetworkAdminService;
@@ -96,7 +98,10 @@
import static org.onosproject.openstacknetworking.api.Constants.PRIORITY_ARP_GATEWAY_RULE;
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.getGwByInstancePort;
+import static org.onosproject.openstacknetworking.util.OpenstackNetworkingUtil.isAssociatedWithVM;
import static org.onosproject.openstacknode.api.OpenstackNode.NodeType.GATEWAY;
import static org.slf4j.LoggerFactory.getLogger;
@@ -159,11 +164,13 @@
private final OpenstackRouterListener osRouterListener = new InternalRouterEventListener();
private final HostListener hostListener = new InternalHostListener();
private final OpenstackNodeListener osNodeListener = new InternalNodeEventListener();
+ private final InstancePortListener instPortListener = new InternalInstancePortListener();
private ApplicationId appId;
private NodeId localNodeId;
private Map<String, MacAddress> floatingIpMacMap = Maps.newConcurrentMap();
private Map<MacAddress, InstancePort> removedPorts = Maps.newConcurrentMap();
+ private Map<String, DeviceId> migrationPool = Maps.newConcurrentMap();
private final ExecutorService eventExecutor = newSingleThreadExecutor(
groupedThreads(this.getClass().getSimpleName(), "event-handler", log));
@@ -178,6 +185,7 @@
hostService.addListener(hostListener);
osRouterService.addListener(osRouterListener);
osNodeService.addListener(osNodeListener);
+ instancePortService.addListener(instPortListener);
leadershipService.runForLeadership(appId.name());
packetService.addProcessor(packetProcessor, PacketProcessor.director(1));
log.info("Started");
@@ -189,6 +197,7 @@
hostService.removeListener(hostListener);
osRouterService.removeListener(osRouterListener);
osNodeService.removeListener(osNodeListener);
+ instancePortService.removeListener(instPortListener);
leadershipService.withdraw(appId.name());
eventExecutor.shutdown();
configService.unregisterProperties(getClass(), false);
@@ -410,6 +419,33 @@
}
/**
+ * Installs/uninstalls ARP flow rules to the corresponding gateway by
+ * looking for compute node's device ID.
+ *
+ * @param fip floating IP
+ * @param port instance port
+ * @param gateways a collection of gateways
+ * @param install install flag
+ */
+ private void setFloatingIpArpRuleWithPortEvent(NetFloatingIP fip,
+ InstancePort port,
+ Set<OpenstackNode> gateways,
+ boolean install) {
+ if (arpMode.equals(ARP_BROADCAST_MODE)) {
+
+ OpenstackNode gw = getGwByInstancePort(gateways, port);
+
+ if (gw == null) {
+ return;
+ }
+
+ String macString = osNetworkAdminService.port(fip.getPortId()).getMacAddress();
+
+ setArpRule(fip, MacAddress.valueOf(macString), gw, install);
+ }
+ }
+
+ /**
* Installs static ARP rules used in ARP BROAD_CAST mode.
* Note that, those rules will be only matched ARP_REQUEST packets,
* used for telling gateway node the mapped MAC address of requested IP,
@@ -460,36 +496,41 @@
return;
}
- TrafficSelector selector = DefaultTrafficSelector.builder()
- .matchEthType(EthType.EtherType.ARP.ethType().toShort())
- .matchArpOp(ARP.OP_REQUEST)
- .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
- .build();
+ setArpRule(fip, targetMac, gw, install);
+ }
+ }
- TrafficTreatment treatment = DefaultTrafficTreatment.builder()
- .setArpOp(ARP.OP_REPLY)
- .setArpSha(targetMac)
- .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
- .setOutput(PortNumber.IN_PORT)
- .build();
+ private void setArpRule(NetFloatingIP fip, MacAddress targetMac,
+ OpenstackNode gateway, boolean install) {
+ TrafficSelector selector = DefaultTrafficSelector.builder()
+ .matchEthType(EthType.EtherType.ARP.ethType().toShort())
+ .matchArpOp(ARP.OP_REQUEST)
+ .matchArpTpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
+ .build();
- osFlowRuleService.setRule(
- appId,
- gw.intgBridge(),
- selector,
- treatment,
- PRIORITY_ARP_GATEWAY_RULE,
- GW_COMMON_TABLE,
- install
- );
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setArpOp(ARP.OP_REPLY)
+ .setArpSha(targetMac)
+ .setArpSpa(Ip4Address.valueOf(fip.getFloatingIpAddress()))
+ .setOutput(PortNumber.IN_PORT)
+ .build();
- if (install) {
- log.info("Install ARP Rule for Floating IP {}",
- fip.getFloatingIpAddress());
- } else {
- log.info("Uninstall ARP Rule for Floating IP {}",
- fip.getFloatingIpAddress());
- }
+ osFlowRuleService.setRule(
+ appId,
+ gateway.intgBridge(),
+ selector,
+ treatment,
+ PRIORITY_ARP_GATEWAY_RULE,
+ GW_COMMON_TABLE,
+ install
+ );
+
+ if (install) {
+ log.info("Install ARP Rule for Floating IP {}",
+ fip.getFloatingIpAddress());
+ } else {
+ log.info("Uninstall ARP Rule for Floating IP {}",
+ fip.getFloatingIpAddress());
}
}
@@ -780,4 +821,58 @@
);
}
}
+
+ private class InternalInstancePortListener implements InstancePortListener {
+
+ @Override
+ public boolean isRelevant(InstancePortEvent event) {
+ Set<NetFloatingIP> ips = osRouterService.floatingIps();
+ NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
+ Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
+
+ if (gateways.size() == 1) {
+ return false;
+ }
+
+ return fip != null && isAssociatedWithVM(osNetworkService, fip);
+ }
+
+ @Override
+ public void event(InstancePortEvent event) {
+ Set<NetFloatingIP> ips = osRouterService.floatingIps();
+ NetFloatingIP fip = associatedFloatingIp(event.subject(), ips);
+ Set<OpenstackNode> gateways = osNodeService.completeNodes(GATEWAY);
+
+ switch (event.type()) {
+ case OPENSTACK_INSTANCE_MIGRATION_STARTED:
+
+ migrationPool.put(fip.getFloatingIpAddress(), event.subject().deviceId());
+
+ eventExecutor.execute(() -> {
+ setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
+ gateways, true);
+ });
+ break;
+ case OPENSTACK_INSTANCE_MIGRATION_ENDED:
+
+ DeviceId newDeviceId = migrationPool.get(fip.getFloatingIpAddress());
+ DeviceId oldDeviceId = event.subject().deviceId();
+ migrationPool.remove(fip.getFloatingIpAddress());
+
+ OpenstackNode oldGw = getGwByComputeDevId(gateways, oldDeviceId);
+ OpenstackNode newGw = getGwByComputeDevId(gateways, newDeviceId);
+
+ if (oldGw != null && oldGw.equals(newGw)) {
+ return;
+ }
+
+ eventExecutor.execute(() ->
+ setFloatingIpArpRuleWithPortEvent(fip, event.subject(),
+ gateways, false));
+ break;
+ default:
+ break;
+ }
+ }
+ }
}
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;
+ }
+ }
+ }
}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
index 142a9f7..e174eef 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackRoutingHandler.java
@@ -1060,6 +1060,14 @@
instPortRemoved(event.subject());
});
break;
+ case OPENSTACK_INSTANCE_MIGRATION_ENDED:
+ eventExecutor.execute(() -> {
+ log.info("RoutingHandler: Instance port vanished MAC:{} IP:{} due to VM migration",
+ instPort.macAddress(),
+ instPort.ipAddress());
+ // TODO: need to reconfigure rules to point to update VM
+ });
+ break;
default:
break;
}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
index f6b8e50..837c891 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingArpHandler.java
@@ -531,6 +531,9 @@
setArpRequestRule(event.subject(), false);
setArpReplyRule(event.subject(), false);
break;
+ case OPENSTACK_INSTANCE_MIGRATION_ENDED:
+ setArpRequestRule(event.subject(), false);
+ break;
default:
break;
}
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
index 4869101..aab3780 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackSwitchingHandler.java
@@ -161,6 +161,27 @@
}
}
+ /**
+ * Removes virtual port.
+ *
+ * @param instPort instance port
+ */
+ private void removeVportRules(InstancePort instPort) {
+ NetworkType type = osNetworkService.network(instPort.networkId()).getNetworkType();
+
+ switch (type) {
+ case VXLAN:
+ setTunnelTagFlowRules(instPort, false);
+ break;
+ case VLAN:
+ setVlanTagFlowRules(instPort, false);
+ break;
+ default:
+ log.warn("Unsupported network tunnel type {}", type.name());
+ break;
+ }
+ }
+
private void setFlatJumpRules(InstancePort port, boolean install) {
TrafficSelector.Builder selector = DefaultTrafficSelector.builder();
selector.matchInPort(port.portNumber());
@@ -597,6 +618,14 @@
instPortRemoved(event.subject());
});
break;
+ case OPENSTACK_INSTANCE_MIGRATION_ENDED:
+ eventExecutor.execute(() -> {
+ log.info("Instance port vanished MAC:{} IP:{}, due to VM migration",
+ instPort.macAddress(),
+ instPort.ipAddress());
+ removeVportRules(event.subject());
+ });
+ break;
default:
break;
}
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()));
});
});
diff --git a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
index 2ebf9db..9914957 100644
--- a/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
+++ b/apps/openstacknetworking/app/src/main/java/org/onosproject/openstacknetworking/util/OpenstackNetworkingUtil.java
@@ -18,9 +18,11 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.google.common.base.Strings;
import org.onosproject.net.DeviceId;
import org.onosproject.openstacknetworking.api.InstancePort;
import org.onosproject.openstacknetworking.api.OpenstackRouterAdminService;
+import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
import org.onosproject.openstacknode.api.OpenstackAuth;
import org.onosproject.openstacknode.api.OpenstackAuth.Perspective;
import org.onosproject.openstacknode.api.OpenstackNode;
@@ -32,6 +34,8 @@
import org.openstack4j.core.transport.ObjectMapperSingleton;
import org.openstack4j.model.ModelEntity;
import org.openstack4j.model.common.Identifier;
+import org.openstack4j.model.network.NetFloatingIP;
+import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Port;
import org.openstack4j.model.network.RouterInterface;
import org.openstack4j.openstack.OSFactory;
@@ -78,6 +82,8 @@
private static final String IDENTITY_PATH = "identity/";
private static final String SSL_TYPE = "SSL";
+ private static final String ERR_FLOW = "Failed set flows for floating IP %s: ";
+
/**
* Prevents object instantiation from external.
*/
@@ -125,6 +131,57 @@
}
/**
+ * Obtains a floating IP associated with the given instance port.
+ *
+ * @param port instance port
+ * @param fips a collection of floating IPs
+ * @return associated floating IP
+ */
+ public static NetFloatingIP associatedFloatingIp(InstancePort port,
+ Set<NetFloatingIP> fips) {
+ for (NetFloatingIP fip : fips) {
+ if (Strings.isNullOrEmpty(fip.getFixedIpAddress())) {
+ continue;
+ }
+ if (Strings.isNullOrEmpty(fip.getFloatingIpAddress())) {
+ continue;
+ }
+ if (fip.getFixedIpAddress().equals(port.ipAddress().toString())) {
+ return fip;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Checks whether the given floating IP is associated with a VM.
+ *
+ * @param service openstack network service
+ * @param fip floating IP
+ * @return true if the given floating IP associated with a VM, false otherwise
+ */
+ public static boolean isAssociatedWithVM(OpenstackNetworkService service,
+ NetFloatingIP fip) {
+ Port osPort = service.port(fip.getPortId());
+ if (osPort == null) {
+ return false;
+ }
+
+ if (!Strings.isNullOrEmpty(osPort.getDeviceId())) {
+ Network osNet = service.network(osPort.getNetworkId());
+ if (osNet == null) {
+ final String errorFormat = ERR_FLOW + "no network(%s) exists";
+ final String error = String.format(errorFormat,
+ fip.getFloatingIpAddress(), osPort.getNetworkId());
+ throw new IllegalStateException(error);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Obtains the gateway node by instance port.
*
* @param gateways a collection of gateway nodes
diff --git a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java
index 885c7d8..64c7747 100644
--- a/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java
+++ b/apps/openstacknetworking/app/src/test/java/org/onosproject/openstacknetworking/impl/InstancePortServiceAdapter.java
@@ -54,6 +54,14 @@
}
@Override
+ public void migrationPortAdded(InstancePort port) {
+ }
+
+ @Override
+ public void migrationPortRemoved(InstancePort port) {
+ }
+
+ @Override
public void addListener(InstancePortListener listener) {
}