/**
* 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;
import java.util.ArrayDeque;
import java.util.Date;
import org.projectfloodlight.openflow.types.U64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.floodlightcontroller.linkdiscovery.ILinkDiscovery.LinkType;
import com.fasterxml.jackson.annotation.JsonIgnore;
public class LinkInfo {
private static final Logger log = LoggerFactory.getLogger(LinkInfo.class);
private Date firstSeenTime;
private Date lastLldpReceivedTime; /* Standard LLDP received time */
private Date lastBddpReceivedTime; /* Modified LLDP received time */
private U64 currentLatency;
private ArrayDeque<U64> latencyHistory;
private int latencyHistoryWindow;
private double latencyUpdateThreshold;
public LinkInfo(Date firstSeenTime, Date lastLldpReceivedTime, Date lastBddpReceivedTime) {
this.firstSeenTime = firstSeenTime;
this.lastLldpReceivedTime = lastLldpReceivedTime;
this.lastBddpReceivedTime = lastBddpReceivedTime;
this.currentLatency = null;
this.latencyHistory = new ArrayDeque<U64>(LinkDiscoveryManager.LATENCY_HISTORY_SIZE);
this.latencyHistoryWindow = LinkDiscoveryManager.LATENCY_HISTORY_SIZE;
this.latencyUpdateThreshold = LinkDiscoveryManager.LATENCY_UPDATE_THRESHOLD;
}
public LinkInfo(LinkInfo fromLinkInfo) {
this.firstSeenTime = fromLinkInfo.getFirstSeenTime();
this.lastLldpReceivedTime = fromLinkInfo.getUnicastValidTime();
this.lastBddpReceivedTime = fromLinkInfo.getMulticastValidTime();
this.currentLatency = fromLinkInfo.currentLatency;
this.latencyHistory = new ArrayDeque<U64>(fromLinkInfo.getLatencyHistory());
this.latencyHistoryWindow = fromLinkInfo.getLatencyHistoryWindow();
this.latencyUpdateThreshold = fromLinkInfo.getLatencyUpdateThreshold();
}
/**
* The port states stored here are topology's last knowledge of
* the state of the port. This mostly mirrors the state
* maintained in the port list in IOFSwitch (i.e. the one returned
* from getPort), except that during a port status message the
* IOFSwitch port state will already have been updated with the
* new port state, so topology needs to keep its own copy so that
* it can determine if the port state has changed and therefore
* requires the new state to be written to storage.
*/
private int getLatencyHistoryWindow() {
return latencyHistoryWindow;
}
private double getLatencyUpdateThreshold() {
return latencyUpdateThreshold;
}
private ArrayDeque<U64> getLatencyHistory() {
return latencyHistory;
}
private U64 getLatencyHistoryAverage() {
if (!isLatencyHistoryFull()) {
return null;
} else { /* guaranteed to be at latencyHistoryWindow capacity */
double avg = 0;
for (U64 l : latencyHistory) {
avg = avg + l.getValue();
}
avg = avg / latencyHistoryWindow;
return U64.of((long) avg);
}
}
/**
* Retrieve the current latency, and if necessary
* compute and replace the current latency with an
* updated latency based on the historical average.
* @return the most up-to-date latency as permitted by algorithm
*/
private U64 getLatency() {
U64 newLatency = getLatencyHistoryAverage();
if (newLatency != null) {
/* check threshold */
if ((((double) Math.abs(newLatency.getValue() - currentLatency.getValue()))
/ (currentLatency.getValue() == 0 ? 1 : currentLatency.getValue())
)
>= latencyUpdateThreshold) {
/* perform update */
log.debug("Updating link latency from {} to {}", currentLatency.getValue(), newLatency.getValue());
currentLatency = newLatency;
}
}
return currentLatency;
}
/**
* Determine if we've observed enough latency values
* to consider computing a new latency value based
* on the historical average. A minimum history size
* must be met prior to updating a latency.
*
* @return true if full; false if not full
*/
private boolean isLatencyHistoryFull() {
return (latencyHistory.size() == latencyHistoryWindow);
}
/**
* Append a new (presumably most recent) latency
* to the list. Sets the current latency if this
* is the first latency update performed. Note
* the latter serves as a latency initializer.
*
* @param latency
* @return latency to use for the link; either initial or historical average
*/
public U64 addObservedLatency(U64 latency) {
if (isLatencyHistoryFull()) {
latencyHistory.removeFirst();
}
latencyHistory.addLast(latency);
if (currentLatency == null) {
currentLatency = latency;
return currentLatency;
} else {
return getLatency();
}
}
/**
* Read-only. Retrieve the currently-assigned
* latency for this link. Does not attempt to
* update or compute an average.
* @return the latency; null if an initial latency has not been set
*/
public U64 getCurrentLatency() {
return currentLatency;
}
public Date getFirstSeenTime() {
return firstSeenTime;
}
public void setFirstSeenTime(Date firstSeenTime) {
this.firstSeenTime = firstSeenTime;
}
public Date getUnicastValidTime() {
return lastLldpReceivedTime;
}
public void setUnicastValidTime(Date unicastValidTime) {
this.lastLldpReceivedTime = unicastValidTime;
}
public Date getMulticastValidTime() {
return lastBddpReceivedTime;
}
public void setMulticastValidTime(Date multicastValidTime) {
this.lastBddpReceivedTime = multicastValidTime;
}
@JsonIgnore
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()
*/
@Override
public int hashCode() {
final int prime = 5557;
int result = 1;
result = prime * result + ((firstSeenTime == null) ? 0 : firstSeenTime.hashCode());
result = prime * result + ((lastLldpReceivedTime == null) ? 0 : lastLldpReceivedTime.hashCode());
result = prime * result + ((lastBddpReceivedTime == null) ? 0 : lastBddpReceivedTime.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 LinkInfo))
return false;
LinkInfo other = (LinkInfo) obj;
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 (!lastLldpReceivedTime.equals(other.lastLldpReceivedTime))
return false;
if (lastBddpReceivedTime == null) {
if (other.lastBddpReceivedTime != null)
return false;
} else if (!lastBddpReceivedTime.equals(other.lastBddpReceivedTime))
return false;
return true;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "LinkInfo [unicastValidTime=" + ((lastLldpReceivedTime == null) ? "null" : lastLldpReceivedTime.getTime())
+ ", multicastValidTime=" + ((lastBddpReceivedTime == null) ? "null" : lastBddpReceivedTime.getTime())
+ "]";
}
}