From: Yevgeny Khodorkovsky Date: Fri, 2 Aug 2013 23:38:14 +0000 (-0700) Subject: HA - ARPHandler Event sync X-Git-Tag: releasepom-0.1.0~223^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=b34143e6c369a6746c5d5399ed17964812a106f0 HA - ARPHandler Event sync - Replay ARP request/reply events across the cluster using cluster allocated cache. Then the appropriate controller handles sending the ARP. - Add isUnicastMAC() to NetUtils Change-Id: I2d2b60348341710ffbd3ffc749ff52869b9c0b3f Signed-off-by: Yevgeny Khodorkovsky --- diff --git a/opendaylight/arphandler/pom.xml b/opendaylight/arphandler/pom.xml index 069ad8d4a3..68b9c55830 100644 --- a/opendaylight/arphandler/pom.xml +++ b/opendaylight/arphandler/pom.xml @@ -23,17 +23,22 @@ + org.opendaylight.controller.connectionmanager, org.opendaylight.controller.sal.core, org.opendaylight.controller.sal.utils, org.opendaylight.controller.sal.packet, org.opendaylight.controller.switchmanager, org.opendaylight.controller.topologymanager, + org.opendaylight.controller.clustering.services, org.opendaylight.controller.hosttracker, org.opendaylight.controller.hosttracker.hostAware, org.apache.felix.dm, org.osgi.service.component, org.slf4j + + org.opendaylight.controller.arphandler + org.opendaylight.controller.arphandler.internal.Activator @@ -48,6 +53,11 @@ org.opendaylight.controller switchmanager 0.5.0-SNAPSHOT + + + org.opendaylight.controller + connectionmanager + 0.1.0-SNAPSHOT org.opendaylight.controller diff --git a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPEvent.java b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPEvent.java new file mode 100644 index 0000000000..fb92e8df85 --- /dev/null +++ b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPEvent.java @@ -0,0 +1,59 @@ + +/* + * Copyright (c) 2013 Cisco Systems, 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.controller.arphandler; + +import java.io.Serializable; +import java.net.InetAddress; +/* + * ARP Event base class + */ +public abstract class ARPEvent implements Serializable{ + + private static final long serialVersionUID = 1L; + private final InetAddress tIP; + + + @Override + public int hashCode() { + final int prime = 31; + int result = prime + ((tIP == null) ? 0 : tIP.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ARPEvent)) { + return false; + } + ARPEvent other = (ARPEvent) obj; + if (tIP == null) { + if (other.tIP != null) { + return false; + } + } else if (!tIP.equals(other.tIP)) { + return false; + } + return true; + } + + public ARPEvent(InetAddress ip) { + this.tIP = ip; + } + + public InetAddress getTargetIP() { + return tIP; + } +} diff --git a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPReply.java b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPReply.java new file mode 100644 index 0000000000..4ca3e42c7c --- /dev/null +++ b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPReply.java @@ -0,0 +1,95 @@ + +/* + * Copyright (c) 2013 Cisco Systems, 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.controller.arphandler; + +import java.net.InetAddress; +import java.util.Arrays; + +import org.opendaylight.controller.sal.core.NodeConnector; +/* + * ARP Reply event wrapper + */ +public class ARPReply extends ARPEvent { + + private final NodeConnector port; + private final byte[] tMac; + private final byte[] sMac; + private final InetAddress sIP; + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((sIP == null) ? 0 : sIP.hashCode()); + result = prime * result + Arrays.hashCode(sMac); + result = prime * result + Arrays.hashCode(tMac); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ARPReply)) { + return false; + } + ARPReply other = (ARPReply) obj; + if (sIP == null) { + if (other.sIP != null) { + return false; + } + } else if (!sIP.equals(other.sIP)) { + return false; + } + if (!Arrays.equals(sMac, other.sMac)) { + return false; + } + if (!Arrays.equals(tMac, other.tMac)) { + return false; + } + return true; + } + + public ARPReply(NodeConnector port, InetAddress sIP, byte[] sMAC, InetAddress tIP, byte[] tMAC) { + super(tIP); + this.tMac = tMAC; + this.sIP = sIP; + this.sMac = sMAC; + this.port = port; + } + + public ARPReply(InetAddress tIP, byte[] tMAC) { + super(tIP); + this.tMac = tMAC; + this.sIP = null; + this.sMac = null; + this.port = null; + } + + public byte[] getTargetMac() { + return tMac; + } + + public byte[] getSourceMac() { + return sMac; + } + + public InetAddress getSourceIP() { + return sIP; + } + + public NodeConnector getPort() { + return port; + } +} diff --git a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPRequest.java b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPRequest.java new file mode 100644 index 0000000000..7f88a25e31 --- /dev/null +++ b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/ARPRequest.java @@ -0,0 +1,85 @@ + +/* + * Copyright (c) 2013 Cisco Systems, 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.controller.arphandler; + +import java.net.InetAddress; + +import org.opendaylight.controller.arphandler.ARPEvent; +import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; +import org.opendaylight.controller.switchmanager.Subnet; +/* + * ARP Request event wrapper Consists of IP and Subnet (and a + * HostNodeConnector if is unicast) For unicast request, construct with a + * specified host + */ +public class ARPRequest extends ARPEvent { + private final Subnet subnet; + private final HostNodeConnector host; + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + ((host == null) ? 0 : host.hashCode()); + result = prime * result + ((subnet == null) ? 0 : subnet.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ARPRequest)) { + return false; + } + ARPRequest other = (ARPRequest) obj; + if (host == null) { + if (other.host != null) { + return false; + } + } else if (!host.equals(other.host)) { + return false; + } + if (subnet == null) { + if (other.subnet != null) { + return false; + } + } else if (!subnet.equals(other.subnet)) { + return false; + } + return true; + } + + // broadcast + public ARPRequest(InetAddress ip, Subnet subnet) { + super(ip); + this.subnet = subnet; + this.host = null; + } + + // unicast + public ARPRequest(HostNodeConnector host, Subnet subnet) { + super(host.getNetworkAddress()); + this.host = host; + this.subnet = subnet; + } + + public Subnet getSubnet() { + return subnet; + } + + public HostNodeConnector getHost() { + return host; + } +} diff --git a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/Activator.java b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/Activator.java index 705ffbfa63..248623cce8 100644 --- a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/Activator.java +++ b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/Activator.java @@ -9,20 +9,25 @@ package org.opendaylight.controller.arphandler.internal; -import java.util.Hashtable; import java.util.Dictionary; -import org.apache.felix.dm.Component; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.Set; +import org.apache.felix.dm.Component; +import org.opendaylight.controller.clustering.services.ICacheUpdateAware; +import org.opendaylight.controller.clustering.services.IClusterContainerServices; +import org.opendaylight.controller.connectionmanager.IConnectionManager; import org.opendaylight.controller.hosttracker.IfHostListener; import org.opendaylight.controller.hosttracker.IfIptoHost; import org.opendaylight.controller.hosttracker.hostAware.IHostFinder; import org.opendaylight.controller.sal.core.ComponentActivatorAbstractBase; -import org.opendaylight.controller.sal.packet.IListenDataPacket; import org.opendaylight.controller.sal.packet.IDataPacketService; +import org.opendaylight.controller.sal.packet.IListenDataPacket; import org.opendaylight.controller.switchmanager.ISwitchManager; import org.opendaylight.controller.topologymanager.ITopologyManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class Activator extends ComponentActivatorAbstractBase { protected static final Logger logger = LoggerFactory @@ -77,10 +82,22 @@ public class Activator extends ComponentActivatorAbstractBase { public void configureInstance(Component c, Object imp, String containerName) { if (imp.equals(ArpHandler.class)) { // export the service - Dictionary props = new Hashtable(); + Dictionary props = new Hashtable(); props.put("salListenerName", "arphandler"); - c.setInterface(new String[] { IHostFinder.class.getName(), - IListenDataPacket.class.getName() }, props); + Set propSet = new HashSet(); + propSet.add(ArpHandler.ARP_EVENT_CACHE_NAME); + props.put("cachenames", propSet); + + c.setInterface(new String[] { + IHostFinder.class.getName(), + IListenDataPacket.class.getName(), + ICacheUpdateAware.class.getName()}, props); + + // We need connection mgr to distribute packet out across the cluster + c.add(createServiceDependency().setService( + IConnectionManager.class).setCallbacks("setConnectionManager", + "unsetConnectionManager").setRequired(true)); + c.add(createContainerServiceDependency(containerName).setService( ISwitchManager.class).setCallbacks("setSwitchManager", @@ -90,11 +107,16 @@ public class Activator extends ComponentActivatorAbstractBase { ITopologyManager.class).setCallbacks("setTopologyManager", "unsetTopologyMananger").setRequired(true)); - c.add(createContainerServiceDependency(containerName).setService( + c.add(createContainerServiceDependency(containerName).setService( IDataPacketService.class).setCallbacks( "setDataPacketService", "unsetDataPacketService") .setRequired(true)); + c.add(createContainerServiceDependency(containerName).setService( + IClusterContainerServices.class).setCallbacks( + "setClusterContainerService", "unsetClusterContainerService") + .setRequired(true)); + // the Host Listener is optional c.add(createContainerServiceDependency(containerName).setService( IfHostListener.class).setCallbacks("setHostListener", diff --git a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java index 811c7aca83..0ff1cd9bd6 100644 --- a/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java +++ b/opendaylight/arphandler/src/main/java/org/opendaylight/controller/arphandler/internal/ArpHandler.java @@ -16,13 +16,24 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; - +import java.util.concurrent.CopyOnWriteArraySet; + +import org.opendaylight.controller.arphandler.ARPEvent; +import org.opendaylight.controller.arphandler.ARPReply; +import org.opendaylight.controller.arphandler.ARPRequest; +import org.opendaylight.controller.clustering.services.CacheConfigException; +import org.opendaylight.controller.clustering.services.CacheExistException; +import org.opendaylight.controller.clustering.services.ICacheUpdateAware; +import org.opendaylight.controller.clustering.services.IClusterContainerServices; +import org.opendaylight.controller.clustering.services.IClusterServices; +import org.opendaylight.controller.connectionmanager.IConnectionManager; import org.opendaylight.controller.hosttracker.IfHostListener; import org.opendaylight.controller.hosttracker.IfIptoHost; import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; @@ -31,7 +42,6 @@ import org.opendaylight.controller.sal.core.ConstructionException; import org.opendaylight.controller.sal.core.Node; import org.opendaylight.controller.sal.core.NodeConnector; import org.opendaylight.controller.sal.packet.ARP; -import org.opendaylight.controller.sal.packet.BitBufferHelper; import org.opendaylight.controller.sal.packet.Ethernet; import org.opendaylight.controller.sal.packet.IDataPacketService; import org.opendaylight.controller.sal.packet.IListenDataPacket; @@ -48,28 +58,58 @@ import org.opendaylight.controller.topologymanager.ITopologyManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ArpHandler implements IHostFinder, IListenDataPacket { - private static final Logger logger = LoggerFactory - .getLogger(ArpHandler.class); - private IfIptoHost hostTracker = null; - private ISwitchManager switchManager = null; +public class ArpHandler implements IHostFinder, IListenDataPacket, ICacheUpdateAware { + private static final Logger log = LoggerFactory.getLogger(ArpHandler.class); + static final String ARP_EVENT_CACHE_NAME = "arphandler.arpRequestReplyEvent"; + private IfIptoHost hostTracker; + private ISwitchManager switchManager; private ITopologyManager topologyManager; - private IDataPacketService dataPacketService = null; - private Set hostListener = Collections - .synchronizedSet(new HashSet()); + private IDataPacketService dataPacketService; + private IClusterContainerServices clusterContainerService; + private IConnectionManager connectionManager; + private Set hostListeners = new CopyOnWriteArraySet(); private ConcurrentMap> arpRequestors; private ConcurrentMap countDownTimers; private Timer periodicTimer; + /* + * A cluster allocated cache. Used for synchronizing ARP request/reply + * events across all cluster controllers. To raise an event, we put() a specific + * event object (as key) and all nodes handle it in the entryUpdated callback. + * + * In case of ARPReply, we put true value to send replies to any requestors + * by calling generateAndSendReply + */ + private ConcurrentMap arpRequestReplyEvent; + + void setConnectionManager(IConnectionManager cm){ + this.connectionManager = cm; + } + + void unsetConnectionManager(IConnectionManager cm){ + if (this.connectionManager == cm){ + connectionManager = null; + } + } + + void setClusterContainerService(IClusterContainerServices s){ + this.clusterContainerService = s; + } + + void unsetClusterContainerService(IClusterContainerServices s) { + if (this.clusterContainerService == s) { + this.clusterContainerService = null; + } + } void setHostListener(IfHostListener s) { - if (this.hostListener != null) { - this.hostListener.add(s); + if (this.hostListeners != null) { + this.hostListeners.add(s); } } void unsetHostListener(IfHostListener s) { - if (this.hostListener != null) { - this.hostListener.remove(s); + if (this.hostListeners != null) { + this.hostListeners.remove(s); } } @@ -83,17 +123,13 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { } } - public IfIptoHost getHostTracker() { - return hostTracker; - } - public void setHostTracker(IfIptoHost hostTracker) { - logger.debug("Setting HostTracker"); + log.debug("Setting HostTracker"); this.hostTracker = hostTracker; } public void unsetHostTracker(IfIptoHost s) { - logger.debug("UNSetting HostTracker"); + log.debug("UNSetting HostTracker"); if (this.hostTracker == s) { this.hostTracker = null; } @@ -114,17 +150,21 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { byte[] senderIP = sIP.getAddress(); byte[] targetIP = tIP.getAddress(); ARP arp = new ARP(); - arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType( - EtherTypes.IPv4.shortValue()) - .setHardwareAddressLength((byte) 6).setProtocolAddressLength( - (byte) 4).setOpCode(ARP.REPLY) - .setSenderHardwareAddress(sMAC).setSenderProtocolAddress( - senderIP).setTargetHardwareAddress(tMAC) - .setTargetProtocolAddress(targetIP); + arp.setHardwareType(ARP.HW_TYPE_ETHERNET) + .setProtocolType(EtherTypes.IPv4.shortValue()) + .setHardwareAddressLength((byte) 6) + .setProtocolAddressLength((byte) 4) + .setOpCode(ARP.REPLY) + .setSenderHardwareAddress(sMAC) + .setSenderProtocolAddress(senderIP) + .setTargetHardwareAddress(tMAC) + .setTargetProtocolAddress(targetIP); Ethernet ethernet = new Ethernet(); - ethernet.setSourceMACAddress(sMAC).setDestinationMACAddress(tMAC) - .setEtherType(EtherTypes.ARP.shortValue()).setPayload(arp); + ethernet.setSourceMACAddress(sMAC) + .setDestinationMACAddress(tMAC) + .setEtherType(EtherTypes.ARP.shortValue()) + .setPayload(arp); RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet); destPkt.setOutgoingNodeConnector(p); @@ -132,114 +172,79 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { this.dataPacketService.transmitDataPacket(destPkt); } - private boolean isBroadcastMAC(byte[] mac) { - if (BitBufferHelper.toNumber(mac) == 0xffffffffffffL) { //TODO: implement this in our Ethernet - return true; - } - return false; - } - - private boolean isUnicastMAC(byte[] mac) { - if ((BitBufferHelper.toNumber(mac) & 0x010000000000L) == 0) { - return true; - } - return false; - } - protected void handleARPPacket(Ethernet eHeader, ARP pkt, NodeConnector p) { - if (pkt.getOpCode() == 0x1) { - logger.debug("Received ARP REQUEST Packet from NodeConnector: {}", - p); - } else { - logger.debug("Received ARP REPLY Packet from NodeConnector: {}", - p); - } - InetAddress targetIP = null; - try { - targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress()); - } catch (UnknownHostException e1) { - return; - } - InetAddress sourceIP = null; - try { - sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress()); - } catch (UnknownHostException e1) { - return; - } - byte[] targetMAC = eHeader.getDestinationMACAddress(); - byte[] sourceMAC = eHeader.getSourceMACAddress(); + byte[] sourceMAC = eHeader.getSourceMACAddress(); + byte[] targetMAC = eHeader.getDestinationMACAddress(); /* * Sanity Check; drop ARP packets originated by the controller itself. * This is to avoid continuous flooding */ if (Arrays.equals(sourceMAC, getControllerMAC())) { - if (logger.isDebugEnabled()) { - logger.debug( - "Receive the self originated packet (srcMAC {}) --> DROP", - HexEncode.bytesToHexString(sourceMAC)); + if (log.isDebugEnabled()) { + log.debug("Receive a self originated ARP pkt (srcMAC {}) --> DROP", + HexEncode.bytesToHexString(sourceMAC)); } return; } + InetAddress targetIP, sourceIP; + try { + targetIP = InetAddress.getByAddress(pkt.getTargetProtocolAddress()); + sourceIP = InetAddress.getByAddress(pkt.getSenderProtocolAddress()); + } catch (UnknownHostException e1) { + log.debug("Invalid host in ARP packet: {}", e1.getMessage()); + return; + } + Subnet subnet = null; if (switchManager != null) { subnet = switchManager.getSubnetByNetworkAddress(sourceIP); } if (subnet == null) { - logger.debug("can't find subnet matching {}, drop packet",sourceIP); + log.debug("ARPHandler: can't find subnet matching {}, drop packet", sourceIP); return; } - logger.debug("Found {} matching {}", subnet, sourceIP); - /* - * Make sure that the host is a legitimate member of this subnet - */ + + // Make sure that the host is a legitimate member of this subnet if (!subnet.hasNodeConnector(p)) { - logger.debug("{} showing up on {} does not belong to {}", + log.debug("{} showing up on {} does not belong to {}", new Object[] { sourceIP, p, subnet }); return; } HostNodeConnector requestor = null; - if (isUnicastMAC(sourceMAC)) { - // TODO For not this is only OPENFLOW but we need to fix this - if (p.getType().equals( - NodeConnector.NodeConnectorIDType.OPENFLOW)) { - try { - requestor = new HostNodeConnector(sourceMAC, sourceIP, p, subnet - .getVlan()); - } catch (ConstructionException e) { - return; - } - /* - * Learn host from the received ARP REQ/REPLY, inform - * Host Tracker - */ - logger.debug("Inform Host tracker of new host {}", requestor.getNetworkAddress()); - synchronized (this.hostListener) { - for (IfHostListener listener : this.hostListener) { - listener.hostListener(requestor); - } - } + if (NetUtils.isUnicastMACAddr(sourceMAC) && p.getNode() != null) { + try { + requestor = new HostNodeConnector(sourceMAC, sourceIP, p, subnet.getVlan()); + } catch (ConstructionException e) { + log.debug("Received ARP packet with invalid MAC: {}", sourceMAC); + return; + } + /* + * Learn host from the received ARP REQ/REPLY, inform Host Tracker + */ + log.trace("Inform Host tracker of new host {}", requestor.getNetworkAddress()); + for (IfHostListener listener : this.hostListeners) { + listener.hostListener(requestor); } - } - /* - * Gratuitous ARP. If there are hosts (in arpRequestors) waiting for the - * ARP reply for this sourceIP, it's time to generate the reply and it - * to these hosts - */ - if (sourceIP.equals(targetIP)) { - generateAndSendReply(sourceIP, sourceMAC); - return; } /* - * ARP Reply. If there are hosts (in arpRequesttors) waiting for the ARP - * reply for this sourceIP, it's time to generate the reply and it to - * these hosts + * OpCode != request -> ARP Reply. If there are hosts (in + * arpRequestors) waiting for the ARP reply for this sourceIP, it's + * time to generate the reply and send it to these hosts. + * + * If sourceIP==targetIP, it is a Gratuitous ARP. If there are hosts (in + * arpRequestors) waiting for the ARP reply for this sourceIP, it's time + * to generate the reply and send it to these hosts */ - if (pkt.getOpCode() != ARP.REQUEST) { - generateAndSendReply(sourceIP, sourceMAC); + + if (pkt.getOpCode() != ARP.REQUEST || sourceIP.equals(targetIP)) { + // Raise a reply event so that any waiting requestors will be sent a reply + // the true value indicates we should generate replies to requestors across the cluster + log.trace("Received ARP reply packet from {}, reply to all requestors.", sourceIP); + arpRequestReplyEvent.put(new ARPReply(sourceIP, sourceMAC), true); return; } @@ -251,55 +256,71 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { * the IP address defined in the subnet as source address */ /* - * Send ARP reply if target IP is gateway IP + * If target IP is gateway IP, Send ARP reply */ if ((targetIP.equals(subnet.getNetworkAddress())) - && (isBroadcastMAC(targetMAC) || Arrays.equals(targetMAC, - getControllerMAC()))) { - sendARPReply(p, getControllerMAC(), targetIP, pkt - .getSenderHardwareAddress(), sourceIP); + && (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(targetMAC, getControllerMAC()))) { + if (connectionManager.isLocal(p.getNode())){ + if (log.isTraceEnabled()){ + log.trace("Received local ARP req. for default gateway. Replying with controller MAC: {}", getControllerMAC()); + } + sendARPReply(p, getControllerMAC(), targetIP, pkt.getSenderHardwareAddress(), sourceIP); + } else { + log.trace("Received non-local ARP req. for default gateway. Raising reply event"); + arpRequestReplyEvent.put( + new ARPReply(p, targetIP, getControllerMAC(), sourceIP, pkt.getSenderHardwareAddress()), false); + } return; } - /* - * unknown host, initiate ARP request - */ + HostNodeConnector host = hostTracker.hostQuery(targetIP); + // unknown host, initiate ARP request if (host == null) { // add the requestor to the list so that we can replay the reply // when the host responds if (requestor != null) { - Set requestorSet = arpRequestors - .get(targetIP); - if ((requestorSet == null) || requestorSet.isEmpty()) { - requestorSet = new HashSet(); - countDownTimers.put(targetIP, (short) 2); // set max timeout - // to 2sec + Set requestorSet = arpRequestors.get(targetIP); + if (requestorSet == null) { + requestorSet = Collections.newSetFromMap(new ConcurrentHashMap()); + arpRequestors.put(targetIP, requestorSet); } requestorSet.add(requestor); - arpRequestors.put(targetIP, requestorSet); + countDownTimers.put(targetIP, (short) 2); // reset timeout to 2sec } - sendBcastARPRequest(targetIP, subnet); - return; - } - /* - * Known target host, send ARP REPLY - * make sure that targetMAC matches the host's MAC if it is not broadcastMAC - */ - if (isBroadcastMAC(targetMAC) - || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) { - sendARPReply(p, host.getDataLayerAddressBytes(), host - .getNetworkAddress(), pkt.getSenderHardwareAddress(), - sourceIP); - return; + //Raise a bcast request event, all controllers need to send one + log.trace("Sending a bcast ARP request for {}", targetIP); + arpRequestReplyEvent.put(new ARPRequest(targetIP, subnet), false); + } else { /* - * target target MAC has been changed. For now, discard it. - * TODO: We may need to send unicast ARP REQUEST on behalf of the - * target back to the sender to trigger the sender to - * update its table + * Target host known (across the cluster), send ARP REPLY make sure that targetMAC + * matches the host's MAC if it is not broadcastMAC */ - return; + if (NetUtils.isBroadcastMACAddr(targetMAC) || Arrays.equals(host.getDataLayerAddressBytes(), targetMAC)) { + log.trace("Received ARP req. for known host {}, sending reply...", targetIP); + if (connectionManager.isLocal(p.getNode())) { + sendARPReply(p, + host.getDataLayerAddressBytes(), + host.getNetworkAddress(), + pkt.getSenderHardwareAddress(), + sourceIP); + } else { + arpRequestReplyEvent.put(new ARPReply( + p, + host.getNetworkAddress(), + host.getDataLayerAddressBytes(), + sourceIP, + pkt.getSenderHardwareAddress()), false); + } + } else { + /* + * Target MAC has been changed. For now, discard it. + * TODO: We may need to send unicast ARP REQUEST on behalf of + * the target back to the sender to trigger the sender to update + * its table + */ + } } } @@ -314,19 +335,21 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { if (subnet.isFlatLayer2()) { nodeConnectors = new HashSet(); for (Node n : this.switchManager.getNodes()) { - nodeConnectors.addAll(this.switchManager - .getUpNodeConnectors(n)); + nodeConnectors.addAll(this.switchManager.getUpNodeConnectors(n)); } } else { nodeConnectors = subnet.getNodeConnectors(); } + for (NodeConnector p : nodeConnectors) { - if (topologyManager.isInternal(p)) { + + //fiter out any non-local or internal ports + if (! connectionManager.isLocal(p.getNode()) || topologyManager.isInternal(p)) { continue; } ARP arp = new ARP(); byte[] senderIP = subnet.getNetworkAddress().getAddress(); - byte[] targetIPB = targetIP.getAddress(); + byte[] targetIPByte = targetIP.getAddress(); arp.setHardwareType(ARP.HW_TYPE_ETHERNET) .setProtocolType(EtherTypes.IPv4.shortValue()) .setHardwareAddressLength((byte) 6) @@ -334,14 +357,13 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { .setOpCode(ARP.REQUEST) .setSenderHardwareAddress(getControllerMAC()) .setSenderProtocolAddress(senderIP) - .setTargetHardwareAddress(new byte[] { (byte) 0, (byte) 0, - (byte) 0, (byte) 0, - (byte) 0, (byte) 0 }) - .setTargetProtocolAddress(targetIPB); + .setTargetHardwareAddress( + new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0 }) + .setTargetProtocolAddress(targetIPByte); Ethernet ethernet = new Ethernet(); ethernet.setSourceMACAddress(getControllerMAC()) - .setDestinationMACAddress(new byte[] { (byte) -1, + .setDestinationMACAddress(new byte[] {(byte) -1, (byte) -1, (byte) -1, (byte) -1, @@ -365,19 +387,10 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { * The sender MAC is the controller's MAC */ protected void sendUcastARPRequest(HostNodeConnector host, Subnet subnet) { - //Long swID = host.getnodeconnectornodeId(); - //Short portID = host.getnodeconnectorportId(); - //Node n = NodeCreator.createOFNode(swID); - Node n = host.getnodeconnectorNode(); - if (n == null) { - logger.error("cannot send UcastARP because cannot extract node " - + "from HostNodeConnector: {}", host); - return; - } + NodeConnector outPort = host.getnodeConnector(); if (outPort == null) { - logger.error("cannot send UcastARP because cannot extract " - + "outPort from HostNodeConnector: {}", host); + log.error("Failed sending UcastARP because cannot extract output port from Host: {}", host); return; } @@ -385,18 +398,21 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { byte[] targetIP = host.getNetworkAddress().getAddress(); byte[] targetMAC = host.getDataLayerAddressBytes(); ARP arp = new ARP(); - arp.setHardwareType(ARP.HW_TYPE_ETHERNET).setProtocolType( - EtherTypes.IPv4.shortValue()) - .setHardwareAddressLength((byte) 6).setProtocolAddressLength( - (byte) 4).setOpCode(ARP.REQUEST) - .setSenderHardwareAddress(getControllerMAC()) - .setSenderProtocolAddress(senderIP).setTargetHardwareAddress( - targetMAC).setTargetProtocolAddress(targetIP); + arp.setHardwareType(ARP.HW_TYPE_ETHERNET) + .setProtocolType(EtherTypes.IPv4.shortValue()) + .setHardwareAddressLength((byte) 6) + .setProtocolAddressLength((byte) 4) + .setOpCode(ARP.REQUEST) + .setSenderHardwareAddress(getControllerMAC()) + .setSenderProtocolAddress(senderIP) + .setTargetHardwareAddress(targetMAC) + .setTargetProtocolAddress(targetIP); Ethernet ethernet = new Ethernet(); ethernet.setSourceMACAddress(getControllerMAC()) - .setDestinationMACAddress(targetMAC).setEtherType( - EtherTypes.ARP.shortValue()).setPayload(arp); + .setDestinationMACAddress(targetMAC) + .setEtherType(EtherTypes.ARP.shortValue()) + .setPayload(arp); RawPacket destPkt = this.dataPacketService.encodeDataPacket(ethernet); destPkt.setOutgoingNodeConnector(outPort); @@ -405,27 +421,26 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { } public void find(InetAddress networkAddress) { - logger.debug("Received find IP {}", networkAddress); + log.trace("Received find IP {}", networkAddress); Subnet subnet = null; if (switchManager != null) { subnet = switchManager.getSubnetByNetworkAddress(networkAddress); } if (subnet == null) { - logger.debug("can't find subnet matching IP {}", networkAddress); + log.debug("Can't find subnet matching IP {}", networkAddress); return; } - logger.debug("found subnet {}", subnet); - // send a broadcast ARP Request to this interface - sendBcastARPRequest(networkAddress, subnet); + // send a broadcast ARP Request to this IP + arpRequestReplyEvent.put(new ARPRequest(networkAddress, subnet), false); } /* * Probe the host by sending a unicast ARP Request to the host */ public void probe(HostNodeConnector host) { - logger.debug("Received probe host {}", host); + log.trace("Received probe host {}", host); Subnet subnet = null; if (switchManager != null) { @@ -433,11 +448,17 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { .getNetworkAddress()); } if (subnet == null) { - logger.debug("can't find subnet matching {}", host - .getNetworkAddress()); + log.debug("can't find subnet matching {}", host.getNetworkAddress()); return; } - sendUcastARPRequest(host, subnet); + + if (connectionManager.isLocal(host.getnodeconnectorNode())){ + log.trace("Send a ucast ARP req. to: {}", host); + sendUcastARPRequest(host, subnet); + } else { + log.trace("Raise a ucast ARP req. event to: {}", host); + arpRequestReplyEvent.put(new ARPRequest(host, subnet), false); + } } /* @@ -446,12 +467,10 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { * Need to discover it by sending a Broadcast ARP Request */ protected void handlePuntedIPPacket(IPv4 pkt, NodeConnector p) { - InetAddress dIP = null; - try { - dIP = InetAddress.getByAddress(NetUtils.intToByteArray4(pkt - .getDestinationAddress())); - } catch (UnknownHostException e1) { - return; + + InetAddress dIP = NetUtils.getInetAddress(pkt.getDestinationAddress()); + if (dIP == null) { + return; } Subnet subnet = null; @@ -459,14 +478,14 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { subnet = switchManager.getSubnetByNetworkAddress(dIP); } if (subnet == null) { - logger.debug("can't find subnet matching {}, drop packet", dIP); + log.debug("Can't find subnet matching {}, drop packet", dIP); return; } - logger.debug("Found {} matching {}", subnet, dIP); + log.trace("Punted IP pkt from {}, sending bcast ARP event...", dIP); /* - * unknown destination host, initiate ARP request + * unknown destination host, initiate bcast ARP request */ - sendBcastARPRequest(dIP, subnet); + arpRequestReplyEvent.put(new ARPRequest(dIP, subnet), false); return; } @@ -485,8 +504,50 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { void init() { arpRequestors = new ConcurrentHashMap>(); countDownTimers = new ConcurrentHashMap(); + + allocateCaches(); + retrieveCaches(); + } + + @SuppressWarnings({ "unchecked", "deprecation" }) + private void retrieveCaches() { + ConcurrentMap map; + + if (this.clusterContainerService == null){ + log.error("Cluster service unavailable, can't retieve ARPHandler caches!"); + return; + } + + map = clusterContainerService.getCache(ARP_EVENT_CACHE_NAME); + if (map != null){ + this.arpRequestReplyEvent = (ConcurrentMap) map; + } else { + log.error("Cache allocation failed for {}", ARP_EVENT_CACHE_NAME); + } } + @SuppressWarnings("deprecation") + private void allocateCaches() { + if (clusterContainerService == null){ + nonClusterObjectCreate(); + log.error("Clustering service unavailable. Allocated non-cluster caches for ARPHandler."); + return; + } + + try{ + clusterContainerService.createCache(ARP_EVENT_CACHE_NAME, + EnumSet.of(IClusterServices.cacheMode.NON_TRANSACTIONAL)); + } catch (CacheConfigException e){ + log.error("ARPHandler cache configuration invalid!"); + } catch (CacheExistException e){ + log.debug("ARPHandler cache exists, skipped allocation."); + } + + } + + private void nonClusterObjectCreate(){ + arpRequestReplyEvent = new ConcurrentHashMap(); + } /** * Function called by the dependency manager when at least one * dependency become unsatisfied or when the component is shutting @@ -512,18 +573,21 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { * followed by a "destroy ()" calls * */ - void stop() { + void stop(){ + } + + void stopping() { cancelPeriodicTimer(); } void setSwitchManager(ISwitchManager s) { - logger.debug("SwitchManager set"); + log.debug("SwitchManager service set."); this.switchManager = s; } void unsetSwitchManager(ISwitchManager s) { if (this.switchManager == s) { - logger.debug("SwitchManager removed!"); + log.debug("SwitchManager service UNset."); this.switchManager = null; } } @@ -533,21 +597,17 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { if (inPkt == null) { return PacketResult.IGNORED; } - logger - .trace("Received a frame of size: {}", - inPkt.getPacketData().length); + log.trace("Received a frame of size: {}", inPkt.getPacketData().length); Packet formattedPak = this.dataPacketService.decodeDataPacket(inPkt); if (formattedPak instanceof Ethernet) { Object nextPak = formattedPak.getPayload(); if (nextPak instanceof IPv4) { - handlePuntedIPPacket((IPv4) nextPak, inPkt - .getIncomingNodeConnector()); - logger.trace("Handled IP packet"); - } - if (nextPak instanceof ARP) { + log.trace("Handle IP packet: {}", formattedPak); + handlePuntedIPPacket((IPv4) nextPak, inPkt.getIncomingNodeConnector()); + } else if (nextPak instanceof ARP) { + log.trace("Handle ARP packet: {}", formattedPak); handleARPPacket((Ethernet) formattedPak, (ARP) nextPak, inPkt .getIncomingNodeConnector()); - logger.trace("Handled ARP packet"); } } return PacketResult.IGNORED; @@ -556,6 +616,7 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { private void startPeriodicTimer() { this.periodicTimer = new Timer("ArpHandler Periodic Timer"); this.periodicTimer.scheduleAtFixedRate(new TimerTask() { + @SuppressWarnings("deprecation") @Override public void run() { Set targetIPs = countDownTimers.keySet(); @@ -569,12 +630,21 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { countDownTimers.replace(t, tick); } } - for (InetAddress t : expiredTargets) { - countDownTimers.remove(t); - // remove the requestor(s) who have been waited for the ARP + for (InetAddress tIP : expiredTargets) { + countDownTimers.remove(tIP); + // Remove the requestor(s) who have been waiting for the ARP // reply from this target for more than 1sec - arpRequestors.remove(t); - logger.debug("{} didn't respond to ARP request", t); + arpRequestors.remove(tIP); + log.debug("ARP reply was not received from {}", tIP); + } + + // Clean up ARP event cache + try { + if (clusterContainerService.amICoordinator() && ! arpRequestReplyEvent.isEmpty()){ + arpRequestReplyEvent.clear(); + } + } catch (Exception e){ + log.warn("ARPHandler: A cluster member failed to clear event cache."); } } }, 0, 1000); @@ -593,13 +663,63 @@ public class ArpHandler implements IHostFinder, IListenDataPacket { } countDownTimers.remove(sourceIP); for (HostNodeConnector host : hosts) { - logger.debug( - "Sending ARP Reply with src {}/{}, target {}/{}", - new Object[] { sourceMAC, sourceIP, - host.getDataLayerAddressBytes(), - host.getNetworkAddress() }); - sendARPReply(host.getnodeConnector(), sourceMAC, sourceIP, - host.getDataLayerAddressBytes(), host.getNetworkAddress()); + log.trace("Sending ARP Reply with src {}/{}, target {}/{}", + new Object[] { sourceMAC, sourceIP, host.getDataLayerAddressBytes(), host.getNetworkAddress() }); + + if (connectionManager.isLocal(host.getnodeconnectorNode())){ + sendARPReply(host.getnodeConnector(), + sourceMAC, + sourceIP, + host.getDataLayerAddressBytes(), + host.getNetworkAddress()); + } else { + arpRequestReplyEvent.put( + new ARPReply( + host.getnodeConnector(), + sourceIP, + sourceMAC, + host.getNetworkAddress(), + host.getDataLayerAddressBytes()), false); + } + } + } + + + @Override + public void entryUpdated(ARPEvent key, Boolean new_value, String cacheName, boolean originLocal) { + if (key instanceof ARPRequest) { + ARPRequest req = (ARPRequest) key; + // If broadcast request + if (req.getHost() == null) { + sendBcastARPRequest(req.getTargetIP(), req.getSubnet()); + + //If unicast and local, send reply + } else if (connectionManager.isLocal(req.getHost().getnodeconnectorNode())) { + sendUcastARPRequest(req.getHost(), req.getSubnet()); + } + } else if (key instanceof ARPReply) { + ARPReply rep = (ARPReply) key; + // New reply received by controller, notify all awaiting requestors across the cluster + if (new_value) { + generateAndSendReply(rep.getTargetIP(), rep.getTargetMac()); + + // Otherwise, a specific reply. If local, send out. + } else if (connectionManager.isLocal(rep.getPort().getNode())) { + sendARPReply(rep.getPort(), + rep.getSourceMac(), + rep.getSourceIP(), + rep.getTargetMac(), + rep.getTargetIP()); + } } } + + @Override + public void entryCreated(ARPEvent key, String cacheName, boolean originLocal) { + // nothing to do + } + @Override + public void entryDeleted(ARPEvent key, String cacheName, boolean originLocal) { + // nothing to do + } } diff --git a/opendaylight/hosttracker/implementation/pom.xml b/opendaylight/hosttracker/implementation/pom.xml index 5507e46035..b5383842f0 100644 --- a/opendaylight/hosttracker/implementation/pom.xml +++ b/opendaylight/hosttracker/implementation/pom.xml @@ -95,6 +95,26 @@ + + org.opendaylight.controller + connectionmanager + 0.1.0-SNAPSHOT + + + org.opendaylight.controller + connectionmanager.implementation + 0.1.0-SNAPSHOT + + + org.opendaylight.controller + sal.connection + 0.1.0-SNAPSHOT + + + org.opendaylight.controller + sal.connection.implementation + 0.1.0-SNAPSHOT + org.opendaylight.controller topologymanager diff --git a/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java b/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java index aa1c8592d9..0f07ff60af 100644 --- a/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java +++ b/opendaylight/hosttracker/implementation/src/main/java/org/opendaylight/controller/hosttracker/internal/HostTracker.java @@ -147,7 +147,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw * * We can't recover from condition 3 above */ - private final ArrayList failedARPReqList = new ArrayList(); + private final List failedARPReqList = new ArrayList(); public HostTracker() { } @@ -821,7 +821,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw } } - private void edgeUpdate(Edge e, UpdateType type, Set props) { + private void debugEdgeUpdate(Edge e, UpdateType type, Set props) { Long srcNid = null; Short srcPort = null; Long dstNid = null; @@ -843,7 +843,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw } if (!srcType.equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) { - logger.error("For now we cannot handle updates for " + "non-openflow nodes"); + logger.debug("For now we cannot handle updates for non-openflow nodes"); return; } @@ -853,7 +853,7 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw } if (!dstType.equals(NodeConnector.NodeConnectorIDType.OPENFLOW)) { - logger.error("For now we cannot handle updates for " + "non-openflow nodes"); + logger.debug("For now we cannot handle updates for non-openflow nodes"); return; } @@ -881,11 +881,14 @@ public class HostTracker implements IfIptoHost, IfHostListener, ISwitchManagerAw @Override public void edgeUpdate(List topoedgeupdateList) { - for (int i = 0; i < topoedgeupdateList.size(); i++) { - Edge e = topoedgeupdateList.get(i).getEdge(); - Set p = topoedgeupdateList.get(i).getProperty(); - UpdateType type = topoedgeupdateList.get(i).getUpdateType(); - edgeUpdate(e, type, p); + if (logger.isDebugEnabled()) { + for (TopoEdgeUpdate topoEdgeUpdate : topoedgeupdateList) { + Edge e = topoEdgeUpdate.getEdge(); + Set p = topoEdgeUpdate.getProperty(); + UpdateType type = topoEdgeUpdate.getUpdateType(); + + debugEdgeUpdate(e, type, p); + } } } diff --git a/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java b/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java index 04219502f6..b5e66296c1 100644 --- a/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java +++ b/opendaylight/hosttracker/integrationtest/src/test/java/org/opendaylight/controller/hosttracker/internal/HostTrackerIT.java @@ -1,323 +1,265 @@ -/* - * Copyright (c) 2013 Cisco Systems, 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.controller.hosttracker.internal; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.Map.Entry; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.osgi.framework.ServiceReference; -import org.osgi.framework.Bundle; -import javax.inject.Inject; - -import org.eclipse.osgi.framework.console.CommandProvider; -import org.junit.Assert; -import org.junit.Test; -import org.junit.Before; -import org.junit.After; -import org.junit.runner.RunWith; -import org.opendaylight.controller.sal.core.Node; -import org.opendaylight.controller.sal.core.NodeConnector; -import org.opendaylight.controller.sal.core.UpdateType; -import org.opendaylight.controller.sal.utils.NodeConnectorCreator; -import org.opendaylight.controller.sal.utils.NodeCreator; -import org.opendaylight.controller.sal.utils.Status; -//import org.opendaylight.controller.hosttracker.*; -import org.opendaylight.controller.hosttracker.IfIptoHost; -import org.opendaylight.controller.hosttracker.IfHostListener; -import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; -import org.opendaylight.controller.switchmanager.IInventoryListener; -import org.opendaylight.controller.switchmanager.ISwitchManager; -import org.opendaylight.controller.switchmanager.ISwitchManagerAware; -import org.opendaylight.controller.topologymanager.ITopologyManagerAware; - -import org.ops4j.pax.exam.junit.PaxExam; -import org.ops4j.pax.exam.util.Filter; -import org.osgi.framework.BundleContext; -import static org.junit.Assert.*; -import org.ops4j.pax.exam.junit.Configuration; -import static org.ops4j.pax.exam.CoreOptions.*; - -import org.ops4j.pax.exam.Option; -import org.ops4j.pax.exam.util.PathUtils; -import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy; -import org.ops4j.pax.exam.spi.reactors.PerClass; - -@RunWith(PaxExam.class) -public class HostTrackerIT { - private Logger log = LoggerFactory.getLogger(HostTrackerIT.class); - // get the OSGI bundle context - @Inject - private BundleContext bc; - - private IfIptoHost hosttracker = null; - private ISwitchManagerAware switchManagerAware = null; - private IInventoryListener invtoryListener = null; - private IfHostListener hostListener = null; - private ITopologyManagerAware topologyManagerAware = null; - - // Configure the OSGi container - @Configuration - public Option[] config() { - return options( - // - systemProperty("logback.configurationFile").value( - "file:" + PathUtils.getBaseDir() - + "/src/test/resources/logback.xml"), - // To start OSGi console for inspection remotely - systemProperty("osgi.console").value("2401"), - // Set the systemPackages (used by clustering) - systemPackages("sun.reflect", "sun.reflect.misc", "sun.misc"), - // List framework bundles - mavenBundle("equinoxSDK381", "org.eclipse.equinox.console", - "1.0.0.v20120522-1841"), - mavenBundle("equinoxSDK381", "org.eclipse.equinox.util", - "1.0.400.v20120522-2049"), - mavenBundle("equinoxSDK381", "org.eclipse.osgi.services", - "3.3.100.v20120522-1822"), - mavenBundle("equinoxSDK381", "org.eclipse.equinox.ds", - "1.4.0.v20120522-1841"), - mavenBundle("equinoxSDK381", "org.apache.felix.gogo.command", - "0.8.0.v201108120515"), - mavenBundle("equinoxSDK381", "org.apache.felix.gogo.runtime", - "0.8.0.v201108120515"), - mavenBundle("equinoxSDK381", "org.apache.felix.gogo.shell", - "0.8.0.v201110170705"), - // List logger bundles - mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), - mavenBundle("org.slf4j", "log4j-over-slf4j") - .versionAsInProject(), - mavenBundle("ch.qos.logback", "logback-core") - .versionAsInProject(), - mavenBundle("ch.qos.logback", "logback-classic") - .versionAsInProject(), - - // List all the bundles on which the test case depends - mavenBundle("org.opendaylight.controller", "sal") - .versionAsInProject(), - mavenBundle("org.opendaylight.controller", "sal.implementation") - .versionAsInProject(), - - // needed by statisticsmanager - mavenBundle("org.opendaylight.controller", "containermanager") - .versionAsInProject(), - mavenBundle("org.opendaylight.controller", - "containermanager.implementation").versionAsInProject(), - - mavenBundle("org.opendaylight.controller", - "clustering.services").versionAsInProject(), - mavenBundle("org.opendaylight.controller", "clustering.stub") - .versionAsInProject(), - - // needed by forwardingrulesmanager - mavenBundle("org.opendaylight.controller", "switchmanager") - .versionAsInProject(), - mavenBundle("org.opendaylight.controller", - "switchmanager.implementation").versionAsInProject(), - mavenBundle("org.opendaylight.controller", "configuration") - .versionAsInProject(), - mavenBundle("org.opendaylight.controller", - "configuration.implementation").versionAsInProject(), - mavenBundle("org.opendaylight.controller", "hosttracker") - .versionAsInProject(), - mavenBundle("org.opendaylight.controller", - "hosttracker.implementation").versionAsInProject(), - - // needed by hosttracker - mavenBundle("org.opendaylight.controller", "topologymanager") - .versionAsInProject(), - mavenBundle("org.opendaylight.controller", "arphandler") - .versionAsInProject(), - - mavenBundle("org.jboss.spec.javax.transaction", - "jboss-transaction-api_1.1_spec").versionAsInProject(), - mavenBundle("org.apache.commons", "commons-lang3") - .versionAsInProject(), - mavenBundle("org.apache.felix", - "org.apache.felix.dependencymanager") - .versionAsInProject(), junitBundles()); - } - - private String stateToString(int state) { - switch (state) { - case Bundle.ACTIVE: - return "ACTIVE"; - case Bundle.INSTALLED: - return "INSTALLED"; - case Bundle.RESOLVED: - return "RESOLVED"; - case Bundle.UNINSTALLED: - return "UNINSTALLED"; - default: - return "Not CONVERTED"; - } - } - - @Before - public void areWeReady() { - assertNotNull(bc); - boolean debugit = false; - Bundle b[] = bc.getBundles(); - for (int i = 0; i < b.length; i++) { - int state = b[i].getState(); - if (state != Bundle.ACTIVE && state != Bundle.RESOLVED) { - log.debug("Bundle:" + b[i].getSymbolicName() + " state:" - + stateToString(state)); - debugit = true; - } - } - if (debugit) { - log.debug("Do some debugging because some bundle is " - + "unresolved"); - } - - // Assert if true, if false we are good to go! - assertFalse(debugit); - - // Now lets create a hosttracker for testing purpose - ServiceReference s = bc.getServiceReference(IfIptoHost.class.getName()); - if (s != null) { - this.hosttracker = (IfIptoHost) bc.getService(s); - this.switchManagerAware = (ISwitchManagerAware) this.hosttracker; - this.invtoryListener = (IInventoryListener) this.hosttracker; - this.hostListener = (IfHostListener) this.hosttracker; - this.topologyManagerAware = (ITopologyManagerAware) this.hosttracker; - } - - // If StatisticsManager is null, cannot run tests. - assertNotNull(this.hosttracker); - } - - @Test - public void testStaticHost() throws UnknownHostException { - String ip; - - assertNotNull(this.hosttracker); - - // create one node and two node connectors - Node node1 = NodeCreator.createOFNode(1L); - NodeConnector nc1_1 = NodeConnectorCreator.createOFNodeConnector( - (short) 1, node1); - NodeConnector nc1_2 = NodeConnectorCreator.createOFNodeConnector( - (short) 2, node1); - - // test addStaticHost(), store into inactive host DB - Status st = this.hosttracker.addStaticHost("192.168.0.8", - "11:22:33:44:55:66", nc1_1, "0"); - Assert.assertTrue(st.isSuccess()); - st = this.hosttracker.addStaticHost("192.168.0.13", - "11:22:33:44:55:77", nc1_2, "0"); - Assert.assertTrue(st.isSuccess()); - - // check inactive DB - Iterator hnci = this.hosttracker - .getInactiveStaticHosts().iterator(); - while (hnci.hasNext()) { - ip = hnci.next().getNetworkAddressAsString(); - Assert.assertTrue(ip.equals("192.168.0.8") - || ip.equals("192.168.0.13")); - } - - // check active host DB - hnci = this.hosttracker.getActiveStaticHosts().iterator(); - Assert.assertFalse(hnci.hasNext()); - - // test removeStaticHost() - st = this.hosttracker.removeStaticHost("192.168.0.8"); - Assert.assertTrue(st.isSuccess()); - - hnci = this.hosttracker.getInactiveStaticHosts().iterator(); - while (hnci.hasNext()) { - ip = hnci.next().getNetworkAddressAsString(); - Assert.assertTrue(ip.equals("192.168.0.13")); - } - } - - @Test - public void testNotifyNodeConnector() throws UnknownHostException { - String ip; - - assertNotNull(this.invtoryListener); - - // create one node and two node connectors - Node node1 = NodeCreator.createOFNode(1L); - NodeConnector nc1_1 = NodeConnectorCreator.createOFNodeConnector( - (short) 1, node1); - NodeConnector nc1_2 = NodeConnectorCreator.createOFNodeConnector( - (short) 2, node1); - - // test addStaticHost(), put into inactive host DB if not verifiable - Status st = this.hosttracker.addStaticHost("192.168.0.8", - "11:22:33:44:55:66", nc1_1, "0"); - st = this.hosttracker.addStaticHost("192.168.0.13", - "11:22:33:44:55:77", nc1_2, "0"); - - this.invtoryListener.notifyNodeConnector(nc1_1, UpdateType.ADDED, null); - - // check all host list - Iterator hnci = this.hosttracker.getAllHosts() - .iterator(); - while (hnci.hasNext()) { - ip = hnci.next().getNetworkAddressAsString(); - Assert.assertTrue(ip.equals("192.168.0.8")); - } - - // check active host DB - hnci = this.hosttracker.getActiveStaticHosts().iterator(); - while (hnci.hasNext()) { - ip = hnci.next().getNetworkAddressAsString(); - Assert.assertTrue(ip.equals("192.168.0.8")); - } - - // check inactive host DB - hnci = this.hosttracker.getInactiveStaticHosts().iterator(); - while (hnci.hasNext()) { - ip = hnci.next().getNetworkAddressAsString(); - Assert.assertTrue(ip.equals("192.168.0.13")); - } - } - - @Test - public void testHostFind() throws UnknownHostException { - - assertNotNull(this.invtoryListener); - - // create one node and two node connectors - Node node1 = NodeCreator.createOFNode(1L); - NodeConnector nc1_1 = NodeConnectorCreator.createOFNodeConnector( - (short) 1, node1); - NodeConnector nc1_2 = NodeConnectorCreator.createOFNodeConnector( - (short) 2, node1); - - // test addStaticHost(), put into inactive host DB if not verifiable - Status st = this.hosttracker.addStaticHost("192.168.0.8", - "11:22:33:44:55:66", nc1_1, "0"); - st = this.hosttracker.addStaticHost("192.168.0.13", - "11:22:33:44:55:77", nc1_2, "0"); - - HostNodeConnector hnc_1 = this.hosttracker.hostFind(InetAddress - .getByName("192.168.0.8")); - assertNull(hnc_1); - - this.invtoryListener.notifyNodeConnector(nc1_1, UpdateType.ADDED, null); - - hnc_1 = this.hosttracker.hostFind(InetAddress.getByName("192.168.0.8")); - assertNotNull(hnc_1); - - } - -} +/* + * Copyright (c) 2013 Cisco Systems, 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.controller.hosttracker.internal; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.ops4j.pax.exam.CoreOptions.junitBundles; +import static org.ops4j.pax.exam.CoreOptions.mavenBundle; +import static org.ops4j.pax.exam.CoreOptions.options; +import static org.ops4j.pax.exam.CoreOptions.systemPackages; +import static org.ops4j.pax.exam.CoreOptions.systemProperty; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Iterator; + +import javax.inject.Inject; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.opendaylight.controller.hosttracker.IfIptoHost; +import org.opendaylight.controller.hosttracker.hostAware.HostNodeConnector; +import org.opendaylight.controller.sal.core.Node; +import org.opendaylight.controller.sal.core.NodeConnector; +import org.opendaylight.controller.sal.core.UpdateType; +import org.opendaylight.controller.sal.utils.NodeConnectorCreator; +import org.opendaylight.controller.sal.utils.NodeCreator; +import org.opendaylight.controller.sal.utils.Status; +import org.opendaylight.controller.switchmanager.IInventoryListener; +import org.ops4j.pax.exam.Option; +import org.ops4j.pax.exam.junit.Configuration; +import org.ops4j.pax.exam.junit.PaxExam; +import org.ops4j.pax.exam.util.PathUtils; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +//import org.opendaylight.controller.hosttracker.*; + +@RunWith(PaxExam.class) +public class HostTrackerIT { + private Logger log = LoggerFactory.getLogger(HostTrackerIT.class); + // get the OSGI bundle context + @Inject + private BundleContext bc; + + private IfIptoHost hosttracker = null; + private IInventoryListener invtoryListener = null; + // Configure the OSGi container + @Configuration + public Option[] config() { + return options( + + // + systemProperty("logback.configurationFile").value( + "file:" + PathUtils.getBaseDir() + "/src/test/resources/logback.xml"), + // To start OSGi console for inspection remotely + systemProperty("osgi.console").value("2401"), + // Set the systemPackages (used by clustering) + systemPackages("sun.reflect", "sun.reflect.misc", "sun.misc"), + // List framework bundles + mavenBundle("equinoxSDK381", "org.eclipse.equinox.console", "1.0.0.v20120522-1841"), + mavenBundle("equinoxSDK381", "org.eclipse.equinox.util", "1.0.400.v20120522-2049"), + mavenBundle("equinoxSDK381", "org.eclipse.osgi.services","3.3.100.v20120522-1822"), + mavenBundle("equinoxSDK381", "org.eclipse.equinox.ds", "1.4.0.v20120522-1841"), + mavenBundle("equinoxSDK381", "org.apache.felix.gogo.command", "0.8.0.v201108120515"), + mavenBundle("equinoxSDK381", "org.apache.felix.gogo.runtime", "0.8.0.v201108120515"), + mavenBundle("equinoxSDK381", "org.apache.felix.gogo.shell", "0.8.0.v201110170705"), + // List logger bundles + mavenBundle("org.slf4j", "slf4j-api").versionAsInProject(), + mavenBundle("org.slf4j", "log4j-over-slf4j").versionAsInProject(), + mavenBundle("ch.qos.logback", "logback-core").versionAsInProject(), + mavenBundle("ch.qos.logback", "logback-classic").versionAsInProject(), + + // List all the bundles on which the test case depends + mavenBundle("org.opendaylight.controller", "sal").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "sal.implementation").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "sal.connection").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "sal.connection.implementation").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "connectionmanager").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "connectionmanager.implementation").versionAsInProject(), + + // needed by statisticsmanager + mavenBundle("org.opendaylight.controller", "containermanager").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "containermanager.implementation").versionAsInProject(), + + mavenBundle("org.opendaylight.controller", "clustering.services").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "clustering.stub").versionAsInProject(), + + // needed by forwardingrulesmanager + mavenBundle("org.opendaylight.controller", "switchmanager").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "switchmanager.implementation").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "configuration").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "configuration.implementation").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "hosttracker").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "hosttracker.implementation").versionAsInProject(), + + // needed by hosttracker + mavenBundle("org.opendaylight.controller", "topologymanager").versionAsInProject(), + mavenBundle("org.opendaylight.controller", "arphandler").versionAsInProject(), + + mavenBundle("org.jboss.spec.javax.transaction", "jboss-transaction-api_1.1_spec").versionAsInProject(), + mavenBundle("org.apache.commons", "commons-lang3").versionAsInProject(), + mavenBundle("org.apache.felix", "org.apache.felix.dependencymanager").versionAsInProject(), + junitBundles()); + } + + private String stateToString(int state) { + switch (state) { + case Bundle.ACTIVE: + return "ACTIVE"; + case Bundle.INSTALLED: + return "INSTALLED"; + case Bundle.RESOLVED: + return "RESOLVED"; + case Bundle.UNINSTALLED: + return "UNINSTALLED"; + default: + return "Not CONVERTED"; + } + } + + @Before + public void areWeReady() { + assertNotNull(bc); + boolean debugit = false; + Bundle b[] = bc.getBundles(); + for (int i = 0; i < b.length; i++) { + int state = b[i].getState(); + if (state != Bundle.ACTIVE && state != Bundle.RESOLVED) { + log.debug("Bundle:" + b[i].getSymbolicName() + " state:" + stateToString(state)); + debugit = true; + } + } + if (debugit) { + log.debug("Do some debugging because some bundle is " + "unresolved"); + } + + // Assert if true, if false we are good to go! + assertFalse(debugit); + + // Now lets create a hosttracker for testing purpose + ServiceReference s = bc.getServiceReference(IfIptoHost.class.getName()); + if (s != null) { + this.hosttracker = (IfIptoHost) bc.getService(s); + this.invtoryListener = (IInventoryListener) this.hosttracker; + } + + // If StatisticsManager is null, cannot run tests. + assertNotNull(this.hosttracker); + } + + @Test + public void testStaticHost() throws UnknownHostException { + String ip; + + assertNotNull(this.hosttracker); + + // create one node and two node connectors + Node node1 = NodeCreator.createOFNode(1L); + NodeConnector nc1_1 = NodeConnectorCreator.createOFNodeConnector((short) 1, node1); + NodeConnector nc1_2 = NodeConnectorCreator.createOFNodeConnector((short) 2, node1); + + // test addStaticHost(), store into inactive host DB + Status st = this.hosttracker.addStaticHost("192.168.0.8", "11:22:33:44:55:66", nc1_1, "0"); + Assert.assertTrue(st.isSuccess()); + st = this.hosttracker.addStaticHost("192.168.0.13", "11:22:33:44:55:77", nc1_2, "0"); + Assert.assertTrue(st.isSuccess()); + + // check inactive DB + Iterator hnci = this.hosttracker.getInactiveStaticHosts().iterator(); + while (hnci.hasNext()) { + ip = hnci.next().getNetworkAddressAsString(); + Assert.assertTrue(ip.equals("192.168.0.8") || ip.equals("192.168.0.13")); + } + + // check active host DB + hnci = this.hosttracker.getActiveStaticHosts().iterator(); + Assert.assertFalse(hnci.hasNext()); + + // test removeStaticHost() + st = this.hosttracker.removeStaticHost("192.168.0.8"); + Assert.assertTrue(st.isSuccess()); + + hnci = this.hosttracker.getInactiveStaticHosts().iterator(); + while (hnci.hasNext()) { + ip = hnci.next().getNetworkAddressAsString(); + Assert.assertTrue(ip.equals("192.168.0.13")); + } + } + + @Test + public void testNotifyNodeConnector() throws UnknownHostException { + String ip; + + assertNotNull(this.invtoryListener); + + // create one node and two node connectors + Node node1 = NodeCreator.createOFNode(1L); + NodeConnector nc1_1 = NodeConnectorCreator.createOFNodeConnector((short) 1, node1); + NodeConnector nc1_2 = NodeConnectorCreator.createOFNodeConnector((short) 2, node1); + + // test addStaticHost(), put into inactive host DB if not verifiable + Status st = this.hosttracker.addStaticHost("192.168.0.8", "11:22:33:44:55:66", nc1_1, "0"); + st = this.hosttracker.addStaticHost("192.168.0.13", "11:22:33:44:55:77", nc1_2, "0"); + + this.invtoryListener.notifyNodeConnector(nc1_1, UpdateType.ADDED, null); + + // check all host list + Iterator hnci = this.hosttracker.getAllHosts().iterator(); + while (hnci.hasNext()) { + ip = hnci.next().getNetworkAddressAsString(); + Assert.assertTrue(ip.equals("192.168.0.8")); + } + + // check active host DB + hnci = this.hosttracker.getActiveStaticHosts().iterator(); + while (hnci.hasNext()) { + ip = hnci.next().getNetworkAddressAsString(); + Assert.assertTrue(ip.equals("192.168.0.8")); + } + + // check inactive host DB + hnci = this.hosttracker.getInactiveStaticHosts().iterator(); + while (hnci.hasNext()) { + ip = hnci.next().getNetworkAddressAsString(); + Assert.assertTrue(ip.equals("192.168.0.13")); + } + } + + @Test + public void testHostFind() throws UnknownHostException { + + assertNotNull(this.invtoryListener); + + // create one node and two node connectors + Node node1 = NodeCreator.createOFNode(1L); + NodeConnector nc1_1 = NodeConnectorCreator.createOFNodeConnector((short) 1, node1); + NodeConnector nc1_2 = NodeConnectorCreator.createOFNodeConnector((short) 2, node1); + + // test addStaticHost(), put into inactive host DB if not verifiable + Status st = this.hosttracker.addStaticHost("192.168.0.8", "11:22:33:44:55:66", nc1_1, "0"); + st = this.hosttracker.addStaticHost("192.168.0.13", "11:22:33:44:55:77", nc1_2, "0"); + + HostNodeConnector hnc_1 = this.hosttracker.hostFind(InetAddress.getByName("192.168.0.8")); + assertNull(hnc_1); + + this.invtoryListener.notifyNodeConnector(nc1_1, UpdateType.ADDED, null); + + hnc_1 = this.hosttracker.hostFind(InetAddress.getByName("192.168.0.8")); + assertNotNull(hnc_1); + + } + +} diff --git a/opendaylight/northbound/integrationtest/pom.xml b/opendaylight/northbound/integrationtest/pom.xml index 5312f8b91f..210001583b 100644 --- a/opendaylight/northbound/integrationtest/pom.xml +++ b/opendaylight/northbound/integrationtest/pom.xml @@ -21,6 +21,16 @@ + + org.opendaylight.controller + connectionmanager + 0.1.0-SNAPSHOT + + + org.opendaylight.controller + connectionmanager.implementation + 0.1.0-SNAPSHOT + org.opendaylight.controller sal @@ -31,6 +41,16 @@ sal.implementation 0.4.0-SNAPSHOT + + org.opendaylight.controller + sal.connection + 0.1.0-SNAPSHOT + + + org.opendaylight.controller + sal.connection.implementation + 0.1.0-SNAPSHOT + org.opendaylight.controller forwarding.staticrouting diff --git a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java index 4c3fa97864..dff17ff086 100644 --- a/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java +++ b/opendaylight/northbound/integrationtest/src/test/java/org/opendaylight/controller/northbound/integrationtest/NorthboundIT.java @@ -1271,7 +1271,11 @@ public class NorthboundIT { mavenBundle("org.opendaylight.controller", "security", "0.4.0-SNAPSHOT").noStart(), mavenBundle("org.opendaylight.controller", "sal", "0.5.0-SNAPSHOT"), mavenBundle("org.opendaylight.controller", "sal.implementation", "0.4.0-SNAPSHOT"), + mavenBundle("org.opendaylight.controller", "sal.connection", "0.1.0-SNAPSHOT"), + mavenBundle("org.opendaylight.controller", "sal.connection.implementation", "0.1.0-SNAPSHOT"), mavenBundle("org.opendaylight.controller", "switchmanager", "0.5.0-SNAPSHOT"), + mavenBundle("org.opendaylight.controller", "connectionmanager", "0.1.0-SNAPSHOT"), + mavenBundle("org.opendaylight.controller", "connectionmanager.implementation", "0.1.0-SNAPSHOT"), mavenBundle("org.opendaylight.controller", "switchmanager.implementation", "0.4.0-SNAPSHOT"), mavenBundle("org.opendaylight.controller", "forwardingrulesmanager", "0.4.0-SNAPSHOT"), mavenBundle("org.opendaylight.controller", "forwardingrulesmanager.implementation", "0.4.0-SNAPSHOT"), diff --git a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java index a3f21cff3e..f81e7e3d03 100644 --- a/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java +++ b/opendaylight/sal/api/src/main/java/org/opendaylight/controller/sal/utils/NetUtils.java @@ -322,6 +322,19 @@ public abstract class NetUtils { return false; } + /** + * Returns true if the MAC address is a unicast MAC address and false + * otherwise. + * + * @param MACAddress + * @return + */ + public static boolean isUnicastMACAddr(byte[] MACAddress) { + if (MACAddress.length == MACAddrLengthInBytes) { + return (MACAddress[0] & 1) == 0; + } + return false; + } /** * Returns true if the MAC address is a multicast MAC address and false