/*
- * Copyright (C) 2014 Red Hat, Inc.
+ * Copyright (c) 2014, 2015 Red Hat, Inc. and others. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 which accompanies this distribution,
* and is available at http://www.eclipse.org/legal/epl-v10.html
- *
- * Authors : Dave Tucker, Flavio Fernandes
*/
package org.opendaylight.ovsdb.openstack.netvirt.impl;
-import org.opendaylight.neutron.spi.INeutronNetworkCRUD;
-import org.opendaylight.neutron.spi.INeutronPortCRUD;
-import org.opendaylight.neutron.spi.INeutronSubnetCRUD;
-import org.opendaylight.neutron.spi.NeutronFloatingIP;
-import org.opendaylight.neutron.spi.NeutronNetwork;
-import org.opendaylight.neutron.spi.NeutronPort;
-import org.opendaylight.neutron.spi.NeutronRouter;
-import org.opendaylight.neutron.spi.NeutronRouter_Interface;
-import org.opendaylight.neutron.spi.NeutronSubnet;
-import org.opendaylight.neutron.spi.Neutron_IPs;
-import org.opendaylight.controller.sal.core.Node;
-import org.opendaylight.controller.sal.utils.HexEncode;
-import org.opendaylight.controller.sal.utils.Status;
-import org.opendaylight.controller.sal.utils.StatusCode;
-import org.opendaylight.ovsdb.lib.notation.Row;
-import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
-import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
-import org.opendaylight.ovsdb.openstack.netvirt.api.MultiTenantAwareRouter;
-import org.opendaylight.ovsdb.openstack.netvirt.api.NetworkingProviderManager;
-import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
-import org.opendaylight.ovsdb.compatibility.plugin.api.OvsdbConfigurationService;
-import org.opendaylight.ovsdb.compatibility.plugin.api.OvsdbConnectionService;
-import org.opendaylight.ovsdb.schema.openvswitch.Bridge;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.api.ConfigurationService;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Constants;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
+import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
import org.opendaylight.ovsdb.openstack.netvirt.api.OutboundNatProvider;
import org.opendaylight.ovsdb.openstack.netvirt.api.RoutingProvider;
-import org.opendaylight.ovsdb.schema.openvswitch.Interface;
-import org.opendaylight.ovsdb.utils.config.ConfigProperties;
-
-import com.google.common.base.Preconditions;
+import org.opendaylight.ovsdb.openstack.netvirt.api.SecurityServicesManager;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Southbound;
+import org.opendaylight.ovsdb.openstack.netvirt.api.Status;
+import org.opendaylight.ovsdb.openstack.netvirt.api.StatusCode;
+import org.opendaylight.ovsdb.openstack.netvirt.api.TenantNetworkManager;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronPort;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronRouter_Interface;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSecurityGroup;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronSubnet;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.Neutron_IPs;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronFloatingIPCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronNetworkCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronPortCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.crud.INeutronSubnetCRUD;
+import org.opendaylight.ovsdb.openstack.netvirt.translator.iaware.impl.NeutronIAwareUtil;
+import org.opendaylight.ovsdb.utils.neutron.utils.NeutronModelsDataStoreHelper;
+import org.opendaylight.ovsdb.utils.servicehelper.ServiceHelper;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.Routers;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.l3.rev150712.routers.attributes.routers.Router;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105.OvsdbTerminationPointAugmentation;
+import org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.network.topology.topology.Node;
+import org.osgi.framework.ServiceReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import com.google.common.base.Preconditions;
+import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.util.concurrent.Futures;
+import com.google.common.util.concurrent.ListenableFuture;
/**
* Neutron L3 Adapter implements a hub-like adapter for the various Neutron events. Based on
* these events, the abstract router callbacks can be generated to the multi-tenant aware router,
* as well as the multi-tenant router forwarding provider.
*/
-public class NeutronL3Adapter {
-
- /**
- * Logger instance.
- */
- static final Logger logger = LoggerFactory.getLogger(NeutronL3Adapter.class);
+public class NeutronL3Adapter implements ConfigInterface {
+ private static final Logger LOG = LoggerFactory.getLogger(NeutronL3Adapter.class);
// The implementation for each of these services is resolved by the OSGi Service Manager
private volatile ConfigurationService configurationService;
private volatile TenantNetworkManager tenantNetworkManager;
- private volatile NetworkingProviderManager networkingProviderManager;
- private volatile OvsdbConfigurationService ovsdbConfigurationService;
- private volatile OvsdbConnectionService connectionService;
+ private volatile NodeCacheManager nodeCacheManager;
private volatile INeutronNetworkCRUD neutronNetworkCache;
private volatile INeutronSubnetCRUD neutronSubnetCache;
private volatile INeutronPortCRUD neutronPortCache;
- private volatile MultiTenantAwareRouter multiTenantAwareRouter;
+ private volatile INeutronFloatingIPCRUD neutronFloatingIpCache;
private volatile L3ForwardingProvider l3ForwardingProvider;
private volatile InboundNatProvider inboundNatProvider;
private volatile OutboundNatProvider outboundNatProvider;
private volatile ArpProvider arpProvider;
private volatile RoutingProvider routingProvider;
+ private volatile GatewayMacResolver gatewayMacResolver;
+ private volatile SecurityServicesManager securityServicesManager;
+
+ private class FloatIpData {
+ // br-int of node where floating ip is associated with tenant port
+ private final Long dpid;
+ // patch port in br-int used to reach br-ex
+ private final Long ofPort;
+ // segmentation id of the net where fixed ip is instantiated
+ private final String segId;
+ // mac address assigned to neutron port of floating ip
+ private final String macAddress;
+ private final String floatingIpAddress;
+ // ip address given to tenant vm
+ private final String fixedIpAddress;
+ private final String neutronRouterMac;
+
+ FloatIpData(final Long dpid, final Long ofPort, final String segId, final String macAddress,
+ final String floatingIpAddress, final String fixedIpAddress, final String neutronRouterMac) {
+ this.dpid = dpid;
+ this.ofPort = ofPort;
+ this.segId = segId;
+ this.macAddress = macAddress;
+ this.floatingIpAddress = floatingIpAddress;
+ this.fixedIpAddress = fixedIpAddress;
+ this.neutronRouterMac = neutronRouterMac;
+ }
+ }
- private Set<String> inboundIpRewriteCache;
- private Set<String> outboundIpRewriteCache;
- private Set<String> inboundIpRewriteExclusionCache;
- private Set<String> outboundIpRewriteExclusionCache;
- private Set<String> routerInterfacesCache;
- private Set<String> staticArpEntryCache;
- private Set<String> l3ForwardingCache;
- private Set<String> defaultRouteCache;
private Map<String, String> networkIdToRouterMacCache;
+ private Map<String, List<Neutron_IPs>> networkIdToRouterIpListCache;
private Map<String, NeutronRouter_Interface> subnetIdToRouterInterfaceCache;
+
+ private Map<String, Pair<Long, Uuid>> neutronPortToDpIdCache;
+ private Map<String, FloatIpData> floatIpDataMapCache;
+
+ private String externalRouterMac;
private Boolean enabled = false;
+ private Boolean flgDistributedARPEnabled = true;
+ private Boolean isCachePopulationDone = false;
+ private final ExecutorService gatewayMacResolverPool = Executors.newFixedThreadPool(5);
+
+ private Southbound southbound;
+ private NeutronModelsDataStoreHelper neutronModelsDataStoreHelper;
+
+ private static final String OWNER_ROUTER_INTERFACE = "network:router_interface";
+ private static final String OWNER_ROUTER_INTERFACE_DISTRIBUTED = "network:router_interface_distributed";
+ private static final String OWNER_ROUTER_GATEWAY = "network:router_gateway";
+ private static final String OWNER_FLOATING_IP = "network:floatingip";
+ private static final String DEFAULT_EXT_RTR_MAC = "00:00:5E:00:01:01";
+
+ public NeutronL3Adapter(NeutronModelsDataStoreHelper neutronHelper) {
+ LOG.info(">>>>>> NeutronL3Adapter constructor {}", this.getClass());
+ this.neutronModelsDataStoreHelper = neutronHelper;
+ }
- void init() {
- final String enabledPropertyStr = ConfigProperties.getProperty(this.getClass(), "ovsdb.l3.fwd.enabled");
- if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) {
- this.inboundIpRewriteCache = new HashSet<>();
- this.outboundIpRewriteCache = new HashSet<>();
- this.inboundIpRewriteExclusionCache = new HashSet<>();
- this.outboundIpRewriteExclusionCache = new HashSet<>();
- this.routerInterfacesCache = new HashSet<>();
- this.staticArpEntryCache = new HashSet<>();
- this.l3ForwardingCache = new HashSet<>();
- this.defaultRouteCache = new HashSet<>();
+ private void initL3AdapterMembers() {
+ Preconditions.checkNotNull(configurationService);
+
+ if (configurationService.isL3ForwardingEnabled()) {
this.networkIdToRouterMacCache = new HashMap<>();
+ this.networkIdToRouterIpListCache = new HashMap<>();
this.subnetIdToRouterInterfaceCache = new HashMap<>();
+ this.neutronPortToDpIdCache = new HashMap<>();
+ this.floatIpDataMapCache = new HashMap<>();
+
+ this.externalRouterMac = configurationService.getDefaultGatewayMacAddress(null);
+ if (this.externalRouterMac == null) {
+ this.externalRouterMac = DEFAULT_EXT_RTR_MAC;
+ }
this.enabled = true;
- logger.info("OVSDB L3 forwarding is enabled");
+ LOG.info("OVSDB L3 forwarding is enabled");
+ if (configurationService.isDistributedArpDisabled()) {
+ this.flgDistributedARPEnabled = false;
+ LOG.info("Distributed ARP responder is disabled");
+ } else {
+ LOG.debug("Distributed ARP responder is enabled");
+ }
} else {
- logger.debug("OVSDB L3 forwarding is disabled");
+ LOG.debug("OVSDB L3 forwarding is disabled");
}
}
- //
- // Callbacks from OVSDB's northbound handlers
- //
- public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
- logger.debug("Neutron subnet {} event : {}", action, subnet.toString());
- if (!this.enabled)
+ private void populateL3ForwardingCaches() {
+ if (!this.enabled) {
+ return;
+ }
+ if(this.isCachePopulationDone || this.neutronFloatingIpCache == null
+ || this.neutronPortCache == null ||this.neutronNetworkCache == null) {
return;
+ }
+ this.isCachePopulationDone = true;
+ LOG.debug("Populating NetVirt L3 caches from data store configuration");
+ Routers routers = this.neutronModelsDataStoreHelper.readAllNeutronRouters();
+ Ports ports = this.neutronModelsDataStoreHelper.readAllNeutronPorts();
+ if(routers != null && routers.getRouter() != null && ports != null) {
+ LOG.debug("L3 Cache Population : {} Neutron router present in data store",routers.getRouter().size());
+ for( Router router : routers.getRouter()) {
+ LOG.debug("L3 Cache Population : Populate caches for router {}",router);
+ if(!ports.getPort().isEmpty()) {
+ for( Port port : ports.getPort()) {
+ if (port.getDeviceId().equals(router.getUuid().getValue()) &&
+ port.getDeviceOwner().equals(OWNER_ROUTER_INTERFACE)) {
+ LOG.debug("L3 Cache Population : Router interface {} found.",port);
+ networkIdToRouterMacCache.put(port.getNetworkId().getValue()
+ , port.getMacAddress());
+
+ networkIdToRouterIpListCache.put(port.getNetworkId().getValue(),
+ NeutronIAwareUtil.convertMDSalIpToNeutronIp(port.getFixedIps()));
+ subnetIdToRouterInterfaceCache.put(port.getFixedIps().get(0).getSubnetId().getValue(),
+ NeutronIAwareUtil.convertMDSalInterfaceToNeutronRouterInterface(port));
+ }
+ }
+ }else {
+ LOG.warn("L3 Cache Population :Did not find any port information " +
+ "in config Data Store for router {}",router);
+ }
+ }
+ }
+ LOG.debug("NetVirt L3 caches population is done");
+ }
+
+ private Pair<Long, Uuid> getDpIdOfNeutronPort(String neutronTenantPortUuid) {
+ if(neutronPortToDpIdCache.get(neutronTenantPortUuid) == null) {
+ List<Node> bridges = this.southbound.readOvsdbTopologyBridgeNodes();
+ LOG.debug("getDpIdOfNeutronPort : {} bridges present in ovsdb topology",bridges.size());
+ for(Node bridge : bridges) {
+ List<OvsdbTerminationPointAugmentation> interfaces
+ = southbound.extractTerminationPointAugmentations(bridge);
+ if(interfaces != null && !interfaces.isEmpty()) {
+ LOG.debug("getDpIdOfNeutronPort : {} termination point present on bridge {}",
+ interfaces.size(), bridge.getNodeId());
+ for (OvsdbTerminationPointAugmentation intf : interfaces) {
+ NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
+ if(neutronPort != null && neutronPort.getID().equals(neutronTenantPortUuid)) {
+ Long dpId = getDpidForIntegrationBridge(bridge);
+ Uuid interfaceUuid = intf.getInterfaceUuid();
+ LOG.debug("getDpIdOfNeutronPort : Found bridge {} and interface {} for the tenant neutron" +
+ " port {}",dpId,interfaceUuid,neutronTenantPortUuid);
+ handleInterfaceEventAdd(neutronPort.getPortUUID(), dpId, interfaceUuid);
+ break;
+ }
+ }
+ }
+ }
+ }
+ return neutronPortToDpIdCache.get(neutronTenantPortUuid);
+ }
+
+ private Collection<FloatIpData> getAllFloatingIPsWithMetadata() {
+ LOG.debug("getAllFloatingIPsWithMetadata : Fechting all floating Ips and it's metadata");
+ List<NeutronFloatingIP> neutronFloatingIps = neutronFloatingIpCache.getAllFloatingIPs();
+ if(neutronFloatingIps != null && !neutronFloatingIps.isEmpty()) {
+ for (NeutronFloatingIP neutronFloatingIP : neutronFloatingIps) {
+ if(!floatIpDataMapCache.containsKey(neutronFloatingIP.getID())){
+ LOG.debug("Metadata for floating ip {} is not present in the cache. " +
+ "Fetching from data store.",neutronFloatingIP.getID());
+ this.getFloatingIPWithMetadata(neutronFloatingIP.getID());
+ }
+ }
+ }
+ LOG.debug("getAllFloatingIPsWithMetadata : {} floating points found in data store",floatIpDataMapCache.size());
+ return floatIpDataMapCache.values();
+ }
+ private FloatIpData getFloatingIPWithMetadata(String neutronFloatingId) {
+ LOG.debug("getFloatingIPWithMetadata : Get Floating ip and it's meta data for neutron " +
+ "floating id {} ",neutronFloatingId);
+ if(floatIpDataMapCache.get(neutronFloatingId) == null) {
+ NeutronFloatingIP neutronFloatingIP = neutronFloatingIpCache.getFloatingIP(neutronFloatingId);
+ if (neutronFloatingIP == null) {
+ LOG.error("getFloatingIPWithMetadata : Floating ip {} is missing from data store, that should not happen",neutronFloatingId);
+ return null;
+ }
+ List<NeutronPort> neutronPorts = neutronPortCache.getAllPorts();
+ NeutronPort neutronPortForFloatIp = null;
+ for (NeutronPort neutronPort : neutronPorts) {
+ if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
+ neutronPort.getDeviceID().equals(neutronFloatingIP.getID())) {
+ neutronPortForFloatIp = neutronPort;
+ break;
+ }
+ }
+
+ String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
+ if(neutronTenantPortUuid == null) {
+ return null;
+ }
+ Pair<Long, Uuid> nodeIfPair = this.getDpIdOfNeutronPort(neutronTenantPortUuid);
+ String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
+ String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
+ String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
+
+ NeutronPort tenantNeutronPort = neutronPortCache.getPort(neutronTenantPortUuid);
+ NeutronNetwork tenantNeutronNetwork = tenantNeutronPort != null ?
+ neutronNetworkCache.getNetwork(tenantNeutronPort.getNetworkUUID()) : null;
+ String providerSegmentationId = tenantNeutronNetwork != null ?
+ tenantNeutronNetwork.getProviderSegmentationID() : null;
+ String neutronRouterMac = tenantNeutronNetwork != null ?
+ networkIdToRouterMacCache.get(tenantNeutronNetwork.getID()) : null;
+
+ if (nodeIfPair == null || neutronTenantPortUuid == null ||
+ providerSegmentationId == null || providerSegmentationId.isEmpty() ||
+ floatingIpMac == null || floatingIpMac.isEmpty() ||
+ neutronRouterMac == null || neutronRouterMac.isEmpty()) {
+ LOG.debug("getFloatingIPWithMetadata :Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} " +
+ "seg {} mac {} rtrMac {}",
+ fixedIpAddress,
+ floatingIpAddress,
+ neutronPortForFloatIp,
+ neutronTenantPortUuid,
+ providerSegmentationId,
+ floatingIpMac,
+ neutronRouterMac);
+
+ return null;
+ }
+
+ // get ofport for patch port in br-int
+ final Long dpId = nodeIfPair.getLeft();
+ final Long ofPort = findOFPortForExtPatch(dpId);
+ if (ofPort == null) {
+ LOG.warn("getFloatingIPWithMetadata : Unable to locate OF port of patch port " +
+ "to connect floating ip to external bridge. dpid {}",
+ dpId);
+ return null;
+ }
+
+ final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
+ floatingIpAddress, fixedIpAddress, neutronRouterMac);
+ floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
+
+ }
+ return floatIpDataMapCache.get(neutronFloatingId);
+ }
+ /**
+ * Invoked to configure the mac address for the external gateway in br-ex. ovsdb netvirt needs help in getting
+ * mac for given ip in br-ex (bug 3378). For example, since ovsdb has no real arp, it needs a service in can
+ * subscribe so that the mac address associated to the gateway ip address is available.
+ *
+ * @param externalRouterMacUpdate The mac address to be associated to the gateway.
+ */
+ public void updateExternalRouterMac(final String externalRouterMacUpdate) {
+ Preconditions.checkNotNull(externalRouterMacUpdate);
+
+ flushExistingIpRewrite();
+ this.externalRouterMac = externalRouterMacUpdate;
+ rebuildExistingIpRewrite();
+ }
+
+ /**
+ * Process the event.
+ *
+ * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
+ * @param subnet An instance of NeutronSubnet object.
+ */
+ public void handleNeutronSubnetEvent(final NeutronSubnet subnet, Action action) {
+ LOG.debug("Neutron subnet {} event : {}", action, subnet.toString());
}
+ /**
+ * Process the port event as a router interface event.
+ * For a not delete action, since a port is only create when the tennat uses the subnet, it is required to
+ * verify if all routers across all nodes have the interface for the port's subnet(s) configured.
+ *
+ * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
+ * @param neutronPort An instance of NeutronPort object.
+ */
public void handleNeutronPortEvent(final NeutronPort neutronPort, Action action) {
- logger.debug("Neutron port {} event : {}", action, neutronPort.toString());
- if (!this.enabled)
+ LOG.debug("Neutron port {} event : {}", action, neutronPort.toString());
+
+ this.processSecurityGroupUpdate(neutronPort);
+ if (!this.enabled) {
return;
+ }
final boolean isDelete = action == Action.DELETE;
+ if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_GATEWAY)){
+ if(!isDelete){
+ Node externalBridgeNode = getExternalBridgeNode();
+ if(externalBridgeNode != null){
+ LOG.info("Port {} is network router gateway interface, "
+ + "triggering gateway resolution for the attached external network on node {}", neutronPort, externalBridgeNode);
+ this.triggerGatewayMacResolver(externalBridgeNode, neutronPort);
+ }else{
+ LOG.error("Did not find Node that has external bridge (br-ex), Gateway resolution failed");
+ }
+ }else{
+ NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(neutronPort.getNetworkUUID());
+
+ if (externalNetwork != null && externalNetwork.isRouterExternal()) {
+ final NeutronSubnet externalSubnet = getExternalNetworkSubnet(neutronPort);
+ // TODO support IPv6
+ if (externalSubnet != null &&
+ externalSubnet.getIpVersion() == 4) {
+ gatewayMacResolver.stopPeriodicRefresh(new Ipv4Address(externalSubnet.getGatewayIP()));
+ }
+ }
+ }
+ }
+
// Treat the port event as a router interface event if the port belongs to router. This is a
// helper for handling cases when handleNeutronRouterInterfaceEvent is not available
//
- if (neutronPort.getDeviceOwner().equalsIgnoreCase("network:router_interface")) {
- for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
- NeutronRouter_Interface neutronRouterInterface =
+ if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) ||
+ neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) {
+
+ if (neutronPort.getFixedIPs() != null) {
+ for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
+ NeutronRouter_Interface neutronRouterInterface =
new NeutronRouter_Interface(neutronIP.getSubnetUUID(), neutronPort.getPortUUID());
- neutronRouterInterface.setID(neutronIP.getSubnetUUID()); // id of router interface to be same as subnet
- neutronRouterInterface.setTenantID(neutronPort.getTenantID());
+ // id of router interface to be same as subnet
+ neutronRouterInterface.setID(neutronIP.getSubnetUUID());
+ neutronRouterInterface.setTenantID(neutronPort.getTenantID());
- this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
+ this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action);
+ }
}
} else {
// We made it here, port is not used as a router interface. If this is not a delete action, make sure that
// need to do this check here because a router interface is not added to a node until tenant becomes needed
// there.
//
- if (!isDelete) {
+ if (!isDelete && neutronPort.getFixedIPs() != null) {
for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
NeutronRouter_Interface neutronRouterInterface =
subnetIdToRouterInterfaceCache.get(neutronIP.getSubnetUUID());
}
}
+ /**
+ * Process the event.
+ *
+ * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
+ * @param neutronRouter An instance of NeutronRouter object.
+ */
public void handleNeutronRouterEvent(final NeutronRouter neutronRouter, Action action) {
- logger.debug("Neutron router {} event : {}", action, neutronRouter.toString());
- if (!this.enabled)
- return;
+ LOG.debug("Neutron router {} event : {}", action, neutronRouter.toString());
}
+ /**
+ * Process the event enforcing actions and verifying dependencies between all router's interface. For example,
+ * delete the ports on the same subnet.
+ *
+ * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
+ * @param neutronRouter An instance of NeutronRouter object.
+ * @param neutronRouterInterface An instance of NeutronRouter_Interface object.
+ */
public void handleNeutronRouterInterfaceEvent(final NeutronRouter neutronRouter,
final NeutronRouter_Interface neutronRouterInterface,
Action action) {
- logger.debug("Router interface {} got event {}. Subnet {}",
+ LOG.debug("Router interface {} got event {}. Subnet {}",
neutronRouterInterface.getPortUUID(),
action,
neutronRouterInterface.getSubnetUUID());
- if (!this.enabled)
+ if (!this.enabled) {
return;
+ }
final boolean isDelete = action == Action.DELETE;
boolean currPortShouldBeDeleted = false;
// Note: delete in this case only applies to 1)router interface delete and 2)ports on the same subnet
if (isDelete) {
- for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
- if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
- currPortShouldBeDeleted = true;
- break;
+ if (neutronPort.getFixedIPs() != null) {
+ for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
+ if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) {
+ currPortShouldBeDeleted = true;
+ break;
+ }
}
}
}
this.updateL3ForNeutronPort(neutronPort, currPortShouldBeDeleted);
}
+
+ if (isDelete) {
+ /*
+ * Bug 4277: Remove the router interface cache only after deleting the neutron port l3 flows.
+ */
+ this.cleanupRouterCache(neutronRouterInterface);
+ }
}
+ /**
+ * Invoked when a neutron message regarding the floating ip association is sent to odl via ml2. If the action is
+ * a creation, it will first add ARP rules for the given floating ip and then configure the DNAT (rewrite the
+ * packets from the floating IP address to the internal fixed ip) rules on OpenFlow Table 30 and SNAT rules (other
+ * way around) on OpenFlow Table 100.
+ *
+ * @param actionIn the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
+ * @param neutronFloatingIP An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronFloatingIP} instance of NeutronFloatingIP object.
+ */
public void handleNeutronFloatingIPEvent(final NeutronFloatingIP neutronFloatingIP,
- Action action) {
- logger.debug(" Floating IP {} {}<->{}, network uuid {}", action,
- neutronFloatingIP.getFixedIPAddress(),
- neutronFloatingIP.getFloatingIPAddress(),
- neutronFloatingIP.getFloatingNetworkUUID());
- if (!this.enabled)
+ Action actionIn) {
+ Preconditions.checkNotNull(neutronFloatingIP);
+
+ LOG.debug(" Floating IP {} {}<->{}, network uuid {}", actionIn,
+ neutronFloatingIP.getFixedIPAddress(),
+ neutronFloatingIP.getFloatingIPAddress(),
+ neutronFloatingIP.getFloatingNetworkUUID());
+ if (!this.enabled) {
return;
+ }
+
+ Action action;
+
+ // Consider action to be delete if getFixedIPAddress is null
+ //
+ if (neutronFloatingIP.getFixedIPAddress() == null) {
+ action = Action.DELETE;
+ } else {
+ action = actionIn;
+ }
+
+ // this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
+
+ if (action != Action.DELETE) {
+ // must be first, as it updates floatIpDataMapCache
+ programFlowsForFloatingIPArpAdd(neutronFloatingIP);
+
+ programFlowsForFloatingIPInbound(neutronFloatingIP, Action.ADD);
+ programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.ADD);
+ } else {
+ programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.DELETE);
+ programFlowsForFloatingIPInbound(neutronFloatingIP, Action.DELETE);
- this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE);
+ // must be last, as it updates floatIpDataMapCache
+ programFlowsForFloatingIPArpDelete(neutronFloatingIP.getID());
+ }
}
- public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
- logger.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
- if (!this.enabled)
+ /**
+ * This method performs creation or deletion of in-bound rules into Table 30 for a existing available floating
+ * ip, otherwise for newer one.
+ */
+ private void programFlowsForFloatingIPInbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
+ Preconditions.checkNotNull(neutronFloatingIP);
+
+ final FloatIpData fid = getFloatingIPWithMetadata(neutronFloatingIP.getID());
+ if (fid == null) {
+ LOG.trace("programFlowsForFloatingIPInboundAdd {} for {} uuid {} not in local cache",
+ action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
return;
+ }
+ programInboundIpRewriteStage1(fid.dpid, fid.ofPort, fid.segId, fid.floatingIpAddress, fid.fixedIpAddress,
+ action);
}
- //
- // Callbacks from OVSDB's southbound handler
- //
- public void handleInterfaceEvent(final Node node, final Interface intf, final NeutronNetwork neutronNetwork,
- Action action) {
- logger.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
- action, node, intf.getName(), neutronNetwork);
- if (!this.enabled)
+ /**
+ * This method performs creation or deletion of out-bound rules into Table 100 for a existing available floating
+ * ip, otherwise for newer one.
+ */
+ private void programFlowsForFloatingIPOutbound(final NeutronFloatingIP neutronFloatingIP, final Action action) {
+ Preconditions.checkNotNull(neutronFloatingIP);
+
+ final FloatIpData fid = getFloatingIPWithMetadata(neutronFloatingIP.getID());
+ if (fid == null) {
+ LOG.trace("programFlowsForFloatingIPOutbound {} for {} uuid {} not in local cache",
+ action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID());
+ return;
+ }
+ programOutboundIpRewriteStage1(fid, action);
+ }
+
+ private void flushExistingIpRewrite() {
+ for (FloatIpData fid : getAllFloatingIPsWithMetadata()) {
+ programOutboundIpRewriteStage1(fid, Action.DELETE);
+ }
+ }
+
+ private void rebuildExistingIpRewrite() {
+ for (FloatIpData fid : getAllFloatingIPsWithMetadata()) {
+ programOutboundIpRewriteStage1(fid, Action.ADD);
+ }
+ }
+
+ /**
+ * This method creates ARP response rules into OpenFlow Table 30 for a given floating ip. In order to connect
+ * to br-ex from br-int, a patch-port is used. Thus, the patch-port will be responsible to respond the ARP
+ * requests.
+ */
+ private void programFlowsForFloatingIPArpAdd(final NeutronFloatingIP neutronFloatingIP) {
+ Preconditions.checkNotNull(neutronFloatingIP);
+ Preconditions.checkNotNull(neutronFloatingIP.getFixedIPAddress());
+ Preconditions.checkNotNull(neutronFloatingIP.getFloatingIPAddress());
+
+ // find bridge Node where floating ip is configured by looking up cache for its port
+ final NeutronPort neutronPortForFloatIp = findNeutronPortForFloatingIp(neutronFloatingIP.getID());
+ final String neutronTenantPortUuid = neutronFloatingIP.getPortUUID();
+ final Pair<Long, Uuid> nodeIfPair = this.getDpIdOfNeutronPort(neutronTenantPortUuid);
+ final String floatingIpMac = neutronPortForFloatIp == null ? null : neutronPortForFloatIp.getMacAddress();
+ final String fixedIpAddress = neutronFloatingIP.getFixedIPAddress();
+ final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
+
+ final NeutronPort tenantNeutronPort = neutronPortCache.getPort(neutronTenantPortUuid);
+ final NeutronNetwork tenantNeutronNetwork = tenantNeutronPort != null ?
+ neutronNetworkCache.getNetwork(tenantNeutronPort.getNetworkUUID()) : null;
+ final String providerSegmentationId = tenantNeutronNetwork != null ?
+ tenantNeutronNetwork.getProviderSegmentationID() : null;
+ final String neutronRouterMac = tenantNeutronNetwork != null ?
+ networkIdToRouterMacCache.get(tenantNeutronNetwork.getID()) : null;
+
+ if (nodeIfPair == null || neutronTenantPortUuid == null ||
+ providerSegmentationId == null || providerSegmentationId.isEmpty() ||
+ floatingIpMac == null || floatingIpMac.isEmpty() ||
+ neutronRouterMac == null || neutronRouterMac.isEmpty()) {
+ LOG.trace("Floating IP {}<->{}, incomplete floatPort {} tenantPortUuid {} seg {} mac {} rtrMac {}",
+ fixedIpAddress,
+ floatingIpAddress,
+ neutronPortForFloatIp,
+ neutronTenantPortUuid,
+ providerSegmentationId,
+ floatingIpMac,
+ neutronRouterMac);
return;
+ }
- // See if there is an external uuid, so we can find the respective neutronPort
- Map<String, String> externalIds = intf.getExternalIdsColumn().getData();
- if (externalIds == null) {
+ // get ofport for patch port in br-int
+ final Long dpId = nodeIfPair.getLeft();
+ final Long ofPort = findOFPortForExtPatch(dpId);
+ if (ofPort == null) {
+ LOG.warn("Unable to locate OF port of patch port to connect floating ip to external bridge. dpid {}",
+ dpId);
return;
}
- String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID);
- if (neutronPortId == null) {
+
+ // Respond to ARPs for the floating ip address by default, via the patch port that connects br-int to br-ex
+ //
+ if (programStaticArpStage1(dpId, encodeExcplicitOFPort(ofPort), floatingIpMac, floatingIpAddress,
+ Action.ADD)) {
+ final FloatIpData floatIpData = new FloatIpData(dpId, ofPort, providerSegmentationId, floatingIpMac,
+ floatingIpAddress, fixedIpAddress, neutronRouterMac);
+ floatIpDataMapCache.put(neutronFloatingIP.getID(), floatIpData);
+ LOG.info("Floating IP {}<->{} programmed ARP mac {} on OFport {} seg {} dpid {}",
+ neutronFloatingIP.getFixedIPAddress(), neutronFloatingIP.getFloatingIPAddress(),
+ floatingIpMac, ofPort, providerSegmentationId, dpId);
+ }
+ }
+
+ private void programFlowsForFloatingIPArpDelete(final String neutronFloatingIPUuid) {
+ final FloatIpData floatIpData = getFloatingIPWithMetadata(neutronFloatingIPUuid);
+ if (floatIpData == null) {
+ LOG.trace("programFlowsForFloatingIPArpDelete for uuid {} is not needed", neutronFloatingIPUuid);
return;
}
- final NeutronPort neutronPort = neutronPortCache.getPort(neutronPortId);
- if (neutronPort == null) {
- logger.warn("southbound interface {} node:{} interface:{}, neutronNetwork:{} did not find port:{}",
- action, node, intf.getName(), neutronNetwork, neutronPortId);
+
+ if (programStaticArpStage1(floatIpData.dpid, encodeExcplicitOFPort(floatIpData.ofPort), floatIpData.macAddress,
+ floatIpData.floatingIpAddress, Action.DELETE)) {
+ floatIpDataMapCache.remove(neutronFloatingIPUuid);
+ LOG.info("Floating IP {} un-programmed ARP mac {} on {} dpid {}",
+ floatIpData.floatingIpAddress, floatIpData.macAddress, floatIpData.ofPort, floatIpData.dpid);
+ }
+ }
+
+ private NeutronPort findNeutronPortForFloatingIp(final String floatingIpUuid) {
+ for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) {
+ if (neutronPort.getDeviceOwner().equals(OWNER_FLOATING_IP) &&
+ neutronPort.getDeviceID().equals(floatingIpUuid)) {
+ return neutronPort;
+ }
+ }
+ return null;
+ }
+
+ private Long findOFPortForExtPatch(Long dpId) {
+ final String brInt = configurationService.getIntegrationBridgeName();
+ final String brExt = configurationService.getExternalBridgeName();
+ final String portNameInt = configurationService.getPatchPortName(new ImmutablePair<>(brInt, brExt));
+
+ Preconditions.checkNotNull(dpId);
+ Preconditions.checkNotNull(portNameInt);
+
+ final long dpidPrimitive = dpId;
+ for (Node node : nodeCacheManager.getBridgeNodes()) {
+ if (dpidPrimitive == southbound.getDataPathId(node)) {
+ final OvsdbTerminationPointAugmentation terminationPointOfBridge =
+ southbound.getTerminationPointOfBridge(node, portNameInt);
+ return terminationPointOfBridge == null ? null : terminationPointOfBridge.getOfport();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Process the event.
+ *
+ * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
+ * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronFloatingIP object.
+ */
+ public void handleNeutronNetworkEvent(final NeutronNetwork neutronNetwork, Action action) {
+ LOG.debug("neutronNetwork {}: network: {}", action, neutronNetwork);
+ }
+
+ //
+ // Callbacks from OVSDB's southbound handler
+ //
+ /**
+ * Process the event.
+ *
+ * @param bridgeNode An instance of Node object.
+ * @param intf An {@link org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.ovsdb.rev150105
+ * .OvsdbTerminationPointAugmentation} instance of OvsdbTerminationPointAugmentation object.
+ * @param neutronNetwork An {@link org.opendaylight.ovsdb.openstack.netvirt.translator.NeutronNetwork} instance of NeutronNetwork
+ * object.
+ * @param action the {@link org.opendaylight.ovsdb.openstack.netvirt.api.Action} action to be handled.
+ */
+ public void handleInterfaceEvent(final Node bridgeNode, final OvsdbTerminationPointAugmentation intf,
+ final NeutronNetwork neutronNetwork, Action action) {
+ LOG.debug("southbound interface {} node:{} interface:{}, neutronNetwork:{}",
+ action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork);
+ if (!this.enabled) {
return;
}
- this.handleNeutronPortEvent(neutronPort, action);
+
+ final NeutronPort neutronPort = tenantNetworkManager.getTenantPort(intf);
+ final Long dpId = getDpidForIntegrationBridge(bridgeNode);
+ final Uuid interfaceUuid = intf.getInterfaceUuid();
+
+ LOG.trace("southbound interface {} node:{} interface:{}, neutronNetwork:{} port:{} dpid:{} intfUuid:{}",
+ action, bridgeNode.getNodeId().getValue(), intf.getName(), neutronNetwork, neutronPort, dpId, interfaceUuid);
+
+ if (neutronPort != null) {
+ final String neutronPortUuid = neutronPort.getPortUUID();
+
+ if (action != Action.DELETE && dpId != null && interfaceUuid != null) {
+ handleInterfaceEventAdd(neutronPortUuid, dpId, interfaceUuid);
+ }
+
+ handleNeutronPortEvent(neutronPort, action);
+ }
+
+ if (action == Action.DELETE && interfaceUuid != null) {
+ handleInterfaceEventDelete(intf, dpId);
+ }
+ }
+
+ private void handleInterfaceEventAdd(final String neutronPortUuid, Long dpId, final Uuid interfaceUuid) {
+ neutronPortToDpIdCache.put(neutronPortUuid, new ImmutablePair<>(dpId, interfaceUuid));
+ LOG.debug("handleInterfaceEvent add cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
+ neutronPortUuid, dpId, interfaceUuid.getValue());
+ }
+
+ private void handleInterfaceEventDelete(final OvsdbTerminationPointAugmentation intf, final Long dpId) {
+ // Remove entry from neutronPortToDpIdCache based on interface uuid
+ for (Map.Entry<String, Pair<Long, Uuid>> entry : neutronPortToDpIdCache.entrySet()) {
+ final String currPortUuid = entry.getKey();
+ if (intf.getInterfaceUuid().equals(entry.getValue().getRight())) {
+ LOG.debug("handleInterfaceEventDelete remove cache entry NeutronPortUuid {} : dpid {}, ifUuid {}",
+ currPortUuid, dpId, intf.getInterfaceUuid().getValue());
+ neutronPortToDpIdCache.remove(currPortUuid);
+ break;
+ }
+ }
}
//
if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
tenantMac == null || tenantMac.isEmpty()) {
- return; // done: go no further w/out all the info needed...
+ // done: go no further w/out all the info needed...
+ return;
}
final Action action = isDelete ? Action.DELETE : Action.ADD;
- List<Node> nodes = connectionService.getNodes();
+ List<Node> nodes = nodeCacheManager.getBridgeNodes();
if (nodes.isEmpty()) {
- logger.trace("updateL3ForNeutronPort has no nodes to work with");
+ LOG.trace("updateL3ForNeutronPort has no nodes to work with");
}
for (Node node : nodes) {
- final Long dpid = getDpid(node);
- final boolean tenantNetworkPresentInNode =
- tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId);
+ final Long dpid = getDpidForIntegrationBridge(node);
+ if (dpid == null) {
+ continue;
+ }
+ if (neutronPort.getFixedIPs() == null) {
+ continue;
+ }
for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) {
final String tenantIpStr = neutronIP.getIpAddress();
if (tenantIpStr.isEmpty()) {
// still needed when routing to subnets non-local to node (bug 2076).
programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
- // Configure distributed ARP responder. Only needed if tenant network exists in node.
- programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr,
- tenantNetworkPresentInNode ? action : Action.DELETE);
+ // Configure distributed ARP responder
+ if (flgDistributedARPEnabled) {
+ programStaticArpStage1(dpid, providerSegmentationId, tenantMac, tenantIpStr, action);
+ }
+ }
+ }
+ }
+
+ private void processSecurityGroupUpdate(NeutronPort neutronPort) {
+ LOG.trace("processSecurityGroupUpdate:" + neutronPort);
+ /**
+ * Get updated data and original data for the the changed. Identify the security groups that got
+ * added and removed and call the appropriate providers for updating the flows.
+ */
+ try {
+ NeutronPort originalPort = neutronPort.getOriginalPort();
+ if (null == originalPort) {
+ LOG.debug("processSecurityGroupUpdate: originalport is empty");
+ return;
+ }
+ List<NeutronSecurityGroup> addedGroup = getsecurityGroupChanged(neutronPort,
+ neutronPort.getOriginalPort());
+ List<NeutronSecurityGroup> deletedGroup = getsecurityGroupChanged(neutronPort.getOriginalPort(),
+ neutronPort);
+
+ if (null != addedGroup && !addedGroup.isEmpty()) {
+ securityServicesManager.syncSecurityGroup(neutronPort,addedGroup,true);
+ }
+ if (null != deletedGroup && !deletedGroup.isEmpty()) {
+ securityServicesManager.syncSecurityGroup(neutronPort,deletedGroup,false);
+ }
+
+ } catch (Exception e) {
+ LOG.error("Exception in processSecurityGroupUpdate", e);
+ }
+ }
+
+ private List<NeutronSecurityGroup> getsecurityGroupChanged(NeutronPort port1, NeutronPort port2) {
+ LOG.trace("getsecurityGroupChanged:" + "Port1:" + port1 + "Port2" + port2);
+ List<NeutronSecurityGroup> list1 = new ArrayList<>(port1.getSecurityGroups());
+ List<NeutronSecurityGroup> list2 = new ArrayList<>(port2.getSecurityGroups());
+ for (Iterator<NeutronSecurityGroup> iterator = list1.iterator(); iterator.hasNext();) {
+ NeutronSecurityGroup securityGroup1 = iterator.next();
+ for (NeutronSecurityGroup securityGroup2 :list2) {
+ if (securityGroup1.getID().equals(securityGroup2.getID())) {
+ iterator.remove();
+ }
}
}
+ return list1;
}
private void programL3ForwardingStage1(Node node, Long dpid, String providerSegmentationId,
String macAddress, String ipStr,
Action actionForNode) {
- // Based on the local cache, figure out whether programming needs to occur. To do this, we
- // will look at desired action for node.
- //
- final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
- final Boolean isProgrammed = l3ForwardingCache.contains(cacheKey);
-
- if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
- logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
- return;
+ if (actionForNode == Action.DELETE) {
+ LOG.trace("Deleting Flow : programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {}",
+ node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
}
- if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
- logger.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
- return;
+ if (actionForNode == Action.ADD) {
+ LOG.trace("Adding Flow : programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {}",
+ node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode);
}
- Status status = this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
+ this.programL3ForwardingStage2(node, dpid, providerSegmentationId,
macAddress, ipStr, actionForNode);
- if (status.isSuccess()) {
- // Update cache
- if (actionForNode == Action.ADD) {
- l3ForwardingCache.add(cacheKey);
- } else {
- l3ForwardingCache.remove(cacheKey);
- }
- }
}
private Status programL3ForwardingStage2(Node node, Long dpid, String providerSegmentationId,
InetAddress inetAddress = InetAddress.getByName(address);
status = l3ForwardingProvider == null ?
new Status(StatusCode.SUCCESS) :
- l3ForwardingProvider.programForwardingTableEntry(node, dpid, providerSegmentationId,
+ l3ForwardingProvider.programForwardingTableEntry(dpid, providerSegmentationId,
inetAddress, macAddress, actionForNode);
} catch (UnknownHostException e) {
status = new Status(StatusCode.BADREQUEST);
}
if (status.isSuccess()) {
- logger.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
+ LOG.debug("ProgramL3Forwarding {} for mac:{} addr:{} node:{} action:{}",
l3ForwardingProvider == null ? "skipped" : "programmed",
- macAddress, address, node, actionForNode);
+ macAddress, address, node.getNodeId().getValue(), actionForNode);
} else {
- logger.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
- macAddress, address, node, actionForNode, status);
+ LOG.error("ProgramL3Forwarding failed for mac:{} addr:{} node:{} action:{} status:{}",
+ macAddress, address, node.getNodeId().getValue(), actionForNode, status);
}
return status;
}
Preconditions.checkNotNull(destNeutronRouterInterface);
final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID());
- final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
- final List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
+ String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null;
+ List<Neutron_IPs> ipList = neutronPort != null ? neutronPort.getFixedIPs() : null;
final NeutronSubnet subnet = neutronSubnetCache.getSubnet(destNeutronRouterInterface.getSubnetUUID());
final NeutronNetwork neutronNetwork = subnet != null ?
neutronNetworkCache.getNetwork(subnet.getNetworkUUID()) : null;
final String destinationSegmentationId = neutronNetwork != null ?
neutronNetwork.getProviderSegmentationID() : null;
- final String gatewayIp = subnet != null ? subnet.getGatewayIP() : null;
final Boolean isExternal = neutronNetwork != null ? neutronNetwork.getRouterExternal() : Boolean.TRUE;
final String cidr = subnet != null ? subnet.getCidr() : null;
final int mask = getMaskLenFromCidr(cidr);
- logger.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
+ LOG.trace("programFlowsForNeutronRouterInterface called for interface {} isDelete {}",
destNeutronRouterInterface, isDelete);
+ // in delete path, mac address as well as ip address are not provided. Being so, let's find them from
+ // the local cache
+ if (neutronNetwork != null) {
+ if (macAddress == null || macAddress.isEmpty()) {
+ macAddress = networkIdToRouterMacCache.get(neutronNetwork.getNetworkUUID());
+ }
+ if (ipList == null || ipList.isEmpty()) {
+ ipList = networkIdToRouterIpListCache.get(neutronNetwork.getNetworkUUID());
+ }
+ }
+
if (destinationSegmentationId == null || destinationSegmentationId.isEmpty() ||
cidr == null || cidr.isEmpty() ||
macAddress == null || macAddress.isEmpty() ||
ipList == null || ipList.isEmpty()) {
- logger.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
+ LOG.debug("programFlowsForNeutronRouterInterface is bailing seg:{} cidr:{} mac:{} ip:{}",
destinationSegmentationId, cidr, macAddress, ipList);
- return; // done: go no further w/out all the info needed...
+ // done: go no further w/out all the info needed...
+ return;
}
- final Action action = isDelete ? Action.DELETE : Action.ADD;
+ final Action actionForNode = isDelete ? Action.DELETE : Action.ADD;
// Keep cache for finding router's mac from network uuid -- add
//
if (! isDelete) {
networkIdToRouterMacCache.put(neutronNetwork.getNetworkUUID(), macAddress);
+ networkIdToRouterIpListCache.put(neutronNetwork.getNetworkUUID(), new ArrayList<>(ipList));
subnetIdToRouterInterfaceCache.put(subnet.getSubnetUUID(), destNeutronRouterInterface);
}
- List<Node> nodes = connectionService.getNodes();
+ List<Node> nodes = nodeCacheManager.getBridgeNodes();
if (nodes.isEmpty()) {
- logger.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
+ LOG.trace("programFlowsForNeutronRouterInterface has no nodes to work with");
}
for (Node node : nodes) {
- final Long dpid = getDpid(node);
- final Action actionForNode =
- tenantNetworkManager.isTenantNetworkPresentInNode(node, destinationSegmentationId) ?
- action : Action.DELETE;
+ final Long dpid = getDpidForIntegrationBridge(node);
+ if (dpid == null) {
+ continue;
+ }
for (Neutron_IPs neutronIP : ipList) {
final String ipStr = neutronIP.getIpAddress();
if (ipStr.isEmpty()) {
- logger.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
- node.getID(), ipStr);
+ LOG.debug("programFlowsForNeutronRouterInterface is skipping node {} ip {}",
+ node.getNodeId().getValue(), ipStr);
continue;
}
true /*isReflexsive*/);
}
- programStaticArpStage1(node, dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
+ if (! isExternal) {
+ programFlowForNetworkFromExternal(node, dpid, destinationSegmentationId, macAddress, ipStr, mask,
+ actionForNode);
+ }
+ // Enable ARP responder by default, because router interface needs to be responded always.
+ programStaticArpStage1(dpid, destinationSegmentationId, macAddress, ipStr, actionForNode);
}
// Compute action to be programmed. In the case of rewrite exclusions, we must never program rules
//
{
final Action actionForRewriteExclusion = isExternal ? Action.DELETE : actionForNode;
- programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, true /* isInbound */,
- cidr, actionForRewriteExclusion);
- programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, false /* isInbound */,
- cidr, actionForRewriteExclusion);
- }
-
- // Default route. For non-external subnet, make sure that there is none configured.
- //
- if (gatewayIp != null && !gatewayIp.isEmpty()) {
- final Action actionForNodeDefaultRoute =
- isExternal ? actionForNode : Action.DELETE;
- final String defaultGatewayMacAddress = configurationService.getDefaultGatewayMacAddress(node);
- programDefaultRouteStage1(node, dpid, destinationSegmentationId, defaultGatewayMacAddress, gatewayIp,
- actionForNodeDefaultRoute);
+ programIpRewriteExclusionStage1(node, dpid, destinationSegmentationId, cidr, actionForRewriteExclusion);
}
}
- // Keep cache for finding router's mac from network uuid -- remove
- //
- if (isDelete) {
- networkIdToRouterMacCache.remove(neutronNetwork.getNetworkUUID());
- subnetIdToRouterInterfaceCache.remove(subnet.getSubnetUUID());
- }
+ // Keep cache for finding router's mac from network uuid -- NOTE: remove is done later, via cleanupRouterCache()
+ }
+
+ private void programFlowForNetworkFromExternal(final Node node,
+ final Long dpid,
+ final String destinationSegmentationId,
+ final String dstMacAddress,
+ final String destIpStr,
+ final int destMask,
+ final Action actionForNode) {
+ programRouterInterfaceStage1(node, dpid, Constants.EXTERNAL_NETWORK, destinationSegmentationId,
+ dstMacAddress, destIpStr, destMask, actionForNode);
}
private void programFlowsForNeutronRouterInterfacePair(final Node node,
final String sourceSubnetId = srcNeutronRouterInterface.getSubnetUUID();
if (sourceSubnetId == null) {
- logger.error("Could not get provider Subnet ID from router interface {}",
+ LOG.error("Could not get provider Subnet ID from router interface {}",
srcNeutronRouterInterface.getID());
return;
}
final NeutronSubnet sourceSubnet = neutronSubnetCache.getSubnet(sourceSubnetId);
final String sourceNetworkId = sourceSubnet == null ? null : sourceSubnet.getNetworkUUID();
if (sourceNetworkId == null) {
- logger.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
+ LOG.error("Could not get provider Network ID from subnet {}", sourceSubnetId);
return;
}
final NeutronNetwork sourceNetwork = neutronNetworkCache.getNetwork(sourceNetworkId);
if (sourceNetwork == null) {
- logger.error("Could not get provider Network for Network ID {}", sourceNetworkId);
+ LOG.error("Could not get provider Network for Network ID {}", sourceNetworkId);
return;
}
}
final String sourceSegmentationId = sourceNetwork.getProviderSegmentationID();
if (sourceSegmentationId == null) {
- logger.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
+ LOG.error("Could not get provider Segmentation ID for Subnet {}", sourceSubnetId);
return;
}
if (sourceSegmentationId.equals(destinationSegmentationId)) {
if (cidr2 == null || cidr2.isEmpty() ||
macAddress2 == null || macAddress2.isEmpty() ||
ipList2 == null || ipList2.isEmpty()) {
- logger.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
+ LOG.trace("programFlowsForNeutronRouterInterfacePair reflexive is bailing seg:{} cidr:{} mac:{} ip:{}",
sourceSegmentationId, cidr2, macAddress2, ipList2);
- return; // done: go no further w/out all the info needed...
+ // done: go no further w/out all the info needed...
+ return;
}
for (Neutron_IPs neutronIP2 : ipList2) {
String destinationSegmentationId,
String macAddress, String ipStr, int mask,
Action actionForNode) {
- // Based on the local cache, figure out whether programming needs to occur. To do this, we
- // will look at desired action for node.
- //
- final String cacheKey = node.toString() + ":" + sourceSegmentationId + ":" + destinationSegmentationId + ":" +
- ipStr + "/" + Integer.toString(mask);
- final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey);
-
- if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
- logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
- "action {} is already done",
- node.getNodeIDString(), sourceSegmentationId, destinationSegmentationId,
- ipStr, mask, actionForNode);
+ if (actionForNode == Action.DELETE) {
+ LOG.trace("Deleting Flow : programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
+ " action {}",
+ node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
+ macAddress, ipStr, mask, actionForNode);
return;
}
- if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
- logger.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
- "action {} is already done",
- node.getNodeIDString(), sourceSegmentationId, destinationSegmentationId,
- ipStr, mask, actionForNode);
- return;
+ if (actionForNode == Action.ADD) {
+ LOG.trace("Adding Flow : programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" +
+ " action {}",
+ node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
+ macAddress, ipStr, mask, actionForNode);
}
- Status status = this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
+ this.programRouterInterfaceStage2(node, dpid, sourceSegmentationId, destinationSegmentationId,
macAddress, ipStr, mask, actionForNode);
- if (status.isSuccess()) {
- // Update cache
- if (actionForNode == Action.ADD) {
- // TODO: multiTenantAwareRouter.addInterface(UUID.fromString(tenant), ...);
- routerInterfacesCache.add(cacheKey);
- } else {
- // TODO: multiTenantAwareRouter.removeInterface(...);
- routerInterfacesCache.remove(cacheKey);
- }
- }
}
private Status programRouterInterfaceStage2(Node node, Long dpid, String sourceSegmentationId,
InetAddress inetAddress = InetAddress.getByName(address);
status = routingProvider == null ?
new Status(StatusCode.SUCCESS) :
- routingProvider.programRouterInterface(node, dpid, sourceSegmentationId, destinationSegmentationId,
+ routingProvider.programRouterInterface(dpid, sourceSegmentationId, destinationSegmentationId,
macAddress, inetAddress, mask, actionForNode);
} catch (UnknownHostException e) {
status = new Status(StatusCode.BADREQUEST);
}
if (status.isSuccess()) {
- logger.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} action:{}",
+ LOG.debug("ProgramRouterInterface {} for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{}",
routingProvider == null ? "skipped" : "programmed",
- macAddress, address, mask, node, actionForNode);
+ macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
+ actionForNode);
} else {
- logger.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} action:{} status:{}",
- macAddress, address, mask, node, actionForNode, status);
+ LOG.error("ProgramRouterInterface failed for mac:{} addr:{}/{} node:{} srcTunId:{} destTunId:{} action:{} status:{}",
+ macAddress, address, mask, node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId,
+ actionForNode, status);
}
return status;
}
- private void programStaticArpStage1(Node node, Long dpid, String providerSegmentationId,
- String macAddress, String ipStr,
- Action actionForNode) {
- // Based on the local cache, figure out whether programming needs to occur. To do this, we
- // will look at desired action for node.
- //
- final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + ipStr;
- final Boolean isProgrammed = staticArpEntryCache.contains(cacheKey);
-
- if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
- logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
- return;
+ private boolean programStaticArpStage1(Long dpid, String segOrOfPort,
+ String macAddress, String ipStr,
+ Action action) {
+ if (action == Action.DELETE ) {
+ LOG.trace("Deleting Flow : programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {}",
+ dpid, segOrOfPort, macAddress, ipStr, action);
}
- if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
- logger.trace("programStaticArpStage1 node {} providerId {} mac {} ip {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, macAddress, ipStr, actionForNode);
- return;
+ if (action == Action.ADD) {
+ LOG.trace("Adding Flow : programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done",
+ dpid, segOrOfPort, macAddress, ipStr, action);
}
- Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId,
- macAddress, ipStr, actionForNode);
- if (status.isSuccess()) {
- // Update cache
- if (actionForNode == Action.ADD) {
- staticArpEntryCache.add(cacheKey);
- } else {
- staticArpEntryCache.remove(cacheKey);
- }
- }
+ Status status = this.programStaticArpStage2(dpid, segOrOfPort, macAddress, ipStr, action);
+ return status.isSuccess();
}
- private Status programStaticArpStage2(Node node, Long dpid, String providerSegmentationId,
- String macAddress,
- String address,
- Action actionForNode) {
+ private Status programStaticArpStage2(Long dpid,
+ String segOrOfPort,
+ String macAddress,
+ String address,
+ Action action) {
Status status;
try {
InetAddress inetAddress = InetAddress.getByName(address);
status = arpProvider == null ?
new Status(StatusCode.SUCCESS) :
- arpProvider.programStaticArpEntry(node, dpid, providerSegmentationId,
- macAddress, inetAddress, actionForNode);
+ arpProvider.programStaticArpEntry(dpid, segOrOfPort,
+ macAddress, inetAddress, action);
} catch (UnknownHostException e) {
status = new Status(StatusCode.BADREQUEST);
}
if (status.isSuccess()) {
- logger.debug("ProgramStaticArp {} for mac:{} addr:{} node:{} action:{}",
+ LOG.debug("ProgramStaticArp {} for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{}",
arpProvider == null ? "skipped" : "programmed",
- macAddress, address, node, actionForNode);
+ macAddress, address, dpid, segOrOfPort, action);
} else {
- logger.error("ProgramStaticArp failed for mac:{} addr:{} node:{} action:{} status:{}",
- macAddress, address, node, actionForNode, status);
+ LOG.error("ProgramStaticArp failed for mac:{} addr:{} dpid:{} segOrOfPort:{} action:{} status:{}",
+ macAddress, address, dpid, segOrOfPort, action, status);
}
return status;
}
- private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId,
- final boolean isInbound, String cidr,
- Action actionForRewriteExclusion) {
- // Based on the local cache, figure out whether programming needs to occur. To do this, we
- // will look at desired action for node.
- //
- final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + cidr;
- final Boolean isProgrammed = isInbound ?
- inboundIpRewriteExclusionCache.contains(cacheKey):
- outboundIpRewriteExclusionCache.contains(cacheKey);
-
- if (actionForRewriteExclusion == Action.DELETE && isProgrammed == Boolean.FALSE) {
- logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
- actionForRewriteExclusion);
- return;
+ private boolean programInboundIpRewriteStage1(Long dpid, Long inboundOFPort, String providerSegmentationId,
+ String matchAddress, String rewriteAddress,
+ Action action) {
+ if (action == Action.DELETE ) {
+ LOG.trace("Deleting Flow : programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
+ " action {}",
+ dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
}
- if (actionForRewriteExclusion == Action.ADD && isProgrammed == Boolean.TRUE) {
- logger.trace("programIpRewriteExclusionStage1 node {} providerId {} {} cidr {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound" : "outbound", cidr,
- actionForRewriteExclusion);
- return;
+ if (action == Action.ADD ) {
+ LOG.trace("Adding Flow : programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" +
+ " action {}",
+ dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
}
- Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,
- isInbound, actionForRewriteExclusion);
- if (status.isSuccess()) {
- // Update cache
- if (actionForRewriteExclusion == Action.ADD) {
- if (isInbound) {
- inboundIpRewriteExclusionCache.add(cacheKey);
- } else {
- outboundIpRewriteExclusionCache.add(cacheKey);
- }
- } else {
- if (isInbound) {
- inboundIpRewriteExclusionCache.remove(cacheKey);
- } else {
- outboundIpRewriteExclusionCache.remove(cacheKey);
- }
- }
- }
+ Status status = programInboundIpRewriteStage2(dpid, inboundOFPort, providerSegmentationId, matchAddress,
+ rewriteAddress, action);
+ return status.isSuccess();
}
- private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
- final boolean isInbound, Action actionForNode) {
+ private Status programInboundIpRewriteStage2(Long dpid, Long inboundOFPort, String providerSegmentationId,
+ String matchAddress, String rewriteAddress,
+ Action action) {
Status status;
- if (isInbound) {
- status = inboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
- inboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
- actionForNode);
- } else {
- status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
- outboundNatProvider.programIpRewriteExclusion(node, dpid, providerSegmentationId, cidr,
- actionForNode);
+ try {
+ InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
+ InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
+ status = inboundNatProvider == null ?
+ new Status(StatusCode.SUCCESS) :
+ inboundNatProvider.programIpRewriteRule(dpid, inboundOFPort, providerSegmentationId,
+ inetMatchAddress, inetRewriteAddress,
+ action);
+ } catch (UnknownHostException e) {
+ status = new Status(StatusCode.BADREQUEST);
}
if (status.isSuccess()) {
- final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
- logger.debug("IpRewriteExclusion {} {} for cidr:{} node:{} action:{}",
- (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
- cidr, node, actionForNode);
+ final boolean isSkipped = inboundNatProvider == null;
+ LOG.debug("programInboundIpRewriteStage2 {} for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}",
+ isSkipped ? "skipped" : "programmed",
+ dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action);
} else {
- logger.error("IpRewriteExclusion {} failed for cidr:{} node:{} action:{} status:{}",
- (isInbound ? "inbound" : "outbound"), cidr, node, actionForNode, status);
+ LOG.error("programInboundIpRewriteStage2 failed for dpid:{} ofPort:{} seg:{} match:{} rewrite:{} action:{}" +
+ " status:{}",
+ dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action,
+ status);
}
return status;
}
- private void programDefaultRouteStage1(Node node, Long dpid, String providerSegmentationId,
- String defaultGatewayMacAddress, String gatewayIp,
- Action actionForNodeDefaultRoute) {
- // Based on the local cache, figure out whether programming needs to occur. To do this, we
- // will look at desired action for node.
- //
- final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" + gatewayIp;
- final Boolean isProgrammed = defaultRouteCache.contains(cacheKey);
-
- if (actionForNodeDefaultRoute == Action.DELETE && isProgrammed == Boolean.FALSE) {
- logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
- actionForNodeDefaultRoute);
- return;
+ private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId, String cidr,
+ Action actionForRewriteExclusion) {
+ if (actionForRewriteExclusion == Action.DELETE ) {
+ LOG.trace("Deleting Flow : programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {}",
+ node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
}
- if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) {
- logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done",
- node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp,
- actionForNodeDefaultRoute);
- return;
+ if (actionForRewriteExclusion == Action.ADD) {
+ LOG.trace("Adding Flow : programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {}",
+ node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion);
}
- Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId,
- defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute);
- if (status.isSuccess()) {
- // Update cache
- if (actionForNodeDefaultRoute == Action.ADD) {
- defaultRouteCache.add(cacheKey);
- } else {
- defaultRouteCache.remove(cacheKey);
- }
- }
+ this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr,actionForRewriteExclusion);
}
- private Status programDefaultRouteStage2(Node node, Long dpid, String providerSegmentationId,
- String defaultGatewayMacAddress,
- String gatewayIp,
- Action actionForNodeDefaultRoute) {
- // TODO: As of Helium, mac address for default gateway is required (bug 1705).
- if (defaultGatewayMacAddress == null) {
- logger.error("ProgramDefaultRoute mac not provided. gatewayIp:{} node:{} action:{}",
- gatewayIp, node, actionForNodeDefaultRoute);
- return new Status(StatusCode.NOTIMPLEMENTED); // Bug 1705
- }
-
- Status status;
- try {
- InetAddress inetAddress = InetAddress.getByName(gatewayIp);
- status = routingProvider == null ?
- new Status(StatusCode.SUCCESS) :
- routingProvider.programDefaultRouteEntry(node, dpid, providerSegmentationId,
- defaultGatewayMacAddress, inetAddress,
- actionForNodeDefaultRoute);
- } catch (UnknownHostException e) {
- status = new Status(StatusCode.BADREQUEST);
- }
+ private Status programIpRewriteExclusionStage2(Node node, Long dpid, String providerSegmentationId, String cidr,
+ Action actionForNode) {
+ final Status status = outboundNatProvider == null ? new Status(StatusCode.SUCCESS) :
+ outboundNatProvider.programIpRewriteExclusion(dpid, providerSegmentationId, cidr, actionForNode);
if (status.isSuccess()) {
- logger.debug("ProgramDefaultRoute {} for mac:{} gatewayIp:{} node:{} action:{}",
- routingProvider == null ? "skipped" : "programmed",
- defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute);
+ final boolean isSkipped = outboundNatProvider == null;
+ LOG.debug("IpRewriteExclusion {} for cidr:{} node:{} action:{}",
+ isSkipped ? "skipped" : "programmed",
+ cidr, node.getNodeId().getValue(), actionForNode);
} else {
- logger.error("ProgramDefaultRoute failed for mac:{} gatewayIp:{} node:{} action:{} status:{}",
- defaultGatewayMacAddress, gatewayIp, node, actionForNodeDefaultRoute, status);
+ LOG.error("IpRewriteExclusion failed for cidr:{} node:{} action:{} status:{}",
+ cidr, node.getNodeId().getValue(), actionForNode, status);
}
return status;
}
- private void programFlowsForFloatingIP(final NeutronFloatingIP neutronFloatingIP, Boolean isDelete) {
- Preconditions.checkNotNull(neutronFloatingIP);
-
- final String networkUUID = neutronFloatingIP.getFloatingNetworkUUID();
- final String routerMacAddress = networkIdToRouterMacCache.get(networkUUID);
-
- // If there is no router interface handling the networkUUID, we are done
- if (routerMacAddress == null || routerMacAddress.isEmpty()) {
- return;
- }
-
- final NeutronNetwork neutronNetwork = neutronNetworkCache.getNetwork(networkUUID);
- final String providerSegmentationId = neutronNetwork != null ?
- neutronNetwork.getProviderSegmentationID() : null;
- final String fixedIPAddress = neutronFloatingIP.getFixedIPAddress();
- final String floatingIpAddress = neutronFloatingIP.getFloatingIPAddress();
-
- if (providerSegmentationId == null || providerSegmentationId.isEmpty() ||
- // routerMacAddress == null || routerMacAddress.isEmpty() ||
- fixedIPAddress == null || fixedIPAddress.isEmpty() ||
- floatingIpAddress == null || floatingIpAddress.isEmpty()) {
- return; // done: go no further w/out all the info needed...
- }
+ private void programOutboundIpRewriteStage1(FloatIpData fid, Action action) {
- final Action action = isDelete ? Action.DELETE : Action.ADD;
- List<Node> nodes = connectionService.getNodes();
- if (nodes.isEmpty()) {
- logger.trace("programFlowsForFloatingIP has no nodes to work with");
+ if (action == Action.DELETE) {
+ LOG.trace("Deleting Flow : programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} ",
+ fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
}
- for (Node node : nodes) {
- final Long dpid = getDpid(node);
- final Action actionForNode =
- tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ?
- action : Action.DELETE;
-
- // Rewrite from float to fixed and vice-versa
- //
- programIpRewriteStage1(node, dpid, providerSegmentationId, true /* isInbound */,
- floatingIpAddress, fixedIPAddress, actionForNode);
- programIpRewriteStage1(node, dpid, providerSegmentationId, false /* isInboubd */,
- fixedIPAddress, floatingIpAddress, actionForNode);
-
- // Respond to arps for the floating ip address
- //
- programStaticArpStage1(node, dpid, providerSegmentationId, routerMacAddress, floatingIpAddress,
- actionForNode);
- }
- }
-
- private void programIpRewriteStage1(Node node, Long dpid, String providerSegmentationId,
- final boolean isInbound,
- String matchAddress, String rewriteAddress,
- Action actionForNode) {
- // Based on the local cache, figure out whether programming needs to occur. To do this, we
- // will look at desired action for node.
- //
- final String cacheKey = node.toString() + ":" + providerSegmentationId + ":" +
- matchAddress + ":" + rewriteAddress;
- final Boolean isProgrammed = isInbound ?
- inboundIpRewriteCache.contains(cacheKey) :
- outboundIpRewriteCache.contains(cacheKey);
-
- if (actionForNode == Action.DELETE && isProgrammed == Boolean.FALSE) {
- logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
- " is already done",
- node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
- matchAddress, rewriteAddress, actionForNode);
- return;
- }
- if (actionForNode == Action.ADD && isProgrammed == Boolean.TRUE) {
- logger.trace("programIpRewriteStage1 node {} providerId {} {} matchAddr {} rewriteAddr {} action {}" +
- " is already done",
- node.getNodeIDString(), providerSegmentationId, isInbound ? "inbound": "outbound",
- matchAddress, rewriteAddress, actionForNode);
- return;
+ if (action == Action.ADD) {
+ LOG.trace("Adding Flow : programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " ,
+ fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
}
- Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound,
- matchAddress, rewriteAddress, actionForNode);
- if (status.isSuccess()) {
- // Update cache
- if (actionForNode == Action.ADD) {
- if (isInbound) {
- inboundIpRewriteCache.add(cacheKey);
- } else {
- outboundIpRewriteCache.add(cacheKey);
- }
- } else {
- if (isInbound) {
- inboundIpRewriteCache.remove(cacheKey);
- } else {
- outboundIpRewriteCache.remove(cacheKey);
- }
- }
- }
+ this.programOutboundIpRewriteStage2(fid, action);
}
- private Status programIpRewriteStage2(Node node, Long dpid, String providerSegmentationId,
- final boolean isInbound,
- String matchAddress, String rewriteAddress,
- Action actionForNode) {
+ private Status programOutboundIpRewriteStage2(FloatIpData fid, Action action) {
Status status;
try {
- InetAddress inetMatchAddress = InetAddress.getByName(matchAddress);
- InetAddress inetRewriteAddress = InetAddress.getByName(rewriteAddress);
- if (isInbound) {
- status = inboundNatProvider == null ?
- new Status(StatusCode.SUCCESS) :
- inboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
- inetMatchAddress, inetRewriteAddress, actionForNode);
- } else {
- status = outboundNatProvider == null ?
- new Status(StatusCode.SUCCESS) :
- outboundNatProvider.programIpRewriteRule(node, dpid, providerSegmentationId,
- inetMatchAddress, inetRewriteAddress, actionForNode);
- }
+ InetAddress matchSrcAddress = InetAddress.getByName(fid.fixedIpAddress);
+ InetAddress rewriteSrcAddress = InetAddress.getByName(fid.floatingIpAddress);
+ status = outboundNatProvider == null ?
+ new Status(StatusCode.SUCCESS) :
+ outboundNatProvider.programIpRewriteRule(
+ fid.dpid, fid.segId, fid.neutronRouterMac, matchSrcAddress, fid.macAddress,
+ this.externalRouterMac, rewriteSrcAddress, fid.ofPort, action);
} catch (UnknownHostException e) {
status = new Status(StatusCode.BADREQUEST);
}
if (status.isSuccess()) {
- final boolean isSkipped = isInbound ? inboundNatProvider == null : outboundNatProvider == null;
- logger.debug("ProgramIpRewrite {} {} for match:{} rewrite:{} node:{} action:{}",
- (isInbound ? "inbound" : "outbound"), (isSkipped ? "skipped" : "programmed"),
- matchAddress, rewriteAddress, node, actionForNode);
+ final boolean isSkipped = outboundNatProvider == null;
+ LOG.debug("programOutboundIpRewriteStage2 {} for dpid {} seg {} fixedIpAddress {} floatIp {}" +
+ " action {}",
+ isSkipped ? "skipped" : "programmed",
+ fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action);
} else {
- logger.error("ProgramIpRewrite {} failed for match:{} rewrite:{} node:{} action:{} status:{}",
- (isInbound ? "inbound" : "outbound"),
- matchAddress, rewriteAddress, node, actionForNode, status);
+ LOG.error("programOutboundIpRewriteStage2 failed for dpid {} seg {} fixedIpAddress {} floatIp {}" +
+ " action {} status:{}",
+ fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action, status);
}
return status;
}
- //
- // More Internals
- //
-
private int getMaskLenFromCidr(String cidr) {
- if (cidr == null) return 0;
+ if (cidr == null) {
+ return 0;
+ }
String[] splits = cidr.split("/");
- if (splits.length != 2) return 0;
+ if (splits.length != 2) {
+ return 0;
+ }
int result;
try {
result = Integer.parseInt(splits[1].trim());
- }
- catch (NumberFormatException nfe)
- {
+ } catch (NumberFormatException nfe) {
result = 0;
}
return result;
}
- private Long getDpid (Node node) {
- Preconditions.checkNotNull(ovsdbConfigurationService);
+ private Long getDpidForIntegrationBridge(Node node) {
+ // Check if node is integration bridge; and only then return its dpid
+ if (southbound.getBridge(node, configurationService.getIntegrationBridgeName()) != null) {
+ return southbound.getDataPathId(node);
+ }
+ return null;
+ }
- String bridgeName = configurationService.getIntegrationBridgeName();
- String bridgeUuid = this.getInternalBridgeUUID(node, bridgeName);
- if (bridgeUuid == null) {
- logger.error("Unable to spot Bridge Identifier for {} in {}", bridgeName, node);
- return 0L;
+ private Long getDpidForExternalBridge(Node node) {
+ // Check if node is integration bridge; and only then return its dpid
+ if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+ return southbound.getDataPathId(node);
}
+ return null;
+ }
- try {
- Row bridgeRow = ovsdbConfigurationService
- .getRow(node, ovsdbConfigurationService.getTableName(node, Bridge.class), bridgeUuid);
- Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeRow);
- Set<String> dpids = bridge.getDatapathIdColumn().getData();
- if (dpids == null || dpids.size() == 0) return 0L;
- return HexEncode.stringToLong((String) dpids.toArray()[0]);
- } catch (Exception e) {
- logger.error("Error finding Bridge's OF DPID", e);
- return 0L;
+ private Node getExternalBridgeNode(){
+ //Pickup the first node that has external bridge (br-ex).
+ //NOTE: We are assuming that all the br-ex are serving one external network and gateway ip of
+ //the external network is reachable from every br-ex
+ // TODO: Consider other deployment scenario, and thing of better solution.
+ List<Node> allBridges = nodeCacheManager.getBridgeNodes();
+ for(Node node : allBridges){
+ if (southbound.getBridge(node, configurationService.getExternalBridgeName()) != null) {
+ return node;
+ }
}
+ return null;
}
- private String getInternalBridgeUUID (Node node, String bridgeName) {
- Preconditions.checkNotNull(ovsdbConfigurationService);
- try {
- Map<String, Row> bridgeTable =
- ovsdbConfigurationService.getRows(node,
- ovsdbConfigurationService.getTableName(node, Bridge.class));
- if (bridgeTable == null) return null;
- for (String key : bridgeTable.keySet()) {
- Bridge bridge = ovsdbConfigurationService.getTypedRow(node, Bridge.class, bridgeTable.get(key));
- if (bridge.getName().equals(bridgeName)) return key;
+ private NeutronSubnet getExternalNetworkSubnet(NeutronPort gatewayPort){
+ if (gatewayPort.getFixedIPs() == null) {
+ return null;
+ }
+ for (Neutron_IPs neutronIPs : gatewayPort.getFixedIPs()) {
+ String subnetUUID = neutronIPs.getSubnetUUID();
+ NeutronSubnet extSubnet = neutronSubnetCache.getSubnet(subnetUUID);
+ if (extSubnet != null && extSubnet.getGatewayIP() != null) {
+ return extSubnet;
}
- } catch (Exception e) {
- logger.error("Error getting Bridge Identifier for {} / {}", node, bridgeName, e);
+ if (extSubnet == null) {
+ // TODO: when subnet is created, try again.
+ LOG.debug("subnet {} in not found", subnetUUID);
+ }
}
return null;
}
-}
+ private void cleanupRouterCache(final NeutronRouter_Interface neutronRouterInterface) {
+ /*
+ * Fix for 4277
+ * Remove the router cache only after deleting the neutron
+ * port l3 flows.
+ */
+ final NeutronPort neutronPort = neutronPortCache.getPort(neutronRouterInterface.getPortUUID());
+
+ if (neutronPort != null) {
+ networkIdToRouterMacCache.remove(neutronPort.getNetworkUUID());
+ networkIdToRouterIpListCache.remove(neutronPort.getNetworkUUID());
+ subnetIdToRouterInterfaceCache.remove(neutronRouterInterface.getSubnetUUID());
+ }
+ }
+
+ public void triggerGatewayMacResolver(final Node node, final NeutronPort gatewayPort ){
+
+ Preconditions.checkNotNull(node);
+ Preconditions.checkNotNull(gatewayPort);
+ NeutronNetwork externalNetwork = neutronNetworkCache.getNetwork(gatewayPort.getNetworkUUID());
+
+ if(externalNetwork != null){
+ if(externalNetwork.isRouterExternal()){
+ final NeutronSubnet externalSubnet = getExternalNetworkSubnet(gatewayPort);
+
+ // TODO: address IPv6 case.
+ if (externalSubnet != null &&
+ externalSubnet.getIpVersion() == 4 &&
+ gatewayPort.getFixedIPs() != null) {
+ LOG.info("Trigger MAC resolution for gateway ip {} on Node {}",externalSubnet.getGatewayIP(),node.getNodeId());
+ ListenableFuture<MacAddress> gatewayMacAddress =
+ gatewayMacResolver.resolveMacAddress(getDpidForExternalBridge(node),
+ new Ipv4Address(externalSubnet.getGatewayIP()),
+ new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
+ new MacAddress(gatewayPort.getMacAddress()),
+ true);
+ if(gatewayMacAddress != null){
+ Futures.addCallback(gatewayMacAddress, new FutureCallback<MacAddress>(){
+ @Override
+ public void onSuccess(MacAddress result) {
+ if(result != null){
+ if(!result.getValue().equals(externalRouterMac)){
+ updateExternalRouterMac(result.getValue());
+ LOG.info("Resolved MAC address for gateway IP {} is {}", externalSubnet.getGatewayIP(),result.getValue());
+ }
+ }else{
+ LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
+ }
+ }
+
+ @Override
+ public void onFailure(Throwable t) {
+ LOG.warn("MAC address resolution failed for gateway IP {}", externalSubnet.getGatewayIP());
+ }
+ }, gatewayMacResolverPool);
+ }
+ } else {
+ LOG.warn("No gateway IP address found for external network {}", externalNetwork);
+ }
+ }
+ }else{
+ LOG.warn("Neutron network not found for router interface {}", gatewayPort);
+ }
+ }
+
+ /**
+ * Return String that represents OF port with marker explicitly provided (reverse of MatchUtils:parseExplicitOFPort)
+ *
+ * @param ofPort the OF port number
+ * @return the string with encoded OF port (example format "OFPort|999")
+ */
+ public static String encodeExcplicitOFPort(Long ofPort) {
+ return "OFPort|" + ofPort.toString();
+ }
+ @Override
+ public void setDependencies(ServiceReference serviceReference) {
+ tenantNetworkManager =
+ (TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
+ configurationService =
+ (ConfigurationService) ServiceHelper.getGlobalInstance(ConfigurationService.class, this);
+ arpProvider =
+ (ArpProvider) ServiceHelper.getGlobalInstance(ArpProvider.class, this);
+ inboundNatProvider =
+ (InboundNatProvider) ServiceHelper.getGlobalInstance(InboundNatProvider.class, this);
+ outboundNatProvider =
+ (OutboundNatProvider) ServiceHelper.getGlobalInstance(OutboundNatProvider.class, this);
+ routingProvider =
+ (RoutingProvider) ServiceHelper.getGlobalInstance(RoutingProvider.class, this);
+ l3ForwardingProvider =
+ (L3ForwardingProvider) ServiceHelper.getGlobalInstance(L3ForwardingProvider.class, this);
+ nodeCacheManager =
+ (NodeCacheManager) ServiceHelper.getGlobalInstance(NodeCacheManager.class, this);
+ southbound =
+ (Southbound) ServiceHelper.getGlobalInstance(Southbound.class, this);
+ gatewayMacResolver =
+ (GatewayMacResolver) ServiceHelper.getGlobalInstance(GatewayMacResolver.class, this);
+ securityServicesManager =
+ (SecurityServicesManager) ServiceHelper.getGlobalInstance(SecurityServicesManager.class, this);
+ initL3AdapterMembers();
+ }
+
+ @Override
+ public void setDependencies(Object impl) {
+ if (impl instanceof INeutronNetworkCRUD) {
+ neutronNetworkCache = (INeutronNetworkCRUD)impl;
+ } else if (impl instanceof INeutronPortCRUD) {
+ neutronPortCache = (INeutronPortCRUD)impl;
+ } else if (impl instanceof INeutronSubnetCRUD) {
+ neutronSubnetCache = (INeutronSubnetCRUD)impl;
+ } else if (impl instanceof INeutronFloatingIPCRUD) {
+ neutronFloatingIpCache = (INeutronFloatingIPCRUD)impl;
+ } else if (impl instanceof ArpProvider) {
+ arpProvider = (ArpProvider)impl;
+ } else if (impl instanceof InboundNatProvider) {
+ inboundNatProvider = (InboundNatProvider)impl;
+ } else if (impl instanceof OutboundNatProvider) {
+ outboundNatProvider = (OutboundNatProvider)impl;
+ } else if (impl instanceof RoutingProvider) {
+ routingProvider = (RoutingProvider)impl;
+ } else if (impl instanceof L3ForwardingProvider) {
+ l3ForwardingProvider = (L3ForwardingProvider)impl;
+ }else if (impl instanceof GatewayMacResolver) {
+ gatewayMacResolver = (GatewayMacResolver)impl;
+ }
+ populateL3ForwardingCaches();
+ }
+}