Gather topology element info to TopologyImpl
- Moved all the self-contained topology elements (*Event) to
TopologyImpl. (ONOS-1651)
Now {Switch, Port, Link, Host}Impl is just a handler attached to
TopologyImpl.
- BugFix: TopologyManager.addHost(HostEvent)
HostEvent could be pushed to reorder queue multiple times,
if multiple attachment point was given.
- BugFix: TopologyManager.{addLink, removePort}
Properly handle if Host attachment point was removed as side-effect.
- BugFix: Copy HostEvent#lastSeenTime
- BugFix: Event instance notified to listeners (api*Events) should be
the event which was/will be in the replica. (TopologyManager)
- Added/Modified debug log in TopologyManager so that log will be in
same format for each event type.
"{Added, Update, Removed} <Self-contained>"
- Removed backdoor method and use TestUtils instead.
Change-Id: If053d6f11f39574a188e7a52cb6194114f8afe5d
diff --git a/src/main/java/net/onrc/onos/core/topology/HostEvent.java b/src/main/java/net/onrc/onos/core/topology/HostEvent.java
index 4c8f78d..c35764e 100644
--- a/src/main/java/net/onrc/onos/core/topology/HostEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/HostEvent.java
@@ -59,6 +59,7 @@
super(original);
this.mac = original.mac;
this.attachmentPoints = new ArrayList<>(original.attachmentPoints);
+ this.lastSeenTime = original.lastSeenTime;
}
diff --git a/src/main/java/net/onrc/onos/core/topology/HostImpl.java b/src/main/java/net/onrc/onos/core/topology/HostImpl.java
index 469d335..d0964a1 100644
--- a/src/main/java/net/onrc/onos/core/topology/HostImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/HostImpl.java
@@ -9,50 +9,29 @@
import net.onrc.onos.core.util.SwitchPort;
/**
- * Host Object stored in In-memory Topology.
+ * Handler to Host object stored in In-memory Topology snapshot.
+ * <p/>
*/
public class HostImpl extends TopologyObject implements Host {
- //////////////////////////////////////////////////////
- /// Topology element attributes
- /// - any changes made here needs to be replicated.
- //////////////////////////////////////////////////////
- private HostEvent deviceObj;
+ private final MACAddress id;
/**
- * Creates a Host object based on {@link HostEvent}.
- *
- * @param topology Topology instance this object belongs to
- * @param scHost self contained {@link HostEvent}
- */
- public HostImpl(Topology topology, HostEvent scHost) {
- super(topology);
- Validate.notNull(scHost);
-
- // TODO should we assume deviceObj is already frozen before this call
- // or expect attribute update will happen after .
- if (scHost.isFrozen()) {
- this.deviceObj = scHost;
- } else {
- this.deviceObj = new HostEvent(scHost);
- this.deviceObj.freeze();
- }
- }
-
- /**
- * Creates a Host object with empty attributes.
+ * Creates a Host handler object.
*
* @param topology Topology instance this object belongs to
* @param mac MAC address of the host
*/
- public HostImpl(Topology topology, MACAddress mac) {
- this(topology, new HostEvent(mac).freeze());
+ HostImpl(TopologyInternal topology, MACAddress mac) {
+ super(topology);
+ Validate.notNull(mac);
+ this.id = mac;
}
@Override
public MACAddress getMacAddress() {
- return this.deviceObj.getMac();
+ return id;
}
@Override
@@ -60,7 +39,7 @@
List<Port> ports = new ArrayList<>();
topology.acquireReadLock();
try {
- for (SwitchPort swp : this.deviceObj.getAttachmentPoints()) {
+ for (SwitchPort swp : getHostEvent().getAttachmentPoints()) {
Port p = this.topology.getPort(swp);
if (p != null) {
ports.add(p);
@@ -74,7 +53,7 @@
@Override
public long getLastSeenTime() {
- return deviceObj.getLastSeenTime();
+ return this.topology.getHostEvent(id).getLastSeenTime();
}
@Override
@@ -82,45 +61,16 @@
return getMacAddress().toString();
}
- // TODO we may no longer need this. confirm and delete later.
- void setLastSeenTime(long lastSeenTime) {
- // XXX Following will make this instance thread unsafe. Need to use AtomicRef.
- HostEvent updated = new HostEvent(this.deviceObj);
- updated.setLastSeenTime(lastSeenTime);
- updated.freeze();
- this.deviceObj = updated;
- }
-
/**
- * Only {@link TopologyManager} should use this method.
+ * Gets the current HostEvent.
*
- * @param port the port that the device is attached to
+ * @return HostEvent
*/
- void addAttachmentPoint(Port port) {
- // XXX Following will make this instance thread unsafe. Need to use AtomicRef.
- HostEvent updated = new HostEvent(this.deviceObj);
- updated.removeAttachmentPoint(port.asSwitchPort());
- updated.addAttachmentPoint(port.asSwitchPort());
- updated.freeze();
- this.deviceObj = updated;
+ private HostEvent getHostEvent() {
+ return this.topology.getHostEvent(id);
}
/**
- * Only {@link TopologyManager} should use this method.
- *
- * @param port the port that the device is attached to
- */
- boolean removeAttachmentPoint(Port port) {
- // XXX Following will make this instance thread unsafe. Need to use AtomicRef.
- HostEvent updated = new HostEvent(this.deviceObj);
- final boolean result = updated.removeAttachmentPoint(port.asSwitchPort());
- updated.freeze();
- this.deviceObj = updated;
- return result;
- }
-
-
- /**
* Returns the type of topology object.
*
* @return the type of the topology object
diff --git a/src/main/java/net/onrc/onos/core/topology/LinkImpl.java b/src/main/java/net/onrc/onos/core/topology/LinkImpl.java
index 5ad0194..8379279 100644
--- a/src/main/java/net/onrc/onos/core/topology/LinkImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/LinkImpl.java
@@ -7,63 +7,36 @@
import org.apache.commons.lang.Validate;
/**
- * Link Object stored in In-memory Topology.
+ * Handler to Link object stored in In-memory Topology snapshot.
* <p/>
- * TODO REMOVE following design memo: This object itself may hold the DBObject,
- * but this Object itself will not issue any read/write to the DataStore.
*/
public class LinkImpl extends TopologyObject implements Link {
- //////////////////////////////////////////////////////
- /// Topology element attributes
- /// - any changes made here needs to be replicated.
- //////////////////////////////////////////////////////
- private LinkEvent linkObj;
+ private final LinkTuple id;
/**
- * Creates a Link object based on {@link LinkEvent}.
+ * Creates a Link handler object.
*
* @param topology Topology instance this object belongs to
- * @param scPort self contained {@link LinkEvent}
+ * @param linkTuple Link identifier
*/
- public LinkImpl(Topology topology, LinkEvent scPort) {
+ LinkImpl(TopologyInternal topology, LinkTuple linkTuple) {
super(topology);
- Validate.notNull(scPort);
-
- // TODO should we assume linkObj is already frozen before this call
- // or expect attribute update will happen after .
- if (scPort.isFrozen()) {
- this.linkObj = scPort;
- } else {
- this.linkObj = new LinkEvent(scPort);
- this.linkObj.freeze();
- }
- }
-
- /**
- * Creates a Link object with empty attributes.
- *
- * @param topology Topology instance this object belongs to
- * @param srcPort source port
- * @param dstPort destination port
- */
- public LinkImpl(Topology topology, Port srcPort, Port dstPort) {
- this(topology,
- new LinkEvent(srcPort.asSwitchPort(),
- dstPort.asSwitchPort()).freeze());
+ Validate.notNull(linkTuple);
+ this.id = linkTuple;
}
@Override
public LinkTuple getLinkTuple() {
- return linkObj.getLinkTuple();
+ return id;
}
@Override
public Switch getSrcSwitch() {
topology.acquireReadLock();
try {
- return topology.getSwitch(linkObj.getSrc().getDpid());
+ return topology.getSwitch(id.getSrc().getDpid());
} finally {
topology.releaseReadLock();
}
@@ -73,7 +46,7 @@
public Port getSrcPort() {
topology.acquireReadLock();
try {
- return topology.getPort(linkObj.getSrc());
+ return topology.getPort(id.getSrc());
} finally {
topology.releaseReadLock();
}
@@ -83,7 +56,7 @@
public Switch getDstSwitch() {
topology.acquireReadLock();
try {
- return topology.getSwitch(linkObj.getDst().getDpid());
+ return topology.getSwitch(id.getDst().getDpid());
} finally {
topology.releaseReadLock();
}
@@ -93,7 +66,7 @@
public Port getDstPort() {
topology.acquireReadLock();
try {
- return topology.getPort(linkObj.getDst());
+ return topology.getPort(id.getDst());
} finally {
topology.releaseReadLock();
}
@@ -107,38 +80,12 @@
@Override
public Double getCapacity() {
- return this.linkObj.getCapacity();
+ return this.topology.getLinkEvent(id).getCapacity();
}
- void setCapacity(Double capacity) {
- if (this.linkObj.isFrozen()) {
- this.linkObj = new LinkEvent(this.linkObj);
- this.linkObj.setCapacity(capacity);
- this.linkObj.freeze();
- } else {
- this.linkObj.setCapacity(capacity);
- }
- }
-
- // XXX actually replaces everything
- void replaceStringAttributes(LinkEvent updated) {
- Validate.isTrue(this.linkObj.getSrc().equals(updated.getSrc()),
- "Wrong LinkEvent given.");
- Validate.isTrue(this.linkObj.getDst().equals(updated.getDst()),
- "Wrong LinkEvent given.");
-
- // XXX simply replacing whole self-contained object for now
- if (updated.isFrozen()) {
- this.linkObj = updated;
- } else {
- this.linkObj = new LinkEvent(updated).freeze();
- }
- }
-
-
@Override
public String getStringAttribute(String attr) {
- return linkObj.getStringAttribute(attr);
+ return this.topology.getLinkEvent(id).getStringAttribute(attr);
}
@Override
@@ -153,7 +100,7 @@
@Override
public Map<String, String> getAllStringAttributes() {
- return linkObj.getAllStringAttributes();
+ return this.topology.getLinkEvent(id).getAllStringAttributes();
}
@Override
diff --git a/src/main/java/net/onrc/onos/core/topology/Port.java b/src/main/java/net/onrc/onos/core/topology/Port.java
index 4758b16..50feb99 100644
--- a/src/main/java/net/onrc/onos/core/topology/Port.java
+++ b/src/main/java/net/onrc/onos/core/topology/Port.java
@@ -120,11 +120,10 @@
*/
public Collection<Link> getIncomingLinks();
- // XXX Iterable or Collection?
/**
* Gets all the devices attached to this port.
*
* @return {@link Host}s attached to this port
*/
- public Iterable<Host> getHosts();
+ public Collection<Host> getHosts();
}
diff --git a/src/main/java/net/onrc/onos/core/topology/PortImpl.java b/src/main/java/net/onrc/onos/core/topology/PortImpl.java
index 935b3c5..4e0da5e 100644
--- a/src/main/java/net/onrc/onos/core/topology/PortImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/PortImpl.java
@@ -10,61 +10,35 @@
import net.onrc.onos.core.util.SwitchPort;
/**
- * Port Object stored in In-memory Topology.
+ * Handler to Port object stored in In-memory Topology snapshot.
* <p/>
- * TODO REMOVE following design memo: This object itself may hold the DBObject,
- * but this Object itself will not issue any read/write to the DataStore.
*/
public class PortImpl extends TopologyObject implements Port {
- //////////////////////////////////////////////////////
- /// Topology element attributes
- /// - any changes made here needs to be replicated.
- //////////////////////////////////////////////////////
- private PortEvent portObj;
+ private final SwitchPort id;
/**
- * Creates a Port object based on {@link PortEvent}.
- *
- * @param topology Topology instance this object belongs to
- * @param scPort self contained {@link PortEvent}
- */
- public PortImpl(Topology topology, PortEvent scPort) {
- super(topology);
- Validate.notNull(scPort);
-
- // TODO should we assume portObj is already frozen before this call
- // or expect attribute update will happen after .
- if (scPort.isFrozen()) {
- this.portObj = scPort;
- } else {
- this.portObj = new PortEvent(scPort);
- this.portObj.freeze();
- }
- }
-
- /**
- * Creates a Port object with empty attributes.
+ * Creates a Port handler object.
*
* @param topology Topology instance this object belongs to
* @param switchPort SwitchPort
*/
- public PortImpl(Topology topology, SwitchPort switchPort) {
- this(topology, new PortEvent(switchPort).freeze());
+ PortImpl(TopologyInternal topology, SwitchPort switchPort) {
+ super(topology);
+ Validate.notNull(switchPort);
+ this.id = switchPort;
}
/**
- * Creates a Port object with empty attributes.
+ * Creates a Port handler object.
*
* @param topology Topology instance this object belongs to
* @param dpid DPID
* @param number PortNumber
*/
- public PortImpl(Topology topology, Dpid dpid, PortNumber number) {
+ PortImpl(TopologyInternal topology, Dpid dpid, PortNumber number) {
this(topology, new SwitchPort(dpid, number));
- Validate.notNull(dpid);
- Validate.notNull(number);
}
@Override
@@ -79,7 +53,7 @@
@Override
public SwitchPort asSwitchPort() {
- return portObj.getSwitchPort();
+ return id;
}
@Override
@@ -87,12 +61,6 @@
return getStringAttribute(PortEvent.DESCRIPTION, "");
}
- void setDescription(String description) {
-// portObj.createStringAttribute(attr, value);
- // TODO implement using attributes
- throw new UnsupportedOperationException("Not implemented yet");
- }
-
@Override
public Long getHardwareAddress() {
// TODO implement using attributes?
@@ -170,7 +138,7 @@
}
@Override
- public Iterable<Host> getHosts() {
+ public Collection<Host> getHosts() {
topology.acquireReadLock();
try {
return topology.getHosts(this.asSwitchPort());
@@ -179,21 +147,9 @@
}
}
- void replaceStringAttributes(PortEvent updated) {
- Validate.isTrue(this.asSwitchPort().equals(updated.getSwitchPort()),
- "Wrong PortEvent given.");
-
- // XXX simply replacing whole self-contained object for now
- if (updated.isFrozen()) {
- this.portObj = updated;
- } else {
- this.portObj = new PortEvent(updated).freeze();
- }
- }
-
@Override
public String getStringAttribute(String attr) {
- return portObj.getStringAttribute(attr);
+ return this.topology.getPortEvent(id).getStringAttribute(attr);
}
@Override
@@ -208,7 +164,7 @@
@Override
public Map<String, String> getAllStringAttributes() {
- return portObj.getAllStringAttributes();
+ return this.topology.getPortEvent(id).getAllStringAttributes();
}
@Override
diff --git a/src/main/java/net/onrc/onos/core/topology/SwitchImpl.java b/src/main/java/net/onrc/onos/core/topology/SwitchImpl.java
index b0486b0..6be8b13 100644
--- a/src/main/java/net/onrc/onos/core/topology/SwitchImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/SwitchImpl.java
@@ -14,53 +14,30 @@
import org.apache.commons.lang.Validate;
/**
- * Switch Object stored in In-memory Topology.
+ * Handler to Switch object stored in In-memory Topology snapshot.
* <p/>
- * TODO REMOVE following design memo: This object itself may hold the DBObject,
- * but this Object itself will not issue any read/write to the DataStore.
+ *
*/
public class SwitchImpl extends TopologyObject implements Switch {
- //////////////////////////////////////////////////////
- /// Topology element attributes
- /// - any changes made here needs to be replicated.
- //////////////////////////////////////////////////////
- private SwitchEvent switchObj;
+ private final Dpid id;
/**
- * Creates a Switch object with empty attributes.
+ * Creates a Switch handler object.
*
* @param topology Topology instance this object belongs to
* @param dpid DPID
*/
- public SwitchImpl(Topology topology, Dpid dpid) {
- this(topology, new SwitchEvent(dpid).freeze());
- }
-
- /**
- * Creates a Switch object based on {@link SwitchEvent}.
- *
- * @param topology Topology instance this object belongs to
- * @param scSw self contained {@link SwitchEvent}
- */
- public SwitchImpl(Topology topology, SwitchEvent scSw) {
+ SwitchImpl(TopologyInternal topology, Dpid dpid) {
super(topology);
- Validate.notNull(scSw);
-
- // TODO should we assume switchObj is already frozen before this call
- // or expect attribute update will happen after .
- if (scSw.isFrozen()) {
- this.switchObj = scSw;
- } else {
- this.switchObj = new SwitchEvent(scSw);
- this.switchObj.freeze();
- }
+ Validate.notNull(dpid);
+ this.id = dpid;
}
@Override
public Dpid getDpid() {
- return switchObj.getDpid();
+ return this.id;
}
@Override
@@ -121,18 +98,6 @@
return hosts;
}
- void replaceStringAttributes(SwitchEvent updated) {
- Validate.isTrue(this.getDpid().equals(updated.getDpid()),
- "Wrong SwitchEvent given.");
-
- // XXX simply replacing whole self-contained object for now
- if (updated.isFrozen()) {
- this.switchObj = updated;
- } else {
- this.switchObj = new SwitchEvent(updated).freeze();
- }
- }
-
@Override
public Iterable<Link> getOutgoingLinks() {
LinkedList<Link> links = new LinkedList<Link>();
@@ -159,7 +124,7 @@
@Override
public String getStringAttribute(String attr) {
- return this.switchObj.getStringAttribute(attr);
+ return this.topology.getSwitchEvent(getDpid()).getStringAttribute(attr);
}
@Override
@@ -174,7 +139,7 @@
@Override
public Map<String, String> getAllStringAttributes() {
- return this.switchObj.getAllStringAttributes();
+ return this.topology.getSwitchEvent(getDpid()).getAllStringAttributes();
}
@Override
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
index decab6b..d79d204 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyImpl.java
@@ -6,6 +6,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
@@ -16,6 +17,7 @@
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.LinkTuple;
import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;
@@ -26,90 +28,113 @@
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
-public class TopologyImpl implements Topology {
- @SuppressWarnings("unused")
+
+/**
+ * Class to represent an instance of Topology Snapshot.
+ */
+public class TopologyImpl implements Topology, TopologyInternal {
+
private static final Logger log = LoggerFactory.getLogger(TopologyImpl.class);
+ // TODO Revisit Map types after implementing CoW/lock-free
+
// DPID -> Switch
- private final ConcurrentMap<Dpid, Switch> switches;
- // XXX may need to be careful when shallow copying.
- private final ConcurrentMap<Dpid, ConcurrentMap<PortNumber, Port>> ports;
+ private final ConcurrentMap<Dpid, SwitchEvent> switches;
+ private final ConcurrentMap<Dpid, ConcurrentMap<PortNumber, PortEvent>> ports;
// Index from Port to Host
- private final Multimap<SwitchPort, Host> hosts;
- private final ConcurrentMap<MACAddress, Host> mac2Host;
+ private final Multimap<SwitchPort, HostEvent> hosts;
+ private final ConcurrentMap<MACAddress, HostEvent> mac2Host;
// SwitchPort -> (type -> Link)
- private final ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> outgoingLinks;
- private final ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> incomingLinks;
+ private final ConcurrentMap<SwitchPort, ConcurrentMap<String, LinkEvent>> outgoingLinks;
+ private final ConcurrentMap<SwitchPort, ConcurrentMap<String, LinkEvent>> incomingLinks;
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock readLock = readWriteLock.readLock();
// TODO use the write lock after refactor
private Lock writeLock = readWriteLock.writeLock();
+ /**
+ * Create an empty Topology.
+ */
public TopologyImpl() {
// TODO: Does these object need to be stored in Concurrent Collection?
switches = new ConcurrentHashMap<>();
ports = new ConcurrentHashMap<>();
hosts = Multimaps.synchronizedMultimap(
- HashMultimap.<SwitchPort, Host>create());
+ HashMultimap.<SwitchPort, HostEvent>create());
mac2Host = new ConcurrentHashMap<>();
outgoingLinks = new ConcurrentHashMap<>();
incomingLinks = new ConcurrentHashMap<>();
}
+ /**
+ * Create a shallow copy of given Topology.
+ *
+ * @param original Topology
+ */
+ public TopologyImpl(TopologyImpl original) {
+ original.acquireReadLock();
+ try {
+ this.switches = new ConcurrentHashMap<>(original.switches);
+
+ // shallow copy Map in Map
+ this.ports = new ConcurrentHashMap<>(original.ports.size());
+ for (Entry<Dpid, ConcurrentMap<PortNumber, PortEvent>> entry
+ : original.ports.entrySet()) {
+ this.ports.put(entry.getKey(), new ConcurrentHashMap<>(entry.getValue()));
+ }
+
+ this.hosts = Multimaps.synchronizedMultimap(
+ HashMultimap.<SwitchPort, HostEvent>create(original.hosts));
+ this.mac2Host = new ConcurrentHashMap<>(original.mac2Host);
+
+ // shallow copy Map in Map
+ this.outgoingLinks = new ConcurrentHashMap<>(original.outgoingLinks.size());
+ for (Entry<SwitchPort, ConcurrentMap<String, LinkEvent>> entry
+ : original.outgoingLinks.entrySet()) {
+ this.outgoingLinks.put(entry.getKey(), new ConcurrentHashMap<>(entry.getValue()));
+ }
+
+ // shallow copy Map in Map
+ this.incomingLinks = new ConcurrentHashMap<>(original.incomingLinks.size());
+ for (Entry<SwitchPort, ConcurrentMap<String, LinkEvent>> entry
+ : original.incomingLinks.entrySet()) {
+ this.incomingLinks.put(entry.getKey(), new ConcurrentHashMap<>(entry.getValue()));
+ }
+ } finally {
+ original.releaseReadLock();
+ }
+ }
+
@Override
public Switch getSwitch(Dpid dpid) {
- // TODO Check if it is safe to directly return this Object.
- return switches.get(dpid);
- }
-
- // Only add switch.
- protected void putSwitch(Switch sw) {
- switches.put(sw.getDpid(), sw);
- ports.putIfAbsent(sw.getDpid(), new ConcurrentHashMap<PortNumber, Port>());
- }
-
- // XXX Will remove ports in snapshot as side-effect.
- protected void removeSwitch(Dpid dpid) {
- switches.remove(dpid);
- ports.remove(dpid);
- }
-
- // This method is expected to be serialized by writeLock.
- protected void putPort(Port port) {
- ConcurrentMap<PortNumber, Port> portMap = ports.get(port.getDpid());
- if (portMap == null) {
- portMap = new ConcurrentHashMap<>();
- ConcurrentMap<PortNumber, Port> existing =
- ports.putIfAbsent(port.getDpid(), portMap);
- if (existing != null) {
- // port map was added concurrently, using theirs
- portMap = existing;
- }
- }
- portMap.put(port.getNumber(), port);
- }
-
- protected void removePort(Port port) {
- ConcurrentMap<PortNumber, Port> portMap = ports.get(port.getDpid());
- if (portMap != null) {
- portMap.remove(port.getNumber());
+ final SwitchEvent sw = switches.get(dpid);
+ if (sw != null) {
+ return new SwitchImpl(this, dpid);
+ } else {
+ return null;
}
}
@Override
public Iterable<Switch> getSwitches() {
- // TODO Check if it is safe to directly return this Object.
- return Collections.unmodifiableCollection(switches.values());
+ List<Switch> list = new ArrayList<>(switches.size());
+ for (SwitchEvent elm : switches.values()) {
+ list.add(new SwitchImpl(this, elm.getDpid()));
+ }
+ return list;
}
@Override
public Port getPort(Dpid dpid, PortNumber number) {
- ConcurrentMap<PortNumber, Port> portMap = ports.get(dpid);
+ ConcurrentMap<PortNumber, PortEvent> portMap = ports.get(dpid);
if (portMap != null) {
- return portMap.get(number);
+ final PortEvent port = portMap.get(number);
+ if (port != null) {
+ return new PortImpl(this, port.getSwitchPort());
+ }
}
return null;
}
@@ -121,11 +146,15 @@
@Override
public Collection<Port> getPorts(Dpid dpid) {
- ConcurrentMap<PortNumber, Port> portMap = ports.get(dpid);
+ ConcurrentMap<PortNumber, PortEvent> portMap = ports.get(dpid);
if (portMap == null) {
return Collections.emptyList();
}
- return Collections.unmodifiableCollection(portMap.values());
+ List<Port> list = new ArrayList<>(portMap.size());
+ for (PortEvent elm : portMap.values()) {
+ list.add(new PortImpl(this, elm.getSwitchPort()));
+ }
+ return list;
}
@Override
@@ -135,32 +164,33 @@
@Override
public Link getOutgoingLink(SwitchPort port) {
- Map<String, Link> links = outgoingLinks.get(port);
+ Map<String, LinkEvent> links = outgoingLinks.get(port);
return getPacketLinkIfExists(links);
}
// TODO remove when we no longer need packet fall back behavior
/**
- * Gets the "packet" link if such exists, if not return whatever found.
+ * Gets the "packet" link if such exists,
+ * if not return whichever link is found first.
*
* @param links Collection of links to search from
* @return Link instance found or null if no link exists
*/
- private Link getPacketLinkIfExists(Map<String, Link> links) {
+ private Link getPacketLinkIfExists(Map<String, LinkEvent> links) {
if (links == null) {
return null;
}
- Link link = links.get(TopologyElement.TYPE_PACKET_LAYER);
+ LinkEvent link = links.get(TopologyElement.TYPE_PACKET_LAYER);
if (link != null) {
// return packet link
- return link;
+ return new LinkImpl(this, link.getLinkTuple());
} else {
// return whatever found
- Iterator<Link> it = links.values().iterator();
+ Iterator<LinkEvent> it = links.values().iterator();
if (it.hasNext()) {
- return it.next();
+ return new LinkImpl(this, it.next().getLinkTuple());
}
}
return null;
@@ -173,13 +203,38 @@
@Override
public Link getOutgoingLink(SwitchPort port, String type) {
- Map<String, Link> links = outgoingLinks.get(port);
- return links.get(type);
+ Map<String, LinkEvent> links = outgoingLinks.get(port);
+ final LinkEvent link = links.get(type);
+ if (link != null) {
+ return new LinkImpl(this, link.getLinkTuple());
+ }
+ return null;
}
@Override
public Collection<Link> getOutgoingLinks(SwitchPort port) {
- return Collections.unmodifiableCollection(outgoingLinks.get(port).values());
+ ConcurrentMap<String, LinkEvent> typeMap = outgoingLinks.get(port);
+ if (typeMap == null) {
+ return Collections.emptyList();
+ }
+ return toLinkImpls(typeMap.values());
+ }
+
+ /**
+ * Converts collection of LinkEvent to collection of LinkImpls.
+ *
+ * @param links collection of LinkEvent
+ * @return collection of LinkImpls
+ */
+ private Collection<Link> toLinkImpls(final Collection<LinkEvent> links) {
+ if (links == null) {
+ return Collections.emptyList();
+ }
+ List<Link> list = new ArrayList<>(links.size());
+ for (LinkEvent elm : links) {
+ list.add(new LinkImpl(this, elm.getLinkTuple()));
+ }
+ return list;
}
@Override
@@ -189,7 +244,7 @@
@Override
public Link getIncomingLink(SwitchPort port) {
- Map<String, Link> links = incomingLinks.get(port);
+ Map<String, LinkEvent> links = incomingLinks.get(port);
return getPacketLinkIfExists(links);
}
@@ -200,13 +255,21 @@
@Override
public Link getIncomingLink(SwitchPort port, String type) {
- Map<String, Link> links = incomingLinks.get(port);
- return links.get(type);
+ Map<String, LinkEvent> links = incomingLinks.get(port);
+ final LinkEvent link = links.get(type);
+ if (link != null) {
+ return new LinkImpl(this, link.getLinkTuple());
+ }
+ return null;
}
@Override
public Collection<Link> getIncomingLinks(SwitchPort port) {
- return Collections.unmodifiableCollection(incomingLinks.get(port).values());
+ ConcurrentMap<String, LinkEvent> typeMap = incomingLinks.get(port);
+ if (typeMap == null) {
+ return Collections.emptyList();
+ }
+ return toLinkImpls(typeMap.values());
}
@Override
@@ -248,94 +311,300 @@
public Iterable<Link> getLinks() {
List<Link> links = new ArrayList<>();
- for (Map<String, Link> portLinks : outgoingLinks.values()) {
- links.addAll(portLinks.values());
+ for (Map<String, LinkEvent> portLinks : outgoingLinks.values()) {
+ if (portLinks == null) {
+ continue;
+ }
+ for (LinkEvent elm : portLinks.values()) {
+ links.add(new LinkImpl(this, elm.getLinkTuple()));
+ }
}
return links;
}
- @GuardedBy("topology.writeLock")
- protected void putLink(Link link) {
- putLinkMap(outgoingLinks, link.getSrcPort().asSwitchPort(), link);
- putLinkMap(incomingLinks, link.getDstPort().asSwitchPort(), link);
+ @Override
+ public Host getHostByMac(MACAddress address) {
+ HostEvent host = mac2Host.get(address);
+ if (host != null) {
+ return new HostImpl(this, address);
+ }
+ return null;
+ }
+
+ @Override
+ public Iterable<Host> getHosts() {
+ return toHostImpls(mac2Host.values());
+ }
+
+ /**
+ * Converts collection of HostEvent to collection of HostImpl.
+ *
+ * @param events collection of HostEvent
+ * @return collection of HostImpl
+ */
+ private List<Host> toHostImpls(Collection<HostEvent> events) {
+ if (events == null) {
+ return Collections.emptyList();
+ }
+ List<Host> list = new ArrayList<>(events.size());
+ for (HostEvent elm : events) {
+ list.add(new HostImpl(this, elm.getMac()));
+ }
+ return list;
+ }
+
+ @Override
+ public Collection<Host> getHosts(SwitchPort port) {
+ return toHostImpls(hosts.get(port));
+ }
+
+ @Override
+ public SwitchEvent getSwitchEvent(final Dpid dpid) {
+ return this.switches.get(dpid);
+ }
+
+ @Override
+ public PortEvent getPortEvent(final SwitchPort port) {
+ ConcurrentMap<PortNumber, PortEvent> portMap = this.ports.get(port.getDpid());
+ if (portMap != null) {
+ return portMap.get(port.getPortNumber());
+ }
+ return null;
+ }
+
+ @Override
+ public LinkEvent getLinkEvent(final LinkTuple linkId) {
+ ConcurrentMap<String, LinkEvent> links = this.outgoingLinks.get(linkId.getSrc());
+ if (links == null) {
+ return null;
+ }
+
+ // TODO Should we look for Packet link first?
+ // Not unless invariant is broken.
+
+ for (LinkEvent link : links.values()) {
+ if (link.getDst().equals(linkId.getDst())) {
+ return link;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public LinkEvent getLinkEvent(final LinkTuple linkId, final String type) {
+ ConcurrentMap<String, LinkEvent> links = this.outgoingLinks.get(linkId.getSrc());
+ if (links == null) {
+ return null;
+ }
+ return links.get(type);
+ }
+
+ @Override
+ public Collection<LinkEvent> getLinkEvents(final LinkTuple linkId) {
+ ConcurrentMap<String, LinkEvent> links = this.outgoingLinks.get(linkId.getSrc());
+ if (links == null) {
+ return Collections.emptyList();
+ }
+
+ // unless invariant is broken, this should contain at most 1 element.
+ return Collections.unmodifiableCollection(links.values());
+ }
+
+ @Override
+ public HostEvent getHostEvent(final MACAddress mac) {
+ return this.mac2Host.get(mac);
+ }
+
+ /**
+ * Puts a SwitchEvent.
+ *
+ * @param sw Switch to add. (Will be frozen if not already)
+ */
+ @GuardedBy("writeLock")
+ protected void putSwitch(SwitchEvent sw) {
+ // TODO isFrozen check once we implement CoW/lock-free
+ switches.put(sw.getDpid(), sw.freeze());
+ ports.putIfAbsent(sw.getDpid(), new ConcurrentHashMap<PortNumber, PortEvent>());
+ }
+
+ /**
+ * Removes a SwitchEvent from this snapshot.
+ * <p/>
+ * Will also remove ports, if it has not been removed already.
+ *
+ * @param dpid Switch DPID
+ */
+ @GuardedBy("writeLock")
+ protected void removeSwitch(Dpid dpid) {
+ // TODO isFrozen check once we implement CoW/lock-free
+ switches.remove(dpid);
+ ConcurrentMap<PortNumber, PortEvent> removedPorts = ports.remove(dpid);
+ if (removedPorts != null && !removedPorts.isEmpty()) {
+ log.warn("Some ports were removed as side-effect of #removeSwitch({})", dpid);
+ }
+ }
+
+ /**
+ * Puts a PortEvent.
+ *
+ * @param port Port to add. (Will be frozen if not already)
+ */
+ @GuardedBy("writeLock")
+ protected void putPort(PortEvent port) {
+
+ ConcurrentMap<PortNumber, PortEvent> portMap = ports.get(port.getDpid());
+ if (portMap == null) {
+ portMap = new ConcurrentHashMap<>();
+ ConcurrentMap<PortNumber, PortEvent> existing
+ = ports.putIfAbsent(port.getDpid(), portMap);
+ if (existing != null) {
+ // port map was added concurrently, using theirs
+ portMap = existing;
+ }
+ }
+ portMap.put(port.getPortNumber(), port.freeze());
+ }
+
+ /**
+ * Removes a PortEvent from this snapshot.
+ *
+ * @param port SwitchPort to remove
+ */
+ @GuardedBy("writeLock")
+ protected void removePort(SwitchPort port) {
+ removePort(port.getDpid(), port.getPortNumber());
+ }
+
+ /**
+ * Removes a PortEvent from this snapshot.
+ * <p/>
+ * Will also remove ports, if it has not been removed already.
+ *
+ * @param dpid Switch DPID
+ * @param number PortNumber
+ */
+ @GuardedBy("writeLock")
+ protected void removePort(Dpid dpid, PortNumber number) {
+ // TODO sanity check Host attachment point.
+ ConcurrentMap<PortNumber, PortEvent> portMap = ports.get(dpid);
+ if (portMap != null) {
+ portMap.remove(number);
+ }
+ }
+
+ /**
+ * Puts a LinkEvent.
+ *
+ * @param link LinkEvent
+ */
+ @GuardedBy("writeLock")
+ protected void putLink(LinkEvent link) {
+ // TODO Do sanity check?
+ // - There cannot be 2 links in same direction between a port pair.
+ putLinkMap(outgoingLinks, link.getSrc(), link);
+ putLinkMap(incomingLinks, link.getDst(), link);
}
/**
* Helper method to update outgoingLinks, incomingLinks.
*
- * @param linkMap outgoingLinks or incomingLinks
- * @param port Map key
+ * @param linkMap outgoingLinks or incomingLinks to update
+ * @param port {@code linkMap} key to update
* @param link Link to add
*/
- @GuardedBy("topology.writeLock")
- private void putLinkMap(ConcurrentMap<SwitchPort, ConcurrentMap<String, Link>> linkMap,
- SwitchPort port, Link link) {
- ConcurrentMap<String, Link> portLinks = new ConcurrentHashMap<String, Link>(3);
- portLinks.put(link.getType(), link);
- Map<String, Link> existing = linkMap.putIfAbsent(
+ @GuardedBy("writeLock")
+ private void putLinkMap(ConcurrentMap<SwitchPort, ConcurrentMap<String, LinkEvent>> linkMap,
+ SwitchPort port, LinkEvent link) {
+
+ ConcurrentMap<String, LinkEvent> linksOnPort = linkMap.get(port);
+ if (linksOnPort == null) {
+ linksOnPort = new ConcurrentHashMap<>(4);
+ ConcurrentMap<String, LinkEvent> existing
+ = linkMap.putIfAbsent(
port,
- portLinks);
- if (existing != null) {
- // no conditional update here
- existing.put(link.getType(), link);
+ linksOnPort);
+
+ if (existing != null) {
+ linksOnPort = existing;
+ }
}
+ linksOnPort.put(link.getType(), link);
}
- @GuardedBy("topology.writeLock")
- protected void removeLink(Link link) {
- ConcurrentMap<String, Link> portLinks = outgoingLinks.get(link.getSrcPort().asSwitchPort());
+ /**
+ * Removes a LinkEvent from this snapshot.
+ *
+ * @param link Link to remove
+ * @param type type of link to remove
+ */
+ @GuardedBy("writeLock")
+ protected void removeLink(LinkTuple link, String type) {
+ ConcurrentMap<String, LinkEvent> portLinks
+ = outgoingLinks.get(link.getSrc());
if (portLinks != null) {
// no conditional update here
- portLinks.remove(link.getType());
+ portLinks.remove(type);
}
- portLinks = incomingLinks.get(link.getDstPort().asSwitchPort());
+ portLinks
+ = incomingLinks.get(link.getDst());
if (portLinks != null) {
// no conditional update here
- portLinks.remove(link.getType());
+ portLinks.remove(type);
}
}
- @Override
- public Host getHostByMac(MACAddress address) {
- return mac2Host.get(address);
- }
-
- @Override
- public Iterable<Host> getHosts() {
- return Collections.unmodifiableCollection(mac2Host.values());
- }
-
- @Override
- public Collection<Host> getHosts(SwitchPort port) {
- return Collections.unmodifiableCollection(hosts.get(port));
- }
-
- // This method is expected to be serialized by writeLock.
- // XXX new or updated device
- protected void putHost(Host host) {
- // assuming Host is immutable
- Host oldHost = mac2Host.get(host.getMacAddress());
- if (oldHost != null) {
- // remove old attachment point
- removeHost(oldHost);
+ /**
+ * Removes a LinkEvent from this snapshot.
+ *
+ * @param link Link to remove
+ */
+ @GuardedBy("writeLock")
+ protected void removeLink(LinkTuple link) {
+ Collection<LinkEvent> links = getLinkEvents(link);
+ for (LinkEvent l : links) {
+ removeLink(link, l.getType());
}
+ }
+
+ /**
+ * Puts a HostEvent.
+ * <p/>
+ * Removes attachment points for previous HostEvent and update
+ * them with new HostEvent
+ *
+ * @param host HostEvent
+ */
+ @GuardedBy("writeLock")
+ protected void putHost(HostEvent host) {
+ // Host cannot be simply put() to replace instance since it has mobility.
+ // Simply remove -> put for now.
+
+ // remove old attachment points
+ removeHost(host.getMac());
+
// add new attachment points
- for (Port port : host.getAttachmentPoints()) {
- // TODO Won't need remove() if we define Host equality to reflect
- // all of it's fields.
- hosts.remove(port.asSwitchPort(), host);
- hosts.put(port.asSwitchPort(), host);
+ for (SwitchPort port : host.getAttachmentPoints()) {
+ hosts.put(port, host);
}
- mac2Host.put(host.getMacAddress(), host);
+ mac2Host.put(host.getMac(), host);
}
- protected void removeHost(Host host) {
- for (Port port : host.getAttachmentPoints()) {
- hosts.remove(port.asSwitchPort(), host);
+ /**
+ * Removes a HostEvent from this snapshot.
+ *
+ * @param mac MACAddress of the Host to remove
+ */
+ @GuardedBy("writeLock")
+ protected void removeHost(MACAddress mac) {
+ HostEvent host = mac2Host.remove(mac);
+ if (host != null) {
+ for (SwitchPort port : host.getAttachmentPoints()) {
+ hosts.remove(port, host);
+ }
}
- mac2Host.remove(host.getMacAddress());
}
+
@Override
public void acquireReadLock() {
readLock.lock();
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyInternal.java b/src/main/java/net/onrc/onos/core/topology/TopologyInternal.java
new file mode 100644
index 0000000..10b008e
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyInternal.java
@@ -0,0 +1,64 @@
+package net.onrc.onos.core.topology;
+
+import java.util.Collection;
+
+import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.LinkTuple;
+import net.onrc.onos.core.util.SwitchPort;
+
+/**
+ * Interface to reference internal self-contained elements.
+ */
+public interface TopologyInternal extends Topology {
+
+ /**
+ * Gets a SwitchEvent.
+ *
+ * @param dpid Switch DPID
+ * @return the SwitchEvent for the Switch DPID if found, otherwise null
+ */
+ public SwitchEvent getSwitchEvent(Dpid dpid);
+
+ /**
+ * Gets a PortEvent.
+ *
+ * @param port Port identifier
+ * @return the PortEvent for the Port identifier if found, otherwise null
+ */
+ public PortEvent getPortEvent(SwitchPort port);
+
+ /**
+ * Gets a LinkEvent.
+ *
+ * @param linkId Link identifier
+ * @return the LinkEvent for the Link identifier if found, otherwise null
+ */
+ public LinkEvent getLinkEvent(LinkTuple linkId);
+
+ /**
+ * Gets a LinkEvent.
+ *
+ * @param linkId Link identifier
+ * @param type type
+ * @return the LinkEvent for the Link identifier and type if found, otherwise null
+ */
+ public LinkEvent getLinkEvent(final LinkTuple linkId, final String type);
+
+ /**
+ * Gets a LinkEvent.
+ *
+ * @param linkId Link identifier
+ * @return Collection of LinkEvent
+ */
+ public Collection<LinkEvent> getLinkEvents(LinkTuple linkId);
+
+ /**
+ * Gets a HostEvent.
+ *
+ * @param mac MACAddress of the host
+ * @return the HostEvent for the MACAddress if found, otherwise null
+ */
+ public HostEvent getHostEvent(MACAddress mac);
+
+}
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
index bdc6d67..7e7f2b8 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
@@ -2,13 +2,16 @@
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingQueue;
@@ -153,7 +156,7 @@
/**
* Event handler class.
*/
- private class EventHandler extends Thread implements
+ class EventHandler extends Thread implements
IEventChannelListener<byte[], TopologyEvent> {
private BlockingQueue<EventEntry<TopologyEvent>> topologyEvents =
new LinkedBlockingQueue<EventEntry<TopologyEvent>>();
@@ -243,6 +246,8 @@
//
// Extract the events
//
+ // FIXME Following event squashing logic based only on ID
+ // potentially lose attribute change.
switch (event.eventType()) {
case ENTRY_ADD:
log.debug("Topology event ENTRY_ADD: {}", topologyEvent);
@@ -836,6 +841,10 @@
}
}
+ //
+ // Methods to update topology replica
+ //
+
/**
* Adds a switch to the topology replica.
*
@@ -843,16 +852,15 @@
*/
@GuardedBy("topology.writeLock")
private void addSwitch(SwitchEvent switchEvent) {
- Switch sw = topology.getSwitch(switchEvent.getDpid());
- if (sw == null) {
- sw = new SwitchImpl(topology, switchEvent);
- topology.putSwitch(sw);
- } else {
- // TODO: Update the switch attributes
- log.debug("Update switch attributes");
- SwitchImpl impl = (SwitchImpl) sw;
- impl.replaceStringAttributes(switchEvent);
+ if (log.isDebugEnabled()) {
+ SwitchEvent sw = topology.getSwitchEvent(switchEvent.getDpid());
+ if (sw != null) {
+ log.debug("Update {}", switchEvent);
+ } else {
+ log.debug("Added {}", switchEvent);
+ }
}
+ topology.putSwitch(switchEvent.freeze());
apiAddedSwitchEvents.add(switchEvent);
}
@@ -865,8 +873,10 @@
*/
@GuardedBy("topology.writeLock")
private void removeSwitch(SwitchEvent switchEvent) {
- Switch sw = topology.getSwitch(switchEvent.getDpid());
- if (sw == null) {
+ final Dpid dpid = switchEvent.getDpid();
+
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ if (swInTopo == null) {
log.warn("Switch {} already removed, ignoring", switchEvent);
return;
}
@@ -875,19 +885,19 @@
// Remove all Ports on the Switch
//
ArrayList<PortEvent> portsToRemove = new ArrayList<>();
- for (Port port : sw.getPorts()) {
+ for (Port port : topology.getPorts(dpid)) {
log.warn("Port {} on Switch {} should be removed prior to removing Switch. Removing Port now.",
port, switchEvent);
- PortEvent portEvent = new PortEvent(port.getDpid(),
- port.getNumber());
+ PortEvent portEvent = new PortEvent(port.asSwitchPort());
portsToRemove.add(portEvent);
}
for (PortEvent portEvent : portsToRemove) {
removePort(portEvent);
}
- topology.removeSwitch(switchEvent.getDpid());
- apiRemovedSwitchEvents.add(switchEvent);
+ log.debug("Removed {}", swInTopo);
+ topology.removeSwitch(dpid);
+ apiRemovedSwitchEvents.add(swInTopo);
}
/**
@@ -906,100 +916,101 @@
return;
}
-
- Port port = sw.getPort(portEvent.getPortNumber());
- if (port == null) {
- port = new PortImpl(topology, portEvent);
- topology.putPort(port);
- } else {
- // TODO: Update the port attributes
- log.debug("Update port attributes");
- PortImpl impl = (PortImpl) port;
- impl.replaceStringAttributes(portEvent);
+ if (log.isDebugEnabled()) {
+ PortEvent port = topology.getPortEvent(portEvent.getSwitchPort());
+ if (port != null) {
+ log.debug("Update {}", portEvent);
+ } else {
+ log.debug("Added {}", portEvent);
+ }
}
+ topology.putPort(portEvent.freeze());
apiAddedPortEvents.add(portEvent);
}
/**
* Removes a port from the topology replica.
* <p/>
- * It will call {@link #removeHost(HostEvent)} for each hosts on this port
+ * It will remove attachment points from each hosts on this port
* and call {@link #removeLink(LinkEvent)} for each links on this port.
*
* @param portEvent the PortEvent with the port to remove.
*/
@GuardedBy("topology.writeLock")
private void removePort(PortEvent portEvent) {
- Switch sw = topology.getSwitch(portEvent.getDpid());
+ SwitchEvent sw = topology.getSwitchEvent(portEvent.getDpid());
if (sw == null) {
log.warn("Parent Switch for Port {} already removed, ignoring",
portEvent);
return;
}
- Port port = sw.getPort(portEvent.getPortNumber());
- if (port == null) {
+ final SwitchPort switchPort = portEvent.getSwitchPort();
+ PortEvent portInTopo = topology.getPortEvent(switchPort);
+ if (portInTopo == null) {
log.warn("Port {} already removed, ignoring", portEvent);
return;
}
//
- // Remove all Hosts attached to the Port
+ // Remove all Host attachment points bound to this Port
//
- ArrayList<HostEvent> hostsToRemove = new ArrayList<>();
- for (Host host : port.getHosts()) {
- log.debug("Removing Host {} on Port {}", host, portEvent);
- HostEvent hostEvent = new HostEvent(host.getMacAddress());
- SwitchPort switchPort = new SwitchPort(port.getSwitch().getDpid(),
- port.getNumber());
- hostEvent.addAttachmentPoint(switchPort);
- hostsToRemove.add(hostEvent);
+ List<HostEvent> hostsToUpdate = new ArrayList<>();
+ for (Host host : topology.getHosts(switchPort)) {
+ log.debug("Removing Host {} on Port {}", host, portInTopo);
+ HostEvent hostEvent = topology.getHostEvent(host.getMacAddress());
+ hostsToUpdate.add(hostEvent);
}
- for (HostEvent hostEvent : hostsToRemove) {
- removeHost(hostEvent);
+ for (HostEvent hostEvent : hostsToUpdate) {
+ HostEvent newHostEvent = new HostEvent(hostEvent);
+ newHostEvent.removeAttachmentPoint(switchPort);
+ newHostEvent.freeze();
+
+ // TODO should this event be fired inside #addHost?
+ if (newHostEvent.getAttachmentPoints().isEmpty()) {
+ // No more attachment point left -> remove Host
+ removeHost(hostEvent);
+ } else {
+ // Update Host
+ addHost(newHostEvent);
+ }
}
//
// Remove all Links connected to the Port
//
Set<Link> links = new HashSet<>();
- links.add(port.getOutgoingLink());
- links.add(port.getIncomingLink());
- ArrayList<LinkEvent> linksToRemove = new ArrayList<>();
+ links.addAll(topology.getOutgoingLinks(switchPort));
+ links.addAll(topology.getIncomingLinks(switchPort));
for (Link link : links) {
if (link == null) {
continue;
}
- log.debug("Removing Link {} on Port {}", link, portEvent);
- LinkEvent linkEvent = new LinkEvent(link.getSrcSwitch().getDpid(),
- link.getSrcPort().getNumber(),
- link.getDstSwitch().getDpid(),
- link.getDstPort().getNumber());
- linksToRemove.add(linkEvent);
- }
- for (LinkEvent linkEvent : linksToRemove) {
- removeLink(linkEvent);
+ LinkEvent linkEvent = topology.getLinkEvent(link.getLinkTuple());
+ if (linkEvent != null) {
+ log.debug("Removing Link {} on Port {}", link, portInTopo);
+ removeLink(linkEvent);
+ }
}
- // Remove the Port from the Switch
- topology.removePort(port);
+ // Remove the Port from Topology
+ log.debug("Removed {}", portInTopo);
+ topology.removePort(switchPort);
- apiRemovedPortEvents.add(portEvent);
+ apiRemovedPortEvents.add(portInTopo);
}
/**
* Adds a link to the topology replica.
* <p/>
- * It will call {@link #removeHost(HostEvent)} for each hosts on both ports.
+ * It will remove attachment points from each hosts using the same ports.
*
* @param linkEvent the LinkEvent with the link to add.
*/
@GuardedBy("topology.writeLock")
private void addLink(LinkEvent linkEvent) {
- Port srcPort = topology.getPort(linkEvent.getSrc().getDpid(),
- linkEvent.getSrc().getPortNumber());
- Port dstPort = topology.getPort(linkEvent.getDst().getDpid(),
- linkEvent.getDst().getPortNumber());
+ PortEvent srcPort = topology.getPortEvent(linkEvent.getSrc());
+ PortEvent dstPort = topology.getPortEvent(linkEvent.getDst());
if ((srcPort == null) || (dstPort == null)) {
log.debug("{} reordered because {} port is null", linkEvent,
(srcPort == null) ? "src" : "dst");
@@ -1011,47 +1022,64 @@
return;
}
- // Get the Link instance from the Destination Port Incoming Link
- // XXX domain knowledge: Link is discovered by LLDP,
- // thus incoming link is likely to be more up-to-date
+ // XXX domain knowledge: Sanity check: Port cannot have both Link and Host
// FIXME potentially local replica may not be up-to-date yet due to HZ delay.
// may need to manage local truth and use them instead.
- Link link = dstPort.getIncomingLink();
- assert (link == srcPort.getOutgoingLink());
- if (link == null) {
- link = new LinkImpl(topology, linkEvent);
- topology.putLink(link);
+ if (topology.getLinkEvent(linkEvent.getLinkTuple()) == null) {
+ // Only check for existing Host when adding new Link.
- // Remove all Hosts attached to the Ports
- ArrayList<HostEvent> hostsToRemove = new ArrayList<>();
- ArrayList<Port> ports = new ArrayList<>();
- ports.add(srcPort);
- ports.add(dstPort);
- for (Port port : ports) {
- for (Host host : port.getHosts()) {
+ // Remove all Hosts attached to the ports on both ends
+
+ Set<HostEvent> hostsToUpdate = new TreeSet<>(new Comparator<HostEvent>() {
+ // comparison only using ID(=MAC)
+ @Override
+ public int compare(HostEvent o1, HostEvent o2) {
+ return Long.compare(o1.getMac().toLong(), o2.getMac().toLong());
+ }
+ });
+
+ List<SwitchPort> portsToCheck = Arrays.asList(
+ srcPort.getSwitchPort(),
+ dstPort.getSwitchPort());
+
+ // Enumerate Host which needs to be updated by this Link add event
+ for (SwitchPort port : portsToCheck) {
+ for (Host host : topology.getHosts(port)) {
log.error("Host {} on Port {} should have been removed prior to adding Link {}",
host, port, linkEvent);
- // FIXME must get Host info from topology, when we add attrs.
- HostEvent hostEvent =
- new HostEvent(host.getMacAddress());
- SwitchPort switchPort =
- new SwitchPort(port.getSwitch().getDpid(),
- port.getNumber());
- // adding attachment port which needs to be removed
- hostEvent.addAttachmentPoint(switchPort);
- hostsToRemove.add(hostEvent);
+
+ HostEvent hostEvent = topology.getHostEvent(host.getMacAddress());
+ hostsToUpdate.add(hostEvent);
}
}
- for (HostEvent hostEvent : hostsToRemove) {
- removeHost(hostEvent);
+ // remove attachment point from them.
+ for (HostEvent hostEvent : hostsToUpdate) {
+ // remove port from attachment point and update
+ HostEvent newHostEvent = new HostEvent(hostEvent);
+ newHostEvent.removeAttachmentPoint(srcPort.getSwitchPort());
+ newHostEvent.removeAttachmentPoint(dstPort.getSwitchPort());
+ newHostEvent.freeze();
+
+ // TODO should this event be fired inside #addHost?
+ if (newHostEvent.getAttachmentPoints().isEmpty()) {
+ // No more attachment point left -> remove Host
+ removeHost(hostEvent);
+ } else {
+ // Update Host
+ addHost(newHostEvent);
+ }
}
- } else {
- // TODO: Update the link attributes
- log.debug("Update link attributes");
- LinkImpl impl = (LinkImpl) link;
- impl.replaceStringAttributes(linkEvent);
}
+ if (log.isDebugEnabled()) {
+ LinkEvent link = topology.getLinkEvent(linkEvent.getLinkTuple());
+ if (link != null) {
+ log.debug("Update {}", linkEvent);
+ } else {
+ log.debug("Added {}", linkEvent);
+ }
+ }
+ topology.putLink(linkEvent.freeze());
apiAddedLinkEvents.add(linkEvent);
}
@@ -1078,25 +1106,29 @@
return;
}
- //
- // Remove the Link instance from the Destination Port Incoming Link
- // and the Source Port Outgoing Link.
- //
- Link link = dstPort.getIncomingLink();
- if (link == null) {
- log.warn("Link {} already removed on destination Port", linkEvent);
- }
- link = srcPort.getOutgoingLink();
- if (link == null) {
- log.warn("Link {} already removed on src Port", linkEvent);
+ LinkEvent linkInTopo = topology.getLinkEvent(linkEvent.getLinkTuple(),
+ linkEvent.getType());
+ if (linkInTopo == null) {
+ log.warn("Link {} already removed, ignoring", linkEvent);
+ return;
}
- // TODO should we check that we get the same link from each port?
- if (link != null) {
- topology.removeLink(link);
+ if (log.isDebugEnabled()) {
+ // only do sanity check on debug level
+
+ Link linkIn = dstPort.getIncomingLink(linkEvent.getType());
+ if (linkIn == null) {
+ log.warn("Link {} already removed on destination Port", linkEvent);
+ }
+ Link linkOut = srcPort.getOutgoingLink(linkEvent.getType());
+ if (linkOut == null) {
+ log.warn("Link {} already removed on src Port", linkEvent);
+ }
}
- apiRemovedLinkEvents.add(linkEvent);
+ log.debug("Removed {}", linkInTopo);
+ topology.removeLink(linkEvent.getLinkTuple(), linkEvent.getType());
+ apiRemovedLinkEvents.add(linkInTopo);
}
/**
@@ -1104,23 +1136,23 @@
* <p/>
* TODO: Host-related work is incomplete.
* TODO: Eventually, we might need to consider reordering
- * or addLink() and addDevice() events on the same port.
+ * or {@link #addLink(LinkEvent)} and {@link #addHost(HostEvent)} events on the same port.
*
* @param hostEvent the HostEvent with the host to add.
*/
@GuardedBy("topology.writeLock")
private void addHost(HostEvent hostEvent) {
- log.debug("Adding a host to the topology with mac {}", hostEvent.getMac());
- Host host = topology.getHostByMac(hostEvent.getMac());
- if (host == null) {
- log.debug("Existing host was not found in the Topology: Adding new host: mac {}", hostEvent.getMac());
- host = new HostImpl(topology, hostEvent.getMac());
- }
+ // TODO Decide how to handle update scenario.
+ // If the new HostEvent has less attachment point compared to
+ // existing HostEvent, what should the event be?
+ // - AddHostEvent with some attachment point removed? (current behavior)
- HostImpl hostImpl = (HostImpl) host;
+ // create unfrozen copy
+ // for removing attachment points which already has a link
+ HostEvent modifiedHostEvent = new HostEvent(hostEvent);
- // Process each attachment point
+ // Verify each attachment point
boolean attachmentFound = false;
for (SwitchPort swp : hostEvent.getAttachmentPoints()) {
// XXX domain knowledge: Port must exist before Host
@@ -1129,32 +1161,49 @@
// Attached Ports must exist
Port port = topology.getPort(swp.getDpid(), swp.getPortNumber());
if (port == null) {
+ log.debug("{} reordered because port {} was not there", hostEvent, swp);
// Reordered event: delay the event in local cache
ByteBuffer id = hostEvent.getIDasByteBuffer();
reorderedAddedHostEvents.put(id, hostEvent);
- continue;
+ return; // should not continue if re-applying later
}
// Attached Ports must not have Link
if (port.getOutgoingLink() != null ||
port.getIncomingLink() != null) {
- log.warn("Link (Out:{},In:{}) exist on the attachment point, skipping mutation.",
- port.getOutgoingLink(),
- port.getIncomingLink());
+ log.warn("Link (Out:{},In:{}) exist on the attachment point. "
+ + "Ignoring this attachmentpoint ({}) from {}.",
+ port.getOutgoingLink(), port.getIncomingLink(),
+ swp, modifiedHostEvent);
+ // FIXME Should either reject, reorder this HostEvent,
+ // or remove attachment point from given HostEvent
+ // Removing attachment point from given HostEvent for now.
+ modifiedHostEvent.removeAttachmentPoint(swp);
continue;
}
- // Add Host <-> Port attachment
- hostImpl.addAttachmentPoint(port);
attachmentFound = true;
}
- hostImpl.setLastSeenTime(hostEvent.getLastSeenTime());
-
// Update the host in the topology
if (attachmentFound) {
- log.debug("Storing the host info into the Topology: mac {}", hostEvent.getMac());
- topology.putHost(host);
- apiAddedHostEvents.add(hostEvent);
+ if (modifiedHostEvent.getAttachmentPoints().isEmpty()) {
+ log.warn("No valid attachment point left. Ignoring."
+ + "original: {}, modified: {}", hostEvent, modifiedHostEvent);
+ // TODO Should we call #removeHost to trigger remove event?
+ // only if this call is update.
+ return;
+ }
+
+ if (log.isDebugEnabled()) {
+ HostEvent host = topology.getHostEvent(hostEvent.getMac());
+ if (host != null) {
+ log.debug("Update {}", modifiedHostEvent);
+ } else {
+ log.debug("Added {}", modifiedHostEvent);
+ }
+ }
+ topology.putHost(modifiedHostEvent.freeze());
+ apiAddedHostEvents.add(modifiedHostEvent);
}
}
@@ -1167,15 +1216,17 @@
*/
@GuardedBy("topology.writeLock")
private void removeHost(HostEvent hostEvent) {
- log.debug("Removing a host from the topology: mac {}", hostEvent.getMac());
- Host host = topology.getHostByMac(hostEvent.getMac());
- if (host == null) {
+
+ final MACAddress mac = hostEvent.getMac();
+ HostEvent hostInTopo = topology.getHostEvent(mac);
+ if (hostInTopo == null) {
log.warn("Host {} already removed, ignoring", hostEvent);
return;
}
- topology.removeHost(host);
- apiRemovedHostEvents.add(hostEvent);
+ log.debug("Removed {}", hostInTopo);
+ topology.removeHost(mac);
+ apiRemovedHostEvents.add(hostInTopo);
}
/**
@@ -1243,14 +1294,4 @@
return collection;
}
- /**
- * Replaces the internal datastore instance.
- *
- * @param dataStore instance
- *
- * @exclude Backdoor for unit testing purpose only, do not use.
- */
- void debugReplaceDataStore(final TopologyDatastore dataStoreService) {
- this.datastore = dataStoreService;
- }
}
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyObject.java b/src/main/java/net/onrc/onos/core/topology/TopologyObject.java
index f929926..445c2d8 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyObject.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyObject.java
@@ -3,28 +3,40 @@
import org.apache.commons.lang.Validate;
-
/**
* Base class for Topology Objects.
*/
public abstract class TopologyObject implements ITopologyElement {
- // XXX This will be a snapshot, thus should be replaceable
/**
- * Topology instance this object belongs to.
+ * Topology snapshot this object belongs to.
*/
- protected final Topology topology;
+ protected volatile TopologyInternal topology;
+
+ // XXX Updater to be used once we implement snapshot update.
+ // Should it be static or not.
+ // static: Low memory consumption, but higher contention on atomic update
+ // non-static: Updater per instance, but less chance of contention
+// private static final AtomicReferenceFieldUpdater<TopologyObject, TopologyImpl>
+// TOPOLOGY_UPDATER =
+// AtomicReferenceFieldUpdater.newUpdater(
+// TopologyObject.class, TopologyImpl.class, "topology");
/**
* Constructor.
*
* @param topology Topology instance this object belongs to
*/
- protected TopologyObject(Topology topology) {
+ protected TopologyObject(TopologyInternal topology) {
Validate.notNull(topology);
this.topology = topology;
}
+ // TODO Add method to replace topology snapshot
+ // - Request TopologyManager for latest TopologyImpl and swap?
+ // - Make caller specify TopologyImpl instance?
+ // -
+
/**
* Returns the type of topology object.
diff --git a/src/test/java/net/onrc/onos/core/topology/MockTopology.java b/src/test/java/net/onrc/onos/core/topology/MockTopology.java
index a089b6c..3208a0d 100644
--- a/src/test/java/net/onrc/onos/core/topology/MockTopology.java
+++ b/src/test/java/net/onrc/onos/core/topology/MockTopology.java
@@ -2,7 +2,9 @@
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.LinkTuple;
import net.onrc.onos.core.util.PortNumber;
+import net.onrc.onos.core.util.SwitchPort;
/**
* A mock class of Topology.
@@ -20,35 +22,35 @@
public SwitchImpl sw1, sw2, sw3, sw4;
public Switch addSwitch(Long switchId) {
- SwitchImpl sw = new SwitchImpl(this, new Dpid(switchId));
+ SwitchEvent sw = new SwitchEvent(new Dpid(switchId));
this.putSwitch(sw);
- return sw;
+ return this.getSwitch(sw.getDpid());
}
public Port addPort(Switch sw, Long portNumber) {
- PortImpl port = new PortImpl(this, sw.getDpid(),
+ PortEvent port = new PortEvent(sw.getDpid(),
new PortNumber(portNumber.shortValue()));
((TopologyImpl) this).putPort(port);
- return port;
+ return this.getPort(port.getSwitchPort());
}
- public Link[] addBidirectionalLinks(Long srcDpid, Long srcPortNo, Long dstDpid, Long dstPortNo) {
- Link[] links = new Link[2];
- final Dpid srcDpidObj = new Dpid(srcDpid);
- final Dpid dstDpidObj = new Dpid(dstDpid);
- final PortNumber srcPortNum = new PortNumber(srcPortNo.shortValue());
- final PortNumber dstPortNum = new PortNumber(dstPortNo.shortValue());
- links[0] = new LinkImpl(this,
- getPort(srcDpidObj, srcPortNum),
- getPort(dstDpidObj, dstPortNum));
- links[1] = new LinkImpl(this,
- getPort(dstDpidObj, dstPortNum),
- getPort(srcDpidObj, srcPortNum));
+ public void addBidirectionalLinks(Long srcDpid, Long srcPortNo, Long dstDpid, Long dstPortNo) {
+ addBidirectionalLinks(srcDpid, srcPortNo, dstDpid, dstPortNo, null);
+ }
+
+ public void addBidirectionalLinks(Long srcDpid, Long srcPortNo, Long dstDpid, Long dstPortNo, Double capacity) {
+ LinkEvent[] links = new LinkEvent[2];
+ final SwitchPort src = new SwitchPort(srcDpid, srcPortNo);
+ final SwitchPort dst = new SwitchPort(dstDpid, dstPortNo);
+ links[0] = new LinkEvent(src, dst);
+ links[1] = new LinkEvent(dst, src);
+ if (capacity != null) {
+ links[0].setCapacity(capacity);
+ links[1].setCapacity(capacity);
+ }
putLink(links[0]);
putLink(links[1]);
-
- return links;
}
/**
@@ -81,16 +83,11 @@
addPort(sw4, 42L); // sw4 -> sw2
addPort(sw4, 43L); // sw4 -> sw3
- addBidirectionalLinks(1L, 12L, 2L, 21L);
- addBidirectionalLinks(2L, 23L, 3L, 32L);
- addBidirectionalLinks(3L, 34L, 4L, 43L);
- addBidirectionalLinks(4L, 41L, 1L, 14L);
- addBidirectionalLinks(2L, 24L, 4L, 42L);
-
- // set capacity of all links to 1000Mbps
- for (Link link : getLinks()) {
- ((LinkImpl) link).setCapacity(1000.0);
- }
+ addBidirectionalLinks(1L, 12L, 2L, 21L, 1000.0);
+ addBidirectionalLinks(2L, 23L, 3L, 32L, 1000.0);
+ addBidirectionalLinks(3L, 34L, 4L, 43L, 1000.0);
+ addBidirectionalLinks(4L, 41L, 1L, 14L, 1000.0);
+ addBidirectionalLinks(2L, 24L, 4L, 42L, 1000.0);
}
/**
@@ -128,38 +125,39 @@
Port port43 = addPort(sw4, 43L); // sw4 -> sw3
MACAddress mac1 = MACAddress.valueOf("00:44:33:22:11:00");
- HostImpl dev1 = new HostImpl(this, mac1);
- dev1.addAttachmentPoint(port15);
- dev1.setLastSeenTime(1L);
- this.putHost(dev1);
+ HostEvent host1 = new HostEvent(mac1);
+ host1.addAttachmentPoint(port15.asSwitchPort());
+ host1.setLastSeenTime(1L);
+ this.putHost(host1);
MACAddress mac3 = MACAddress.valueOf("00:11:22:33:44:55");
- HostImpl dev3 = new HostImpl(this, mac3);
- dev3.addAttachmentPoint(port35);
- dev3.setLastSeenTime(1L);
- this.putHost(dev3);
+ HostEvent host3 = new HostEvent(mac3);
+ host3.addAttachmentPoint(port35.asSwitchPort());
+ host3.setLastSeenTime(1L);
+ this.putHost(host3);
- addBidirectionalLinks(1L, 12L, 2L, 21L);
- addBidirectionalLinks(2L, 23L, 3L, 32L);
- addBidirectionalLinks(3L, 34L, 4L, 43L);
- addBidirectionalLinks(4L, 41L, 1L, 14L);
- addBidirectionalLinks(2L, 24L, 4L, 42L);
-
- // set capacity of all links to 1000Mbps
- for (Link link : getLinks()) {
- ((LinkImpl) link).setCapacity(1000.0);
- }
+ addBidirectionalLinks(1L, 12L, 2L, 21L, 1000.0);
+ addBidirectionalLinks(2L, 23L, 3L, 32L, 1000.0);
+ addBidirectionalLinks(3L, 34L, 4L, 43L, 1000.0);
+ addBidirectionalLinks(4L, 41L, 1L, 14L, 1000.0);
+ addBidirectionalLinks(2L, 24L, 4L, 42L, 1000.0);
}
public void removeLink(Long srcDpid, Long srcPortNo, Long dstDpid, Long dstPortNo) {
- removeLink(getLink(new Dpid(srcDpid),
+ this.removeLink(new Dpid(srcDpid),
new PortNumber(srcPortNo.shortValue()),
new Dpid(dstDpid),
- new PortNumber(dstPortNo.shortValue())));
+ new PortNumber(dstPortNo.shortValue()));
}
public void removeLink(Dpid srcDpid, PortNumber srcPortNo,
- Dpid dstDpid, PortNumber dstPortNo) {
- super.removeLink(getLink(srcDpid, srcPortNo, dstDpid, dstPortNo));
+ Dpid dstDpid, PortNumber dstPortNo) {
+ this.removeLink(srcDpid, srcPortNo, dstDpid, dstPortNo,
+ TopologyElement.TYPE_PACKET_LAYER);
+ }
+ public void removeLink(Dpid srcDpid, PortNumber srcPortNo,
+ Dpid dstDpid, PortNumber dstPortNo, String type) {
+ super.removeLink(new LinkTuple(srcDpid, srcPortNo, dstDpid, dstPortNo),
+ type);
}
}
diff --git a/src/test/java/net/onrc/onos/core/topology/TopologyImplTest.java b/src/test/java/net/onrc/onos/core/topology/TopologyImplTest.java
index d02142a..df0818f 100644
--- a/src/test/java/net/onrc/onos/core/topology/TopologyImplTest.java
+++ b/src/test/java/net/onrc/onos/core/topology/TopologyImplTest.java
@@ -3,11 +3,14 @@
import static org.junit.Assert.*;
import static org.hamcrest.Matchers.*;
+import java.util.Collection;
import java.util.Iterator;
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.LinkTuple;
import net.onrc.onos.core.util.PortNumber;
+import net.onrc.onos.core.util.SwitchPort;
import org.junit.After;
import org.junit.Before;
@@ -31,6 +34,7 @@
// Set the test network size, it should be larger than 3
private static final long TEST_SWITCH_NUM = 100L;
+ private static final long TEST_HOST_NUM = TEST_SWITCH_NUM;
@Before
public void setUp() throws Exception {
@@ -39,32 +43,33 @@
// Create a number of switches and install two ports for each switch
for (long switchID = 1; switchID <= TEST_SWITCH_NUM; switchID++) {
- SwitchImpl testSwitch = new SwitchImpl(testTopology, new Dpid(switchID));
+ final Dpid dpid = new Dpid(switchID);
+ SwitchEvent testSwitch = new SwitchEvent(dpid);
testTopology.putSwitch(testSwitch);
- testTopology.putPort(new PortImpl(testTopology,
- new Dpid(switchID), PORT_NUMBER_1));
- testTopology.putPort(new PortImpl(testTopology,
- new Dpid(switchID), PORT_NUMBER_2));
- Port hostPort = new PortImpl(testTopology,
- new Dpid(switchID), new PortNumber(SWITCH_HOST_PORT.shortValue()));
+ testTopology.putPort(new PortEvent(dpid, PORT_NUMBER_1));
+ testTopology.putPort(new PortEvent(dpid, PORT_NUMBER_2));
+ PortEvent hostPort = new PortEvent(dpid,
+ new PortNumber(SWITCH_HOST_PORT.shortValue()));
testTopology.putPort(hostPort);
// Create a host for each switch
MACAddress devMac = MACAddress.valueOf(switchID);
- HostImpl testHost = new HostImpl(testTopology, devMac);
- testHost.addAttachmentPoint(hostPort);
+ HostEvent testHost = new HostEvent(devMac);
+ testHost.addAttachmentPoint(hostPort.getSwitchPort());
testTopology.putHost(testHost);
}
// Create one bidirectional link b/w two switches to construct a ring topology
for (long switchID = 1; switchID <= TEST_SWITCH_NUM; switchID++) {
- LinkImpl testLinkEast = new LinkImpl(testTopology,
- testTopology.getPort(new Dpid(switchID), PORT_NUMBER_2),
- testTopology.getPort(new Dpid(switchID % TEST_SWITCH_NUM + 1), PORT_NUMBER_1)
+ final Dpid dpidA = new Dpid(switchID);
+ final Dpid dpidB = new Dpid(switchID % TEST_SWITCH_NUM + 1);
+ LinkEvent testLinkEast = new LinkEvent(
+ testTopology.getPort(dpidA, PORT_NUMBER_2).asSwitchPort(),
+ testTopology.getPort(dpidB, PORT_NUMBER_1).asSwitchPort()
);
- LinkImpl testLinkWest = new LinkImpl(testTopology,
- testTopology.getPort(new Dpid(switchID % TEST_SWITCH_NUM + 1), PORT_NUMBER_1),
- testTopology.getPort(new Dpid(switchID), PORT_NUMBER_2)
+ LinkEvent testLinkWest = new LinkEvent(
+ testTopology.getPort(dpidB, PORT_NUMBER_1).asSwitchPort(),
+ testTopology.getPort(dpidA, PORT_NUMBER_2).asSwitchPort()
);
testTopology.putLink(testLinkEast);
testTopology.putLink(testLinkWest);
@@ -108,11 +113,12 @@
PortNumber bogusPortNum = new PortNumber((short) (SWITCH_PORT_2 + 1));
for (long switchID = 1; switchID <= TEST_SWITCH_NUM; switchID++) {
// Verify ports are in the graphDB
- assertNotNull(testTopology.getSwitch(new Dpid(switchID)).getPort(PORT_NUMBER_1));
- assertNotNull(testTopology.getSwitch(new Dpid(switchID)).getPort(PORT_NUMBER_2));
+ final Dpid dpid = new Dpid(switchID);
+ assertNotNull(testTopology.getSwitch(dpid).getPort(PORT_NUMBER_1));
+ assertNotNull(testTopology.getSwitch(dpid).getPort(PORT_NUMBER_2));
// Verify there is no such port in the graphDB
- assertNull(testTopology.getSwitch(new Dpid(switchID)).getPort(bogusPortNum));
+ assertNull(testTopology.getSwitch(dpid).getPort(bogusPortNum));
}
}
@@ -134,6 +140,23 @@
Switch srcSw = (objectLink.getSrcSwitch());
Switch dstSw = (objectLink.getDstSwitch());
+ LinkTuple linkId = objectLink.getLinkTuple();
+ // Verify the link through #getLink
+ Link linkA = testTopology.getLink(linkId.getSrc().getDpid(),
+ linkId.getSrc().getPortNumber(),
+ linkId.getDst().getDpid(),
+ linkId.getDst().getPortNumber());
+ assertEquals(linkId, linkA.getLinkTuple());
+
+ Link linkB = testTopology.getLink(linkId.getSrc().getDpid(),
+ linkId.getSrc().getPortNumber(),
+ linkId.getDst().getDpid(),
+ linkId.getDst().getPortNumber(),
+ TopologyElement.TYPE_PACKET_LAYER);
+ assertEquals(linkId, linkB.getLinkTuple());
+
+
+
// confirm link is forming a link
final long smallerDpid = Math.min(srcSw.getDpid().value(), dstSw.getDpid().value());
final long largerDpid = Math.max(srcSw.getDpid().value(), dstSw.getDpid().value());
@@ -152,11 +175,22 @@
public void testGetOutgoingLink() {
PortNumber bogusPortNum = new PortNumber((short) (SWITCH_PORT_2 + 1));
for (long switchID = 1; switchID <= TEST_SWITCH_NUM; switchID++) {
- assertNotNull(testTopology.getOutgoingLink(new Dpid(switchID), PORT_NUMBER_1));
- assertNotNull(testTopology.getOutgoingLink(new Dpid(switchID), PORT_NUMBER_2));
+ final Dpid dpid = new Dpid(switchID);
+ assertNotNull(testTopology.getOutgoingLink(dpid, PORT_NUMBER_1));
+ assertNotNull(testTopology.getOutgoingLink(dpid, PORT_NUMBER_2));
+
+ Link la = testTopology.getOutgoingLink(dpid, PORT_NUMBER_2,
+ TopologyElement.TYPE_PACKET_LAYER);
+ Link lb = testTopology.getOutgoingLink(dpid, PORT_NUMBER_2);
+
+ assertTrue(la.getLinkTuple().equals(lb.getLinkTuple()));
+
+ Collection<Link> links = testTopology.getOutgoingLinks(
+ new SwitchPort(dpid, PORT_NUMBER_1));
+ assertEquals(1, links.size());
// Verify there is no such link in the graphDB
- assertNull(testTopology.getOutgoingLink(new Dpid(switchID), bogusPortNum));
+ assertNull(testTopology.getOutgoingLink(dpid, bogusPortNum));
}
}
@@ -168,22 +202,33 @@
PortNumber bogusPortNum = new PortNumber((short) (SWITCH_PORT_2 + 1));
for (long switchID = 1; switchID <= TEST_SWITCH_NUM; switchID++) {
// Verify the links are in the graphDB
+ final Dpid dpid = new Dpid(switchID);
assertNotNull(testTopology.getIncomingLink(
- new Dpid(switchID), PORT_NUMBER_1));
+ dpid, PORT_NUMBER_1));
assertNotNull(testTopology.getIncomingLink(
- new Dpid(switchID), PORT_NUMBER_2));
+ dpid, PORT_NUMBER_2));
+
+ Link la = testTopology.getIncomingLink(dpid, PORT_NUMBER_2,
+ TopologyElement.TYPE_PACKET_LAYER);
+ Link lb = testTopology.getIncomingLink(dpid, PORT_NUMBER_2);
+
+ assertTrue(la.getLinkTuple().equals(lb.getLinkTuple()));
+
+ Collection<Link> links = testTopology.getIncomingLinks(
+ new SwitchPort(dpid, PORT_NUMBER_1));
+ assertEquals(1, links.size());
// Verify there is no such link in the graphDB
assertNull(testTopology.getIncomingLink(
- new Dpid(switchID), bogusPortNum));
+ dpid, bogusPortNum));
}
}
/**
- * Test the result of getDeviceByMac function.
+ * Test the result of getHostByMac function.
*/
@Test
- public void testGetDeviceByMac() {
+ public void testGetHostByMac() {
for (long switchID = 1; switchID <= TEST_SWITCH_NUM; switchID++) {
MACAddress devMac = MACAddress.valueOf(switchID);
@@ -193,21 +238,27 @@
}
/**
- * Test the result of removeDevice function.
+ * Test the result of removeHost function.
*/
@Test
- public void testRemoveDevice() {
+ public void testRemoveHost() {
int devCount = 0;
Iterator<Host> itr = testTopology.getHosts().iterator();
while (itr.hasNext()) {
Host currDev = itr.next();
- testTopology.removeHost(currDev);
- testTopology.getHostByMac(currDev.getMacAddress());
+ final MACAddress mac = currDev.getMacAddress();
+ testTopology.removeHost(mac);
+ assertNull(testTopology.getHostByMac(mac));
devCount++;
}
+ for (Switch sw : testTopology.getSwitches()) {
+ for (Port port : sw.getPorts()) {
+ assertTrue(port.getHosts().isEmpty());
+ }
+ }
// Verify all hosts have been removed successfully
- assertEquals(TEST_SWITCH_NUM, devCount);
+ assertEquals(TEST_HOST_NUM, devCount);
}
/**
@@ -224,7 +275,8 @@
Port srcPort = objectLink.getSrcPort();
Switch dstSw = (objectLink.getDstSwitch());
Port dstPort = objectLink.getDstPort();
- testTopology.removeLink(objectLink);
+
+ testTopology.removeLink(objectLink.getLinkTuple());
// Verify the link was removed successfully
assertNull(testTopology.getLink(
@@ -242,14 +294,18 @@
@Test
public void testRemoveSwitch() {
for (long switchID = 1; switchID <= TEST_SWITCH_NUM; switchID++) {
- Iterator<Host> itr = testTopology.getSwitch(new Dpid(switchID)).getHosts().iterator();
+ final Dpid dpid = new Dpid(switchID);
+ Iterator<Host> itr = testTopology.getSwitch(dpid).getHosts().iterator();
while (itr.hasNext()) {
- testTopology.removeHost(itr.next());
+ testTopology.removeHost(itr.next().getMacAddress());
}
- testTopology.removeSwitch(new Dpid(switchID));
+ for (Port port : testTopology.getSwitch(dpid).getPorts()) {
+ testTopology.removePort(port.asSwitchPort());
+ }
+ testTopology.removeSwitch(dpid);
// Verify the switch has been removed from the graphDB successfully
- assertNull(testTopology.getSwitch(new Dpid(switchID)));
+ assertNull(testTopology.getSwitch(dpid));
}
// Verify all switches have been removed successfully
diff --git a/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java b/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java
index e4f6c8c..309bfb4 100644
--- a/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java
+++ b/src/test/java/net/onrc/onos/core/topology/TopologyManagerTest.java
@@ -6,6 +6,8 @@
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
import java.util.ArrayList;
import java.util.Collection;
@@ -20,6 +22,7 @@
import net.onrc.onos.core.util.Dpid;
import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;
+import net.onrc.onos.core.util.TestUtils;
import org.easymock.EasyMock;
import org.junit.After;
@@ -111,8 +114,14 @@
// Create a topologyManager object for testing
topologyListeners = new CopyOnWriteArrayList<>();
theTopologyManager = new TopologyManager(registryService, topologyListeners);
+
+ // replace EventHandler to avoid thread from starting
+ TestUtils.setField(theTopologyManager, "eventHandler",
+ EasyMock.createNiceMock(TopologyManager.EventHandler.class));
theTopologyManager.startup(datagridService);
- theTopologyManager.debugReplaceDataStore(dataStoreService);
+
+ // replace data store with Mocked object
+ TestUtils.setField(theTopologyManager, "datastore", dataStoreService);
}
@After
@@ -335,4 +344,621 @@
verify(eventChannel);
}
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testAddSwitch() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ // check events to be fired
+ List<SwitchEvent> apiAddedSwitchEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedSwitchEvents");
+ assertThat(apiAddedSwitchEvents, hasItem(sw));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testAddPort() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumber = new PortNumber((short) 2);
+ PortEvent port = new PortEvent(dpid, portNumber);
+ port.createStringAttribute("fuzz", "buzz");
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, port);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portId = new SwitchPort(dpid, portNumber);
+ PortEvent portInTopo = topology.getPortEvent(portId);
+ assertEquals(port, portInTopo);
+ assertTrue(portInTopo.isFrozen());
+ assertEquals("buzz", portInTopo.getStringAttribute("fuzz"));
+
+ // check events to be fired
+ List<PortEvent> apiAddedPortEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedPortEvents");
+ assertThat(apiAddedPortEvents, hasItem(port));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testRemovePortThenSwitch() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumber = new PortNumber((short) 2);
+ PortEvent port = new PortEvent(dpid, portNumber);
+ port.createStringAttribute("fuzz", "buzz");
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, port);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portId = new SwitchPort(dpid, portNumber);
+ PortEvent portInTopo = topology.getPortEvent(portId);
+ assertEquals(port, portInTopo);
+ assertTrue(portInTopo.isFrozen());
+ assertEquals("buzz", portInTopo.getStringAttribute("fuzz"));
+
+ // remove in proper order
+ TestUtils.callMethod(theTopologyManager, "removePort",
+ PortEvent.class, new PortEvent(port));
+ TestUtils.callMethod(theTopologyManager, "removeSwitch",
+ SwitchEvent.class, new SwitchEvent(sw));
+
+
+ // check events to be fired
+ List<PortEvent> apiRemovedPortEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedPortEvents");
+ assertThat(apiRemovedPortEvents, hasItem(port));
+ List<SwitchEvent> apiRemovedSwitchEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedSwitchEvents");
+ assertThat(apiRemovedSwitchEvents, hasItem(sw));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testRemoveSwitch() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumber = new PortNumber((short) 2);
+ PortEvent port = new PortEvent(dpid, portNumber);
+ port.createStringAttribute("fuzz", "buzz");
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, port);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portId = new SwitchPort(dpid, portNumber);
+ PortEvent portInTopo = topology.getPortEvent(portId);
+ assertEquals(port, portInTopo);
+ assertTrue(portInTopo.isFrozen());
+ assertEquals("buzz", portInTopo.getStringAttribute("fuzz"));
+
+ // remove in in-proper order
+// TestUtils.callMethod(theTopologyManager, "removePort",
+// PortEvent.class, new PortEvent(port));
+ TestUtils.callMethod(theTopologyManager, "removeSwitch",
+ SwitchEvent.class, new SwitchEvent(sw));
+
+
+ // check events to be fired
+ // outcome should be the same as #testRemovePortThenSwitch
+ List<PortEvent> apiRemovedPortEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedPortEvents");
+ assertThat(apiRemovedPortEvents, hasItem(port));
+ List<SwitchEvent> apiRemovedSwitchEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedSwitchEvents");
+ assertThat(apiRemovedSwitchEvents, hasItem(sw));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testAddLink() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumberA = new PortNumber((short) 2);
+ PortEvent portA = new PortEvent(dpid, portNumberA);
+ portA.createStringAttribute("fuzz", "buzz");
+
+ final PortNumber portNumberB = new PortNumber((short) 3);
+ PortEvent portB = new PortEvent(dpid, portNumberB);
+ portB.createStringAttribute("fizz", "buz");
+
+ LinkEvent linkA = new LinkEvent(portA.getSwitchPort(), portB.getSwitchPort());
+ linkA.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+ LinkEvent linkB = new LinkEvent(portB.getSwitchPort(), portA.getSwitchPort());
+ linkB.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portA);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portB);
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkA);
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkB);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portIdA = new SwitchPort(dpid, portNumberA);
+ PortEvent portAInTopo = topology.getPortEvent(portIdA);
+ assertEquals(portA, portAInTopo);
+ assertTrue(portAInTopo.isFrozen());
+ assertEquals("buzz", portAInTopo.getStringAttribute("fuzz"));
+
+ final SwitchPort portIdB = new SwitchPort(dpid, portNumberB);
+ PortEvent portBInTopo = topology.getPortEvent(portIdB);
+ assertEquals(portB, portBInTopo);
+ assertTrue(portBInTopo.isFrozen());
+ assertEquals("buz", portBInTopo.getStringAttribute("fizz"));
+
+ LinkEvent linkAInTopo = topology.getLinkEvent(linkA.getLinkTuple());
+ assertEquals(linkA, linkAInTopo);
+ assertTrue(linkAInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkAInTopo.getType());
+
+ LinkEvent linkBInTopo = topology.getLinkEvent(linkB.getLinkTuple());
+ assertEquals(linkB, linkBInTopo);
+ assertTrue(linkBInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkBInTopo.getType());
+
+ // check events to be fired
+ List<LinkEvent> apiAddedLinkEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedLinkEvents");
+ assertThat(apiAddedLinkEvents, containsInAnyOrder(linkA, linkB));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testAddLinkKickingOffHost() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumberA = new PortNumber((short) 2);
+ PortEvent portA = new PortEvent(dpid, portNumberA);
+ portA.createStringAttribute("fuzz", "buzz");
+
+ final PortNumber portNumberB = new PortNumber((short) 3);
+ PortEvent portB = new PortEvent(dpid, portNumberB);
+ portB.createStringAttribute("fizz", "buz");
+
+ final PortNumber portNumberC = new PortNumber((short) 4);
+ PortEvent portC = new PortEvent(dpid, portNumberC);
+ portC.createStringAttribute("fizz", "buz");
+
+ final MACAddress macA = MACAddress.valueOf(666L);
+ HostEvent hostA = new HostEvent(macA);
+ hostA.addAttachmentPoint(portA.getSwitchPort());
+ final long timestampA = 392893200L;
+ hostA.setLastSeenTime(timestampA);
+
+ final MACAddress macB = MACAddress.valueOf(999L);
+ HostEvent hostB = new HostEvent(macB);
+ hostB.addAttachmentPoint(portB.getSwitchPort());
+ hostB.addAttachmentPoint(portC.getSwitchPort());
+ final long timestampB = 392893201L;
+ hostB.setLastSeenTime(timestampB);
+
+
+ LinkEvent linkA = new LinkEvent(portA.getSwitchPort(), portB.getSwitchPort());
+ linkA.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+ LinkEvent linkB = new LinkEvent(portB.getSwitchPort(), portA.getSwitchPort());
+ linkB.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portA);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portB);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portC);
+ TestUtils.callMethod(theTopologyManager, "addHost", HostEvent.class, hostA);
+ TestUtils.callMethod(theTopologyManager, "addHost", HostEvent.class, hostB);
+
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkA);
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkB);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portIdA = new SwitchPort(dpid, portNumberA);
+ PortEvent portAInTopo = topology.getPortEvent(portIdA);
+ assertEquals(portA, portAInTopo);
+ assertTrue(portAInTopo.isFrozen());
+ assertEquals("buzz", portAInTopo.getStringAttribute("fuzz"));
+
+ final SwitchPort portIdB = new SwitchPort(dpid, portNumberB);
+ PortEvent portBInTopo = topology.getPortEvent(portIdB);
+ assertEquals(portB, portBInTopo);
+ assertTrue(portBInTopo.isFrozen());
+ assertEquals("buz", portBInTopo.getStringAttribute("fizz"));
+
+ // hostA expected to be removed
+ assertNull(topology.getHostEvent(macA));
+ // hostB expected to be there with reduced attachment point
+ HostEvent hostBrev = new HostEvent(macB);
+ hostBrev.addAttachmentPoint(portC.getSwitchPort());
+ hostBrev.setLastSeenTime(timestampB);
+ hostBrev.freeze();
+ assertEquals(hostBrev, topology.getHostEvent(macB));
+
+
+ LinkEvent linkAInTopo = topology.getLinkEvent(linkA.getLinkTuple());
+ assertEquals(linkA, linkAInTopo);
+ assertTrue(linkAInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkAInTopo.getType());
+
+ LinkEvent linkBInTopo = topology.getLinkEvent(linkB.getLinkTuple());
+ assertEquals(linkB, linkBInTopo);
+ assertTrue(linkBInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkBInTopo.getType());
+
+ // check events to be fired
+ List<HostEvent> apiAddedHostEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedHostEvents");
+ assertThat(apiAddedHostEvents, hasItem(hostBrev));
+
+ List<HostEvent> apiRemovedHostEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedHostEvents");
+ assertThat(apiRemovedHostEvents, hasItem(hostA));
+ List<LinkEvent> apiAddedLinkEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedLinkEvents");
+ assertThat(apiAddedLinkEvents, containsInAnyOrder(linkA, linkB));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testRemoveLink() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumberA = new PortNumber((short) 2);
+ PortEvent portA = new PortEvent(dpid, portNumberA);
+ portA.createStringAttribute("fuzz", "buzz");
+
+ final PortNumber portNumberB = new PortNumber((short) 3);
+ PortEvent portB = new PortEvent(dpid, portNumberB);
+ portB.createStringAttribute("fizz", "buz");
+
+ LinkEvent linkA = new LinkEvent(portA.getSwitchPort(), portB.getSwitchPort());
+ linkA.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+ LinkEvent linkB = new LinkEvent(portB.getSwitchPort(), portA.getSwitchPort());
+ linkB.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portA);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portB);
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkA);
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkB);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portIdA = new SwitchPort(dpid, portNumberA);
+ PortEvent portAInTopo = topology.getPortEvent(portIdA);
+ assertEquals(portA, portAInTopo);
+ assertTrue(portAInTopo.isFrozen());
+ assertEquals("buzz", portAInTopo.getStringAttribute("fuzz"));
+
+ final SwitchPort portIdB = new SwitchPort(dpid, portNumberB);
+ PortEvent portBInTopo = topology.getPortEvent(portIdB);
+ assertEquals(portB, portBInTopo);
+ assertTrue(portBInTopo.isFrozen());
+ assertEquals("buz", portBInTopo.getStringAttribute("fizz"));
+
+ LinkEvent linkAInTopo = topology.getLinkEvent(linkA.getLinkTuple());
+ assertEquals(linkA, linkAInTopo);
+ assertTrue(linkAInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkAInTopo.getType());
+
+
+ LinkEvent linkBInTopo = topology.getLinkEvent(linkB.getLinkTuple());
+ assertEquals(linkB, linkBInTopo);
+ assertTrue(linkBInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkBInTopo.getType());
+
+ // check events to be fired
+ // FIXME if link flapped (linkA in this scenario),
+ // linkA appears in both removed and added is this expected behavior?
+ List<LinkEvent> apiAddedLinkEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedLinkEvents");
+ assertThat(apiAddedLinkEvents, containsInAnyOrder(linkA, linkB));
+
+ // clear event before removing Link
+ apiAddedLinkEvents.clear();
+
+ // remove link
+ TestUtils.callMethod(theTopologyManager, "removeLink", LinkEvent.class, new LinkEvent(linkA));
+
+ LinkEvent linkANotInTopo = topology.getLinkEvent(linkA.getLinkTuple());
+ assertNull(linkANotInTopo);
+
+ List<LinkEvent> apiRemovedLinkEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedLinkEvents");
+ assertThat(apiRemovedLinkEvents, hasItem(linkA));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testAddHostIgnoredByLink() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumberA = new PortNumber((short) 2);
+ PortEvent portA = new PortEvent(dpid, portNumberA);
+ portA.createStringAttribute("fuzz", "buzz");
+
+ final PortNumber portNumberB = new PortNumber((short) 3);
+ PortEvent portB = new PortEvent(dpid, portNumberB);
+ portB.createStringAttribute("fizz", "buz");
+
+ final PortNumber portNumberC = new PortNumber((short) 4);
+ PortEvent portC = new PortEvent(dpid, portNumberC);
+ portC.createStringAttribute("fizz", "buz");
+
+ LinkEvent linkA = new LinkEvent(portA.getSwitchPort(), portB.getSwitchPort());
+ linkA.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+ LinkEvent linkB = new LinkEvent(portB.getSwitchPort(), portA.getSwitchPort());
+ linkB.createStringAttribute(TopologyElement.TYPE,
+ TopologyElement.TYPE_OPTICAL_LAYER);
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portA);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portB);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portC);
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkA);
+ TestUtils.callMethod(theTopologyManager, "addLink", LinkEvent.class, linkB);
+
+ // Add hostA attached to a port which already has a link
+ final MACAddress macA = MACAddress.valueOf(666L);
+ HostEvent hostA = new HostEvent(macA);
+ hostA.addAttachmentPoint(portA.getSwitchPort());
+ final long timestampA = 392893200L;
+ hostA.setLastSeenTime(timestampA);
+
+ TestUtils.callMethod(theTopologyManager, "addHost", HostEvent.class, hostA);
+
+ // Add hostB attached to multiple ports,
+ // some of them which already has a link
+ final MACAddress macB = MACAddress.valueOf(999L);
+ HostEvent hostB = new HostEvent(macB);
+ hostB.addAttachmentPoint(portB.getSwitchPort());
+ hostB.addAttachmentPoint(portC.getSwitchPort());
+ final long timestampB = 392893201L;
+ hostB.setLastSeenTime(timestampB);
+
+ TestUtils.callMethod(theTopologyManager, "addHost", HostEvent.class, hostB);
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portIdA = new SwitchPort(dpid, portNumberA);
+ PortEvent portAInTopo = topology.getPortEvent(portIdA);
+ assertEquals(portA, portAInTopo);
+ assertTrue(portAInTopo.isFrozen());
+ assertEquals("buzz", portAInTopo.getStringAttribute("fuzz"));
+
+ final SwitchPort portIdB = new SwitchPort(dpid, portNumberB);
+ PortEvent portBInTopo = topology.getPortEvent(portIdB);
+ assertEquals(portB, portBInTopo);
+ assertTrue(portBInTopo.isFrozen());
+ assertEquals("buz", portBInTopo.getStringAttribute("fizz"));
+
+ // hostA expected to be completely ignored
+ assertNull(topology.getHostEvent(macA));
+ // hostB expected to be there with reduced attachment point
+ HostEvent hostBrev = new HostEvent(macB);
+ hostBrev.addAttachmentPoint(portC.getSwitchPort());
+ hostBrev.setLastSeenTime(timestampB);
+ hostBrev.freeze();
+ assertEquals(hostBrev, topology.getHostEvent(macB));
+
+
+ LinkEvent linkAInTopo = topology.getLinkEvent(linkA.getLinkTuple());
+ assertEquals(linkA, linkAInTopo);
+ assertTrue(linkAInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkAInTopo.getType());
+
+ LinkEvent linkBInTopo = topology.getLinkEvent(linkB.getLinkTuple());
+ assertEquals(linkB, linkBInTopo);
+ assertTrue(linkBInTopo.isFrozen());
+ assertEquals(TopologyElement.TYPE_OPTICAL_LAYER, linkBInTopo.getType());
+
+ // check events to be fired
+ // hostB should be added with reduced attachment points
+ List<HostEvent> apiAddedHostEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedHostEvents");
+ assertThat(apiAddedHostEvents, hasItem(hostBrev));
+
+ // hostA should not be ignored
+ List<HostEvent> apiRemovedHostEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedHostEvents");
+ assertThat(apiRemovedHostEvents, not(hasItem(hostA)));
+
+ List<LinkEvent> apiAddedLinkEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedLinkEvents");
+ assertThat(apiAddedLinkEvents, containsInAnyOrder(linkA, linkB));
+ }
+
+ /**
+ * Test to confirm topology replica transformation.
+ */
+ @Test
+ public void testAddHostMove() {
+ setupTopologyManager();
+
+ final Dpid dpid = new Dpid(1);
+ SwitchEvent sw = new SwitchEvent(dpid);
+ sw.createStringAttribute("foo", "bar");
+
+ final PortNumber portNumberA = new PortNumber((short) 2);
+ PortEvent portA = new PortEvent(dpid, portNumberA);
+ portA.createStringAttribute("fuzz", "buzz");
+
+ final PortNumber portNumberB = new PortNumber((short) 3);
+ PortEvent portB = new PortEvent(dpid, portNumberB);
+ portB.createStringAttribute("fizz", "buz");
+
+ final PortNumber portNumberC = new PortNumber((short) 4);
+ PortEvent portC = new PortEvent(dpid, portNumberC);
+ portC.createStringAttribute("fizz", "buz");
+
+ TestUtils.callMethod(theTopologyManager, "addSwitch", SwitchEvent.class, sw);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portA);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portB);
+ TestUtils.callMethod(theTopologyManager, "addPort", PortEvent.class, portC);
+
+ // Add hostA attached to a port which already has a link
+ final MACAddress macA = MACAddress.valueOf(666L);
+ HostEvent hostA = new HostEvent(macA);
+ hostA.addAttachmentPoint(portA.getSwitchPort());
+ final long timestampA = 392893200L;
+ hostA.setLastSeenTime(timestampA);
+
+ TestUtils.callMethod(theTopologyManager, "addHost", HostEvent.class, hostA);
+
+
+ // check topology structure
+ TopologyInternal topology = (TopologyInternal) theTopologyManager.getTopology();
+ SwitchEvent swInTopo = topology.getSwitchEvent(dpid);
+ assertEquals(sw, swInTopo);
+ assertTrue(swInTopo.isFrozen());
+ assertEquals("bar", swInTopo.getStringAttribute("foo"));
+
+ final SwitchPort portIdA = new SwitchPort(dpid, portNumberA);
+ PortEvent portAInTopo = topology.getPortEvent(portIdA);
+ assertEquals(portA, portAInTopo);
+ assertTrue(portAInTopo.isFrozen());
+ assertEquals("buzz", portAInTopo.getStringAttribute("fuzz"));
+
+ final SwitchPort portIdB = new SwitchPort(dpid, portNumberB);
+ PortEvent portBInTopo = topology.getPortEvent(portIdB);
+ assertEquals(portB, portBInTopo);
+ assertTrue(portBInTopo.isFrozen());
+ assertEquals("buz", portBInTopo.getStringAttribute("fizz"));
+
+ // hostA expected to be there
+ assertEquals(hostA, topology.getHostEvent(macA));
+ assertEquals(timestampA, topology.getHostEvent(macA).getLastSeenTime());
+
+ // check events to be fired
+ // hostA should be added
+ List<HostEvent> apiAddedHostEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedHostEvents");
+ assertThat(apiAddedHostEvents, hasItem(hostA));
+
+
+ // clear event before moving host
+ apiAddedHostEvents.clear();
+
+ HostEvent hostAmoved = new HostEvent(macA);
+ hostAmoved.addAttachmentPoint(portB.getSwitchPort());
+ final long timestampAmoved = 392893201L;
+ hostAmoved.setLastSeenTime(timestampAmoved);
+
+ TestUtils.callMethod(theTopologyManager, "addHost", HostEvent.class, hostAmoved);
+
+ assertEquals(hostAmoved, topology.getHostEvent(macA));
+ assertEquals(timestampAmoved, topology.getHostEvent(macA).getLastSeenTime());
+
+ // hostA expected to be there with new attachment point
+ apiAddedHostEvents
+ = TestUtils.getField(theTopologyManager, "apiAddedHostEvents");
+ assertThat(apiAddedHostEvents, hasItem(hostAmoved));
+
+ // hostA is updated not removed
+ List<HostEvent> apiRemovedHostEvents
+ = TestUtils.getField(theTopologyManager, "apiRemovedHostEvents");
+ assertThat(apiRemovedHostEvents, not(hasItem(hostA)));
+ }
}