diff --git a/src/main/java/net/floodlightcontroller/core/web/serializers/LinkTupleSerializer.java b/src/main/java/net/floodlightcontroller/core/web/serializers/LinkTupleSerializer.java deleted file mode 100644 index 34e54d5cf26942abf938391b48fa0ef529138499..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/core/web/serializers/LinkTupleSerializer.java +++ /dev/null @@ -1,41 +0,0 @@ -package net.floodlightcontroller.core.web.serializers; - -import java.io.IOException; - -import net.floodlightcontroller.linkdiscovery.LinkTuple; - -import org.codehaus.jackson.JsonGenerator; -import org.codehaus.jackson.JsonProcessingException; -import org.codehaus.jackson.map.JsonSerializer; -import org.codehaus.jackson.map.SerializerProvider; -import org.openflow.util.HexString; - -public class LinkTupleSerializer extends JsonSerializer<LinkTuple> { - - /** - * Serializes the @LinkTuple object. - * @param linkTuple The LinkTuple to serialize - * @param jGen The JSON Generator to use - * @param serializer The Jackson serializer provider - */ - @Override - public void serialize(LinkTuple linkTuple, JsonGenerator jGen, SerializerProvider serializer) throws IOException, JsonProcessingException { - jGen.writeStartObject(); - jGen.writeNumberField("dst-port", linkTuple.getDst().getPort()); - jGen.writeStringField("dst-switch", HexString.toHexString(linkTuple.getDst().getSw().getId())); - jGen.writeNumberField("src-port", linkTuple.getSrc().getPort()); - jGen.writeStringField("src-switch", HexString.toHexString(linkTuple.getSrc().getSw().getId())); - if (linkTuple.getType() != null) { - jGen.writeStringField("type", linkTuple.getType().toString()); - } - jGen.writeEndObject(); - } - - /** - * Tells SimpleModule that we are the serializer for @LinkTuple - */ - @Override - public Class<LinkTuple> handledType() { - return LinkTuple.class; - } -} diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java index 35dc6e084569c12fbe40d022cd3be02e656b5b8b..08f7c50cd221eb577ac1177871f53871cb17a356 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscovery.java @@ -29,25 +29,18 @@ public interface ILinkDiscovery { this.operation = operation; } - public LDUpdate(LinkTuple lt, int srcPortState, - int dstPortState, ILinkDiscovery.LinkType type, UpdateOperation operation) { - this(lt.getSrc().getSw().getId(), lt.getSrc().getPort(), - srcPortState, lt.getDst().getSw().getId(), lt.getDst().getPort(), - dstPortState, type, operation); - } - public LDUpdate(LDUpdate old) { - this.src = old.src; - this.srcPort = old.srcPort; - this.srcPortState = old.srcPortState; - this.dst = old.dst; - this.dstPort = old.dstPort; - this.dstPortState = old.dstPortState; - this.srcType = old.srcType; - this.type = old.type; - this.operation = old.operation; + this.src = old.src; + this.srcPort = old.srcPort; + this.srcPortState = old.srcPortState; + this.dst = old.dst; + this.dstPort = old.dstPort; + this.dstPortState = old.dstPortState; + this.srcType = old.srcType; + this.type = old.type; + this.operation = old.operation; } - + // For updtedSwitch(sw) public LDUpdate(long switchId, SwitchType stype) { this.operation = UpdateOperation.SWITCH_UPDATED; diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java index f1142fd31c2b310e1789bf8125f2e9c776ce6289..3e43b6c527ebe786e641d6a891ecf429369ddc0b 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/ILinkDiscoveryService.java @@ -17,78 +17,52 @@ package net.floodlightcontroller.linkdiscovery; -/** - * - * - * @author David Erickson (daviderickson@cs.stanford.edu) - */ - import java.util.Map; import java.util.Set; -import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.module.IFloodlightService; +import net.floodlightcontroller.routing.Link; +import net.floodlightcontroller.topology.NodePortTuple; -/** - * - * - * @author David Erickson (daviderickson@cs.stanford.edu) - */ -public interface ILinkDiscoveryService extends IFloodlightService { - /** - * Get the link that either sources from the port or terminates - * at the port. isSrcPort determines which of the two links is - * returned. - * @param idPort - * @param isSrcPort true for link that sources from idPort. - * @return linkTuple - */ - public LinkInfo getLinkInfo(SwitchPortTuple idPort, boolean isSrcPort); +public interface ILinkDiscoveryService extends IFloodlightService { /** - * Get the link type of the link tuple based on link info. - * @param linkTuple - * @param linkInfo - * @return linkType + * Retrieves a map of all known link connections between OpenFlow switches + * and the associated info (valid time, port states) for the link. */ - public ILinkDiscovery.LinkType getLinkType(LinkTuple lt, LinkInfo info); + public Map<Link, LinkInfo> getLinks(); /** - * Retrieves a map of all known link connections between OpenFlow switches - * and the associated info (valid time, port states) for the link. + * Returns link type of a given link + * @param info * @return */ - public Map<LinkTuple, LinkInfo> getLinks(); - + public ILinkDiscovery.LinkType getLinkType(LinkInfo info); + /** * Returns an unmodifiable map from switch id to a set of all links with it * as an endpoint. */ - public Map<IOFSwitch, Set<LinkTuple>> getSwitchLinks(); - + public Map<Long, Set<Link>> getSwitchLinks(); + /** * Adds a listener to listen for ILinkDiscoveryService messages * @param listener The listener that wants the notifications */ public void addListener(ILinkDiscoveryListener listener); - + /** * Retrieves a set of all switch ports on which lldps are suppressed. - * @return */ - public Set<SwitchPortTuple> getSuppressLLDPsInfo(); - + public Set<NodePortTuple> getSuppressLLDPsInfo(); + /** * Adds a switch port to suppress lldp set - * @param sw - * @param port */ - public void AddToSuppressLLDPs(IOFSwitch sw, short port); - + public void AddToSuppressLLDPs(long sw, short port); + /** * Removes a switch port from suppress lldp set - * @param sw - * @param port */ - public void RemoveFromSuppressLLDPs(IOFSwitch sw, short port); + public void RemoveFromSuppressLLDPs(long sw, short port); } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java index 7c8d7491c9f3a391d869b027ecb4c32e4b79f952..b12be9379d67a4bd06ec523fd43a42db62ff90ca 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkInfo.java @@ -15,24 +15,30 @@ package net.floodlightcontroller.linkdiscovery; +import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType; + import org.openflow.protocol.OFPhysicalPort.OFPortState; public class LinkInfo { - public enum PortBroadcastState { - PBS_BLOCK, - PBS_FORWARD, - }; - - /** - * The term unicastValidTime may be slightly misleading here. - * The standard LLDP destination MAC address that is currently - * used is also a multicast address, however since this address - * is specified in the standard, we expect all switches to - * absorb this packet, thus making the standard LLDP packet - * traverse only one link. - */ - protected Long unicastValidTime; - protected Long multicastValidTime; + + public LinkInfo(Long firstSeenTime, + Long lastLldpReceivedTime, + Long lastBddpReceivedTime, + int srcPortState, + int dstPortState) { + super(); + this.srcPortState = srcPortState; + this.dstPortState = dstPortState; + this.firstSeenTime = firstSeenTime; + this.lastLldpReceivedTime = lastLldpReceivedTime; + this.lastBddpReceivedTime = lastBddpReceivedTime; + } + + protected Integer srcPortState; + protected Integer dstPortState; + protected Long firstSeenTime; + protected Long lastLldpReceivedTime; /* Standard LLLDP received time */ + protected Long lastBddpReceivedTime; /* Modified LLDP received time */ /** The port states stored here are topology's last knowledge of * the state of the port. This mostly mirrors the state @@ -43,38 +49,36 @@ public class LinkInfo { * it can determine if the port state has changed and therefore * requires the new state to be written to storage. */ - protected Integer srcPortState; - protected Integer dstPortState; - public LinkInfo(Long unicastValidTime, - Long broadcastValidTime, - Integer srcPortState, - Integer dstPortState) { - this.unicastValidTime = unicastValidTime; - this.multicastValidTime = broadcastValidTime; - this.srcPortState = srcPortState; - this.dstPortState = dstPortState; - } + public boolean linkStpBlocked() { return ((srcPortState & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue()) || ((dstPortState & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue()); } + public Long getFirstSeenTime() { + return firstSeenTime; + } + + public void setFirstSeenTime(Long firstSeenTime) { + this.firstSeenTime = firstSeenTime; + } + public Long getUnicastValidTime() { - return unicastValidTime; + return lastLldpReceivedTime; } public void setUnicastValidTime(Long unicastValidTime) { - this.unicastValidTime = unicastValidTime; + this.lastLldpReceivedTime = unicastValidTime; } public Long getMulticastValidTime() { - return multicastValidTime; + return lastBddpReceivedTime; } public void setMulticastValidTime(Long multicastValidTime) { - this.multicastValidTime = multicastValidTime; + this.lastBddpReceivedTime = multicastValidTime; } public Integer getSrcPortState() { @@ -93,6 +97,15 @@ public class LinkInfo { this.dstPortState = dstPortState; } + public LinkType getLinkType() { + if (lastLldpReceivedTime != null) { + return LinkType.DIRECT_LINK; + } else if (lastBddpReceivedTime != null) { + return LinkType.MULTIHOP_LINK; + } + return LinkType.INVALID_LINK; + } + /* (non-Javadoc) * @see java.lang.Object#hashCode() */ @@ -100,9 +113,10 @@ public class LinkInfo { public int hashCode() { final int prime = 5557; int result = 1; - result = prime * result + ((unicastValidTime == null) ? 0 : unicastValidTime.hashCode()); - result = prime * result + ((multicastValidTime == null) ? 0 : multicastValidTime.hashCode()); - result = prime * result + ((srcPortState == null) ? 0 : unicastValidTime.hashCode()); + result = prime * result + ((firstSeenTime == null) ? 0 : firstSeenTime.hashCode()); + result = prime * result + ((lastLldpReceivedTime == null) ? 0 : lastLldpReceivedTime.hashCode()); + result = prime * result + ((lastBddpReceivedTime == null) ? 0 : lastBddpReceivedTime.hashCode()); + result = prime * result + ((srcPortState == null) ? 0 : lastLldpReceivedTime.hashCode()); result = prime * result + ((dstPortState == null) ? 0 : dstPortState.hashCode()); return result; } @@ -120,16 +134,22 @@ public class LinkInfo { return false; LinkInfo other = (LinkInfo) obj; - if (unicastValidTime == null) { - if (other.unicastValidTime != null) + if (firstSeenTime == null) { + if (other.firstSeenTime != null) + return false; + } else if (!firstSeenTime.equals(other.firstSeenTime)) + return false; + + if (lastLldpReceivedTime == null) { + if (other.lastLldpReceivedTime != null) return false; - } else if (!unicastValidTime.equals(other.unicastValidTime)) + } else if (!lastLldpReceivedTime.equals(other.lastLldpReceivedTime)) return false; - if (multicastValidTime == null) { - if (other.multicastValidTime != null) + if (lastBddpReceivedTime == null) { + if (other.lastBddpReceivedTime != null) return false; - } else if (!multicastValidTime.equals(other.multicastValidTime)) + } else if (!lastBddpReceivedTime.equals(other.lastBddpReceivedTime)) return false; if (srcPortState == null) { @@ -147,13 +167,14 @@ public class LinkInfo { return true; } + /* (non-Javadoc) * @see java.lang.Object#toString() */ @Override public String toString() { - return "LinkInfo [unicastValidTime=" + ((unicastValidTime == null) ? "null" : unicastValidTime) - + ", multicastValidTime=" + ((multicastValidTime == null) ? "null" : multicastValidTime) + return "LinkInfo [unicastValidTime=" + ((lastLldpReceivedTime == null) ? "null" : lastLldpReceivedTime) + + ", multicastValidTime=" + ((lastBddpReceivedTime == null) ? "null" : lastBddpReceivedTime) + ", srcPortState=" + ((srcPortState == null) ? "null" : srcPortState) + ", dstPortState=" + ((dstPortState == null) ? "null" : srcPortState) + "]"; diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkTuple.java b/src/main/java/net/floodlightcontroller/linkdiscovery/LinkTuple.java deleted file mode 100755 index 2e2fe9eae56037c1dc26aebebe70fb8c81558cad..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/LinkTuple.java +++ /dev/null @@ -1,150 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ - -package net.floodlightcontroller.linkdiscovery; - -import org.codehaus.jackson.map.annotate.JsonSerialize; - -import net.floodlightcontroller.core.IOFSwitch; -import net.floodlightcontroller.core.web.serializers.LinkTupleSerializer; -import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType; - -/** - * - * - * @author David Erickson (daviderickson@cs.stanford.edu) - */ -@JsonSerialize(using=LinkTupleSerializer.class) -public class LinkTuple { - protected SwitchPortTuple src; - protected SwitchPortTuple dst; - protected LinkType type = null; - - /** - * @param src - * @param dst - */ - public LinkTuple(SwitchPortTuple src, SwitchPortTuple dst) { - this.src = src; - this.dst = dst; - } - - public LinkTuple(IOFSwitch src, Short srcPort, IOFSwitch dst, Short dstPort) { - this.src = new SwitchPortTuple(src, srcPort); - this.dst = new SwitchPortTuple(dst, dstPort); - } - - /** - * Convenience constructor, ports are cast to shorts - * @param srcId - * @param srcPort - * @param dstId - * @param dstPort - */ - public LinkTuple(IOFSwitch src, Integer srcPort, IOFSwitch dst, Integer dstPort) { - this(src, srcPort.shortValue(), dst, dstPort.shortValue()); - } - - /** - * Set the LinkType, not done by default used primarily for the REST API - * @param t - */ - public void setType(LinkType t) { - this.type = t; - } - - /** - * Gets the LinkType, used by the JSON serializer for the REST API - * @return the LinkType - */ - public LinkType getType() { - return type; - } - - /** - * @return the src - */ - public SwitchPortTuple getSrc() { - return src; - } - - /** - * @param src the src to set - */ - public void setSrc(SwitchPortTuple src) { - this.src = src; - } - - /** - * @return the dst - */ - public SwitchPortTuple getDst() { - return dst; - } - - /** - * @param dst the dst to set - */ - public void setDst(SwitchPortTuple dst) { - this.dst = dst; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 2221; - int result = 1; - result = prime * result + ((dst == null) ? 0 : dst.hashCode()); - result = prime * result + ((src == null) ? 0 : src.hashCode()); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof LinkTuple)) - return false; - LinkTuple other = (LinkTuple) obj; - if (dst == null) { - if (other.dst != null) - return false; - } else if (!dst.equals(other.dst)) - return false; - if (src == null) { - if (other.src != null) - return false; - } else if (!src.equals(other.src)) - return false; - return true; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "LinkTuple [src=" + src + ",dst=" + dst + "]"; - } -} diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/SwitchPortTuple.java b/src/main/java/net/floodlightcontroller/linkdiscovery/SwitchPortTuple.java deleted file mode 100755 index 31508dfe7beaeb4f418825d3ba8ad040e050d98d..0000000000000000000000000000000000000000 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/SwitchPortTuple.java +++ /dev/null @@ -1,109 +0,0 @@ -/** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ - -/** - * - */ -package net.floodlightcontroller.linkdiscovery; - -import net.floodlightcontroller.core.IOFSwitch; - -/** - * @author David Erickson (daviderickson@cs.stanford.edu) - * - */ -public class SwitchPortTuple { - protected IOFSwitch sw; - protected short port; - - public SwitchPortTuple(IOFSwitch sw, short port) { - super(); - this.sw = sw; - this.port = port; - } - - public SwitchPortTuple(IOFSwitch sw, int port) { - this(sw, (short)port); - } - - /** - * @return the sw - */ - public IOFSwitch getSw() { - return sw; - } - - /** - * Set the switch - */ - public void setSw(IOFSwitch sw) { - this.sw = sw; - } - - /** - * @return the port number - */ - public Short getPort() { - return port; - } - - /* (non-Javadoc) - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - final int prime = 5557; - int result = 1; - result = prime * result + ((sw == null) ? 0 : sw.hashCode()); - result = prime * result + (new Short(port)).hashCode(); - return result; - } - - /* (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof SwitchPortTuple)) - return false; - SwitchPortTuple other = (SwitchPortTuple) obj; - if (sw == null) { - if (other.sw != null) - return false; - } else if (!sw.equals(other.sw)) - return false; - - if (port != other.port) - return false; - - return true; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - return "SwitchPortTuple [id=" - + ((sw == null) ? "null" : sw.getStringId()) - + ", port=" + (0xffff & (int)port) + "]"; - } -} diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java index 2d2c83115f61a4bb01b39b3f59565d674f578d0d..9a3441c0e36e8217c1360528e2ade81e08ca5ba3 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManager.java @@ -1,19 +1,19 @@ /** -* Copyright 2011, Big Switch Networks, Inc. -* Originally created by David Erickson, Stanford University -* -* Licensed under the Apache License, Version 2.0 (the "License"); you may -* not use this file except in compliance with the License. You may obtain -* a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -* License for the specific language governing permissions and limitations -* under the License. -**/ + * Copyright 2011, Big Switch Networks, Inc. + * Originally created by David Erickson, Stanford University + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. You may obtain + * a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + **/ package net.floodlightcontroller.linkdiscovery.internal; @@ -60,19 +60,19 @@ import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.UpdateOperation; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService; import net.floodlightcontroller.linkdiscovery.LinkInfo; -import net.floodlightcontroller.linkdiscovery.LinkTuple; -import net.floodlightcontroller.linkdiscovery.SwitchPortTuple; import net.floodlightcontroller.packet.BDDP; import net.floodlightcontroller.packet.Ethernet; import net.floodlightcontroller.packet.IPv4; import net.floodlightcontroller.packet.LLDP; import net.floodlightcontroller.packet.LLDPTLV; +import net.floodlightcontroller.routing.Link; import net.floodlightcontroller.storage.IResultSet; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.IStorageSourceListener; import net.floodlightcontroller.storage.OperatorPredicate; import net.floodlightcontroller.storage.StorageException; import net.floodlightcontroller.threadpool.IThreadPoolService; +import net.floodlightcontroller.topology.NodePortTuple; import net.floodlightcontroller.util.EventHistory; import net.floodlightcontroller.util.EventHistory.EvAction; @@ -112,13 +112,11 @@ import org.slf4j.LoggerFactory; * -Each LinkTuple will be indexed into switchLinks for both * src.id and dst.id, and portLinks for each src and dst * -The updates queue is only added to from within a held write lock - * - * @author David Erickson (daviderickson@cs.stanford.edu) */ public class LinkDiscoveryManager - implements IOFMessageListener, IOFSwitchListener, - IStorageSourceListener, ILinkDiscoveryService, - IFloodlightModule, IInfoProvider, IHAListener { +implements IOFMessageListener, IOFSwitchListener, +IStorageSourceListener, ILinkDiscoveryService, +IFloodlightModule, IInfoProvider, IHAListener { protected static Logger log = LoggerFactory.getLogger(LinkDiscoveryManager.class); // Names of table/fields for links in the storage API @@ -140,9 +138,9 @@ public class LinkDiscoveryManager protected IStorageSourceService storageSource; protected IThreadPoolService threadPool; - protected SingletonTask sendLLDPTask; + private static final byte[] LLDP_STANDARD_DST_MAC_STRING = - HexString.fromHexString("01:80:c2:00:00:0e"); + HexString.fromHexString("01:80:c2:00:00:0e"); private static final long LINK_LOCAL_MASK = 0xfffffffffff0L; private static final long LINK_LOCAL_VALUE = 0x0180c2000000L; @@ -150,73 +148,121 @@ public class LinkDiscoveryManager // private static final String LLDP_BSN_DST_MAC_STRING = "5d:16:c7:00:00:01"; private static final String LLDP_BSN_DST_MAC_STRING = "ff:ff:ff:ff:ff:ff"; + + // Direction TLVs are used to indicate if the LLDPs were sent + // periodically or in response to a recieved LLDP + private static final byte TLV_DIRECTION_TYPE = 0x73; + private static final short TLV_DIRECTION_LENGTH = 1; // 1 byte + private static final byte TLV_DIRECTION_VALUE_FORWARD[] = {0x01}; + private static final byte TLV_DIRECTION_VALUE_REVERSE[] = {0x02}; + private static final LLDPTLV forwardTLV + = new LLDPTLV(). + setType((byte)TLV_DIRECTION_TYPE). + setLength((short)TLV_DIRECTION_LENGTH). + setValue(TLV_DIRECTION_VALUE_FORWARD); + + private static final LLDPTLV reverseTLV + = new LLDPTLV(). + setType((byte)TLV_DIRECTION_TYPE). + setLength((short)TLV_DIRECTION_LENGTH). + setValue(TLV_DIRECTION_VALUE_REVERSE); + + protected SingletonTask discoveryTask; + protected final int DISCOVERY_TASK_INTERVAL = 1; + protected final int LINK_TIMEOUT = 35; // timeout as part of LLDP process. + protected final int LLDP_TO_ALL_INTERVAL = 15 ; //15 seconds. + protected long lldpClock = 0; + + // This value is intentionally kept higher than LLDP_TO_ALL_INTERVAL. + // If we want to identify link failures faster, we could decrease this + // value to a small number, say 1 or 2 sec. + protected final int LLDP_TO_KNOWN_INTERVAL= 20; // LLDP frequency for known links + + protected LLDPTLV controllerTLV; + protected ReentrantReadWriteLock lock; + int lldpTimeCount = 0; + /** * Map from link to the most recent time it was verified functioning */ - protected Map<LinkTuple, LinkInfo> links; - protected int lldpFrequency = 15 * 1000; // sending frequency - protected int lldpTimeout = 35 * 1000; // timeout - protected LLDPTLV controllerTLV; - protected ReentrantReadWriteLock lock; + protected Map<Link, LinkInfo> links; + + /** + * Map from switch id to a set of all links with it as an endpoint + */ + protected Map<Long, Set<Link>> switchLinks; /** * Map from a id:port to the set of links containing it as an endpoint */ - protected Map<SwitchPortTuple, Set<LinkTuple>> portLinks; - protected Set<SwitchPortTuple> suppressLLDPs; + protected Map<NodePortTuple, Set<Link>> portLinks; + protected Set<NodePortTuple> suppressLLDPs; /** * Set of link tuples over which multicast LLDPs are received * and unicast LLDPs are not received. */ - protected Map<SwitchPortTuple, Set<LinkTuple>> portBroadcastDomainLinks; - - SingletonTask loopDetectTask; + protected Map<NodePortTuple, Set<Link>> portBroadcastDomainLinks; protected volatile boolean shuttingDown = false; - /** - * Map from switch id to a set of all links with it as an endpoint - */ - protected Map<IOFSwitch, Set<LinkTuple>> switchLinks; /* topology aware components are called in the order they were added to the * the array */ protected ArrayList<ILinkDiscoveryListener> linkDiscoveryAware; protected BlockingQueue<LDUpdate> updates; protected Thread updatesThread; + /** + * Get the LLDP sending period in seconds. + * @return LLDP sending period in seconds. + */ public int getLldpFrequency() { - return lldpFrequency; + return LLDP_TO_KNOWN_INTERVAL; } + /** + * Get the LLDP timeout value in seconds + * @return LLDP timeout value in seconds + */ public int getLldpTimeout() { - return lldpTimeout; + return LINK_TIMEOUT; } - public Map<SwitchPortTuple, Set<LinkTuple>> getPortLinks() { + public Map<NodePortTuple, Set<Link>> getPortLinks() { return portLinks; } - - public Set<SwitchPortTuple> getSuppressLLDPsInfo() { - return suppressLLDPs; + + public Set<NodePortTuple> getSuppressLLDPsInfo() { + return suppressLLDPs; } - - public void AddToSuppressLLDPs(IOFSwitch sw, short port) + + /** + * Add a switch port to the suppressed LLDP list. + * Remove any known links on the switch port. + */ + public void AddToSuppressLLDPs(long sw, short port) { - SwitchPortTuple swt = new SwitchPortTuple(sw, port); - this.suppressLLDPs.add(swt); + NodePortTuple npt = new NodePortTuple(sw, port); + this.suppressLLDPs.add(npt); + deleteLinksOnPort(npt, "LLDP suppressed."); } - - public void RemoveFromSuppressLLDPs(IOFSwitch sw, short port) + + /** + * Remove a switch port from the suppressed LLDP list. + * Discover links on that switchport. + */ + public void RemoveFromSuppressLLDPs(long sw, short port) { - this.suppressLLDPs.remove(new SwitchPortTuple(sw, port)); + NodePortTuple npt = new NodePortTuple(sw, port); + this.suppressLLDPs.remove(npt); + discover(npt); } public boolean isShuttingDown() { return shuttingDown; } - - public ILinkDiscovery.LinkType getLinkType(LinkTuple lt, LinkInfo info) { + + public ILinkDiscovery.LinkType getLinkType(Link lt, LinkInfo info) { if (info.getUnicastValidTime() != null) { return ILinkDiscovery.LinkType.DIRECT_LINK; } else if (info.getMulticastValidTime() != null) { @@ -224,61 +270,95 @@ public class LinkDiscoveryManager } return ILinkDiscovery.LinkType.INVALID_LINK; } - - private void doUpdatesThread() throws InterruptedException { - do { - LDUpdate update = updates.take(); - - if (linkDiscoveryAware != null) { - if (log.isTraceEnabled()) { - log.trace("Dispatching link discovery update {} {} {} {} {} for {}", - new Object[]{update.getOperation(), - HexString.toHexString(update.getSrc()), update.getSrcPort(), - HexString.toHexString(update.getDst()), update.getDstPort(), - linkDiscoveryAware}); - } - try { - for (ILinkDiscoveryListener lda : linkDiscoveryAware) { // order maintained - lda.linkDiscoveryUpdate(update); - } - } - catch (Exception e) { - log.error("Error in link discovery updates loop", e); - } - } - } while (updates.peek() != null); + + private boolean isLLDPSuppressed(long sw, short portNumber) { + return this.suppressLLDPs.contains(new NodePortTuple(sw, portNumber)); + } + + protected void discoverLinks() { + + // timeout known links. + timeoutLinks(); + + //increment LLDP clock + lldpClock = (lldpClock + 1)% LLDP_TO_ALL_INTERVAL; + + if (lldpClock == 0) { + discoverOnAllPorts(); + return; + } else { + discoverOnKnownLinkPorts(); + } + } + + /** + * Send LLDP and BDDP on known + */ + protected void discoverOnKnownLinkPorts() { + // Copy the port set. + Set<NodePortTuple> nptSet = new HashSet<NodePortTuple>(); + nptSet.addAll(portLinks.keySet()); + + // Send LLDP from each of them. + for(NodePortTuple npt: nptSet) { + discover(npt); + } + } + + protected void discover(NodePortTuple npt) { + discover(npt.getNodeId(), npt.getPortId()); } - private boolean isLLDPSuppressed(IOFSwitch sw, short portNumber) { - return this.suppressLLDPs.contains(new SwitchPortTuple(sw, portNumber)); + protected void discover(long sw, short port) { + // Send standard lldp first + sendDiscoveryMessage(sw, port, true, false); + // Then send the bddp following that. + sendDiscoveryMessage(sw, port, false, false); } - - protected void sendLLDPs(IOFSwitch sw, OFPhysicalPort port, boolean isStandard) { - - if (isLLDPSuppressed(sw, port.getPortNumber())) { - /* Dont send LLDPs out of this port as suppressLLDPs set - * - */ - return; - } + + /** + * Send link discovery message out of a given switch port. + * The discovery message may be a standard LLDP or a modified + * LLDP, where the dst mac address is set to :ff. + * + * TODO: The modified LLDP will updated in the future and may + * use a different eth-type. + * @param sw + * @param port + * @param isStandard indicates standard or modified LLDP + * @param isReverse indicates whether the LLDP was sent as a response + */ + protected void sendDiscoveryMessage(long sw, short port, + boolean isStandard, + boolean isReverse) { + + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + OFPhysicalPort ofpPort = iofSwitch.getPort(port); + + if (isLLDPSuppressed(sw, port)) { + /* Dont send LLDPs out of this port as suppressLLDPs set + * + */ + return; + } if (log.isTraceEnabled()) { log.trace("Sending LLDP packet out of swich: {}, port: {}", - sw.getStringId(), port.getPortNumber()); + sw, port); } LLDP lldp; Ethernet ethernet; - + if (isStandard) { ethernet = new Ethernet() - .setSourceMACAddress(port.getHardwareAddress()) + .setSourceMACAddress(ofpPort.getHardwareAddress()) .setDestinationMACAddress(LLDP_STANDARD_DST_MAC_STRING) .setEtherType(Ethernet.TYPE_LLDP); lldp = new LLDP(); } else { ethernet = new Ethernet() - .setSourceMACAddress(port.getHardwareAddress()) + .setSourceMACAddress(ofpPort.getHardwareAddress()) .setDestinationMACAddress(LLDP_BSN_DST_MAC_STRING) .setEtherType(Ethernet.TYPE_BDDP); lldp = new BDDP(); @@ -303,13 +383,18 @@ public class LinkDiscoveryManager // Add the controller identifier to the TLV value. lldp.getOptionalTLVList().add(controllerTLV); + if (isReverse) { + lldp.getOptionalTLVList().add(reverseTLV); + }else { + lldp.getOptionalTLVList().add(forwardTLV); + } byte[] dpidArray = new byte[8]; ByteBuffer dpidBB = ByteBuffer.wrap(dpidArray); ByteBuffer portBB = ByteBuffer.wrap(portId, 1, 2); - Long dpid = sw.getId(); + Long dpid = sw; dpidBB.putLong(dpid); // set the ethernet source mac to last 6 bytes of dpid @@ -319,14 +404,14 @@ public class LinkDiscoveryManager // set the optional tlv to the full dpid System.arraycopy(dpidArray, 0, dpidTLVValue, 4, 8); - if (port.getPortNumber() == OFPort.OFPP_LOCAL.getValue()) + if (port == OFPort.OFPP_LOCAL.getValue()) return; // set the portId to the outgoing port - portBB.putShort(port.getPortNumber()); + portBB.putShort(port); if (log.isTraceEnabled()) { - log.trace("Sending LLDP out of interface: {}/{}", - sw.toString(), port.getPortNumber()); + log.trace("Sending LLDP out of interface: {}/{}", + HexString.toHexString(sw), port); } // serialize and wrap in a packet out @@ -337,7 +422,7 @@ public class LinkDiscoveryManager // set actions List<OFAction> actions = new ArrayList<OFAction>(); - actions.add(new OFActionOutput(port.getPortNumber(), (short) 0)); + actions.add(new OFActionOutput(port, (short) 0)); po.setActions(actions); po.setActionsLength((short) OFActionOutput.MINIMUM_LENGTH); @@ -347,48 +432,42 @@ public class LinkDiscoveryManager // send try { - sw.write(po, null); + iofSwitch.write(po, null); + iofSwitch.flush(); } catch (IOException e) { log.error("Failure sending LLDP out port {} on switch {}", - new Object[]{ port.getPortNumber(), sw }, e); + new Object[]{ port, HexString.toHexString(sw) }, e); } } - protected void sendLLDPs(SwitchPortTuple swt, boolean isStandard) { - IOFSwitch sw = swt.getSw(); - - if (sw == null) return; - - OFPhysicalPort port = sw.getPort(swt.getPort()); - if (port != null) { - sendLLDPs(sw, port, isStandard); + /** + * Send LLDPs to all switch-ports + */ + protected void discoverOnAllPorts() { + if (log.isTraceEnabled()) { + log.trace("Sending LLDP packets out of all the enabled ports on switch {}"); } - } - - protected void sendLLDPs(IOFSwitch sw, boolean isStandard) { - - if (sw.getEnabledPorts() != null) { - for (OFPhysicalPort port : sw.getEnabledPorts()) { - sendLLDPs(sw, port, isStandard); + Set<Long> switches = floodlightProvider.getSwitches().keySet(); + // Send standard LLDPs + for (long sw: switches) { + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch.getEnabledPorts() != null) { + for (OFPhysicalPort p : iofSwitch.getEnabledPorts()) { + // sends only forward LLDPs and BDDPs + sendDiscoveryMessage(sw, p.getPortNumber(), true, false); + } } } - sw.flush(); - } - - protected void sendLLDPs() { - if (log.isTraceEnabled()) { - log.trace("Sending LLDP packets out of all the enabled ports on switch {}"); - } - Map<Long, IOFSwitch> switches = floodlightProvider.getSwitches(); - for (Entry<Long, IOFSwitch> entry : switches.entrySet()) { - IOFSwitch sw = entry.getValue(); - sendLLDPs(sw, true); - } - for (Entry<Long, IOFSwitch> entry : switches.entrySet()) { - IOFSwitch sw = entry.getValue(); - sendLLDPs(sw, false); + for (long sw: switches) { + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch.getEnabledPorts() != null) { + for (OFPhysicalPort p : iofSwitch.getEnabledPorts()) { + // sends only forward LLDPs and BDDPs + sendDiscoveryMessage(sw, p.getPortNumber(), false, false); + } + } } } @@ -427,29 +506,37 @@ public class LinkDiscoveryManager this.controllerTLV = new LLDPTLV().setType((byte) 0x0c).setLength((short) controllerTLVValue.length).setValue(controllerTLVValue); } + + @Override public String getName() { return "linkdiscovery"; } - + @Override public Command receive(IOFSwitch sw, OFMessage msg, FloodlightContext cntx) { switch (msg.getType()) { case PACKET_IN: - return this.handlePacketIn(sw, (OFPacketIn) msg, cntx); + return this.handlePacketIn(sw.getId(), (OFPacketIn) msg, cntx); case PORT_STATUS: - return this.handlePortStatus(sw, (OFPortStatus) msg); + return this.handlePortStatus(sw.getId(), (OFPortStatus) msg); } log.error("Received an unexpected message {} from switch {}", msg, sw); return Command.CONTINUE; } - private Command handleLldp(LLDP lldp, IOFSwitch sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) { + private Command handleLldp(LLDP lldp, long sw, OFPacketIn pi, boolean isStandard, FloodlightContext cntx) { // If LLDP is suppressed on this port, ignore received packet as well + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch == null) { + log.warn("LLDP received on switch {} that is not known to controller.", + HexString.toHexString(sw)); + } + if (isLLDPSuppressed(sw, pi.getInPort())) - return Command.STOP; - + return Command.STOP; + // If this is a malformed LLDP, or not from us, exit if (lldp.getPortId() == null || lldp.getPortId().getLength() != 3) return Command.CONTINUE; @@ -457,9 +544,11 @@ public class LinkDiscoveryManager long myId = ByteBuffer.wrap(controllerTLV.getValue()).getLong(); long otherId = 0; boolean myLLDP = false; + Boolean isReverse = null; ByteBuffer portBB = ByteBuffer.wrap(lldp.getPortId().getValue()); portBB.position(1); + Short remotePort = portBB.getShort(); IOFSwitch remoteSwitch = null; @@ -474,6 +563,12 @@ public class LinkDiscoveryManager otherId = ByteBuffer.wrap(lldptlv.getValue()).getLong(); if (myId == otherId) myLLDP = true; + } else if (lldptlv.getType() == TLV_DIRECTION_TYPE && + lldptlv.getLength() == TLV_DIRECTION_LENGTH) { + if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_FORWARD[0]) + isReverse = false; + else if (lldptlv.getValue()[0] == TLV_DIRECTION_VALUE_REVERSE[0]) + isReverse = true; } } @@ -490,7 +585,7 @@ public class LinkDiscoveryManager else if (myId < otherId) { if (log.isTraceEnabled()) { log.trace("Getting BDDP packets from a different controller" + - "and letting it go through normal processing chain."); + "and letting it go through normal processing chain."); } return Command.CONTINUE; } @@ -510,50 +605,69 @@ public class LinkDiscoveryManager log.debug("Ignoring link with disabled source port: switch {} port {}", remoteSwitch, remotePort); return Command.STOP; } - if (suppressLLDPs.contains(new SwitchPortTuple(remoteSwitch, remotePort))) { + if (suppressLLDPs.contains(new NodePortTuple(remoteSwitch.getId(), + remotePort))) { log.debug("Ignoring link with suppressed src port: switch {} port {}", - remoteSwitch, remotePort); - return Command.STOP; + remoteSwitch, remotePort); + return Command.STOP; } - if (!sw.portEnabled(pi.getInPort())) { + if (!iofSwitch.portEnabled(pi.getInPort())) { log.debug("Ignoring link with disabled dest port: switch {} port {}", sw, pi.getInPort()); return Command.STOP; } OFPhysicalPort physicalPort = remoteSwitch.getPort(remotePort); int srcPortState = (physicalPort != null) ? physicalPort.getState() : 0; - physicalPort = sw.getPort(pi.getInPort()); + physicalPort = iofSwitch.getPort(pi.getInPort()); int dstPortState = (physicalPort != null) ? physicalPort.getState() : 0; // Store the time of update to this link, and push it out to routingEngine - LinkTuple lt = new LinkTuple(new SwitchPortTuple(remoteSwitch, remotePort), - new SwitchPortTuple(sw, pi.getInPort())); + Link lt = new Link(remoteSwitch.getId(), remotePort, iofSwitch.getId(), pi.getInPort()); - Integer srcPortStateObj = Integer.valueOf(srcPortState); - Integer dstPortStateObj = Integer.valueOf(dstPortState); - Long unicastValidTime = null; - Long multicastValidTime = null; + Long lastLldpTime = null; + Long lastBddpTime = null; + + Long firstSeenTime = System.currentTimeMillis(); if (isStandard) - unicastValidTime = System.currentTimeMillis(); + lastLldpTime = System.currentTimeMillis(); else - multicastValidTime = System.currentTimeMillis(); + lastBddpTime = System.currentTimeMillis(); LinkInfo newLinkInfo = - new LinkInfo(unicastValidTime, multicastValidTime, srcPortStateObj, dstPortStateObj); + new LinkInfo(firstSeenTime, lastLldpTime, lastBddpTime, + srcPortState, dstPortState); addOrUpdateLink(lt, newLinkInfo); + // Check if reverse link exists. + // If it doesn't exist and if the forward link was seen + // first seen within a small interval, send probe on the + // reverse link. + + newLinkInfo = links.get(lt); + if (newLinkInfo != null && isReverse == false) { + Link reverseLink = new Link(lt.getDst(), lt.getDstPort(), + lt.getSrc(), lt.getSrcPort()); + LinkInfo reverseInfo = links.get(reverseLink); + if (reverseInfo == null) { + // the reverse link does not exist. + if (newLinkInfo.getFirstSeenTime() > System.currentTimeMillis() - LINK_TIMEOUT) { + this.sendDiscoveryMessage(lt.getDst(), lt.getDstPort(), isStandard, true); + } + } + } + // Consume this message return Command.STOP; } - protected Command handlePacketIn(IOFSwitch sw, OFPacketIn pi, + protected Command handlePacketIn(long sw, OFPacketIn pi, FloodlightContext cntx) { Ethernet eth = - IFloodlightProviderService.bcStore.get(cntx, - IFloodlightProviderService.CONTEXT_PI_PAYLOAD); + IFloodlightProviderService.bcStore.get(cntx, + IFloodlightProviderService.CONTEXT_PI_PAYLOAD); if(eth.getEtherType() == Ethernet.TYPE_BDDP) { return handleLldp((LLDP) eth.getPayload(), sw, pi, false, cntx); @@ -564,7 +678,7 @@ public class LinkDiscoveryManager if ((destMac & LINK_LOCAL_MASK) == LINK_LOCAL_VALUE){ if (log.isTraceEnabled()) { log.trace("Ignoring packet addressed to 802.1D/Q " + - "reserved address."); + "reserved address."); } return Command.STOP; } @@ -572,76 +686,97 @@ public class LinkDiscoveryManager return Command.CONTINUE; } - protected void addOrUpdateLink(LinkTuple lt, LinkInfo newLinkInfo) { + @Override + public LinkType getLinkType(LinkInfo info) { + + if (info.getUnicastValidTime() != null) + return LinkType.DIRECT_LINK; + else if (info.getMulticastValidTime() != null) + return LinkType.MULTIHOP_LINK; + + return LinkType.INVALID_LINK; + } + + protected boolean addOrUpdateLink(Link lt, LinkInfo newInfo) { + + NodePortTuple srcNpt, dstNpt; + boolean linkChanged = false; + lock.writeLock().lock(); try { - LinkInfo oldLinkInfo = links.put(lt, newLinkInfo); + // put the new info. if an old info exists, it will be returned. + LinkInfo oldInfo = links.put(lt, newInfo); + if (oldInfo != null && + oldInfo.getFirstSeenTime() < newInfo.getFirstSeenTime()) + newInfo.setFirstSeenTime(oldInfo.getFirstSeenTime()); + if (log.isTraceEnabled()) { - log.trace("addOrUpdateLink: {} {}", - lt, - (newLinkInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast"); + log.trace("addOrUpdateLink: {} {}", + lt, + (newInfo.getMulticastValidTime()!=null) ? "multicast" : "unicast"); } UpdateOperation updateOperation = null; - boolean linkChanged = false; + linkChanged = false; + + srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); + dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort()); - if (oldLinkInfo == null) { + if (oldInfo == null) { // index it by switch source - if (!switchLinks.containsKey(lt.getSrc().getSw())) - switchLinks.put(lt.getSrc().getSw(), - new HashSet<LinkTuple>()); - switchLinks.get(lt.getSrc().getSw()).add(lt); + if (!switchLinks.containsKey(lt.getSrc())) + switchLinks.put(lt.getSrc(), new HashSet<Link>()); + switchLinks.get(lt.getSrc()).add(lt); // index it by switch dest - if (!switchLinks.containsKey(lt.getDst().getSw())) - switchLinks.put(lt.getDst().getSw(), - new HashSet<LinkTuple>()); - switchLinks.get(lt.getDst().getSw()).add(lt); + if (!switchLinks.containsKey(lt.getDst())) + switchLinks.put(lt.getDst(), new HashSet<Link>()); + switchLinks.get(lt.getDst()).add(lt); // index both ends by switch:port if (!portLinks.containsKey(lt.getSrc())) - portLinks.put(lt.getSrc(), new HashSet<LinkTuple>()); - portLinks.get(lt.getSrc()).add(lt); + portLinks.put(srcNpt, new HashSet<Link>()); + portLinks.get(srcNpt).add(lt); if (!portLinks.containsKey(lt.getDst())) - portLinks.put(lt.getDst(), new HashSet<LinkTuple>()); - portLinks.get(lt.getDst()).add(lt); + portLinks.put(dstNpt, new HashSet<Link>()); + portLinks.get(dstNpt).add(lt); // Add to portNOFLinks if the unicast valid time is null - if (newLinkInfo.getUnicastValidTime() == null) + if (newInfo.getUnicastValidTime() == null) addLinkToBroadcastDomain(lt); - writeLink(lt, newLinkInfo); + writeLink(lt, newInfo); updateOperation = UpdateOperation.ADD_OR_UPDATE; linkChanged = true; // Add to event history - evHistTopoLink(lt.getSrc().getSw().getId(), - lt.getDst().getSw().getId(), - lt.getSrc().getPort(), - lt.getDst().getPort(), - newLinkInfo.getSrcPortState(), newLinkInfo.getDstPortState(), - getLinkType(lt, newLinkInfo), - EvAction.LINK_ADDED, "LLDP Recvd"); + evHistTopoLink(lt.getSrc(), + lt.getDst(), + lt.getSrcPort(), + lt.getDstPort(), + newInfo.getSrcPortState(), newInfo.getDstPortState(), + getLinkType(lt, newInfo), + EvAction.LINK_ADDED, "LLDP Recvd"); } else { // Since the link info is already there, we need to // update the right fields. - if (newLinkInfo.getUnicastValidTime() == null) { + if (newInfo.getUnicastValidTime() == null) { // This is due to a multicast LLDP, so copy the old unicast // value. - if (oldLinkInfo.getUnicastValidTime() != null) { - newLinkInfo.setUnicastValidTime(oldLinkInfo.getUnicastValidTime()); + if (oldInfo.getUnicastValidTime() != null) { + newInfo.setUnicastValidTime(oldInfo.getUnicastValidTime()); } - } else if (newLinkInfo.getMulticastValidTime() == null) { + } else if (newInfo.getMulticastValidTime() == null) { // This is due to a unicast LLDP, so copy the old multicast // value. - if (oldLinkInfo.getMulticastValidTime() != null) { - newLinkInfo.setMulticastValidTime(oldLinkInfo.getMulticastValidTime()); + if (oldInfo.getMulticastValidTime() != null) { + newInfo.setMulticastValidTime(oldInfo.getMulticastValidTime()); } } - Long oldTime = oldLinkInfo.getUnicastValidTime(); - Long newTime = newLinkInfo.getUnicastValidTime(); + Long oldTime = oldInfo.getUnicastValidTime(); + Long newTime = newInfo.getUnicastValidTime(); // the link has changed its state between openflow and non-openflow // if the unicastValidTimes are null or not null if (oldTime != null & newTime == null) { @@ -657,14 +792,16 @@ public class LinkDiscoveryManager } // Only update the port states if they've changed - if (newLinkInfo.getSrcPortState().intValue() != oldLinkInfo.getSrcPortState().intValue() || - newLinkInfo.getDstPortState().intValue() != oldLinkInfo.getDstPortState().intValue()) + if (newInfo.getSrcPortState().intValue() != + oldInfo.getSrcPortState().intValue() || + newInfo.getDstPortState().intValue() != + oldInfo.getDstPortState().intValue()) linkChanged = true; // Write changes to storage. This will always write the updated // valid time, plus the port states if they've changed (i.e. if // they weren't set to null in the previous block of code. - writeLink(lt, newLinkInfo); + writeLink(lt, newInfo); if (linkChanged) { updateOperation = UpdateOperation.ADD_OR_UPDATE; @@ -672,26 +809,33 @@ public class LinkDiscoveryManager log.debug("Updated link {}", lt); } // Add to event history - evHistTopoLink(lt.getSrc().getSw().getId(), - lt.getDst().getSw().getId(), - lt.getSrc().getPort(), - lt.getDst().getPort(), - newLinkInfo.getSrcPortState(), newLinkInfo.getDstPortState(), - getLinkType(lt, newLinkInfo), - EvAction.LINK_PORT_STATE_UPDATED, - "LLDP Recvd"); + evHistTopoLink(lt.getSrc(), + lt.getDst(), + lt.getSrcPort(), + lt.getDstPort(), + newInfo.getSrcPortState(), newInfo.getDstPortState(), + getLinkType(lt, newInfo), + EvAction.LINK_PORT_STATE_UPDATED, + "LLDP Recvd"); } } if (linkChanged) { - updates.add(new LDUpdate(lt, newLinkInfo.getSrcPortState(), newLinkInfo.getDstPortState(), getLinkType(lt, newLinkInfo), updateOperation)); + updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(), + newInfo.getSrcPortState(), + lt.getDst(), lt.getDstPort(), + newInfo.getDstPortState(), + getLinkType(lt, newInfo), + updateOperation)); } } finally { lock.writeLock().unlock(); } + + return linkChanged; } - public Map<IOFSwitch, Set<LinkTuple>> getSwitchLinks() { + public Map<Long, Set<Link>> getSwitchLinks() { return this.switchLinks; } @@ -699,37 +843,48 @@ public class LinkDiscoveryManager * Removes links from memory and storage. * @param links The List of @LinkTuple to delete. */ - protected void deleteLinks(List<LinkTuple> links, String reason) { + protected void deleteLinks(List<Link> links, String reason) { + NodePortTuple srcNpt, dstNpt; + lock.writeLock().lock(); try { - for (LinkTuple lt : links) { - this.switchLinks.get(lt.getSrc().getSw()).remove(lt); - this.switchLinks.get(lt.getDst().getSw()).remove(lt); - if (this.switchLinks.containsKey(lt.getSrc().getSw()) && - this.switchLinks.get(lt.getSrc().getSw()).isEmpty()) - this.switchLinks.remove(lt.getSrc().getSw()); - if (this.switchLinks.containsKey(lt.getDst().getSw()) && - this.switchLinks.get(lt.getDst().getSw()).isEmpty()) - this.switchLinks.remove(lt.getDst().getSw()); - - this.portLinks.get(lt.getSrc()).remove(lt); - if (this.portLinks.get(lt.getSrc()).isEmpty()) - this.portLinks.remove(lt.getSrc()); - this.portLinks.get(lt.getDst()).remove(lt); - if (this.portLinks.get(lt.getDst()).isEmpty()) - this.portLinks.remove(lt.getDst()); + for (Link lt : links) { + srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); + dstNpt =new NodePortTuple(lt.getDst(), lt.getDstPort()); + + switchLinks.get(lt.getSrc()).remove(lt); + switchLinks.get(lt.getDst()).remove(lt); + if (switchLinks.containsKey(lt.getSrc()) && + switchLinks.get(lt.getSrc()).isEmpty()) + this.switchLinks.remove(lt.getSrc()); + if (this.switchLinks.containsKey(lt.getDst()) && + this.switchLinks.get(lt.getDst()).isEmpty()) + this.switchLinks.remove(lt.getDst()); + + if (this.portLinks.get(srcNpt) != null) { + this.portLinks.get(srcNpt).remove(lt); + if (this.portLinks.get(srcNpt).isEmpty()) + this.portLinks.remove(srcNpt); + } + if (this.portLinks.get(dstNpt) != null) { + this.portLinks.get(dstNpt).remove(lt); + if (this.portLinks.get(dstNpt).isEmpty()) + this.portLinks.remove(dstNpt); + } this.links.remove(lt); - updates.add(new LDUpdate(lt, 0, 0, null, UpdateOperation.REMOVE)); + updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(), 0, + lt.getDst(), lt.getDstPort(), 0, + null, UpdateOperation.REMOVE)); // Update Event History - evHistTopoLink(lt.getSrc().getSw().getId(), - lt.getDst().getSw().getId(), - lt.getSrc().getPort(), - lt.getDst().getPort(), - 0, 0, // Port states - ILinkDiscovery.LinkType.INVALID_LINK, - EvAction.LINK_DELETED, reason); + evHistTopoLink(lt.getSrc(), + lt.getDst(), + lt.getSrcPort(), + lt.getDstPort(), + 0, 0, // Port states + ILinkDiscovery.LinkType.INVALID_LINK, + EvAction.LINK_DELETED, reason); // remove link from storage. removeLinkFromStorage(lt); @@ -751,19 +906,23 @@ public class LinkDiscoveryManager * @param ps The OFPortStatus message * @return The Command to continue or stop after we process this message */ - protected Command handlePortStatus(IOFSwitch sw, OFPortStatus ps) { + protected Command handlePortStatus(long sw, OFPortStatus ps) { + + IOFSwitch iofSwitch = floodlightProvider.getSwitches().get(sw); + if (iofSwitch == null) return Command.CONTINUE; + if (log.isDebugEnabled()) { log.debug("handlePortStatus: Switch {} port #{} reason {}; " + - "config is {} state is {}", - new Object[] {sw.getStringId(), - ps.getDesc().getPortNumber(), - ps.getReason(), - ps.getDesc().getConfig(), - ps.getDesc().getState()}); + "config is {} state is {}", + new Object[] {iofSwitch.getStringId(), + ps.getDesc().getPortNumber(), + ps.getReason(), + ps.getDesc().getConfig(), + ps.getDesc().getState()}); } - SwitchPortTuple tuple = - new SwitchPortTuple(sw, ps.getDesc().getPortNumber()); + short port = ps.getDesc().getPortNumber(); + NodePortTuple npt = new NodePortTuple(sw, port); boolean linkDeleted = false; boolean linkInfoChanged = false; @@ -772,51 +931,43 @@ public class LinkDiscoveryManager // if ps is a delete, or a modify where the port is down or // configured down if ((byte)OFPortReason.OFPPR_DELETE.ordinal() == ps.getReason() || - ((byte)OFPortReason.OFPPR_MODIFY.ordinal() == - ps.getReason() && !portEnabled(ps.getDesc()))) { - - List<LinkTuple> eraseList = new ArrayList<LinkTuple>(); - if (this.portLinks.containsKey(tuple)) { - if (log.isDebugEnabled()) { - log.debug("handlePortStatus: Switch {} port #{} " + - "reason {}; removing links {}", - new Object[] {HexString.toHexString(sw.getId()), - ps.getDesc().getPortNumber(), - ps.getReason(), - this.portLinks.get(tuple)}); - } - eraseList.addAll(this.portLinks.get(tuple)); - deleteLinks(eraseList, "Port Status Changed"); - linkDeleted = true; - } - } else if (ps.getReason() == - (byte)OFPortReason.OFPPR_MODIFY.ordinal()) { + ((byte)OFPortReason.OFPPR_MODIFY.ordinal() == + ps.getReason() && !portEnabled(ps.getDesc()))) { + deleteLinksOnPort(npt, "Port Status Changed"); + linkDeleted = true; + } + else if (ps.getReason() == + (byte)OFPortReason.OFPPR_MODIFY.ordinal()) { // If ps is a port modification and the port state has changed // that affects links in the topology - if (this.portLinks.containsKey(tuple)) { - for (LinkTuple lt: this.portLinks.get(tuple)) { + if (this.portLinks.containsKey(npt)) { + for (Link lt: this.portLinks.get(npt)) { LinkInfo linkInfo = links.get(lt); assert(linkInfo != null); Integer updatedSrcPortState = null; Integer updatedDstPortState = null; - if (lt.getSrc().equals(tuple) && + if (lt.getSrc() == npt.getNodeId() && + lt.getSrcPort() == npt.getPortId() && (linkInfo.getSrcPortState() != - ps.getDesc().getState())) { + ps.getDesc().getState())) { updatedSrcPortState = ps.getDesc().getState(); linkInfo.setSrcPortState(updatedSrcPortState); } - if (lt.getDst().equals(tuple) && + if (lt.getDst() == npt.getNodeId() && + lt.getDstPort() == npt.getPortId() && (linkInfo.getDstPortState() != - ps.getDesc().getState())) { + ps.getDesc().getState())) { updatedDstPortState = ps.getDesc().getState(); linkInfo.setDstPortState(updatedDstPortState); } if ((updatedSrcPortState != null) || - (updatedDstPortState != null)) { + (updatedDstPortState != null)) { // The link is already known to link discovery // manager and the status has changed, therefore // send an LDUpdate. - updates.add(new LDUpdate(lt, linkInfo.getSrcPortState(), + updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(), + linkInfo.getSrcPortState(), + lt.getDst(), lt.getDstPort(), linkInfo.getDstPortState(), getLinkType(lt, linkInfo), UpdateOperation.ADD_OR_UPDATE)); @@ -831,9 +982,9 @@ public class LinkDiscoveryManager if (log.isDebugEnabled()) { log.debug("handlePortStatus: Switch {} port #{} reason {};"+ " no links to update/remove", - new Object[] {HexString.toHexString(sw.getId()), - ps.getDesc().getPortNumber(), - ps.getReason()}); + new Object[] {HexString.toHexString(sw), + ps.getDesc().getPortNumber(), + ps.getReason()}); } } } finally { @@ -846,7 +997,8 @@ public class LinkDiscoveryManager // to send the LLDPs right away and instead we wait for the LLDPs // to be sent on the timer as it is normally done // do it outside the write-lock - sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS); + // sendLLDPTask.reschedule(1000, TimeUnit.MILLISECONDS); + discover(npt); } return Command.CONTINUE; } @@ -860,62 +1012,87 @@ public class LinkDiscoveryManager // It's probably overkill to send LLDP from all switches, but we don't // know which switches might be connected to the new switch. // Need to optimize when supporting a large number of switches. - sendLLDPTask.reschedule(2000, TimeUnit.MILLISECONDS); + NodePortTuple npt; + + if (sw.getEnabledPorts() != null) { + for (OFPhysicalPort p : sw.getEnabledPorts()) { + npt = new NodePortTuple(sw.getId(), p.getPortNumber()); + discover(npt); + } + } // Update event history evHistTopoSwitch(sw, EvAction.SWITCH_CONNECTED, "None"); } /** - * When a switch disconnects we remove any links from our map and re-compute - * the topology. - * @param sw The IOFSwitch that disconnected from the controller + * When a switch disconnects we remove any links from our map and notify. + * @param The id of the switch */ @Override - public void removedSwitch(IOFSwitch sw) { + public void removedSwitch(IOFSwitch iofSwitch) { // Update event history - evHistTopoSwitch(sw, EvAction.SWITCH_DISCONNECTED, "None"); - List<LinkTuple> eraseList = new ArrayList<LinkTuple>(); + long sw = iofSwitch.getId(); + evHistTopoSwitch(iofSwitch, EvAction.SWITCH_DISCONNECTED, "None"); + List<Link> eraseList = new ArrayList<Link>(); lock.writeLock().lock(); try { if (switchLinks.containsKey(sw)) { - if (log.isDebugEnabled()) { - log.debug("Handle switchRemoved. Switch {}; removing links {}", - sw, switchLinks.get(sw)); - } + if (log.isDebugEnabled()) { + log.debug("Handle switchRemoved. Switch {}; removing links {}", + HexString.toHexString(sw), switchLinks.get(sw)); + } // add all tuples with an endpoint on this switch to erase list eraseList.addAll(switchLinks.get(sw)); deleteLinks(eraseList, "Switch Removed"); - //updateClusters(); } } finally { lock.writeLock().unlock(); } } - /** - * Iterates though @SwitchCluster links and then deletes ones - * that have timed out. The timeout is set by lldpTimeout. - * If links are deleted updateClusters() is then called. + /** + * Delete links incident on a given switch port. + * @param npt + * @param reason + */ + protected void deleteLinksOnPort(NodePortTuple npt, String reason) { + List<Link> eraseList = new ArrayList<Link>(); + if (this.portLinks.containsKey(npt)) { + if (log.isDebugEnabled()) { + log.debug("handlePortStatus: Switch {} port #{} " + + "removing links {}", + new Object[] {HexString.toHexString(npt.getNodeId()), + npt.getPortId(), + this.portLinks.get(npt)}); + } + eraseList.addAll(this.portLinks.get(npt)); + deleteLinks(eraseList, reason); + } + } + + /** + * Iterates through the list of links and deletes if the + * last discovery message reception time exceeds timeout values. */ protected void timeoutLinks() { - List<LinkTuple> eraseList = new ArrayList<LinkTuple>(); + List<Link> eraseList = new ArrayList<Link>(); Long curTime = System.currentTimeMillis(); boolean linkChanged = false; // reentrant required here because deleteLink also write locks lock.writeLock().lock(); try { - Iterator<Entry<LinkTuple, LinkInfo>> it = + Iterator<Entry<Link, LinkInfo>> it = this.links.entrySet().iterator(); while (it.hasNext()) { - Entry<LinkTuple, LinkInfo> entry = it.next(); - LinkTuple lt = entry.getKey(); + Entry<Link, LinkInfo> entry = it.next(); + Link lt = entry.getKey(); LinkInfo info = entry.getValue(); // Timeout the unicast and multicast LLDP valid times // independently. if ((info.getUnicastValidTime() != null) && - (info.getUnicastValidTime() + this.lldpTimeout < curTime)){ + (info.getUnicastValidTime() + (this.LINK_TIMEOUT * 1000) < curTime)){ info.setUnicastValidTime(null); if (info.getMulticastValidTime() != null) @@ -925,7 +1102,7 @@ public class LinkDiscoveryManager linkChanged = true; } if ((info.getMulticastValidTime()!= null) && - (info.getMulticastValidTime()+ this.lldpTimeout < curTime)) { + (info.getMulticastValidTime()+ (this.LINK_TIMEOUT * 1000) < curTime)) { info.setMulticastValidTime(null); // if uTime is not null, then link will remain as openflow // link. If uTime is null, it will be deleted. So, we @@ -939,7 +1116,9 @@ public class LinkDiscoveryManager info.getMulticastValidTime() == null){ eraseList.add(entry.getKey()); } else if (linkChanged) { - updates.add(new LDUpdate(lt, info.getSrcPortState(), + updates.add(new LDUpdate(lt.getSrc(), lt.getSrcPort(), + info.getSrcPortState(), + lt.getDst(), lt.getDstPort(), info.getDstPortState(), getLinkType(lt, info), UpdateOperation.ADD_OR_UPDATE)); @@ -949,7 +1128,6 @@ public class LinkDiscoveryManager // if any link was deleted or any link was changed. if ((eraseList.size() > 0) || linkChanged) { deleteLinks(eraseList, "LLDP timeout"); - //updateClusters(); } } finally { lock.writeLock().unlock(); @@ -964,83 +1142,58 @@ public class LinkDiscoveryManager if ((OFPortState.OFPPS_LINK_DOWN.getValue() & port.getState()) > 0) return false; // Port STP state doesn't work with multiple VLANs, so ignore it for now - //if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue()) + // if ((port.getState() & OFPortState.OFPPS_STP_MASK.getValue()) == OFPortState.OFPPS_STP_BLOCK.getValue()) // return false; return true; } - @Override - public LinkInfo getLinkInfo(SwitchPortTuple idPort, boolean isSrcPort) { - Set<LinkTuple> links = this.portLinks.get(idPort); - if (links == null) { - return null; - } - - LinkTuple retLink = null; - for (LinkTuple link : links) { - if (log.isTraceEnabled()) { - log.trace("getLinkInfo: check link {} against swPortTuple {}", - link, idPort); - } - if (link.getSrc().equals(idPort) && isSrcPort) { - retLink = link; - } else if (link.getDst().equals(idPort) && !isSrcPort) { - retLink = link; - } - } - - LinkInfo linkInfo = null; - if (retLink != null) { - linkInfo = this.links.get(retLink); - } else { - if (log.isDebugEnabled()) { - log.debug("getLinkInfo: No link out of {} links is from port {}, "+ - "isSrcPort {}", - new Object[] {links.size(), idPort, isSrcPort}); - } - } - - return linkInfo; - } - - public Map<SwitchPortTuple, Set<LinkTuple>> getPortBroadcastDomainLinks() { + public Map<NodePortTuple, Set<Link>> getPortBroadcastDomainLinks() { return portBroadcastDomainLinks; } @Override - public Map<LinkTuple, LinkInfo> getLinks() { + public Map<Link, LinkInfo> getLinks() { lock.readLock().lock(); - Map<LinkTuple, LinkInfo> result; + Map<Link, LinkInfo> result; try { - result = new HashMap<LinkTuple, LinkInfo>(links); + result = new HashMap<Link, LinkInfo>(links); } finally { lock.readLock().unlock(); } return result; } - protected void addLinkToBroadcastDomain(LinkTuple lt) { + protected void addLinkToBroadcastDomain(Link lt) { + + NodePortTuple srcNpt, dstNpt; + srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); + dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort()); + if (!portBroadcastDomainLinks.containsKey(lt.getSrc())) - portBroadcastDomainLinks.put(lt.getSrc(), new HashSet<LinkTuple>()); - portBroadcastDomainLinks.get(lt.getSrc()).add(lt); + portBroadcastDomainLinks.put(srcNpt, new HashSet<Link>()); + portBroadcastDomainLinks.get(srcNpt).add(lt); if (!portBroadcastDomainLinks.containsKey(lt.getDst())) - portBroadcastDomainLinks.put(lt.getDst(), new HashSet<LinkTuple>()); - portBroadcastDomainLinks.get(lt.getDst()).add(lt); + portBroadcastDomainLinks.put(dstNpt, new HashSet<Link>()); + portBroadcastDomainLinks.get(dstNpt).add(lt); } - protected void removeLinkFromBroadcastDomain(LinkTuple lt) { + protected void removeLinkFromBroadcastDomain(Link lt) { + + NodePortTuple srcNpt, dstNpt; + srcNpt = new NodePortTuple(lt.getSrc(), lt.getSrcPort()); + dstNpt = new NodePortTuple(lt.getDst(), lt.getDstPort()); - if (portBroadcastDomainLinks.containsKey(lt.getSrc())) { - portBroadcastDomainLinks.get(lt.getSrc()).remove(lt); - if (portBroadcastDomainLinks.get(lt.getSrc()).isEmpty()) - portBroadcastDomainLinks.remove(lt.getSrc()); + if (portBroadcastDomainLinks.containsKey(srcNpt)) { + portBroadcastDomainLinks.get(srcNpt).remove(lt); + if (portBroadcastDomainLinks.get(srcNpt).isEmpty()) + portBroadcastDomainLinks.remove(srcNpt); } - if (portBroadcastDomainLinks.containsKey(lt.getDst())) { - portBroadcastDomainLinks.get(lt.getDst()).remove(lt); - if (portBroadcastDomainLinks.get(lt.getDst()).isEmpty()) - portBroadcastDomainLinks.remove(lt.getDst()); + if (portBroadcastDomainLinks.containsKey(dstNpt)) { + portBroadcastDomainLinks.get(dstNpt).remove(lt); + if (portBroadcastDomainLinks.get(dstNpt).isEmpty()) + portBroadcastDomainLinks.remove(dstNpt); } } @@ -1057,11 +1210,11 @@ public class LinkDiscoveryManager * @param lt The LinkTuple to get * @return The storage key as a String */ - private String getLinkId(LinkTuple lt) { - String srcDpid = lt.getSrc().getSw().getStringId(); - String dstDpid = lt.getDst().getSw().getStringId(); - return srcDpid + "-" + lt.getSrc().getPort() + "-" + - dstDpid + "-" + lt.getDst().getPort(); + private String getLinkId(Link lt) { + return HexString.toHexString(lt.getSrc()) + + "-" + lt.getSrcPort() + "-" + + HexString.toHexString(lt.getDst())+ + "-" + lt.getDstPort(); } /** @@ -1069,58 +1222,66 @@ public class LinkDiscoveryManager * @param lt The LinkTuple to write * @param linkInfo The LinkInfo to write */ - void writeLink(LinkTuple lt, LinkInfo linkInfo) { - Map<String, Object> rowValues = new HashMap<String, Object>(); - String id = getLinkId(lt); - rowValues.put(LINK_ID, id); - rowValues.put(LINK_VALID_TIME, linkInfo.getUnicastValidTime()); - String srcDpid = lt.getSrc().getSw().getStringId(); - rowValues.put(LINK_SRC_SWITCH, srcDpid); - rowValues.put(LINK_SRC_PORT, lt.getSrc().getPort()); - - LinkType type = getLinkType(lt, linkInfo); - if (type == LinkType.DIRECT_LINK) - rowValues.put(LINK_TYPE, "internal"); - else if (type == LinkType.MULTIHOP_LINK) - rowValues.put(LINK_TYPE, "external"); - else if (type == LinkType.TUNNEL) - rowValues.put(LINK_TYPE, "tunnel"); - - if (linkInfo.linkStpBlocked()) { - if (log.isTraceEnabled()) { - log.trace("writeLink, link {}, info {}, srcPortState Blocked", - lt, linkInfo); - } - rowValues.put(LINK_SRC_PORT_STATE, - OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue()); - } else { - if (log.isTraceEnabled()) { - log.trace("writeLink, link {}, info {}, srcPortState {}", - new Object[]{ lt, linkInfo, linkInfo.getSrcPortState() }); - } - rowValues.put(LINK_SRC_PORT_STATE, linkInfo.getSrcPortState()); + void writeLink(Link lt, LinkInfo linkInfo) { + LinkType type = getLinkType(lt, linkInfo); + + // Write only direct links. Do not write links to external + // L2 network. + // if (type != LinkType.DIRECT_LINK && type != LinkType.TUNNEL) { + // return; + // } + + Map<String, Object> rowValues = new HashMap<String, Object>(); + String id = getLinkId(lt); + rowValues.put(LINK_ID, id); + rowValues.put(LINK_VALID_TIME, linkInfo.getUnicastValidTime()); + String srcDpid = HexString.toHexString(lt.getSrc()); + rowValues.put(LINK_SRC_SWITCH, srcDpid); + rowValues.put(LINK_SRC_PORT, lt.getSrcPort()); + + if (type == LinkType.DIRECT_LINK) + rowValues.put(LINK_TYPE, "internal"); + else if (type == LinkType.MULTIHOP_LINK) + rowValues.put(LINK_TYPE, "external"); + else if (type == LinkType.TUNNEL) + rowValues.put(LINK_TYPE, "tunnel"); + else rowValues.put(LINK_TYPE, "invalid"); + + if (linkInfo.linkStpBlocked()) { + if (log.isTraceEnabled()) { + log.trace("writeLink, link {}, info {}, srcPortState Blocked", + lt, linkInfo); } - String dstDpid = lt.getDst().getSw().getStringId(); - rowValues.put(LINK_DST_SWITCH, dstDpid); - rowValues.put(LINK_DST_PORT, lt.getDst().getPort()); - if (linkInfo.linkStpBlocked()) { - if (log.isTraceEnabled()) { - log.trace("writeLink, link {}, info {}, dstPortState Blocked", - lt, linkInfo); - } - rowValues.put(LINK_DST_PORT_STATE, - OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue()); - } else { - if (log.isTraceEnabled()) { - log.trace("writeLink, link {}, info {}, dstPortState {}", - new Object[]{ lt, linkInfo, linkInfo.getDstPortState() }); - } - rowValues.put(LINK_DST_PORT_STATE, linkInfo.getDstPortState()); + rowValues.put(LINK_SRC_PORT_STATE, + OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue()); + } else { + if (log.isTraceEnabled()) { + log.trace("writeLink, link {}, info {}, srcPortState {}", + new Object[]{ lt, linkInfo, linkInfo.getSrcPortState() }); + } + rowValues.put(LINK_SRC_PORT_STATE, linkInfo.getSrcPortState()); + } + String dstDpid = HexString.toHexString(lt.getDst()); + rowValues.put(LINK_DST_SWITCH, dstDpid); + rowValues.put(LINK_DST_PORT, lt.getDstPort()); + if (linkInfo.linkStpBlocked()) { + if (log.isTraceEnabled()) { + log.trace("writeLink, link {}, info {}, dstPortState Blocked", + lt, linkInfo); + } + rowValues.put(LINK_DST_PORT_STATE, + OFPhysicalPort.OFPortState.OFPPS_STP_BLOCK.getValue()); + } else { + if (log.isTraceEnabled()) { + log.trace("writeLink, link {}, info {}, dstPortState {}", + new Object[]{ lt, linkInfo, linkInfo.getDstPortState() }); } - storageSource.updateRowAsync(LINK_TABLE_NAME, rowValues); + rowValues.put(LINK_DST_PORT_STATE, linkInfo.getDstPortState()); + } + storageSource.updateRowAsync(LINK_TABLE_NAME, rowValues); } - public Long readLinkValidTime(LinkTuple lt) { + public Long readLinkValidTime(Link lt) { // FIXME: We're not currently using this right now, but if we start // to use this again, we probably shouldn't use it in its current // form, because it's doing synchronous storage calls. Depending @@ -1133,7 +1294,7 @@ public class LinkDiscoveryManager String[] columns = { LINK_VALID_TIME }; String id = getLinkId(lt); resultSet = storageSource.executeQuery(LINK_TABLE_NAME, columns, - new OperatorPredicate(LINK_ID, OperatorPredicate.Operator.EQ, id), null); + new OperatorPredicate(LINK_ID, OperatorPredicate.Operator.EQ, id), null); if (resultSet.next()) validTime = resultSet.getLong(LINK_VALID_TIME); } @@ -1144,7 +1305,7 @@ public class LinkDiscoveryManager return validTime; } - public void writeLinkInfo(LinkTuple lt, LinkInfo linkInfo) { + public void writeLinkInfo(Link lt, LinkInfo linkInfo) { if (linkInfo.getUnicastValidTime() != null) { Map<String, Object> rowValues = new HashMap<String, Object>(); String id = getLinkId(lt); @@ -1192,16 +1353,16 @@ public class LinkDiscoveryManager * Removes a link from storage using an asynchronous call. * @param lt The LinkTuple to delete. */ - void removeLinkFromStorage(LinkTuple lt) { + void removeLinkFromStorage(Link lt) { String id = getLinkId(lt); storageSource.deleteRowAsync(LINK_TABLE_NAME, id); } - + @Override public void addListener(ILinkDiscoveryListener listener) { linkDiscoveryAware.add(listener); } - + /** * Register a link discovery aware component * @param linkDiscoveryAwareComponent @@ -1219,7 +1380,7 @@ public class LinkDiscoveryManager // TODO make this a copy on write set or lock it somehow this.linkDiscoveryAware.remove(linkDiscoveryAwareComponent); } - + /** * Sets the IStorageSource to use for ITology * @param storageSource the storage source to use @@ -1277,10 +1438,10 @@ public class LinkDiscoveryManager updated_switches.add(sw); } } else { - if (log.isDebugEnabled()) { - log.debug("Update for switch which has no entry in switch " + - "list (dpid={}), a delete action.", (String)key); - } + if (log.isDebugEnabled()) { + log.debug("Update for switch which has no entry in switch " + + "list (dpid={}), a delete action.", (String)key); + } } } @@ -1289,14 +1450,14 @@ public class LinkDiscoveryManager if (sw.hasAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH)) { sw.removeAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH); if (log.isDebugEnabled()) { - log.debug("SWITCH_IS_CORE_SWITCH set to False for {}", sw); + log.debug("SWITCH_IS_CORE_SWITCH set to False for {}", sw); } updates.add(new LDUpdate(sw.getId(), SwitchType.BASIC_SWITCH)); } else { sw.setAttribute(IOFSwitch.SWITCH_IS_CORE_SWITCH, new Boolean(true)); if (log.isDebugEnabled()) { - log.debug("SWITCH_IS_CORE_SWITCH set to True for {}", sw); + log.debug("SWITCH_IS_CORE_SWITCH set to True for {}", sw); } updates.add(new LDUpdate(sw.getId(), SwitchType.CORE_SWITCH)); } @@ -1309,7 +1470,7 @@ public class LinkDiscoveryManager } // IFloodlightModule classes - + @Override public Collection<Class<? extends IFloodlightService>> getModuleServices() { Collection<Class<? extends IFloodlightService>> l = @@ -1321,11 +1482,11 @@ public class LinkDiscoveryManager @Override public Map<Class<? extends IFloodlightService>, IFloodlightService> - getServiceImpls() { + getServiceImpls() { Map<Class<? extends IFloodlightService>, IFloodlightService> m = - new HashMap<Class<? extends IFloodlightService>, - IFloodlightService>(); + new HashMap<Class<? extends IFloodlightService>, + IFloodlightService>(); // We are the class that implements the service m.put(ILinkDiscoveryService.class, this); return m; @@ -1343,32 +1504,37 @@ public class LinkDiscoveryManager @Override public void init(FloodlightModuleContext context) - throws FloodlightModuleException { + throws FloodlightModuleException { floodlightProvider = context.getServiceImpl(IFloodlightProviderService.class); storageSource = context.getServiceImpl(IStorageSourceService.class); threadPool = context.getServiceImpl(IThreadPoolService.class); - + // We create this here because there is no ordering guarantee this.linkDiscoveryAware = new ArrayList<ILinkDiscoveryListener>(); this.lock = new ReentrantReadWriteLock(); this.updates = new LinkedBlockingQueue<LDUpdate>(); - this.links = new HashMap<LinkTuple, LinkInfo>(); - this.portLinks = new HashMap<SwitchPortTuple, Set<LinkTuple>>(); + this.links = new HashMap<Link, LinkInfo>(); + this.portLinks = new HashMap<NodePortTuple, Set<Link>>(); this.suppressLLDPs = - Collections.synchronizedSet(new HashSet<SwitchPortTuple>()); - this.portBroadcastDomainLinks = new HashMap<SwitchPortTuple, Set<LinkTuple>>(); - this.switchLinks = new HashMap<IOFSwitch, Set<LinkTuple>>(); + Collections.synchronizedSet(new HashSet<NodePortTuple>()); + this.portBroadcastDomainLinks = new HashMap<NodePortTuple, Set<Link>>(); + this.switchLinks = new HashMap<Long, Set<Link>>(); this.evHistTopologySwitch = - new EventHistory<EventHistoryTopologySwitch>("Topology: Switch"); + new EventHistory<EventHistoryTopologySwitch>("Topology: Switch"); this.evHistTopologyLink = - new EventHistory<EventHistoryTopologyLink>("Topology: Link"); + new EventHistory<EventHistoryTopologyLink>("Topology: Link"); this.evHistTopologyCluster = - new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster"); + new EventHistory<EventHistoryTopologyCluster>("Topology: Cluster"); } @Override public void startUp(FloodlightModuleContext context) { // Create our storage tables + if (storageSource == null) { + log.warn("No storage source found."); + return; + } + storageSource.createTable(LINK_TABLE_NAME, null); storageSource.setTablePrimaryKeyName(LINK_TABLE_NAME, LINK_ID); storageSource.deleteMatchingRows(LINK_TABLE_NAME, null); @@ -1378,67 +1544,30 @@ public class LinkDiscoveryManager } catch (StorageException ex) { log.error("Error in installing listener for switch table - {}", SWITCH_CONFIG_TABLE_NAME); } - + ScheduledExecutorService ses = threadPool.getScheduledExecutor(); // To be started by the first switch connection - sendLLDPTask = new SingletonTask(ses, new Runnable() { - @Override - public void run() { - try { - sendLLDPs(); - } catch (StorageException e) { - log.error("Storage exception in LLDP send timer; " + - "terminating process", e); - floodlightProvider.terminate(); - } catch (Exception e) { - log.error("Exception in LLDP send timer", e); - } finally { - if (!shuttingDown) { - sendLLDPTask.reschedule(lldpFrequency, - TimeUnit.MILLISECONDS); - } - } - } - }); - - Runnable timeoutLinksTimer = new Runnable() { + discoveryTask = new SingletonTask(ses, new Runnable() { @Override public void run() { - if (log.isTraceEnabled()) { - log.trace("Running timeoutLinksTimer"); - } try { - timeoutLinks(); - if (!shuttingDown) { - ScheduledExecutorService ses = - threadPool.getScheduledExecutor(); - ses.schedule(this, lldpTimeout, TimeUnit.MILLISECONDS); - } + discoverLinks(); } catch (StorageException e) { - log.error("Storage exception in link timer; " + - "terminating process", e); + log.error("Storage exception in LLDP send timer; " + + "terminating process", e); floodlightProvider.terminate(); } catch (Exception e) { - log.error("Exception in timeoutLinksTimer", e); + log.error("Exception in LLDP send timer.", e); + } finally { + if (!shuttingDown) { + discoveryTask.reschedule(DISCOVERY_TASK_INTERVAL, + TimeUnit.SECONDS); + } } } - }; - ses.schedule(timeoutLinksTimer, 1000, TimeUnit.MILLISECONDS); + }); - updatesThread = new Thread(new Runnable () { - @Override - public void run() { - while (true) { - try { - doUpdatesThread(); - } catch (InterruptedException e) { - return; - } - } - }}, "Topology Updates"); - updatesThread.start(); - // Register for the OpenFlow messages we want to receive floodlightProvider.addOFMessageListener(OFType.PACKET_IN, this); floodlightProvider.addOFMessageListener(OFType.PORT_STATUS, this); @@ -1469,14 +1598,14 @@ public class LinkDiscoveryManager } evTopoSwitch.dpid = sw.getId(); if ((sw.getChannel() != null) && - (SocketAddress.class.isInstance( - sw.getChannel().getRemoteAddress()))) { + (SocketAddress.class.isInstance( + sw.getChannel().getRemoteAddress()))) { evTopoSwitch.ipv4Addr = - IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel(). - getRemoteAddress())).getAddress().getAddress()); + IPv4.toIPv4Address(((InetSocketAddress)(sw.getChannel(). + getRemoteAddress())).getAddress().getAddress()); evTopoSwitch.l4Port = - ((InetSocketAddress)(sw.getChannel(). - getRemoteAddress())).getPort(); + ((InetSocketAddress)(sw.getChannel(). + getRemoteAddress())).getPort(); } else { evTopoSwitch.ipv4Addr = 0; evTopoSwitch.l4Port = 0; @@ -1486,9 +1615,9 @@ public class LinkDiscoveryManager } private void evHistTopoLink(long srcDpid, long dstDpid, short srcPort, - short dstPort, int srcPortState, int dstPortState, - ILinkDiscovery.LinkType linkType, - EvAction actn, String reason) { + short dstPort, int srcPortState, int dstPortState, + ILinkDiscovery.LinkType linkType, + EvAction actn, String reason) { if (evTopoLink == null) { evTopoLink = new EventHistoryTopologyLink(); } @@ -1500,25 +1629,25 @@ public class LinkDiscoveryManager evTopoLink.dstPortState = dstPortState; evTopoLink.reason = reason; switch (linkType) { - case DIRECT_LINK: - evTopoLink.linkType = "DIRECT_LINK"; - break; - case MULTIHOP_LINK: - evTopoLink.linkType = "MULTIHOP_LINK"; - break; - case TUNNEL: - evTopoLink.linkType = "TUNNEL"; - break; - case INVALID_LINK: - default: - evTopoLink.linkType = "Unknown"; - break; + case DIRECT_LINK: + evTopoLink.linkType = "DIRECT_LINK"; + break; + case MULTIHOP_LINK: + evTopoLink.linkType = "MULTIHOP_LINK"; + break; + case TUNNEL: + evTopoLink.linkType = "TUNNEL"; + break; + case INVALID_LINK: + default: + evTopoLink.linkType = "Unknown"; + break; } evTopoLink = evHistTopologyLink.put(evTopoLink, actn); } public void evHistTopoCluster(long dpid, long clusterIdOld, - long clusterIdNew, EvAction action, String reason) { + long clusterIdNew, EvAction action, String reason) { if (evTopoCluster == null) { evTopoCluster = new EventHistoryTopologyCluster(); } @@ -1536,7 +1665,7 @@ public class LinkDiscoveryManager Map<String, Object> info = new HashMap<String, Object>(); int num_links = 0; - for (Set<LinkTuple> links : switchLinks.values()) + for (Set<Link> links : switchLinks.values()) num_links += links.size(); info.put("# inter-switch links", num_links / 2); @@ -1544,7 +1673,7 @@ public class LinkDiscoveryManager } // IHARoleListener - + @Override public void roleChanged(Role oldRole, Role newRole) { switch(newRole) { @@ -1553,7 +1682,8 @@ public class LinkDiscoveryManager clearAllLinks(); log.debug("Sending LLDPs " + "to HA change from SLAVE->MASTER"); - sendLLDPs(); + clearAllLinks(); + discoverLinks(); } break; case SLAVE: @@ -1563,16 +1693,16 @@ public class LinkDiscoveryManager links.clear(); portLinks.clear(); portBroadcastDomainLinks.clear(); + discoverOnAllPorts(); break; } } - + @Override public void controllerNodeIPsChanged( - Map<String, String> curControllerNodeIPs, - Map<String, String> addedControllerNodeIPs, - Map<String, String> removedControllerNodeIPs) { + Map<String, String> curControllerNodeIPs, + Map<String, String> addedControllerNodeIPs, + Map<String, String> removedControllerNodeIPs) { // ignore } - } diff --git a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java index e721e99045799a9345a5eb1afb63e26dc5c4674b..eda84c591bc3645c42e0643861b044d0097df7a9 100644 --- a/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java +++ b/src/main/java/net/floodlightcontroller/linkdiscovery/web/LinksResource.java @@ -1,31 +1,63 @@ package net.floodlightcontroller.linkdiscovery.web; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; +import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService; import net.floodlightcontroller.linkdiscovery.LinkInfo; -import net.floodlightcontroller.linkdiscovery.LinkTuple; +import net.floodlightcontroller.routing.Link; import org.restlet.resource.Get; import org.restlet.resource.ServerResource; public class LinksResource extends ServerResource { + + public class LinkWithType { + long src; + short srcPort; + long dst; + short dstPort; + String type; + + public LinkWithType(Link link, String type) { + this.src = link.getSrc(); + this.srcPort = link.getSrcPort(); + this.dst = link.getDst(); + this.dstPort = link.getDstPort(); + this.type = type; + } + }; + @Get("json") - public Set<LinkTuple> retrieve() { - ILinkDiscoveryService topo = (ILinkDiscoveryService)getContext().getAttributes(). + public Set<LinkWithType> retrieve() { + String str; + + ILinkDiscoveryService ld = (ILinkDiscoveryService)getContext().getAttributes(). get(ILinkDiscoveryService.class.getCanonicalName()); - Set <LinkTuple> links = new HashSet<LinkTuple>(); - if (topo != null) { - for (Set<LinkTuple> linkSet : topo.getSwitchLinks().values()) { - for (LinkTuple lt : linkSet) { - LinkInfo info = topo.getLinkInfo(lt.getSrc(), true); - LinkTuple withType = new LinkTuple(lt.getSrc(), lt.getDst()); - withType.setType(topo.getLinkType(lt, info)); - links.add(withType); - } + Map<Link, LinkInfo> links = new HashMap<Link, LinkInfo>(); + Set<LinkWithType> returnLinkSet = new HashSet<LinkWithType>(); + + if (ld != null) { + links.putAll(ld.getLinks()); + for (Link link: links.keySet()) { + LinkInfo info = links.get(link); + LinkType type = ld.getLinkType(info); + + if (type == LinkType.DIRECT_LINK) + str = "internal"; + else if (type == LinkType.MULTIHOP_LINK) + str = "external"; + else if (type == LinkType.TUNNEL) + str = "tunnel"; + else str = "invalid"; + + LinkWithType lwt = new LinkWithType(link, str); + returnLinkSet.add(lwt); } } - return links; + return returnLinkSet; } } diff --git a/src/main/java/net/floodlightcontroller/packet/BSN.java b/src/main/java/net/floodlightcontroller/packet/BSN.java index ea4030b961747a09c6ad1e81197cb523b1a1bb41..27c8f70df69cce5896f66a5d64522a2bc1c9f654 100644 --- a/src/main/java/net/floodlightcontroller/packet/BSN.java +++ b/src/main/java/net/floodlightcontroller/packet/BSN.java @@ -31,11 +31,13 @@ public class BSN extends BasePacket { public static final int BSN_MAGIC = 0x20000604; public static final short BSN_VERSION_CURRENT = 0x0; public static final short BSN_TYPE_PROBE = 0x1; + public static final short BSN_TYPE_BDDP = 0x2; public static Map<Short, Class<? extends IPacket>> typeClassMap; static { typeClassMap = new HashMap<Short, Class<? extends IPacket>>(); typeClassMap.put(BSN_TYPE_PROBE, BSNPROBE.class); + typeClassMap.put(BSN_TYPE_BDDP, LLDP.class); } protected short type; diff --git a/src/main/java/net/floodlightcontroller/routing/Link.java b/src/main/java/net/floodlightcontroller/routing/Link.java index f944acf28c8ef3bb9387172b32286d41fa506515..436e67c719146aa44a46d24776f3461faa9a7f2e 100755 --- a/src/main/java/net/floodlightcontroller/routing/Link.java +++ b/src/main/java/net/floodlightcontroller/routing/Link.java @@ -29,6 +29,7 @@ public class Link { private long dst; private short dstPort; + public Link(long srcId, short srcPort, long dstId, short dstPort) { this.src = srcId; this.srcPort = srcPort; @@ -36,7 +37,14 @@ public class Link { this.dstPort = dstPort; } - + // Convenience method + public Link(long srcId, int srcPort, long dstId, int dstPort) { + this.src = srcId; + this.srcPort = (short) srcPort; + this.dst = dstId; + this.dstPort = (short) dstPort; + } + @JsonProperty("src-switch") @JsonSerialize(using=DPIDSerializer.class) public long getSrc() { diff --git a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java index b985bbbcb856f4181fbd5e7a439dd3080bad2c84..821a1737cbe21bfa6d3684aeada84b9e283e6fa6 100644 --- a/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java +++ b/src/main/java/net/floodlightcontroller/topology/NodePortTuple.java @@ -1,7 +1,6 @@ package net.floodlightcontroller.topology; import net.floodlightcontroller.core.web.serializers.DPIDSerializer; -import net.floodlightcontroller.linkdiscovery.SwitchPortTuple; import org.codehaus.jackson.annotate.JsonProperty; import org.codehaus.jackson.map.annotate.JsonSerialize; @@ -26,18 +25,10 @@ public class NodePortTuple { this.nodeId = nodeId; this.portId = portId; } - - /** - * Creates a NodePortTuple from the same information - * in a SwitchPortTuple - * @param swt - */ - public NodePortTuple(SwitchPortTuple swt) { - if (swt.getSw() != null) - this.nodeId = swt.getSw().getId(); - else - this.nodeId = 0; - this.portId = swt.getPort(); + + public NodePortTuple(long nodeId, int portId) { + this.nodeId = nodeId; + this.portId = (short) portId; } @JsonProperty("switch") diff --git a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java index d24df1962fcd2b5c1461ea8c9f5b3dd486aec8bd..6e99acdd14d4a70a2143a66becc0e4b9a7dba8fc 100644 --- a/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java +++ b/src/test/java/net/floodlightcontroller/linkdiscovery/internal/LinkDiscoveryManagerTest.java @@ -21,6 +21,8 @@ import static org.easymock.EasyMock.*; import java.util.Collections; import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; import org.junit.Before; import org.junit.Test; @@ -36,16 +38,17 @@ import net.floodlightcontroller.core.test.MockThreadPoolService; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryListener; import net.floodlightcontroller.linkdiscovery.ILinkDiscoveryService; import net.floodlightcontroller.linkdiscovery.LinkInfo; -import net.floodlightcontroller.linkdiscovery.LinkTuple; import net.floodlightcontroller.linkdiscovery.internal.LinkDiscoveryManager; import net.floodlightcontroller.restserver.IRestApiService; import net.floodlightcontroller.restserver.RestApiServer; import net.floodlightcontroller.routing.IRoutingService; +import net.floodlightcontroller.routing.Link; import net.floodlightcontroller.storage.IStorageSourceService; import net.floodlightcontroller.storage.memory.MemoryStorageSource; import net.floodlightcontroller.test.FloodlightTestCase; import net.floodlightcontroller.threadpool.IThreadPoolService; import net.floodlightcontroller.topology.ITopologyService; +import net.floodlightcontroller.topology.NodePortTuple; import net.floodlightcontroller.topology.TopologyManager; /** @@ -59,15 +62,23 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { public class TestLinkDiscoveryManager extends LinkDiscoveryManager { public boolean isSendLLDPsCalled = false; - + public boolean isClearLinksCalled = false; + @Override - protected void sendLLDPs() { + protected void discoverOnAllPorts() { isSendLLDPsCalled = true; - super.sendLLDPs(); + super.discoverOnAllPorts(); } - + public void reset() { isSendLLDPsCalled = false; + isClearLinksCalled = false; + } + + @Override + protected void clearAllLinks() { + isClearLinksCalled = true; + super.clearAllLinks(); } } @@ -105,44 +116,54 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { tp.startUp(cntx); routingEngine.startUp(cntx); ldm.startUp(cntx); + + IOFSwitch sw1 = createMockSwitch(1L); + IOFSwitch sw2 = createMockSwitch(2L); + Map<Long, IOFSwitch> switches = new HashMap<Long, IOFSwitch>(); + switches.put(1L, sw1); + switches.put(2L, sw2); + getMockFloodlightProvider().setSwitches(switches); + replay(sw1, sw2); } @Test public void testAddOrUpdateLink() throws Exception { LinkDiscoveryManager topology = getTopology(); - IOFSwitch sw1 = createMockSwitch(1L); - IOFSwitch sw2 = createMockSwitch(2L); - replay(sw1, sw2); - LinkTuple lt = new LinkTuple(sw1, 2, sw2, 1); - LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + + Link lt = new Link(1L, 2, 2L, 1); + LinkInfo info = new LinkInfo(System.currentTimeMillis(), + System.currentTimeMillis(), null, 0, 0); topology.addOrUpdateLink(lt, info); + + NodePortTuple srcNpt = new NodePortTuple(1L, 2); + NodePortTuple dstNpt = new NodePortTuple(2L, 1); + // check invariants hold - assertNotNull(topology.switchLinks.get(lt.getSrc().getSw())); - assertTrue(topology.switchLinks.get(lt.getSrc().getSw()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getSrc())); - assertTrue(topology.portLinks.get(lt.getSrc()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getDst())); - assertTrue(topology.portLinks.get(lt.getDst()).contains(lt)); + assertNotNull(topology.switchLinks.get(lt.getSrc())); + assertTrue(topology.switchLinks.get(lt.getSrc()).contains(lt)); + assertNotNull(topology.portLinks.get(srcNpt)); + assertTrue(topology.portLinks.get(srcNpt).contains(lt)); + assertNotNull(topology.portLinks.get(dstNpt)); + assertTrue(topology.portLinks.get(dstNpt).contains(lt)); assertTrue(topology.links.containsKey(lt)); } @Test public void testDeleteLink() throws Exception { LinkDiscoveryManager topology = getTopology(); - IOFSwitch sw1 = createMockSwitch(1L); - IOFSwitch sw2 = createMockSwitch(2L); - replay(sw1, sw2); - LinkTuple lt = new LinkTuple(sw1, 2, sw2, 1); - LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + + Link lt = new Link(1L, 2, 2L, 1); + LinkInfo info = new LinkInfo(System.currentTimeMillis(), + System.currentTimeMillis(), null, 0, 0); topology.addOrUpdateLink(lt, info); topology.deleteLinks(Collections.singletonList(lt), "Test"); // check invariants hold - assertNull(topology.switchLinks.get(lt.getSrc().getSw())); - assertNull(topology.switchLinks.get(lt.getDst().getSw())); + assertNull(topology.switchLinks.get(lt.getSrc())); + assertNull(topology.switchLinks.get(lt.getDst())); assertNull(topology.portLinks.get(lt.getSrc())); assertNull(topology.portLinks.get(lt.getDst())); assertTrue(topology.links.isEmpty()); @@ -151,63 +172,71 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { @Test public void testAddOrUpdateLinkToSelf() throws Exception { LinkDiscoveryManager topology = getTopology(); - IOFSwitch sw1 = createMockSwitch(1L); - IOFSwitch sw2 = createMockSwitch(2L); - replay(sw1, sw2); - LinkTuple lt = new LinkTuple(sw1, 2, sw1, 3); - LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + + Link lt = new Link(1L, 2, 2L, 3); + NodePortTuple srcNpt = new NodePortTuple(1L, 2); + NodePortTuple dstNpt = new NodePortTuple(2L, 3); + + LinkInfo info = new LinkInfo(System.currentTimeMillis(), + System.currentTimeMillis(), null, 0, 0); topology.addOrUpdateLink(lt, info); // check invariants hold - assertNotNull(topology.switchLinks.get(lt.getSrc().getSw())); - assertTrue(topology.switchLinks.get(lt.getSrc().getSw()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getSrc())); - assertTrue(topology.portLinks.get(lt.getSrc()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getDst())); - assertTrue(topology.portLinks.get(lt.getDst()).contains(lt)); + assertNotNull(topology.switchLinks.get(lt.getSrc())); + assertTrue(topology.switchLinks.get(lt.getSrc()).contains(lt)); + assertNotNull(topology.portLinks.get(srcNpt)); + assertTrue(topology.portLinks.get(srcNpt).contains(lt)); + assertNotNull(topology.portLinks.get(dstNpt)); + assertTrue(topology.portLinks.get(dstNpt).contains(lt)); assertTrue(topology.links.containsKey(lt)); } @Test public void testDeleteLinkToSelf() throws Exception { LinkDiscoveryManager topology = getTopology(); - IOFSwitch sw1 = createMockSwitch(1L); - replay(sw1); - LinkTuple lt = new LinkTuple(sw1, 2, sw1, 3); - LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + + Link lt = new Link(1L, 2, 1L, 3); + NodePortTuple srcNpt = new NodePortTuple(1L, 2); + NodePortTuple dstNpt = new NodePortTuple(2L, 3); + + LinkInfo info = new LinkInfo(System.currentTimeMillis(), + System.currentTimeMillis(), null, 0, 0); topology.addOrUpdateLink(lt, info); topology.deleteLinks(Collections.singletonList(lt), "Test to self"); // check invariants hold - assertNull(topology.switchLinks.get(lt.getSrc().getSw())); - assertNull(topology.switchLinks.get(lt.getDst().getSw())); - assertNull(topology.portLinks.get(lt.getSrc())); - assertNull(topology.portLinks.get(lt.getDst())); + assertNull(topology.switchLinks.get(lt.getSrc())); + assertNull(topology.switchLinks.get(lt.getDst())); + assertNull(topology.portLinks.get(srcNpt)); + assertNull(topology.portLinks.get(dstNpt)); assertTrue(topology.links.isEmpty()); } @Test public void testRemovedSwitch() { LinkDiscoveryManager topology = getTopology(); - IOFSwitch sw1 = createMockSwitch(1L); - IOFSwitch sw2 = createMockSwitch(2L); - replay(sw1, sw2); - LinkTuple lt = new LinkTuple(sw1, 2, sw2, 1); - LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + + Link lt = new Link(1L, 2, 2L, 1); + NodePortTuple srcNpt = new NodePortTuple(1L, 2); + NodePortTuple dstNpt = new NodePortTuple(2L, 1); + LinkInfo info = new LinkInfo(System.currentTimeMillis(), + System.currentTimeMillis(), null, 0, 0); topology.addOrUpdateLink(lt, info); + IOFSwitch sw1 = getMockFloodlightProvider().getSwitches().get(1L); + IOFSwitch sw2 = getMockFloodlightProvider().getSwitches().get(2L); // Mock up our expected behavior topology.removedSwitch(sw1); - verify(sw1, sw2); + // check invariants hold - assertNull(topology.switchLinks.get(lt.getSrc().getSw())); - assertNull(topology.switchLinks.get(lt.getDst().getSw())); - assertNull(topology.portLinks.get(lt.getSrc())); - assertNull(topology.portLinks.get(lt.getDst())); + assertNull(topology.switchLinks.get(lt.getSrc())); + assertNull(topology.switchLinks.get(lt.getDst())); + assertNull(topology.portLinks.get(srcNpt)); + assertNull(topology.portLinks.get(dstNpt)); assertTrue(topology.links.isEmpty()); } @@ -216,8 +245,9 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { LinkDiscoveryManager topology = getTopology(); IOFSwitch sw1 = createMockSwitch(1L); replay(sw1); - LinkTuple lt = new LinkTuple(sw1, 2, sw1, 3); - LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + Link lt = new Link(1L, 2, 1L, 3); + LinkInfo info = new LinkInfo(System.currentTimeMillis(), + System.currentTimeMillis(), null, 0, 0); topology.addOrUpdateLink(lt, info); @@ -226,7 +256,7 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { verify(sw1); // check invariants hold - assertNull(topology.switchLinks.get(lt.getSrc().getSw())); + assertNull(topology.switchLinks.get(lt.getSrc())); assertNull(topology.portLinks.get(lt.getSrc())); assertNull(topology.portLinks.get(lt.getDst())); assertTrue(topology.links.isEmpty()); @@ -235,40 +265,42 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { @Test public void testAddUpdateLinks() throws Exception { LinkDiscoveryManager topology = getTopology(); - IOFSwitch sw1 = createMockSwitch(1L); - IOFSwitch sw2 = createMockSwitch(2L); - //expect(ldm.getSwitchClusterId(1L)).andReturn(1L).anyTimes(); - //expect(ldm.getSwitchClusterId(2L)).andReturn(1L).anyTimes(); - replay(sw1, sw2); - LinkTuple lt = new LinkTuple(sw1, 1, sw2, 1); + + Link lt = new Link(1L, 1, 2L, 1); + NodePortTuple srcNpt = new NodePortTuple(1L, 1); + NodePortTuple dstNpt = new NodePortTuple(2L, 1); + LinkInfo info; - info = new LinkInfo(System.currentTimeMillis() - 40000, null, + info = new LinkInfo(System.currentTimeMillis() - 40000, + System.currentTimeMillis() - 40000, null, 0, 0); topology.addOrUpdateLink(lt, info); // check invariants hold - assertNotNull(topology.switchLinks.get(lt.getSrc().getSw())); - assertTrue(topology.switchLinks.get(lt.getSrc().getSw()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getSrc())); - assertTrue(topology.portLinks.get(lt.getSrc()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getDst())); - assertTrue(topology.portLinks.get(lt.getDst()).contains(lt)); + assertNotNull(topology.switchLinks.get(lt.getSrc())); + assertTrue(topology.switchLinks.get(lt.getSrc()).contains(lt)); + assertNotNull(topology.portLinks.get(srcNpt)); + assertTrue(topology.portLinks.get(srcNpt).contains(lt)); + assertNotNull(topology.portLinks.get(dstNpt)); + assertTrue(topology.portLinks.get(dstNpt).contains(lt)); assertTrue(topology.links.containsKey(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()) == null || - topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt) == false); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()) == null || - topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt) == null || + topology.portBroadcastDomainLinks.get(srcNpt).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt) == null || + topology.portBroadcastDomainLinks.get(dstNpt).contains(lt) == false); topology.timeoutLinks(); - info = new LinkInfo(null, System.currentTimeMillis(), 0, 0); + info = new LinkInfo(System.currentTimeMillis(),/* firstseen */ + null,/* unicast */ + System.currentTimeMillis(), 0, 0); topology.addOrUpdateLink(lt, info); assertTrue(topology.links.get(lt).getUnicastValidTime() == null); assertTrue(topology.links.get(lt).getMulticastValidTime() != null); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt).contains(lt)); // Add a link info based on info that woudld be obtained from unicast LLDP @@ -276,78 +308,93 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { // this to test timeout after this test. Although the info is initialized // with LT_OPENFLOW_LINK, the link property should be changed to LT_NON_OPENFLOW // by the addOrUpdateLink method. - info = new LinkInfo(System.currentTimeMillis() - 40000, null, 0, 0); + info = new LinkInfo(System.currentTimeMillis() - 40000, + System.currentTimeMillis() - 40000, null, 0, 0); topology.addOrUpdateLink(lt, info); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()) == null || - topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt) == false); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()) == null || - topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt) == null || + topology.portBroadcastDomainLinks.get(srcNpt).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt) == null || + topology.portBroadcastDomainLinks.get(dstNpt).contains(lt) == false); // Expect to timeout the unicast Valid Time, but not the multicast Valid time // So the link type should go back to non-openflow link. topology.timeoutLinks(); assertTrue(topology.links.get(lt).getUnicastValidTime() == null); assertTrue(topology.links.get(lt).getMulticastValidTime() != null); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt).contains(lt)); // Set the multicastValidTime to be old and see if that also times out. - info = new LinkInfo(null, System.currentTimeMillis() - 40000, 0, 0); + info = new LinkInfo(System.currentTimeMillis() - 40000, + null, System.currentTimeMillis() - 40000, 0, 0); topology.addOrUpdateLink(lt, info); topology.timeoutLinks(); assertTrue(topology.links.get(lt) == null); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()) == null || - topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt) == false); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()) == null || - topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt) == null || + topology.portBroadcastDomainLinks.get(srcNpt).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt) == null || + topology.portBroadcastDomainLinks.get(dstNpt).contains(lt) == false); // Test again only with multicast LLDP - info = new LinkInfo(null, System.currentTimeMillis() - 40000, 0, 0); + info = new LinkInfo(System.currentTimeMillis() - 40000, + null, System.currentTimeMillis() - 40000, 0, 0); topology.addOrUpdateLink(lt, info); assertTrue(topology.links.get(lt).getUnicastValidTime() == null); assertTrue(topology.links.get(lt).getMulticastValidTime() != null); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt).contains(lt)); // Call timeout and check if link is no longer present. topology.timeoutLinks(); assertTrue(topology.links.get(lt) == null); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()) == null || - topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt) == false); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()) == null || - topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt) == null || + topology.portBroadcastDomainLinks.get(srcNpt).contains(lt) == false); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt) == null || + topology.portBroadcastDomainLinks.get(dstNpt).contains(lt) == false); // Start clean and see if loops are also added. - lt = new LinkTuple(sw1, 1, sw1, 2); - info = new LinkInfo(null, System.currentTimeMillis() - 40000, 0, 0); + lt = new Link(1L, 1, 1L, 2); + srcNpt = new NodePortTuple(1L, 1); + dstNpt = new NodePortTuple(1L, 2); + info = new LinkInfo(System.currentTimeMillis() - 40000, + null, System.currentTimeMillis() - 40000, 0, 0); topology.addOrUpdateLink(lt, info); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt).contains(lt)); // Start clean and see if loops are also added. - lt = new LinkTuple(sw1, 1, sw1, 3); - info = new LinkInfo(null, System.currentTimeMillis() - 40000, 0, 0); + lt = new Link(1L, 1, 1L, 3); + srcNpt = new NodePortTuple(1L, 1); + dstNpt = new NodePortTuple(1L, 3); + info = new LinkInfo(System.currentTimeMillis() - 40000, + null, System.currentTimeMillis() - 40000, 0, 0); topology.addOrUpdateLink(lt, info); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt).contains(lt)); // Start clean and see if loops are also added. - lt = new LinkTuple(sw1, 4, sw1, 5); - info = new LinkInfo(null, System.currentTimeMillis() - 40000, 0, 0); + lt = new Link(1L, 4, 1L, 5); + srcNpt = new NodePortTuple(1L, 4); + dstNpt = new NodePortTuple(1L, 5); + info = new LinkInfo(System.currentTimeMillis() - 40000, + null, System.currentTimeMillis() - 40000, 0, 0); topology.addOrUpdateLink(lt, info); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt).contains(lt)); // Start clean and see if loops are also added. - lt = new LinkTuple(sw1, 3, sw1, 5); - info = new LinkInfo(null, System.currentTimeMillis() - 40000, 0, 0); + lt = new Link(1L, 3, 1L, 5); + srcNpt = new NodePortTuple(1L, 3); + dstNpt = new NodePortTuple(1L, 5); + info = new LinkInfo(System.currentTimeMillis() - 40000, + null, System.currentTimeMillis() - 40000, 0, 0); topology.addOrUpdateLink(lt, info); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getSrc()).contains(lt)); - assertTrue(topology.portBroadcastDomainLinks.get(lt.getDst()).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(srcNpt).contains(lt)); + assertTrue(topology.portBroadcastDomainLinks.get(dstNpt).contains(lt)); } @Test @@ -356,18 +403,21 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { IOFSwitch sw1 = createMockSwitch(1L); IOFSwitch sw2 = createMockSwitch(2L); replay(sw1, sw2); - LinkTuple lt = new LinkTuple(sw1, 2, sw2, 1); - LinkInfo info = new LinkInfo(System.currentTimeMillis(), null, + Link lt = new Link(1L, 2, 2L, 1); + NodePortTuple srcNpt = new NodePortTuple(1L, 2); + NodePortTuple dstNpt = new NodePortTuple(2L, 1); + LinkInfo info = new LinkInfo(System.currentTimeMillis(), + System.currentTimeMillis(), null, 0, 0); topology.addOrUpdateLink(lt, info); // check invariants hold - assertNotNull(topology.switchLinks.get(lt.getSrc().getSw())); - assertTrue(topology.switchLinks.get(lt.getSrc().getSw()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getSrc())); - assertTrue(topology.portLinks.get(lt.getSrc()).contains(lt)); - assertNotNull(topology.portLinks.get(lt.getDst())); - assertTrue(topology.portLinks.get(lt.getDst()).contains(lt)); + assertNotNull(topology.switchLinks.get(lt.getSrc())); + assertTrue(topology.switchLinks.get(lt.getSrc()).contains(lt)); + assertNotNull(topology.portLinks.get(srcNpt)); + assertTrue(topology.portLinks.get(srcNpt).contains(lt)); + assertNotNull(topology.portLinks.get(dstNpt)); + assertTrue(topology.portLinks.get(dstNpt).contains(lt)); assertTrue(topology.links.containsKey(lt)); // check that it clears from memory @@ -376,6 +426,7 @@ public class LinkDiscoveryManagerTest extends FloodlightTestCase { getMockFloodlightProvider().dispatchRoleChanged(Role.SLAVE, Role.MASTER); // check that lldps were sent assertTrue(ldm.isSendLLDPsCalled); + assertTrue(ldm.isClearLinksCalled); ldm.reset(); } }