X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=openstack%2Fnet-virt%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fovsdb%2Fopenstack%2Fnetvirt%2Fimpl%2FNeutronL3Adapter.java;h=b381f87d53d720548cf3de5b5cdd59d321cf354a;hb=849e07952037859d19e03e97edc7ab32e8785f34;hp=62b2b4e23f9b4d609c95231dcdc33f4eac8a6c73;hpb=68ab79a396d04bafba6d131d329581ed1b84eb56;p=netvirt.git diff --git a/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/impl/NeutronL3Adapter.java b/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/impl/NeutronL3Adapter.java index 62b2b4e23f..b381f87d53 100644 --- a/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/impl/NeutronL3Adapter.java +++ b/openstack/net-virt/src/main/java/org/opendaylight/ovsdb/openstack/netvirt/impl/NeutronL3Adapter.java @@ -1,50 +1,40 @@ /* - * 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.controller.networkconfig.neutron.INeutronNetworkCRUD; -import org.opendaylight.controller.networkconfig.neutron.INeutronPortCRUD; -import org.opendaylight.controller.networkconfig.neutron.INeutronSubnetCRUD; -import org.opendaylight.controller.networkconfig.neutron.NeutronFloatingIP; -import org.opendaylight.controller.networkconfig.neutron.NeutronNetwork; -import org.opendaylight.controller.networkconfig.neutron.NeutronPort; -import org.opendaylight.controller.networkconfig.neutron.NeutronRouter; -import org.opendaylight.controller.networkconfig.neutron.NeutronRouter_Interface; -import org.opendaylight.controller.networkconfig.neutron.NeutronSubnet; -import org.opendaylight.controller.networkconfig.neutron.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.plugin.api.OvsdbConfigurationService; -import org.opendaylight.ovsdb.plugin.api.OvsdbConnectionService; -import org.opendaylight.ovsdb.schema.openvswitch.Bridge; -import org.opendaylight.ovsdb.openstack.netvirt.api.Action; -import org.opendaylight.ovsdb.openstack.netvirt.api.ArpProvider; -import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider; -import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider; -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.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +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.ovsdb.openstack.netvirt.ConfigInterface; +import org.opendaylight.ovsdb.openstack.netvirt.api.*; +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.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 com.google.common.base.Preconditions; -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.osgi.framework.FrameworkUtil; +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; + +import org.osgi.framework.ServiceReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,113 +42,202 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.HashMap; import java.util.HashSet; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * 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 L3ForwardingProvider l3ForwardingProvider; private volatile InboundNatProvider inboundNatProvider; private volatile OutboundNatProvider outboundNatProvider; private volatile ArpProvider arpProvider; private volatile RoutingProvider routingProvider; + private volatile GatewayMacResolver gatewayMacResolver; + + 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 inboundIpRewriteCache; private Set outboundIpRewriteCache; - private Set inboundIpRewriteExclusionCache; private Set outboundIpRewriteExclusionCache; private Set routerInterfacesCache; private Set staticArpEntryCache; private Set l3ForwardingCache; - private Set defaultRouteCache; private Map networkIdToRouterMacCache; + private Map> networkIdToRouterIpListCache; private Map subnetIdToRouterInterfaceCache; + private Map> neutronPortToDpIdCache; + private Map floatIpDataMapCache; + private String externalRouterMac; private Boolean enabled = false; + private Boolean flgDistributedARPEnabled = true; + private Southbound southbound; + private final ExecutorService gatewayMacResolverPool = Executors.newFixedThreadPool(5); + + 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() { + LOG.info(">>>>>> NeutronL3Adapter constructor {}", this.getClass()); + } + + private void initL3AdapterMembers() { + Preconditions.checkNotNull(configurationService); - void init() { - final String enabledPropertyStr = getProperty(this.getClass(), "ovsdb.l3.fwd.enabled"); - if (enabledPropertyStr != null && enabledPropertyStr.equalsIgnoreCase("yes")) { + if (configurationService.isL3ForwardingEnabled()) { 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<>(); this.networkIdToRouterMacCache = new HashMap<>(); + this.networkIdToRouterIpListCache = new HashMap<>(); this.subnetIdToRouterInterfaceCache = new HashMap<>(); + this.neutronPortToDpIdCache = new HashMap<>(); + this.floatIpDataMapCache = new HashMap<>(); - this.enabled = true; - logger.info("OVSDB L3 forwarding is enabled"); - } else { - logger.debug("OVSDB L3 forwarding is disabled"); - } - } - - // TODO: move getProperty() to a common module - private static String getProperty(Class classParam, final String propertyStr) { - String value = null; - Bundle bundle = FrameworkUtil.getBundle(classParam); + this.externalRouterMac = configurationService.getDefaultGatewayMacAddress(null); + if (this.externalRouterMac == null) { + this.externalRouterMac = DEFAULT_EXT_RTR_MAC; + } - if (bundle != null) { - BundleContext bundleContext = bundle.getBundleContext(); - if (bundleContext != null) { - value = bundleContext.getProperty(propertyStr); + this.enabled = true; + 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 { + LOG.debug("OVSDB L3 forwarding is disabled"); } - if (value == null) { - value = System.getProperty(propertyStr); - } - return value; } // // Callbacks from OVSDB's northbound handlers // + /** + * 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) { - logger.debug("Neutron subnet {} event : {}", action, subnet.toString()); - if (!this.enabled) - return; + 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()); + 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")) { + if (neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE) || + neutronPort.getDeviceOwner().equalsIgnoreCase(OWNER_ROUTER_INTERFACE_DISTRIBUTED)) { + 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 + // id of router interface to be same as subnet + neutronRouterInterface.setID(neutronIP.getSubnetUUID()); neutronRouterInterface.setTenantID(neutronPort.getTenantID()); this.handleNeutronRouterInterfaceEvent(null /*neutronRouter*/, neutronRouterInterface, action); @@ -169,7 +248,7 @@ public class NeutronL3Adapter { // 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()); @@ -182,21 +261,34 @@ public class NeutronL3Adapter { } } + /** + * 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; @@ -206,63 +298,304 @@ public class NeutronL3Adapter { // see if they are affected by l3 // for (NeutronPort neutronPort : neutronPortCache.getAllPorts()) { - boolean currPortIsInSameSubnet = false; - for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) { - if (neutronRouterInterface.getSubnetUUID().equalsIgnoreCase(neutronIP.getSubnetUUID())) { - currPortIsInSameSubnet = true; - break; + 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 (currPortIsInSameSubnet == true) { - this.updateL3ForNeutronPort(neutronPort, isDelete); - } + 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.neutron.spi.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); - this.programFlowsForFloatingIP(neutronFloatingIP, action == Action.DELETE); + programFlowsForFloatingIPInbound(neutronFloatingIP, Action.ADD); + programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.ADD); + } else { + programFlowsForFloatingIPOutbound(neutronFloatingIP, Action.DELETE); + programFlowsForFloatingIPInbound(neutronFloatingIP, 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 = floatIpDataMapCache.get(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 = floatIpDataMapCache.get(neutronFloatingIP.getID()); + if (fid == null) { + LOG.trace("programFlowsForFloatingIPOutbound {} for {} uuid {} not in local cache", + action, neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID()); return; + } + programOutboundIpRewriteStage1(fid, action); + } - // See if there is an external uuid, so we can find the respective neutronPort - Map externalIds = intf.getExternalIdsColumn().getData(); - if (externalIds == null) { + private void flushExistingIpRewrite() { + for (FloatIpData fid : floatIpDataMapCache.values()) { + programOutboundIpRewriteStage1(fid, Action.DELETE); + } + } + + private void rebuildExistingIpRewrite() { + for (FloatIpData fid : floatIpDataMapCache.values()) { + 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()); + + if (floatIpDataMapCache.get(neutronFloatingIP.getID()) != null) { + LOG.trace("programFlowsForFloatingIPArpAdd for neutronFloatingIP {} uuid {} is already done", + neutronFloatingIP.getFloatingIPAddress(), neutronFloatingIP.getID()); return; } - String neutronPortId = externalIds.get(Constants.EXTERNAL_ID_INTERFACE_ID); - if (neutronPortId == null) { + + // 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 nodeIfPair = neutronPortToDpIdCache.get(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; } - 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); + + // 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; } - this.handleNeutronPortEvent(neutronPort, action); + + // 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 = floatIpDataMapCache.get(neutronFloatingIPUuid); + if (floatIpData == null) { + LOG.trace("programFlowsForFloatingIPArpDelete for uuid {} is not needed", neutronFloatingIPUuid); + return; + } + + 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.neutron.spi.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.neutron.spi.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; + } + + 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 && neutronPortToDpIdCache.get(neutronPortUuid) == null && + 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> 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; + } + } } // @@ -291,30 +624,35 @@ public class NeutronL3Adapter { 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 nodes = connectionService.getNodes(); + List 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 Action actionForNode = - tenantNetworkManager.isTenantNetworkPresentInNode(node, providerSegmentationId) ? - action : Action.DELETE; + final Long dpid = getDpidForIntegrationBridge(node); + if (dpid == null) { + continue; + } + for (Neutron_IPs neutronIP : neutronPort.getFixedIPs()) { final String tenantIpStr = neutronIP.getIpAddress(); if (tenantIpStr.isEmpty()) { continue; } - // Configure L3 fwd - programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode); + // Configure L3 fwd. We do that regardless of tenant network present, because these rules are + // still needed when routing to subnets non-local to node (bug 2076). + programL3ForwardingStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, action); // Configure distributed ARP responder - programStaticArpStage1(node, dpid, providerSegmentationId, tenantMac, tenantIpStr, actionForNode); + if (flgDistributedARPEnabled) { + programStaticArpStage1(dpid, providerSegmentationId, tenantMac, tenantIpStr, action); + } } } } @@ -324,18 +662,18 @@ public class NeutronL3Adapter { 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); + final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + ipStr; + final boolean isProgrammed = l3ForwardingCache.contains(cacheKey); + + if (actionForNode == Action.DELETE && !isProgrammed) { + LOG.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done", + node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode); return; } - 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); + if (actionForNode == Action.ADD && isProgrammed) { + LOG.trace("programL3ForwardingStage1 for node {} providerId {} mac {} ip {} action {} is already done", + node.getNodeId().getValue(), providerSegmentationId, macAddress, ipStr, actionForNode); return; } @@ -360,19 +698,19 @@ public class NeutronL3Adapter { 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; } @@ -384,54 +722,66 @@ public class NeutronL3Adapter { Preconditions.checkNotNull(destNeutronRouterInterface); final NeutronPort neutronPort = neutronPortCache.getPort(destNeutronRouterInterface.getPortUUID()); - final String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null; - final List ipList = neutronPort != null ? neutronPort.getFixedIPs() : null; + String macAddress = neutronPort != null ? neutronPort.getMacAddress() : null; + List 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 nodes = connectionService.getNodes(); + List 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; } @@ -445,7 +795,12 @@ public class NeutronL3Adapter { 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 @@ -453,29 +808,22 @@ public class NeutronL3Adapter { // { 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, @@ -494,7 +842,7 @@ public class NeutronL3Adapter { 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; } @@ -502,13 +850,13 @@ public class NeutronL3Adapter { 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; } @@ -518,7 +866,7 @@ public class NeutronL3Adapter { } 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)) { @@ -540,9 +888,10 @@ public class NeutronL3Adapter { 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) { @@ -566,22 +915,23 @@ public class NeutronL3Adapter { // 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 + ":" + + final String cacheKey = node.getNodeId().getValue() + ":" + + sourceSegmentationId + ":" + destinationSegmentationId + ":" + ipStr + "/" + Integer.toString(mask); - final Boolean isProgrammed = routerInterfacesCache.contains(cacheKey); + 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 && !isProgrammed) { + LOG.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" + + " action {} is already done", + 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); + if (actionForNode == Action.ADD && isProgrammed) { + LOG.trace("programRouterInterfaceStage1 for node {} sourceSegId {} destSegId {} mac {} ip {} mask {}" + + " action {} is already done", + node.getNodeId().getValue(), sourceSegmentationId, destinationSegmentationId, + macAddress, ipStr, mask, actionForNode); return; } @@ -609,410 +959,440 @@ public class NeutronL3Adapter { 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) { + private boolean programStaticArpStage1(Long dpid, String segOrOfPort, + String macAddress, String ipStr, + Action action) { // 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); + final String cacheKey = dpid + ":" + segOrOfPort + ":" + 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; + if (action == Action.DELETE && !isProgrammed) { + LOG.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done", + dpid, segOrOfPort, macAddress, ipStr, action); + return true; } - 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 && isProgrammed) { + LOG.trace("programStaticArpStage1 dpid {} segOrOfPort {} mac {} ip {} action {} is already done", + dpid, segOrOfPort, macAddress, ipStr, action); + return true; } - Status status = this.programStaticArpStage2(node, dpid, providerSegmentationId, - macAddress, ipStr, actionForNode); + Status status = this.programStaticArpStage2(dpid, segOrOfPort, macAddress, ipStr, action); if (status.isSuccess()) { // Update cache - if (actionForNode == Action.ADD) { + if (action == Action.ADD) { staticArpEntryCache.add(cacheKey); } else { staticArpEntryCache.remove(cacheKey); } + return true; } + return false; } - 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) { + private boolean programInboundIpRewriteStage1(Long dpid, Long inboundOFPort, String providerSegmentationId, + String matchAddress, String rewriteAddress, + Action action) { // 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; + final String cacheKey = dpid + ":" + inboundOFPort + ":" + providerSegmentationId + ":" + matchAddress; + final boolean isProgrammed = inboundIpRewriteCache.contains(cacheKey); + + if (action == Action.DELETE && !isProgrammed) { + LOG.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" + + " action {} is already done", + dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action); + return true; } - 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 && isProgrammed) { + LOG.trace("programInboundIpRewriteStage1 dpid {} OFPort {} seg {} matchAddress {} rewriteAddress {}" + + " action is already done", + dpid, inboundOFPort, providerSegmentationId, matchAddress, rewriteAddress, action); + return true; } - Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr, - isInbound, actionForRewriteExclusion); + Status status = programInboundIpRewriteStage2(dpid, inboundOFPort, providerSegmentationId, matchAddress, + rewriteAddress, action); if (status.isSuccess()) { // Update cache - if (actionForRewriteExclusion == Action.ADD) { - if (isInbound) { - inboundIpRewriteExclusionCache.add(cacheKey); - } else { - outboundIpRewriteExclusionCache.add(cacheKey); - } + if (action == Action.ADD) { + inboundIpRewriteCache.add(cacheKey); } else { - if (isInbound) { - inboundIpRewriteExclusionCache.remove(cacheKey); - } else { - outboundIpRewriteExclusionCache.remove(cacheKey); - } + inboundIpRewriteCache.remove(cacheKey); } + return true; } + return false; } - 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) { + private void programIpRewriteExclusionStage1(Node node, Long dpid, String providerSegmentationId, 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 + ":" + gatewayIp; - final Boolean isProgrammed = defaultRouteCache.contains(cacheKey); + final String cacheKey = node.getNodeId().getValue() + ":" + providerSegmentationId + ":" + cidr; + final boolean isProgrammed = outboundIpRewriteExclusionCache.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); + if (actionForRewriteExclusion == Action.DELETE && !isProgrammed) { + LOG.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done", + node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion); return; } - if (actionForNodeDefaultRoute == Action.ADD && isProgrammed == Boolean.TRUE) { - logger.trace("programDefaultRouteStage1 node {} providerId {} mac {} gw {} action {} is already done", - node.getNodeIDString(), providerSegmentationId, defaultGatewayMacAddress, gatewayIp, - actionForNodeDefaultRoute); + if (actionForRewriteExclusion == Action.ADD && isProgrammed) { + LOG.trace("programIpRewriteExclusionStage1 node {} providerId {} cidr {} action {} is already done", + node.getNodeId().getValue(), providerSegmentationId, cidr, actionForRewriteExclusion); return; } - Status status = this.programDefaultRouteStage2(node, dpid, providerSegmentationId, - defaultGatewayMacAddress, gatewayIp, actionForNodeDefaultRoute); + Status status = this.programIpRewriteExclusionStage2(node, dpid, providerSegmentationId, cidr, + actionForRewriteExclusion); if (status.isSuccess()) { // Update cache - if (actionForNodeDefaultRoute == Action.ADD) { - defaultRouteCache.add(cacheKey); + if (actionForRewriteExclusion == Action.ADD) { + outboundIpRewriteExclusionCache.add(cacheKey); } else { - defaultRouteCache.remove(cacheKey); + outboundIpRewriteExclusionCache.remove(cacheKey); } } } - 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:{}", - defaultGatewayMacAddress, 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... - } - - final Action action = isDelete ? Action.DELETE : Action.ADD; - List nodes = connectionService.getNodes(); - if (nodes.isEmpty()) { - logger.trace("programFlowsForFloatingIP has no nodes to work with"); - } - 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) { + private void programOutboundIpRewriteStage1(FloatIpData fid, Action action) { // 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); + final String cacheKey = fid.dpid + ":" + fid.segId + ":" + fid.fixedIpAddress; + final boolean isProgrammed = outboundIpRewriteCache.contains(cacheKey); + + if (action == Action.DELETE && !isProgrammed) { + LOG.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " + + "is already done", + fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action); 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); + if (action == Action.ADD && isProgrammed) { + LOG.trace("programOutboundIpRewriteStage1 dpid {} seg {} fixedIpAddress {} floatIp {} action {} " + + "is already done", + fid.dpid, fid.segId, fid.fixedIpAddress, fid.floatingIpAddress, action); return; } - Status status = this.programIpRewriteStage2(node, dpid, providerSegmentationId, isInbound, - matchAddress, rewriteAddress, actionForNode); + Status status = this.programOutboundIpRewriteStage2(fid, action); if (status.isSuccess()) { // Update cache - if (actionForNode == Action.ADD) { - if (isInbound) { - inboundIpRewriteCache.add(cacheKey); - } else { - outboundIpRewriteCache.add(cacheKey); - } + if (action == Action.ADD) { + outboundIpRewriteCache.add(cacheKey); } else { - if (isInbound) { - inboundIpRewriteCache.remove(cacheKey); - } else { - outboundIpRewriteCache.remove(cacheKey); - } + outboundIpRewriteCache.remove(cacheKey); } } } - 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 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 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 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){ + 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) { + LOG.info("Trigger MAC resolution for gateway ip {} on Node {}",externalSubnet.getGatewayIP(),node.getNodeId()); + ListenableFuture 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(){ + @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); + 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 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; + } + } +}