[ONOS-3952] Implement FloatingIP Handler for OpenstackRoutingService
- Implements floatingIp REST interfaces & event handler
- Implements rulePopulate method for floatingIp handler
- Fixes minor logics
- Changes app structure
- exports configuration
- Implements case issue.
when openstack deletes vm w/o deassociating floatingIp,
openstack doesn`t send floatingIp deassociation event.
Change-Id: If4d8ac3fecfed1957d84139f94ae31f593a9097b
diff --git a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
index 2ab19ac..edc340b 100644
--- a/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
+++ b/apps/openstacknetworking/openstackrouting/src/main/java/org/onosproject/openstacknetworking/routing/OpenstackRoutingManager.java
@@ -16,7 +16,6 @@
package org.onosproject.openstacknetworking.routing;
import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
@@ -28,6 +27,7 @@
import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onlab.packet.UDP;
+import org.onlab.util.KryoNamespace;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.DeviceId;
@@ -50,14 +50,18 @@
import org.onosproject.openstackinterface.OpenstackPort;
import org.onosproject.openstackinterface.OpenstackRouter;
import org.onosproject.openstackinterface.OpenstackRouterInterface;
+import org.onosproject.openstacknetworking.OpenstackPortInfo;
import org.onosproject.openstacknetworking.OpenstackRoutingService;
import org.onosproject.openstacknetworking.OpenstackSwitchingService;
+import org.onosproject.store.serializers.KryoNamespaces;
+import org.onosproject.store.service.ConsistentMap;
+import org.onosproject.store.service.Serializer;
+import org.onosproject.store.service.StorageService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@@ -101,14 +105,21 @@
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkConfigRegistry configRegistry;
-
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
private ApplicationId appId;
- private Map<String, OpenstackRouterInterface> routerInterfaceMap = Maps.newHashMap();
- private Map<Integer, String> portNumMap = initPortNumMap();
+ private ConsistentMap<Integer, String> tpPortNumMap; // Map<PortNum, allocated VM`s Mac & destionation Ip address>
+ private ConsistentMap<String, OpenstackFloatingIP> floatingIpMap; // Map<FloatingIp`s Id, FloatingIp object>
private static final String APP_ID = "org.onosproject.openstackrouting";
private static final String PORT_NAME = "portName";
+ private static final String PORTNAME_PREFIX_VM = "tap";
private static final String DEVICE_OWNER_ROUTER_INTERFACE = "network:router_interface";
+ private static final String FLOATING_IP_MAP_NAME = "openstackrouting-floatingip";
+ private static final String TP_PORT_MAP_NAME = "openstackrouting-portnum";
+ private static final int PNAT_PORT_EXPIRE_TIME = 1200 * 1000;
+ private static final int TP_PORT_MINIMUM_NUM = 1024;
+ private static final int TP_PORT_MAXIMUM_NUM = 65535;
private final ConfigFactory configFactory =
new ConfigFactory(SubjectFactories.APP_SUBJECT_FACTORY, OpenstackRoutingConfig.class, "openstackrouting") {
@Override
@@ -119,16 +130,19 @@
private final NetworkConfigListener configListener = new InternalConfigListener();
private OpenstackRoutingConfig config;
- private static final int PNAT_PORT_NUM_START = 1024;
- private static final int PNAT_PORT_NUM_END = 65535;
+ private static final KryoNamespace.Builder FLOATING_IP_SERIALIZER = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(KryoNamespaces.MISC)
+ .register(OpenstackFloatingIP.FloatingIpStatus.class)
+ .register(OpenstackFloatingIP.class)
+ .register(Ip4Address.class)
+ .register(String.class);
- private Map<Integer, String> initPortNumMap() {
- Map<Integer, String> map = Maps.newHashMap();
- for (int i = PNAT_PORT_NUM_START; i < PNAT_PORT_NUM_END; i++) {
- map.put(i, "");
- }
- return map;
- }
+ private static final KryoNamespace.Builder NUMBER_SERIALIZER = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(KryoNamespaces.MISC)
+ .register(Integer.class)
+ .register(String.class);
private InternalPacketProcessor internalPacketProcessor = new InternalPacketProcessor();
private ExecutorService l3EventExecutorService =
@@ -139,6 +153,7 @@
Executors.newSingleThreadExecutor(groupedThreads("onos/openstackrouting", "arp-event"));
private OpenstackIcmpHandler openstackIcmpHandler;
private OpenstackRoutingArpHandler openstackArpHandler;
+ private OpenstackRoutingRulePopulator rulePopulator;
@Activate
protected void activate() {
@@ -149,6 +164,17 @@
readConfiguration();
+ floatingIpMap = storageService.<String, OpenstackFloatingIP>consistentMapBuilder()
+ .withSerializer(Serializer.using(FLOATING_IP_SERIALIZER.build()))
+ .withName(FLOATING_IP_MAP_NAME)
+ .withApplicationId(appId)
+ .build();
+ tpPortNumMap = storageService.<Integer, String>consistentMapBuilder()
+ .withSerializer(Serializer.using(NUMBER_SERIALIZER.build()))
+ .withName(TP_PORT_MAP_NAME)
+ .withApplicationId(appId)
+ .build();
+
log.info("onos-openstackrouting started");
}
@@ -159,43 +185,92 @@
icmpEventExecutorService.shutdown();
arpEventExecutorService.shutdown();
+ floatingIpMap.clear();
+ tpPortNumMap.clear();
+
log.info("onos-openstackrouting stopped");
}
@Override
public void createFloatingIP(OpenstackFloatingIP openstackFloatingIP) {
-
+ floatingIpMap.put(openstackFloatingIP.id(), openstackFloatingIP);
}
@Override
public void updateFloatingIP(OpenstackFloatingIP openstackFloatingIP) {
-
+ if (!floatingIpMap.containsKey(openstackFloatingIP.id())) {
+ log.warn("There`s no information about {} in FloatingIpMap", openstackFloatingIP.id());
+ return;
+ }
+ if (openstackFloatingIP.portId() == null || openstackFloatingIP.portId().equals("null")) {
+ OpenstackFloatingIP floatingIP = floatingIpMap.get(openstackFloatingIP.id()).value();
+ OpenstackPortInfo portInfo = openstackSwitchingService.openstackPortInfo()
+ .get(PORTNAME_PREFIX_VM.concat(floatingIP.portId().substring(0, 11)));
+ if (portInfo == null) {
+ log.warn("There`s no portInfo information about portId {}", floatingIP.portId());
+ return;
+ }
+ l3EventExecutorService.execute(
+ new OpenstackFloatingIPHandler(rulePopulator, floatingIP, false, portInfo));
+ floatingIpMap.replace(floatingIP.id(), openstackFloatingIP);
+ } else {
+ floatingIpMap.put(openstackFloatingIP.id(), openstackFloatingIP);
+ l3EventExecutorService.execute(
+ new OpenstackFloatingIPHandler(rulePopulator, openstackFloatingIP, true, null));
+ }
}
@Override
public void deleteFloatingIP(String id) {
-
+ floatingIpMap.remove(id);
}
@Override
public void createRouter(OpenstackRouter openstackRouter) {
- checkExternalConnection(openstackRouter, getOpenstackRouterInterface(openstackRouter));
}
@Override
public void updateRouter(OpenstackRouter openstackRouter) {
- checkExternalConnection(openstackRouter, getOpenstackRouterInterface(openstackRouter));
+ if (openstackRouter.gatewayExternalInfo().externalFixedIps().size() > 0) {
+ Ip4Address externalIp = openstackRouter.gatewayExternalInfo().externalFixedIps()
+ .values().stream().findFirst().orElse(null);
+ OpenstackRouter router = getRouterfromExternalIp(externalIp);
+ checkExternalConnection(router, getOpenstackRouterInterface(router));
+ } else {
+ unsetExternalConnection();
+ }
+ }
+
+ private void unsetExternalConnection() {
+ Collection<OpenstackRouter> internalRouters = getExternalRouter(false);
+ internalRouters.forEach(r ->
+ getOpenstackRouterInterface(r).forEach(i -> rulePopulator.removeExternalRules(i)));
+ }
+
+ private Collection<OpenstackRouter> getExternalRouter(boolean externalConnection) {
+ List<OpenstackRouter> routers;
+ if (externalConnection) {
+ routers = openstackService.routers()
+ .stream()
+ .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() > 0))
+ .collect(Collectors.toList());
+ } else {
+ routers = openstackService.routers()
+ .stream()
+ .filter(r -> (r.gatewayExternalInfo().externalFixedIps().size() == 0))
+ .collect(Collectors.toList());
+ }
+ return routers;
}
@Override
public void deleteRouter(String id) {
- //TODO
+ //TODO : In now, there`s nothing to do for deleteRouter process. It is reserved.
}
@Override
public void updateRouterInterface(OpenstackRouterInterface routerInterface) {
- routerInterfaceMap.putIfAbsent(routerInterface.id(), routerInterface);
List<OpenstackRouterInterface> routerInterfaces = Lists.newArrayList();
routerInterfaces.add(routerInterface);
checkExternalConnection(getOpenstackRouter(routerInterface.id()), routerInterfaces);
@@ -203,20 +278,55 @@
@Override
public void removeRouterInterface(OpenstackRouterInterface routerInterface) {
- OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
- openstackService, flowObjectiveService, deviceService, driverService, config);
rulePopulator.removeExternalRules(routerInterface);
- routerInterfaceMap.remove(routerInterface.portId());
+ }
+
+ @Override
+ public void checkDisassociatedFloatingIp(String portId, OpenstackPortInfo portInfo) {
+ if (floatingIpMap.size() < 1) {
+ log.info("No information in FloatingIpMap");
+ return;
+ }
+ OpenstackFloatingIP floatingIp = associatedFloatingIps()
+ .stream()
+ .filter(fIp -> fIp.portId().equals(portId))
+ .findAny()
+ .orElse(null);
+ if (floatingIp != null && portInfo != null) {
+ l3EventExecutorService.execute(
+ new OpenstackFloatingIPHandler(rulePopulator, floatingIp, false, portInfo));
+ OpenstackFloatingIP.Builder fBuilder = new OpenstackFloatingIP.Builder()
+ .floatingIpAddress(floatingIp.floatingIpAddress())
+ .id(floatingIp.id())
+ .networkId(floatingIp.networkId())
+ .status(floatingIp.status())
+ .tenantId(floatingIp.tenantId());
+ floatingIpMap.replace(floatingIp.id(), fBuilder.build());
+ } else if (portInfo == null) {
+ log.warn("portInfo is null as timing issue between ovs port update event and openstack deletePort event");
+ }
+ }
+
+ private Collection<OpenstackFloatingIP> associatedFloatingIps() {
+ List<OpenstackFloatingIP> fIps = Lists.newArrayList();
+ floatingIpMap.values()
+ .stream()
+ .filter(fIp -> fIp.value().portId() != null)
+ .forEach(fIp -> fIps.add(fIp.value()));
+ return fIps;
}
private void reloadInitL3Rules() {
- openstackService.ports()
- .stream()
- .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
- .forEach(p -> {
- OpenstackRouterInterface routerInterface = portToRouterInterface(p);
- updateRouterInterface(routerInterface);
- });
+ l3EventExecutorService.submit(() ->
+ openstackService.ports()
+ .stream()
+ .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
+ .forEach(p -> {
+ OpenstackRouterInterface routerInterface = portToRouterInterface(p);
+ updateRouterInterface(routerInterface);
+ })
+ );
+
}
private OpenstackRouterInterface portToRouterInterface(OpenstackPort p) {
@@ -249,8 +359,6 @@
return;
} else if (ethernet.getEtherType() == Ethernet.TYPE_IPV4) {
IPv4 iPacket = (IPv4) ethernet.getPayload();
- OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
- openstackService, flowObjectiveService, deviceService, driverService, config);
switch (iPacket.getProtocol()) {
case IPv4.PROTOCOL_ICMP:
@@ -275,7 +383,7 @@
OpenstackPort openstackPort = getOpenstackPort(ethernet.getSourceMAC(),
Ip4Address.valueOf(iPacket.getSourceAddress()));
l3EventExecutorService.execute(new OpenstackPnatHandler(rulePopulator, context,
- portNum, openstackPort, port));
+ portNum, openstackPort, port, config));
break;
}
} else if (ethernet.getEtherType() == Ethernet.TYPE_ARP) {
@@ -285,11 +393,32 @@
}
private int getPortNum(MacAddress sourceMac, int destinationAddress) {
- int portNum = portNumMap.keySet().stream()
- .filter(k -> portNumMap.get(k).equals("")).findFirst().orElse(0);
- portNumMap.replace(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
+ int portNum = findUnusedPortNum();
+ if (portNum == 0) {
+ clearPortNumMap();
+ portNum = findUnusedPortNum();
+ }
+ tpPortNumMap.put(portNum, sourceMac.toString().concat(":").concat(String.valueOf(destinationAddress)));
return portNum;
}
+
+ private int findUnusedPortNum() {
+ for (int i = TP_PORT_MINIMUM_NUM; i < TP_PORT_MAXIMUM_NUM; i++) {
+ if (!tpPortNumMap.containsKey(i)) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ }
+
+ private void clearPortNumMap() {
+ tpPortNumMap.entrySet().forEach(e -> {
+ if (System.currentTimeMillis() - e.getValue().creationTime() > PNAT_PORT_EXPIRE_TIME) {
+ tpPortNumMap.remove(e.getKey());
+ }
+ });
}
private Port getExternalPort(DeviceId deviceId, String interfaceName) {
@@ -301,29 +430,55 @@
}
private void checkExternalConnection(OpenstackRouter router,
- Collection<OpenstackRouterInterface> routerInterfaces) {
+ Collection<OpenstackRouterInterface> interfaces) {
checkNotNull(router, "Router can not be null");
- checkNotNull(routerInterfaces, "routerInterfaces can not be null");
+ checkNotNull(interfaces, "routerInterfaces can not be null");
Ip4Address externalIp = router.gatewayExternalInfo().externalFixedIps()
.values().stream().findFirst().orElse(null);
if ((externalIp == null) || (!router.gatewayExternalInfo().isEnablePnat())) {
log.debug("Not satisfied to set pnat configuration");
return;
}
- routerInterfaces.forEach(routerInterface -> initiateL3Rule(router, routerInterface));
+ if (router.id() == null) {
+ interfaces.forEach(i -> initiateL3Rule(getRouterfromExternalIp(externalIp), i));
+ } else {
+ interfaces.forEach(i -> initiateL3Rule(router, i));
+ }
+
+ }
+
+ private OpenstackRouter getRouterfromExternalIp(Ip4Address externalIp) {
+ OpenstackRouter router = getExternalRouter(true)
+ .stream()
+ .filter(r -> r.gatewayExternalInfo()
+ .externalFixedIps()
+ .values()
+ .stream()
+ .findFirst()
+ .orElse(null)
+ .equals(externalIp))
+ .findAny()
+ .orElse(null);
+ return checkNotNull(router);
}
private void initiateL3Rule(OpenstackRouter router, OpenstackRouterInterface routerInterface) {
long vni = Long.parseLong(openstackService.network(openstackService
.port(routerInterface.portId()).networkId()).segmentId());
- OpenstackRoutingRulePopulator rulePopulator = new OpenstackRoutingRulePopulator(appId,
- openstackService, flowObjectiveService, deviceService, driverService, config);
rulePopulator.populateExternalRules(vni, router, routerInterface);
}
private Collection<OpenstackRouterInterface> getOpenstackRouterInterface(OpenstackRouter router) {
- return routerInterfaceMap.values().stream().filter(i -> i.id().equals(router.id()))
- .collect(Collectors.toList());
+ List<OpenstackRouterInterface> interfaces = Lists.newArrayList();
+ openstackService.ports()
+ .stream()
+ .filter(p -> p.deviceOwner().equals(DEVICE_OWNER_ROUTER_INTERFACE))
+ .filter(p -> p.deviceId().equals(router.id()))
+ .forEach(p -> {
+ OpenstackRouterInterface routerInterface = portToRouterInterface(p);
+ interfaces.add(routerInterface);
+ });
+ return interfaces;
}
private OpenstackRouter getOpenstackRouter(String id) {
@@ -334,8 +489,8 @@
private OpenstackPort getOpenstackPort(MacAddress sourceMac, Ip4Address ip4Address) {
OpenstackPort openstackPort = openstackService.ports().stream()
.filter(p -> p.macAddress().equals(sourceMac)).findFirst().orElse(null);
- return checkNotNull(openstackPort.fixedIps().values().stream().findFirst().orElse(null))
- .equals(ip4Address) ? openstackPort : null;
+ return openstackPort.fixedIps().values().stream().filter(ip ->
+ ip.equals(ip4Address)).count() > 0 ? openstackPort : null;
}
private void readConfiguration() {
@@ -353,7 +508,8 @@
log.debug("Configured info: {}, {}, {}, {}", config.physicalRouterMac(), config.gatewayBridgeId(),
config.gatewayExternalInterfaceMac(), config.gatewayExternalInterfaceName());
- reloadInitL3Rules();
+ rulePopulator = new OpenstackRoutingRulePopulator(appId,
+ openstackService, flowObjectiveService, deviceService, driverService, config);
openstackIcmpHandler = new OpenstackIcmpHandler(packetService, deviceService,
openstackService, config, openstackSwitchingService);
@@ -361,7 +517,7 @@
openstackIcmpHandler.requestPacket(appId);
openstackArpHandler.requestPacket(appId);
-
+ reloadInitL3Rules();
log.info("OpenstackRouting configured");
}
@@ -373,14 +529,12 @@
return;
}
- switch (event.type()) {
- case CONFIG_ADDED:
- case CONFIG_UPDATED:
- l3EventExecutorService.execute(OpenstackRoutingManager.this::readConfiguration);
- break;
- default:
- log.debug("Unsupported event type {}", event.type().toString());
- break;
+ if (event.type().equals(NetworkConfigEvent.Type.CONFIG_ADDED) ||
+ event.type().equals(NetworkConfigEvent.Type.CONFIG_UPDATED)) {
+ l3EventExecutorService.execute(OpenstackRoutingManager.this::readConfiguration);
+ rulePopulator = new OpenstackRoutingRulePopulator(appId,
+ openstackService, flowObjectiveService, deviceService, driverService, config);
+
}
}
}