package org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.services.arp;
import org.opendaylight.controller.liblldp.NetUtils;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolverListener;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
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.opendaylight.flow.service.rev130819.RemoveFlowInput;
public final class ArpResolverMetadata {
+ private final GatewayMacResolverListener gatewayMacResolverListener;
private final Ipv4Address gatewayIpAddress;
private final Long externalNetworkBridgeDpid;
private final Ipv4Address arpRequestSourceIp;
private int numberOfOutstandingArpRequests;
private static final int MAX_OUTSTANDING_ARP_REQUESTS = 2;
- public ArpResolverMetadata(final Long externalNetworkBridgeDpid,
+ public ArpResolverMetadata(final GatewayMacResolverListener gatewayMacResolverListener,
+ final Long externalNetworkBridgeDpid,
final Ipv4Address gatewayIpAddress, final Ipv4Address arpRequestSourceIp,
final MacAddress arpRequestMacAddress, final boolean periodicRefresh){
+ this.gatewayMacResolverListener = gatewayMacResolverListener;
this.externalNetworkBridgeDpid = externalNetworkBridgeDpid;
this.gatewayIpAddress = gatewayIpAddress;
this.arpRequestSourceIp = arpRequestSourceIp;
}
public void setGatewayMacAddress(MacAddress gatewayMacAddress) {
if (gatewayMacAddress != null) {
+ if (gatewayMacResolverListener != null &&
+ !gatewayMacAddress.equals(this.gatewayMacAddress)) {
+ gatewayMacResolverListener.gatewayMacResolved(externalNetworkBridgeDpid,
+ new IpAddress(gatewayIpAddress), gatewayMacAddress);
+ }
gatewayMacAddressResolved = true;
numberOfOutstandingArpRequests = 0;
} else {
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.openflowplugin.api.OFConstants;
import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolverListener;
import org.opendaylight.ovsdb.openstack.netvirt.providers.ConfigInterface;
import org.opendaylight.ovsdb.openstack.netvirt.providers.NetvirtProvidersProvider;
import org.opendaylight.ovsdb.openstack.netvirt.providers.openflow13.AbstractServiceInstance;
* @return Future object
*/
@Override
- public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
- final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh){
+ public ListenableFuture<MacAddress> resolveMacAddress(
+ final GatewayMacResolverListener gatewayMacResolverListener, final Long externalNetworkBridgeDpid,
+ final Ipv4Address gatewayIp, final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress,
+ final Boolean periodicRefresh){
Preconditions.checkNotNull(sourceIpAddress);
Preconditions.checkNotNull(sourceMacAddress);
Preconditions.checkNotNull(gatewayIp);
});
}
}else{
- gatewayToArpMetadataMap.put(gatewayIp,new ArpResolverMetadata(
+ gatewayToArpMetadataMap.put(gatewayIp,new ArpResolverMetadata(gatewayMacResolverListener,
externalNetworkBridgeDpid, gatewayIp,sourceIpAddress,sourceMacAddress,periodicRefresh));
}
NEUTRON_LOAD_BALANCER,
NEUTRON_LOAD_BALANCER_POOL,
NEUTRON_LOAD_BALANCER_POOL_MEMBER,
- NODE;
+ NODE,
+ NEUTRON_L3_ADAPTER;
public static final int size = HandlerType.values().length;
}
import org.opendaylight.ovsdb.openstack.netvirt.api.EgressAclProvider;
import org.opendaylight.ovsdb.openstack.netvirt.api.EventDispatcher;
import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolverListener;
import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
import org.opendaylight.ovsdb.openstack.netvirt.api.IngressAclProvider;
import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
registerService(context,
new String[]{EventDispatcher.class.getName()}, null, eventDispatcher);
+ Dictionary<String, Object> neutronL3AdapterProperties = new Hashtable<>();
+ neutronL3AdapterProperties.put(Constants.EVENT_HANDLER_TYPE_PROPERTY,
+ AbstractEvent.HandlerType.NEUTRON_L3_ADAPTER);
final NeutronL3Adapter neutronL3Adapter = new NeutronL3Adapter(
new NeutronModelsDataStoreHelper(this.providerContext.getSALService(DataBroker.class)));
registerService(context,
- new String[]{NeutronL3Adapter.class.getName()}, null, neutronL3Adapter);
+ new String[]{NeutronL3Adapter.class.getName(), GatewayMacResolverListener.class.getName()},
+ neutronL3AdapterProperties, neutronL3Adapter);
OpenstackRouter openstackRouter = new OpenstackRouter();
registerService(context,
--- /dev/null
+/*
+ * Copyright (c) 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
+ */
+package org.opendaylight.ovsdb.openstack.netvirt;
+
+import org.opendaylight.ovsdb.openstack.netvirt.api.Action;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+
+public class NeutronL3AdapterEvent extends AbstractEvent {
+ public enum SubType {
+ SUBTYPE_EXTERNAL_MAC_UPDATE; // TODO: Add more subtypes as they come here
+
+ public static final int size = HandlerType.values().length;
+ }
+
+ private final SubType subtype;
+
+ private final Long bridgeDpid;
+ private final IpAddress gatewayIpAddress;
+ private final MacAddress macAddress;
+
+ public NeutronL3AdapterEvent(final Long bridgeDpid, final IpAddress gatewayIpAddress, final MacAddress macAddress) {
+ super(HandlerType.NEUTRON_L3_ADAPTER, Action.UPDATE);
+
+ this.subtype = SubType.SUBTYPE_EXTERNAL_MAC_UPDATE;
+ this.bridgeDpid = bridgeDpid;
+ this.gatewayIpAddress = gatewayIpAddress;
+ this.macAddress = macAddress;
+ }
+
+ public SubType getSubType() {
+ return subtype;
+ }
+
+ public Long getBridgeDpid() {
+ return bridgeDpid;
+ }
+ public IpAddress getGatewayIpAddress() {
+ return gatewayIpAddress;
+ }
+ public MacAddress getMacAddress() {
+ return macAddress;
+ }
+
+ @Override
+ public String toString() {
+ return "NeutronL3AdapterEvent [handler=" + super.getHandlerType()
+ + ", action=" + super.getAction()
+ + ", subtype=" + subtype
+ + ", bridgeDpid=" + bridgeDpid
+ + ", gatewayIpAddress=" + gatewayIpAddress
+ + ", macAddress=" + macAddress
+ + "]";
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((subtype == null) ? 0 : subtype.hashCode());
+ result = prime * result + ((bridgeDpid == null) ? 0 : bridgeDpid.hashCode());
+ result = prime * result + ((gatewayIpAddress == null) ? 0 : gatewayIpAddress.hashCode());
+ result = prime * result + ((macAddress == null) ? 0 : macAddress.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ NeutronL3AdapterEvent other = (NeutronL3AdapterEvent) obj;
+ if (subtype == null) {
+ if (other.subtype != null) {
+ return false;
+ }
+ } else if (!subtype.equals(other.subtype)) {
+ return false;
+ }
+ if (bridgeDpid == null) {
+ if (other.bridgeDpid != null) {
+ return false;
+ }
+ } else if (!bridgeDpid.equals(other.bridgeDpid)) {
+ return false;
+ }
+ if (gatewayIpAddress == null) {
+ if (other.gatewayIpAddress != null) {
+ return false;
+ }
+ } else if (!gatewayIpAddress.equals(other.gatewayIpAddress)) {
+ return false;
+ }
+ if (macAddress == null) {
+ if (other.macAddress != null) {
+ return false;
+ }
+ } else if (!macAddress.equals(other.macAddress)) {
+ return false;
+ }
+ return true;
+ }
+}
* If user call the same method with different source ip and mac address, GatewayMacResolver service will
* update the internally cached data with these new source ip and mac address and will use it as per
* periodicRefresh flag.
+ * @param gatewayMacResolverListener An optional listener for mac update callback (can be null)
* @param externalNetworkBridgeDpid This bridge will be used for sending ARP request
* @param gatewayIp ARP request will be send for this ip address
* @param sourceIpAddress Source IP address for the ARP request (localhost)
* @param periodicRefresh Do you want to periodically refresh the gateway mac?
* @return ListenableFuture that contains the mac address of gateway ip.
*/
- public ListenableFuture<MacAddress> resolveMacAddress( final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp,
- final Ipv4Address sourceIpAddress, final MacAddress sourceMacAddress, final Boolean periodicRefresh);
+ public ListenableFuture<MacAddress> resolveMacAddress(final GatewayMacResolverListener gatewayMacResolverListener,
+ final Long externalNetworkBridgeDpid, final Ipv4Address gatewayIp, final Ipv4Address sourceIpAddress,
+ final MacAddress sourceMacAddress, final Boolean periodicRefresh);
/**
* Method will stop the periodic refresh of the given gateway ip address.
--- /dev/null
+/*
+ * Copyright (c) 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
+ */
+package org.opendaylight.ovsdb.openstack.netvirt.api;
+
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.MacAddress;
+
+/**
+ * This interface allows for notifications from GatewayMacResolver to a generic listener.
+ */
+public interface GatewayMacResolverListener {
+
+ /**
+ * Method will trigger when the mac for gateway IP is resolved or updated.
+ *
+ * @param externalNetworkBridgeDpid Bridge used for sending ARP request
+ * @param gatewayIpAddress Ip address that Mac Resolver ARPed for
+ * @param macAddress Mac Address associated with the gatewayIpAddress
+ */
+ void gatewayMacResolved(final Long externalNetworkBridgeDpid, final IpAddress gatewayIpAddress,
+ final MacAddress macAddress);
+}
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
+import org.opendaylight.ovsdb.openstack.netvirt.AbstractEvent;
+import org.opendaylight.ovsdb.openstack.netvirt.AbstractHandler;
import org.opendaylight.ovsdb.openstack.netvirt.ConfigInterface;
+import org.opendaylight.ovsdb.openstack.netvirt.NeutronL3AdapterEvent;
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.EventDispatcher;
import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolver;
+import org.opendaylight.ovsdb.openstack.netvirt.api.GatewayMacResolverListener;
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.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.IpAddress;
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;
* 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 implements ConfigInterface {
+public class NeutronL3Adapter extends AbstractHandler implements GatewayMacResolverListener, ConfigInterface {
private static final Logger LOG = LoggerFactory.getLogger(NeutronL3Adapter.class);
// The implementation for each of these services is resolved by the OSGi Service Manager
}
}
+ //
+ // Callbacks from AbstractHandler
+ //
+ @Override
+ public void processEvent(AbstractEvent abstractEvent) {
+ if (!(abstractEvent instanceof NeutronL3AdapterEvent)) {
+ LOG.error("Unable to process abstract event " + abstractEvent);
+ return;
+ }
+ NeutronL3AdapterEvent ev = (NeutronL3AdapterEvent) abstractEvent;
+ switch (ev.getAction()) {
+ case UPDATE:
+ if (ev.getSubType() == NeutronL3AdapterEvent.SubType.SUBTYPE_EXTERNAL_MAC_UPDATE) {
+ updateExternalRouterMac( ev.getMacAddress().getValue() );
+ } else {
+ LOG.warn("Received update for an unexpected event " + ev);
+ }
+ break;
+ case ADD:
+ // fall through...
+ // break;
+ case DELETE:
+ // fall through...
+ // break;
+ default:
+ LOG.warn("Unable to process event " + ev);
+ break;
+ }
+ }
+
+ //
+ // Callbacks from GatewayMacResolverListener
+ //
+
+ @Override
+ public void gatewayMacResolved(Long externalNetworkBridgeDpid, IpAddress gatewayIpAddress, MacAddress macAddress) {
+ LOG.info("got gatewayMacResolved callback for ip {} on dpid {} to mac {}",
+ gatewayIpAddress, externalNetworkBridgeDpid, macAddress);
+ if (!this.enabled) {
+ return;
+ }
+
+ if (macAddress == null || macAddress.getValue() == null) {
+ // TODO: handle cases when mac is null
+ return;
+ }
+
+ //
+ // Enqueue event so update is handled by adapter's thread
+ //
+ enqueueEvent( new NeutronL3AdapterEvent(externalNetworkBridgeDpid, gatewayIpAddress, macAddress) );
+ }
private void populateL3ForwardingCaches() {
if (!this.enabled) {
gatewayPort.getFixedIPs() != null) {
LOG.info("Trigger MAC resolution for gateway ip {} on Node {}",externalSubnet.getGatewayIP(),node.getNodeId());
ListenableFuture<MacAddress> gatewayMacAddress =
- gatewayMacResolver.resolveMacAddress(getDpidForExternalBridge(node),
+ gatewayMacResolver.resolveMacAddress(this,
+ getDpidForExternalBridge(node),
new Ipv4Address(externalSubnet.getGatewayIP()),
new Ipv4Address(gatewayPort.getFixedIPs().get(0).getIpAddress()),
new MacAddress(gatewayPort.getMacAddress()),
@Override
public void setDependencies(ServiceReference serviceReference) {
+ eventDispatcher =
+ (EventDispatcher) ServiceHelper.getGlobalInstance(EventDispatcher.class, this);
+ eventDispatcher.eventHandlerAdded(serviceReference, this);
tenantNetworkManager =
(TenantNetworkManager) ServiceHelper.getGlobalInstance(TenantNetworkManager.class, this);
configurationService =
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.AbstractHandler;
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.EventDispatcher;
import org.opendaylight.ovsdb.openstack.netvirt.api.InboundNatProvider;
import org.opendaylight.ovsdb.openstack.netvirt.api.L3ForwardingProvider;
import org.opendaylight.ovsdb.openstack.netvirt.api.NodeCacheManager;
@Test
public void testSetDependencies() throws Exception {
+ EventDispatcher eventDispatcher = mock(EventDispatcher.class);
TenantNetworkManager tenantNetworkManager = mock(TenantNetworkManager.class);
ConfigurationService configurationService = mock(ConfigurationService.class);
ArpProvider arpProvider = mock(ArpProvider.class);
Southbound southbound = mock(Southbound.class);
PowerMockito.mockStatic(ServiceHelper.class);
+ PowerMockito.when(ServiceHelper.getGlobalInstance(EventDispatcher.class, neutronL3Adapter)).thenReturn(eventDispatcher);
PowerMockito.when(ServiceHelper.getGlobalInstance(TenantNetworkManager.class, neutronL3Adapter)).thenReturn(tenantNetworkManager);
PowerMockito.when(ServiceHelper.getGlobalInstance(ConfigurationService.class, neutronL3Adapter)).thenReturn(configurationService);
PowerMockito.when(ServiceHelper.getGlobalInstance(ArpProvider.class, neutronL3Adapter)).thenReturn(arpProvider);
neutronL3Adapter.setDependencies(mock(ServiceReference.class));
+ assertEquals("Error, did not return the correct object", getAbstractHandlerField("eventDispatcher"),
+ eventDispatcher);
assertEquals("Error, did not return the correct object", getField("tenantNetworkManager"), tenantNetworkManager);
assertEquals("Error, did not return the correct object", getField("configurationService"), configurationService);
assertEquals("Error, did not return the correct object", getField("arpProvider"), arpProvider);
MemberModifier.field(NeutronL3Adapter.class, "enabled").set(neutronL3Adapter , true);
}
+ private Object getAbstractHandlerField(String fieldName) throws Exception {
+ Field field = AbstractHandler.class.getDeclaredField(fieldName);
+ field.setAccessible(true);
+ return field.get(neutronL3Adapter);
+ }
+
private Object getField(String fieldName) throws Exception {
Field field = NeutronL3Adapter.class.getDeclaredField(fieldName);
field.setAccessible(true);