[ONOS-7444] Optimize SONA gw doesn't use vrouter app and quagga anymore
- Done: Deriving MAC address from external peer router and simple SNAT functionality
- Todo: SNAT, Floating IP-based routing
Change-Id: Ib1a5784a7304c44b28d7b2c9891b98fd13000db1
diff --git a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
index 6f10112..8d16bce 100644
--- a/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
+++ b/apps/openstacknetworking/src/main/java/org/onosproject/openstacknetworking/impl/OpenstackNetworkManager.java
@@ -23,20 +23,45 @@
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.ARP;
+import org.onlab.packet.Ethernet;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onlab.util.KryoNamespace;
+import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.event.ListenerRegistry;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.flow.DefaultTrafficTreatment;
+import org.onosproject.net.flow.TrafficTreatment;
+import org.onosproject.net.packet.DefaultOutboundPacket;
+import org.onosproject.net.packet.PacketService;
import org.onosproject.openstacknetworking.api.Constants;
+import org.onosproject.openstacknetworking.api.ExternalPeerRouter;
import org.onosproject.openstacknetworking.api.OpenstackNetworkAdminService;
import org.onosproject.openstacknetworking.api.OpenstackNetworkEvent;
import org.onosproject.openstacknetworking.api.OpenstackNetworkListener;
import org.onosproject.openstacknetworking.api.OpenstackNetworkService;
import org.onosproject.openstacknetworking.api.OpenstackNetworkStore;
import org.onosproject.openstacknetworking.api.OpenstackNetworkStoreDelegate;
+import org.onosproject.openstacknode.api.OpenstackNode;
+import org.onosproject.openstacknode.api.OpenstackNodeService;
+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.onosproject.store.service.Versioned;
+import org.openstack4j.model.network.ExternalGateway;
+import org.openstack4j.model.network.IP;
import org.openstack4j.model.network.Network;
import org.openstack4j.model.network.Port;
+import org.openstack4j.model.network.Router;
import org.openstack4j.model.network.Subnet;
import org.slf4j.Logger;
+import java.nio.ByteBuffer;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
@@ -51,6 +76,7 @@
* Provides implementation of administering and interfacing OpenStack network,
* subnet, and port.
*/
+
@Service
@Component(immediate = true)
public class OpenstackNetworkManager
@@ -77,21 +103,57 @@
private static final String ERR_NULL_PORT_ID = "OpenStack port ID cannot be null";
private static final String ERR_NULL_PORT_NET_ID = "OpenStack port network ID cannot be null";
+ private static final String ERR_NOT_FOUND = " does not exist";
private static final String ERR_IN_USE = " still in use";
+ private static final String ERR_DUPLICATE = " already exists";
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PacketService packetService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected OpenstackNetworkStore osNetworkStore;
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected StorageService storageService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected OpenstackNodeService osNodeService;
+
+
private final OpenstackNetworkStoreDelegate delegate = new InternalNetworkStoreDelegate();
+ private ConsistentMap<String, ExternalPeerRouter> externalPeerRouterMap;
+
+ private static final KryoNamespace SERIALIZER_EXTERNAL_PEER_ROUTER_MAP = KryoNamespace.newBuilder()
+ .register(KryoNamespaces.API)
+ .register(ExternalPeerRouter.class)
+ .register(DefaultExternalPeerRouter.class)
+ .register(MacAddress.class)
+ .register(IpAddress.class)
+ .register(VlanId.class)
+ .build();
+
+ private ApplicationId appId;
+
+
@Activate
protected void activate() {
- coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+ appId = coreService.registerApplication(Constants.OPENSTACK_NETWORKING_APP_ID);
+
osNetworkStore.setDelegate(delegate);
log.info("Started");
+
+ externalPeerRouterMap = storageService.<String, ExternalPeerRouter>consistentMapBuilder()
+ .withSerializer(Serializer.using(SERIALIZER_EXTERNAL_PEER_ROUTER_MAP))
+ .withName("external-routermap")
+ .withApplicationId(appId)
+ .build();
}
@Deactivate
@@ -256,12 +318,12 @@
.stream()
.filter(p -> p.getId().contains(portName.substring(3)))
.findFirst();
- return osPort.isPresent() ? osPort.get() : null;
+ return osPort.orElse(null);
}
@Override
public Set<Port> ports() {
- return osNetworkStore.ports();
+ return ImmutableSet.copyOf(osNetworkStore.ports());
}
@Override
@@ -272,6 +334,175 @@
return ImmutableSet.copyOf(osPorts);
}
+ @Override
+ public ExternalPeerRouter externalPeerRouter(IpAddress ipAddress) {
+ if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
+ return externalPeerRouterMap.get(ipAddress.toString()).value();
+ }
+ return null;
+ }
+
+ @Override
+ public void deriveExternalPeerRouterMac(ExternalGateway externalGateway, Router router) {
+ log.info("deriveExternalPeerRouterMac called");
+
+ IpAddress sourceIp = getExternalGatewaySourceIp(externalGateway, router);
+ IpAddress targetIp = getExternalPeerRouterIp(externalGateway);
+
+ if (sourceIp == null || targetIp == null) {
+ log.warn("Failed to derive external router mac address because source IP {} or target IP {} is null",
+ sourceIp, targetIp);
+ return;
+ }
+
+ if (externalPeerRouterMap.containsKey(targetIp.toString()) &&
+ !externalPeerRouterMap.get(
+ targetIp.toString()).value().externalPeerRouterMac().equals(MacAddress.NONE)) {
+ return;
+ }
+
+ MacAddress sourceMac = Constants.DEFAULT_GATEWAY_MAC;
+ Ethernet ethRequest = ARP.buildArpRequest(sourceMac.toBytes(),
+ sourceIp.toOctets(),
+ targetIp.toOctets(),
+ VlanId.NO_VID);
+
+ if (osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY).isEmpty()) {
+ log.warn("There's no complete gateway");
+ return;
+ }
+ OpenstackNode gatewayNode = osNodeService.completeNodes(OpenstackNode.NodeType.GATEWAY)
+ .stream()
+ .findFirst()
+ .orElse(null);
+
+ if (gatewayNode == null) {
+ return;
+ }
+
+ String upLinkPort = gatewayNode.uplinkPort();
+
+ org.onosproject.net.Port port = deviceService.getPorts(gatewayNode.intgBridge()).stream()
+ .filter(p -> Objects.equals(p.annotations().value(PORT_NAME), upLinkPort))
+ .findAny().orElse(null);
+
+ if (port == null) {
+ log.warn("There's no uplink port for gateway node {}", gatewayNode.toString());
+ return;
+ }
+
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
+ .setOutput(port.number())
+ .build();
+
+ packetService.emit(new DefaultOutboundPacket(
+ gatewayNode.intgBridge(),
+ treatment,
+ ByteBuffer.wrap(ethRequest.serialize())));
+
+ externalPeerRouterMap.put(
+ targetIp.toString(), new DefaultExternalPeerRouter(targetIp, MacAddress.NONE, VlanId.NONE));
+
+ log.info("Initializes external peer router map with peer router IP {}", targetIp.toString());
+ }
+
+ @Override
+ public void deleteExternalPeerRouter(ExternalGateway externalGateway) {
+ IpAddress targetIp = getExternalPeerRouterIp(externalGateway);
+ if (targetIp == null) {
+ return;
+ }
+
+ if (externalPeerRouterMap.containsKey(targetIp.toString())) {
+ externalPeerRouterMap.remove(targetIp.toString());
+ }
+ }
+
+ private IpAddress getExternalGatewaySourceIp(ExternalGateway externalGateway, Router router) {
+ Port exGatewayPort = ports(externalGateway.getNetworkId())
+ .stream()
+ .filter(port -> Objects.equals(port.getDeviceId(), router.getId()))
+ .findAny().orElse(null);
+ if (exGatewayPort == null) {
+ log.warn("no external gateway port for router({})", router.getName());
+ return null;
+ }
+
+ IP ipAddress = exGatewayPort.getFixedIps().stream().findFirst().orElse(null);
+
+ return ipAddress == null ? null : IpAddress.valueOf(ipAddress.getIpAddress());
+ }
+
+ private IpAddress getExternalPeerRouterIp(ExternalGateway externalGateway) {
+ Optional<Subnet> externalSubnet = subnets(externalGateway.getNetworkId())
+ .stream()
+ .findFirst();
+
+ if (externalSubnet.isPresent()) {
+ return IpAddress.valueOf(externalSubnet.get().getGateway());
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void updateExternalPeerRouterMac(IpAddress ipAddress, MacAddress macAddress) {
+ try {
+ externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
+ new DefaultExternalPeerRouter(ipAddress, macAddress, existing.externalPeerRouterVlanId()));
+ } catch (Exception e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+
+ log.info("Updated external peer router map {}",
+ externalPeerRouterMap.get(ipAddress.toString()).value().toString());
+ }
+
+
+ @Override
+ public void updateExternalPeerRouter(IpAddress ipAddress, MacAddress macAddress, VlanId vlanId) {
+ try {
+ externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) ->
+ new DefaultExternalPeerRouter(ipAddress, macAddress, vlanId));
+ } catch (Exception e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+ }
+
+ @Override
+ public MacAddress externalPeerRouterMac(ExternalGateway externalGateway) {
+ IpAddress ipAddress = getExternalPeerRouterIp(externalGateway);
+
+ if (ipAddress == null) {
+ return null;
+ }
+ if (externalPeerRouterMap.containsKey(ipAddress.toString())) {
+ return externalPeerRouterMap.get(ipAddress.toString()).value().externalPeerRouterMac();
+ } else {
+ throw new NoSuchElementException();
+ }
+ }
+
+ @Override
+ public void updateExternalPeerRouterVlan(IpAddress ipAddress, VlanId vlanId) {
+
+ try {
+ externalPeerRouterMap.computeIfPresent(ipAddress.toString(), (id, existing) -> {
+ return new DefaultExternalPeerRouter(ipAddress, existing.externalPeerRouterMac(), vlanId);
+ });
+ } catch (Exception e) {
+ log.error("Exception occurred because of {}", e.toString());
+ }
+ }
+
+ @Override
+ public Set<ExternalPeerRouter> externalPeerRouters() {
+ Set<ExternalPeerRouter> externalPeerRouters = externalPeerRouterMap.values().stream()
+ .map(Versioned::value)
+ .collect(Collectors.toSet());
+ return ImmutableSet.copyOf(externalPeerRouters);
+ }
+
private boolean isNetworkInUse(String netId) {
return !subnets(netId).isEmpty() && !ports(netId).isEmpty();
}