Add string attributes to self-containd objects.
- Add common super class to *Event classes, which handles attributes.
- Add string attributes to *Event classes (ONOS-1564)
- Populate string attributes.
Picked random attribute obtained from OF, just to initially populate attrs.
Attributes to be used for each elements should be revisited later.
- *Impl class to use/reference self-contained objects
prep-work for snapshot in mind.
- unified equals implementations
- Add unfrozen Copy constructor.
- Add freeze check to fixed attributes.
- Remove get*Impl which was not really adding value.
Change-Id: I10f9538f87d133a22237bd8ab97b8de421d3930b
diff --git a/src/main/java/net/onrc/onos/core/topology/Device.java b/src/main/java/net/onrc/onos/core/topology/Device.java
index 9c4833e..a3eaf8b 100644
--- a/src/main/java/net/onrc/onos/core/topology/Device.java
+++ b/src/main/java/net/onrc/onos/core/topology/Device.java
@@ -25,7 +25,8 @@
/**
* Get the device attachment points.
* <p/>
- * Add requirement for Iteration order? Latest observed port first.
+ * TODO: There is only 1 attachment point right now.
+ * TODO: Add requirement for Iteration order? Latest observed port first.
*
* @return the device attachment points.
*/
@@ -34,9 +35,8 @@
/**
* Get the device last seen time.
* <p/>
- * TODO: what is the time definition?
*
- * @return the device last seen time.
+ * @return the device last seen time. (UTC in ms)
*/
public long getLastSeenTime();
}
diff --git a/src/main/java/net/onrc/onos/core/topology/DeviceEvent.java b/src/main/java/net/onrc/onos/core/topology/DeviceEvent.java
index 02041eb..110aef5 100644
--- a/src/main/java/net/onrc/onos/core/topology/DeviceEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/DeviceEvent.java
@@ -1,8 +1,11 @@
package net.onrc.onos.core.topology;
import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import java.util.Objects;
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.core.topology.web.serializers.DeviceEventSerializer;
@@ -20,19 +23,22 @@
* Multiple attachmentPoints can be specified to batch events into 1 object.
* Each should be treated as independent events.
* <p/>
- * TODO: We probably want common base class/interface for Self-Contained Event Object
+ * TODO: Rename to match what it is. (Switch/Port/Link/Device)Snapshot?
+ * FIXME: Current implementation directly use this object as
+ * Replication message, but should be sending update operation info.
*/
@JsonSerialize(using = DeviceEventSerializer.class)
-public class DeviceEvent {
+public class DeviceEvent extends TopologyElement<DeviceEvent> {
+
private final MACAddress mac;
- protected List<SwitchPort> attachmentPoints;
+ private List<SwitchPort> attachmentPoints;
private long lastSeenTime;
/**
* Default constructor for Serializer to use.
*/
@Deprecated
- public DeviceEvent() {
+ protected DeviceEvent() {
mac = null;
}
@@ -44,31 +50,103 @@
this.attachmentPoints = new LinkedList<>();
}
+ /**
+ * Create an unfrozen copy of given Object.
+ *
+ * @param original to make copy of.
+ */
+ public DeviceEvent(DeviceEvent original) {
+ super(original);
+ this.mac = original.mac;
+ this.attachmentPoints = new ArrayList<>(original.attachmentPoints);
+ }
+
+
public MACAddress getMac() {
return mac;
}
public List<SwitchPort> getAttachmentPoints() {
- return attachmentPoints;
+ return Collections.unmodifiableList(attachmentPoints);
}
public void setAttachmentPoints(List<SwitchPort> attachmentPoints) {
+ if (isFrozen()) {
+ throw new IllegalStateException("Tried to modify frozen instance: " + this);
+ }
this.attachmentPoints = attachmentPoints;
}
public void addAttachmentPoint(SwitchPort attachmentPoint) {
- // may need to maintain uniqness
+ if (isFrozen()) {
+ throw new IllegalStateException("Tried to modify frozen instance: " + this);
+ }
+ // may need to maintain uniqueness
this.attachmentPoints.add(0, attachmentPoint);
}
+ public boolean removeAttachmentPoint(SwitchPort attachmentPoint) {
+ if (isFrozen()) {
+ throw new IllegalStateException("Tried to modify frozen instance: " + this);
+ }
+ return this.attachmentPoints.remove(attachmentPoint);
+ }
+
+
+ public long getLastSeenTime() {
+ return lastSeenTime;
+ }
+
+ public void setLastSeenTime(long lastSeenTime) {
+ if (isFrozen()) {
+ throw new IllegalStateException("Tried to modify frozen instance: " + this);
+ }
+ this.lastSeenTime = lastSeenTime;
+ }
+
@Override
public String toString() {
return "[DeviceEvent " + mac + " attachmentPoints:" + attachmentPoints + "]";
}
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result
+ + ((attachmentPoints == null) ? 0 : attachmentPoints.hashCode());
+ result = prime * result + ((mac == null) ? 0 : mac.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!super.equals(obj)) {
+ return false;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ DeviceEvent other = (DeviceEvent) obj;
+
+ if (!super.equals(obj)) {
+ return false;
+ }
+
+ // XXX lastSeenTime excluded from Equality condition, is it OK?
+ return Objects.equals(mac, other.mac) &&
+ Objects.equals(this.attachmentPoints, other.attachmentPoints);
+ }
+
// Assuming mac is unique cluster-wide
public static ByteBuffer getDeviceID(final byte[] mac) {
- return (ByteBuffer) ByteBuffer.allocate(2 + mac.length).putChar('D').put(mac).flip();
+ return (ByteBuffer) ByteBuffer.allocate(2 + mac.length)
+ .putChar('D').put(mac).flip();
}
public byte[] getID() {
@@ -78,12 +156,4 @@
public ByteBuffer getIDasByteBuffer() {
return getDeviceID(mac.toBytes());
}
-
- public long getLastSeenTime() {
- return lastSeenTime;
- }
-
- public void setLastSeenTime(long lastSeenTime) {
- this.lastSeenTime = lastSeenTime;
- }
}
diff --git a/src/main/java/net/onrc/onos/core/topology/DeviceImpl.java b/src/main/java/net/onrc/onos/core/topology/DeviceImpl.java
index 19fe0a7..61de0d2 100644
--- a/src/main/java/net/onrc/onos/core/topology/DeviceImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/DeviceImpl.java
@@ -1,47 +1,99 @@
package net.onrc.onos.core.topology;
-import java.util.Collections;
-import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.lang.Validate;
import net.floodlightcontroller.util.MACAddress;
+import net.onrc.onos.core.util.SwitchPort;
/**
* Device Object stored in In-memory Topology.
*/
public class DeviceImpl extends TopologyObject implements Device {
- private final MACAddress macAddr;
- protected LinkedList<Port> attachmentPoints;
- private long lastSeenTime;
+ //////////////////////////////////////////////////////
+ /// Topology element attributes
+ /// - any changes made here needs to be replicated.
+ //////////////////////////////////////////////////////
+ private DeviceEvent deviceObj;
- public DeviceImpl(Topology topology, MACAddress mac) {
+ ///////////////////
+ /// In-memory index
+ ///////////////////
+
+ // none
+
+ /**
+ * Creates a Device object based on {@link DeviceEvent}.
+ *
+ * @param topology Topology instance this object belongs to
+ * @param scHost self contained {@link DeviceEvent}
+ */
+ public DeviceImpl(Topology topology, DeviceEvent scHost) {
super(topology);
- this.macAddr = mac;
- this.attachmentPoints = new LinkedList<>();
+ 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 DeviceEvent(scHost);
+ this.deviceObj.freeze();
+ }
+ }
+
+ /**
+ * Creates a Device object with empty attributes.
+ *
+ * @param topology Topology instance this object belongs to
+ * @param mac MAC address of the host
+ */
+ public DeviceImpl(Topology topology, MACAddress mac) {
+ this(topology, new DeviceEvent(mac).freeze());
}
@Override
public MACAddress getMacAddress() {
- return this.macAddr;
+ return this.deviceObj.getMac();
}
@Override
public Iterable<Port> getAttachmentPoints() {
- return Collections.unmodifiableList(this.attachmentPoints);
+ List<Port> ports = new ArrayList<>();
+ topology.acquireReadLock();
+ try {
+ for (SwitchPort swp : this.deviceObj.getAttachmentPoints()) {
+ Port p = this.topology.getPort(swp);
+ if (p != null) {
+ ports.add(p);
+ }
+ }
+ } finally {
+ topology.releaseReadLock();
+ }
+ return ports;
}
@Override
public long getLastSeenTime() {
- return lastSeenTime;
+ return deviceObj.getLastSeenTime();
}
@Override
public String toString() {
- return macAddr.toString();
+ return getMacAddress().toString();
}
+ // TODO we may no longer need this. confirm and delete later.
void setLastSeenTime(long lastSeenTime) {
- this.lastSeenTime = lastSeenTime;
+ // XXX Following will make this instance thread unsafe. Need to use AtomicRef.
+ DeviceEvent updated = new DeviceEvent(this.deviceObj);
+ updated.setLastSeenTime(lastSeenTime);
+ updated.freeze();
+ this.deviceObj = updated;
}
/**
@@ -50,8 +102,12 @@
* @param port the port that the device is attached to
*/
void addAttachmentPoint(Port port) {
- this.attachmentPoints.remove(port);
- this.attachmentPoints.addFirst(port);
+ // XXX Following will make this instance thread unsafe. Need to use AtomicRef.
+ DeviceEvent updated = new DeviceEvent(this.deviceObj);
+ updated.removeAttachmentPoint(port.asSwitchPort());
+ updated.addAttachmentPoint(port.asSwitchPort());
+ updated.freeze();
+ this.deviceObj = updated;
}
/**
@@ -60,7 +116,12 @@
* @param port the port that the device is attached to
*/
boolean removeAttachmentPoint(Port port) {
- return this.attachmentPoints.remove(port);
+ // XXX Following will make this instance thread unsafe. Need to use AtomicRef.
+ DeviceEvent updated = new DeviceEvent(this.deviceObj);
+ final boolean result = updated.removeAttachmentPoint(port.asSwitchPort());
+ updated.freeze();
+ this.deviceObj = updated;
+ return result;
}
diff --git a/src/main/java/net/onrc/onos/core/topology/LinkEvent.java b/src/main/java/net/onrc/onos/core/topology/LinkEvent.java
index 140e4c7..d73f80f 100644
--- a/src/main/java/net/onrc/onos/core/topology/LinkEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/LinkEvent.java
@@ -1,34 +1,65 @@
package net.onrc.onos.core.topology;
import java.nio.ByteBuffer;
+import java.util.Objects;
import net.onrc.onos.core.topology.web.serializers.LinkEventSerializer;
import net.onrc.onos.core.util.Dpid;
import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;
+import org.apache.commons.lang.Validate;
import org.codehaus.jackson.map.annotate.JsonSerialize;
/**
* Self-contained Link event Object.
* <p/>
- * TODO: We probably want common base class/interface for Self-Contained Event Object.
+ * TODO: Rename to match what it is. (Switch/Port/Link/Device)Snapshot?
+ * FIXME: Current implementation directly use this object as
+ * Replication message, but should be sending update operation info.
*/
@JsonSerialize(using = LinkEventSerializer.class)
-public class LinkEvent {
- protected final SwitchPort src;
- protected final SwitchPort dst;
+public class LinkEvent extends TopologyElement<LinkEvent> {
+
+ private final SwitchPort src;
+ private final SwitchPort dst;
+ // TODO add LastSeenTime, Capacity if appropriate
/**
* Default constructor for Serializer to use.
*/
@Deprecated
- public LinkEvent() {
+ protected LinkEvent() {
src = null;
dst = null;
}
+ /**
+ * Creates the Link object.
+ *
+ * @param src source SwitchPort
+ * @param dst destination SwitchPort
+ */
+ public LinkEvent(SwitchPort src, SwitchPort dst) {
+ Validate.notNull(src);
+ Validate.notNull(dst);
+
+ this.src = src;
+ this.dst = dst;
+ }
+
+ /**
+ * Create an unfrozen copy of given Object.
+ *
+ * @param original to make copy of.
+ */
+ public LinkEvent(LinkEvent original) {
+ super(original);
+ this.src = original.src;
+ this.dst = original.dst;
+ }
+
public LinkEvent(Long srcDpid, Long srcPortNo, Long dstDpid,
Long dstPortNo) {
src = new SwitchPort(srcDpid, srcPortNo);
@@ -42,16 +73,34 @@
link.getDstPort().getNumber());
}
+ /**
+ * Creates the Link object.
+ *
+ * @param srcDpid source switch DPID
+ * @param srcPortNo source port number
+ * @param dstDpid destination switch DPID
+ * @param dstPortNo destination port number
+ */
public LinkEvent(Dpid srcDpid, PortNumber srcPortNo,
Dpid dstDpid, PortNumber dstPortNo) {
src = new SwitchPort(srcDpid, srcPortNo);
dst = new SwitchPort(dstDpid, dstPortNo);
}
+ /**
+ * Gets the source SwitchPort.
+ *
+ * @return source SwitchPort.
+ */
public SwitchPort getSrc() {
return src;
}
+ /**
+ * Gets the destination SwitchPort.
+ *
+ * @return destination SwitchPort.
+ */
public SwitchPort getDst() {
return dst;
}
@@ -71,7 +120,8 @@
public static ByteBuffer getLinkID(Long srcDpid, Long srcPortNo,
Long dstDpid, Long dstPortNo) {
- return (ByteBuffer) ByteBuffer.allocate(LinkEvent.LINKID_BYTES).putChar('L')
+ return (ByteBuffer) ByteBuffer.allocate(LinkEvent.LINKID_BYTES)
+ .putChar('L')
.put(PortEvent.getPortID(srcDpid, srcPortNo))
.put(PortEvent.getPortID(dstDpid, dstPortNo)).flip();
}
@@ -88,7 +138,7 @@
@Override
public int hashCode() {
final int prime = 31;
- int result = 1;
+ int result = super.hashCode();
result = prime * result + ((dst == null) ? 0 : dst.hashCode());
result = prime * result + ((src == null) ? 0 : src.hashCode());
return result;
@@ -99,27 +149,22 @@
if (this == obj) {
return true;
}
+
if (obj == null) {
return false;
}
+
if (getClass() != obj.getClass()) {
return false;
}
LinkEvent other = (LinkEvent) obj;
- if (dst == null) {
- if (other.dst != null) {
- return false;
- }
- } else if (!dst.equals(other.dst)) {
+
+ // compare attributes
+ if (!super.equals(obj)) {
return false;
}
- if (src == null) {
- if (other.src != null) {
- return false;
- }
- } else if (!src.equals(other.src)) {
- return false;
- }
- return true;
+
+ return Objects.equals(this.src, other.src) &&
+ Objects.equals(this.dst, other.dst);
}
}
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 d0276d1..aa635ef 100644
--- a/src/main/java/net/onrc/onos/core/topology/LinkImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/LinkImpl.java
@@ -2,8 +2,7 @@
import java.util.Map;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import net.onrc.onos.core.util.SwitchPort;
+import org.apache.commons.lang.Validate;
/**
* Link Object stored in In-memory Topology.
@@ -12,50 +11,100 @@
* but this Object itself will not issue any read/write to the DataStore.
*/
public class LinkImpl extends TopologyObject implements Link {
- private SwitchPort srcPort;
- private SwitchPort dstPort;
+ //////////////////////////////////////////////////////
+ /// Topology element attributes
+ /// - any changes made here needs to be replicated.
+ //////////////////////////////////////////////////////
+ private LinkEvent linkObj;
+
+ // TODO remove?
protected static final Double DEFAULT_CAPACITY = Double.POSITIVE_INFINITY;
protected Double capacity = DEFAULT_CAPACITY;
+ ///////////////////
+ /// In-memory index
+ ///////////////////
+
+ // none
+
/**
- * Constructor for when a link is read from the database and the Ports
- * already exist in the in-memory topology.
+ * Creates a Link object based on {@link LinkEvent}.
*
- * @param topology
- * @param srcPort
- * @param dstPort
+ * @param topology Topology instance this object belongs to
+ * @param scPort self contained {@link LinkEvent}
+ */
+ public LinkImpl(Topology topology, LinkEvent scPort) {
+ 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) {
- super(topology);
- this.srcPort = srcPort.asSwitchPort();
- this.dstPort = dstPort.asSwitchPort();
+ this(topology,
+ new LinkEvent(srcPort.asSwitchPort(),
+ dstPort.asSwitchPort()).freeze());
}
@Override
public Switch getSrcSwitch() {
- return topology.getSwitch(srcPort.dpid());
+ topology.acquireReadLock();
+ try {
+ return topology.getSwitch(linkObj.getSrc().getDpid());
+ } finally {
+ topology.releaseReadLock();
+ }
}
@Override
public Port getSrcPort() {
- return topology.getPort(srcPort.dpid(), srcPort.port());
+ topology.acquireReadLock();
+ try {
+ return topology.getPort(linkObj.getSrc());
+ } finally {
+ topology.releaseReadLock();
+ }
}
@Override
public Switch getDstSwitch() {
- return topology.getSwitch(dstPort.dpid());
+ topology.acquireReadLock();
+ try {
+ return topology.getSwitch(linkObj.getDst().getDpid());
+ } finally {
+ topology.releaseReadLock();
+ }
}
@Override
public Port getDstPort() {
- return topology.getPort(dstPort.dpid(), dstPort.port());
+ topology.acquireReadLock();
+ try {
+ return topology.getPort(linkObj.getDst());
+ } finally {
+ topology.releaseReadLock();
+ }
}
@Override
public long getLastSeenTime() {
// TODO Auto-generated method stub
- return 0;
+ throw new UnsupportedOperationException("Not implemented yet");
}
@Override
@@ -63,18 +112,31 @@
return capacity;
}
- public void setCapacity(Double capacity) {
+ void setCapacity(Double capacity) {
this.capacity = capacity;
}
+ 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) {
- throw new UnsupportedOperationException("Not implemented yet");
+ return linkObj.getStringAttribute(attr);
}
@Override
- @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE",
- justification = "getStringAttribute might return null once implemented")
public String getStringAttribute(String attr, String def) {
final String v = getStringAttribute(attr);
if (v == null) {
@@ -86,7 +148,7 @@
@Override
public Map<String, String> getAllStringAttributes() {
- throw new UnsupportedOperationException("Not implemented yet");
+ return linkObj.getAllStringAttributes();
}
@Override
diff --git a/src/main/java/net/onrc/onos/core/topology/PortEvent.java b/src/main/java/net/onrc/onos/core/topology/PortEvent.java
index 296a567..222a641 100644
--- a/src/main/java/net/onrc/onos/core/topology/PortEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/PortEvent.java
@@ -7,20 +7,29 @@
import org.apache.commons.lang.Validate;
import org.codehaus.jackson.map.annotate.JsonSerialize;
+
import java.nio.ByteBuffer;
import java.util.Objects;
/**
* Self-contained Port event Object.
* <p/>
- * TODO: We probably want common base class/interface for Self-Contained Event Object.
+ * TODO: Rename to match what it is. (Switch/Port/Link/Device)Snapshot?
+ * FIXME: Current implementation directly use this object as
+ * Replication message, but should be sending update operation info.
*/
@JsonSerialize(using = PortEventSerializer.class)
-public class PortEvent {
+public class PortEvent extends TopologyElement<PortEvent> {
- protected final SwitchPort id;
+ private final SwitchPort id;
// TODO Add Hardware Address
- // TODO Add Description
+
+ // TODO: Where should the attribute names be defined?
+ /**
+ * Attribute name for description.
+ */
+ public static final String DESCRIPTION = "description";
+
/**
* Default constructor for Serializer to use.
@@ -30,22 +39,64 @@
id = null;
}
+ /**
+ * Creates the port object.
+ *
+ * @param switchPort SwitchPort to identify this port
+ */
public PortEvent(SwitchPort switchPort) {
+ Validate.notNull(switchPort);
this.id = switchPort;
}
+ /**
+ * Creates the port object.
+ *
+ * @param dpid SwitchPort to identify this port
+ * @param number PortNumber to identify this port
+ */
public PortEvent(Dpid dpid, PortNumber number) {
this.id = new SwitchPort(dpid, number);
}
+ /**
+ * Create an unfrozen copy of given Object.
+ *
+ * @param original to make copy of.
+ */
+ public PortEvent(PortEvent original) {
+ super(original);
+ this.id = original.id;
+ }
+
+ // TODO remove me when ready
public PortEvent(Long dpid, Long number) {
this.id = new SwitchPort(dpid, number);
}
+ /**
+ * Gets the SwitchPort identifying this port.
+ *
+ * @return SwitchPort
+ */
+ public SwitchPort getSwitchPort() {
+ return id;
+ }
+
+ /**
+ * Gets the Dpid of the switch this port belongs to.
+ *
+ * @return DPID
+ */
public Dpid getDpid() {
return id.getDpid();
}
+ /**
+ * Gets the port number.
+ *
+ * @return port number
+ */
public PortNumber getPortNumber() {
return id.getPortNumber();
}
@@ -56,17 +107,26 @@
return true;
}
- if (!(o instanceof PortEvent)) {
+ if (o == null) {
return false;
}
- PortEvent that = (PortEvent) o;
- return Objects.equals(this.id, that.id);
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ PortEvent other = (PortEvent) o;
+
+ // compare attributes
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ return Objects.equals(this.id, other.id);
}
@Override
public int hashCode() {
- return Objects.hashCode(id);
+ return 31 * super.hashCode() + Objects.hashCode(id);
}
@Override
@@ -89,7 +149,8 @@
if (number == null) {
throw new IllegalArgumentException("number cannot be null");
}
- return (ByteBuffer) ByteBuffer.allocate(PortEvent.PORTID_BYTES).putChar('S').putLong(dpid)
+ return (ByteBuffer) ByteBuffer.allocate(PortEvent.PORTID_BYTES)
+ .putChar('S').putLong(dpid)
.putChar('P').putLong(number).flip();
}
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 9332764..8a5e783 100644
--- a/src/main/java/net/onrc/onos/core/topology/PortImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/PortImpl.java
@@ -1,7 +1,9 @@
package net.onrc.onos.core.topology;
import java.util.Map;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import org.apache.commons.lang.Validate;
+
import net.onrc.onos.core.util.Dpid;
import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;
@@ -14,86 +16,166 @@
*/
public class PortImpl extends TopologyObject implements Port {
- private Switch sw;
+ //////////////////////////////////////////////////////
+ /// Topology element attributes
+ /// - any changes made here needs to be replicated.
+ //////////////////////////////////////////////////////
+ private PortEvent portObj;
- private PortNumber number;
- private String description;
+ ///////////////////
+ /// In-memory index
+ ///////////////////
- private final SwitchPort switchPort;
- public PortImpl(Topology topology, Switch parentSwitch, PortNumber number) {
+ /**
+ * 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);
- this.sw = parentSwitch;
- this.number = number;
+ Validate.notNull(scPort);
- switchPort = new SwitchPort(parentSwitch.getDpid(),
- number);
+ // 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.
+ *
+ * @param topology Topology instance this object belongs to
+ * @param switchPort SwitchPort
+ */
+ public PortImpl(Topology topology, SwitchPort switchPort) {
+ this(topology, new PortEvent(switchPort).freeze());
+ }
+
+ /**
+ * Creates a Port object with empty attributes.
+ *
+ * @param topology Topology instance this object belongs to
+ * @param dpid DPID
+ * @param number PortNumber
+ */
+ public PortImpl(Topology topology, Dpid dpid, PortNumber number) {
+ this(topology, new SwitchPort(dpid, number));
+ Validate.notNull(dpid);
+ Validate.notNull(number);
+ }
+
+ public PortImpl(Topology topology, Long dpid, Long number) {
+ this(topology, new SwitchPort(dpid, number));
+ Validate.notNull(dpid);
+ Validate.notNull(number);
+ }
+
+ @Deprecated
+ public PortImpl(Topology topology, Switch parentSwitch, PortNumber number) {
+ this(topology, new SwitchPort(parentSwitch.getDpid(), number));
+ }
+
+ @Deprecated
public PortImpl(Topology topology, Switch parentSwitch, Long number) {
this(topology, parentSwitch, new PortNumber(number.shortValue()));
}
@Override
public Dpid getDpid() {
- return sw.getDpid();
+ return asSwitchPort().getDpid();
}
@Override
public PortNumber getNumber() {
- return number;
+ return asSwitchPort().getPortNumber();
}
@Override
public SwitchPort asSwitchPort() {
- return switchPort;
+ return portObj.getSwitchPort();
}
@Override
public String getDescription() {
- return description;
+ return getStringAttribute(PortEvent.DESCRIPTION, "");
}
- public void setDescription(String description) {
- this.description = description;
- }
-
- @Override
- public Long getHardwareAddress() {
- // TODO Auto-generated method stub
- return 0L;
- }
-
- @Override
- public Switch getSwitch() {
- return sw;
- }
-
- @Override
- public Link getOutgoingLink() {
- return topology.getOutgoingLink(switchPort.dpid(),
- switchPort.port());
- }
-
- @Override
- public Link getIncomingLink() {
- return topology.getIncomingLink(switchPort.dpid(),
- switchPort.port());
- }
-
- @Override
- public Iterable<Device> getDevices() {
- return topology.getDevices(this.asSwitchPort());
- }
-
- @Override
- public String getStringAttribute(String attr) {
+ void setDescription(String description) {
+// portObj.createStringAttribute(attr, value);
+ // TODO implement using attributes
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
- @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE",
- justification = "getStringAttribute might return null once implemented")
+ public Long getHardwareAddress() {
+ // TODO implement using attributes?
+ throw new UnsupportedOperationException("Not implemented yet");
+ }
+
+ @Override
+ public Switch getSwitch() {
+ topology.acquireReadLock();
+ try {
+ return topology.getSwitch(getDpid());
+ } finally {
+ topology.releaseReadLock();
+ }
+ }
+
+ @Override
+ public Link getOutgoingLink() {
+ topology.acquireReadLock();
+ try {
+ return topology.getOutgoingLink(asSwitchPort());
+ } finally {
+ topology.releaseReadLock();
+ }
+ }
+
+ @Override
+ public Link getIncomingLink() {
+ topology.acquireReadLock();
+ try {
+ return topology.getIncomingLink(asSwitchPort());
+ } finally {
+ topology.releaseReadLock();
+ }
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ topology.acquireReadLock();
+ try {
+ return topology.getDevices(this.asSwitchPort());
+ } finally {
+ topology.releaseReadLock();
+ }
+ }
+
+ 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);
+ }
+
+ @Override
public String getStringAttribute(String attr, String def) {
final String v = getStringAttribute(attr);
if (v == null) {
@@ -105,7 +187,7 @@
@Override
public Map<String, String> getAllStringAttributes() {
- throw new UnsupportedOperationException("Not implemented yet");
+ return portObj.getAllStringAttributes();
}
@Override
diff --git a/src/main/java/net/onrc/onos/core/topology/StringAttributes.java b/src/main/java/net/onrc/onos/core/topology/StringAttributes.java
index 89c5b5c..16f69a8 100644
--- a/src/main/java/net/onrc/onos/core/topology/StringAttributes.java
+++ b/src/main/java/net/onrc/onos/core/topology/StringAttributes.java
@@ -2,6 +2,8 @@
import java.util.Map;
+// TODO We may want to separate configuration attributes and
+// running state attributes.
/**
* Interface for Elements with StringAttributes.
*/
diff --git a/src/main/java/net/onrc/onos/core/topology/SwitchEvent.java b/src/main/java/net/onrc/onos/core/topology/SwitchEvent.java
index de0fff6..b6246d4 100644
--- a/src/main/java/net/onrc/onos/core/topology/SwitchEvent.java
+++ b/src/main/java/net/onrc/onos/core/topology/SwitchEvent.java
@@ -7,28 +7,57 @@
import java.util.Objects;
import org.codehaus.jackson.map.annotate.JsonSerialize;
+import org.apache.commons.lang.Validate;
/**
* Self-contained Switch Object.
* <p/>
- * TODO: We probably want common base class/interface for Self-Contained Event Object.
+ * TODO: Rename to match what it is. (Switch/Port/Link/Device)Snapshot?
+ * FIXME: Current implementation directly use this object as
+ * Replication message, but should be sending update operation info.
*/
@JsonSerialize(using = SwitchEventSerializer.class)
-public class SwitchEvent {
- protected final Dpid dpid;
+public class SwitchEvent extends TopologyElement<SwitchEvent> {
+ private final Dpid dpid;
/**
* Default constructor for Serializer to use.
*/
@Deprecated
- public SwitchEvent() {
+ protected SwitchEvent() {
dpid = null;
}
+ /**
+ * Creates the switch object.
+ *
+ * @param dpid Dpid to identify this switch
+ */
+ public SwitchEvent(Dpid dpid) {
+ Validate.notNull(dpid);
+ this.dpid = dpid;
+ }
+
+ /**
+ * Create an unfrozen copy of given Object.
+ *
+ * @param original to make copy of.
+ */
+ public SwitchEvent(SwitchEvent original) {
+ super(original);
+ this.dpid = original.dpid;
+ }
+
+ // TODO remove me when ready
public SwitchEvent(Long dpid) {
this.dpid = new Dpid(dpid);
}
+ /**
+ * Gets the DPID identifying this switch.
+ *
+ * @return DPID
+ */
public Dpid getDpid() {
return dpid;
}
@@ -39,17 +68,26 @@
return true;
}
- if (!(o instanceof SwitchEvent)) {
+ if (o == null) {
return false;
}
- SwitchEvent that = (SwitchEvent) o;
- return Objects.equals(this.dpid, that.dpid);
+ if (getClass() != o.getClass()) {
+ return false;
+ }
+ SwitchEvent other = (SwitchEvent) o;
+
+ // compare attributes
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ return Objects.equals(this.dpid, other.dpid);
}
@Override
public int hashCode() {
- return Objects.hashCode(dpid);
+ return 31 * super.hashCode() + Objects.hashCode(dpid);
}
@Override
@@ -59,11 +97,16 @@
public static final int SWITCHID_BYTES = 2 + 8;
+ public static ByteBuffer getSwitchID(Dpid dpid) {
+ return getSwitchID(dpid.value());
+ }
+
public static ByteBuffer getSwitchID(Long dpid) {
if (dpid == null) {
throw new IllegalArgumentException("dpid cannot be null");
}
- return (ByteBuffer) ByteBuffer.allocate(SwitchEvent.SWITCHID_BYTES).putChar('S').putLong(dpid).flip();
+ return (ByteBuffer) ByteBuffer.allocate(SwitchEvent.SWITCHID_BYTES)
+ .putChar('S').putLong(dpid).flip();
}
public byte[] getID() {
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 9457cb5..7fbc43d 100644
--- a/src/main/java/net/onrc/onos/core/topology/SwitchImpl.java
+++ b/src/main/java/net/onrc/onos/core/topology/SwitchImpl.java
@@ -10,7 +10,8 @@
import net.onrc.onos.core.util.Dpid;
import net.onrc.onos.core.util.PortNumber;
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import org.apache.commons.lang.Validate;
/**
* Switch Object stored in In-memory Topology.
@@ -20,30 +21,76 @@
*/
public class SwitchImpl extends TopologyObject implements Switch {
- private Dpid dpid;
+ //////////////////////////////////////////////////////
+ /// Topology element attributes
+ /// - any changes made here needs to be replicated.
+ //////////////////////////////////////////////////////
+ private SwitchEvent switchObj;
+ ///////////////////
+ /// In-memory index
+ ///////////////////
+
+ // none
+
+ // TODO remove when test codes are cleaned up.
public SwitchImpl(Topology topology, Long dpid) {
this(topology, new Dpid(dpid));
}
+ /**
+ * Creates a Switch object with empty attributes.
+ *
+ * @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) {
super(topology);
- this.dpid = dpid;
+ 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();
+ }
}
@Override
public Dpid getDpid() {
- return dpid;
+ return switchObj.getDpid();
}
@Override
public Collection<Port> getPorts() {
- return topology.getPorts(getDpid());
+ topology.acquireReadLock();
+ try {
+ return topology.getPorts(getDpid());
+ } finally {
+ topology.releaseReadLock();
+ }
}
@Override
public Port getPort(PortNumber number) {
- return topology.getPort(getDpid(), number);
+ topology.acquireReadLock();
+ try {
+ return topology.getPort(getDpid(), number);
+ } finally {
+ topology.releaseReadLock();
+ }
}
@Override
@@ -84,12 +131,26 @@
return devices;
}
- public Port addPort(Long portNumber) {
- PortImpl port = new PortImpl(topology, this, portNumber);
+ // FIXME this should be removed from here and moved to MockTopology
+ Port addPort(Long portNumber) {
+ PortImpl port = new PortImpl(topology, getDpid(),
+ new PortNumber(portNumber.shortValue()));
((TopologyImpl) topology).putPort(port);
return port;
}
+ 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>();
@@ -116,12 +177,10 @@
@Override
public String getStringAttribute(String attr) {
- throw new UnsupportedOperationException("Not implemented yet");
+ return this.switchObj.getStringAttribute(attr);
}
@Override
- @SuppressFBWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE",
- justification = "getStringAttribute might return null once implemented")
public String getStringAttribute(String attr, String def) {
final String v = getStringAttribute(attr);
if (v == null) {
@@ -133,12 +192,12 @@
@Override
public Map<String, String> getAllStringAttributes() {
- throw new UnsupportedOperationException("Not implemented yet");
+ return this.switchObj.getAllStringAttributes();
}
@Override
public String toString() {
- return dpid.toString();
+ return getDpid().toString();
}
/**
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyElement.java b/src/main/java/net/onrc/onos/core/topology/TopologyElement.java
new file mode 100644
index 0000000..3a6dad8
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyElement.java
@@ -0,0 +1,154 @@
+package net.onrc.onos.core.topology;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.commons.lang.Validate;
+
+/**
+ * Base class for Topology Elements.
+ * <p/>
+ * Self-contained element, it is expected to be used as if it is an immutable
+ * object.
+ *
+ * @param <T> Sub-class' type.
+ * (Required to define a method returning itself's type)
+ */
+public class TopologyElement<T extends TopologyElement<T>>
+ implements StringAttributes, UpdateStringAttributes {
+
+ private boolean isFrozen = false;
+
+ private ConcurrentMap<String, String> stringAttributes;
+
+
+
+ /**
+ * Default constructor for serializer.
+ */
+ protected TopologyElement() {
+ this.isFrozen = false;
+ this.stringAttributes = new ConcurrentHashMap<>();
+ }
+
+ /**
+ * Create an unfrozen copy of given Object.
+ * <p/>
+ * Sub-classes should do a deep-copies if necessary.
+ *
+ * @param original to make copy of.
+ */
+ public TopologyElement(TopologyElement<T> original) {
+ this.isFrozen = false;
+ this.stringAttributes = new ConcurrentHashMap<>(original.stringAttributes);
+ }
+
+
+ /**
+ * Tests if this instance is frozen.
+ *
+ * @return true if frozen.
+ */
+ public boolean isFrozen() {
+ return isFrozen;
+ }
+
+ /**
+ * Freezes this instance to avoid further modifications.
+ *
+ * @return this
+ */
+ @SuppressWarnings("unchecked")
+ public T freeze() {
+ isFrozen = true;
+ return (T) this;
+ }
+
+ @Override
+ public int hashCode() {
+ return stringAttributes.hashCode();
+ }
+
+ /*
+ * (non-Javadoc)
+ * Equality based only on string attributes.
+ *
+ * Subclasses should call super.equals().
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ TopologyElement<T> other = (TopologyElement<T>) obj;
+ return Objects.equals(stringAttributes, other.stringAttributes);
+ }
+
+ @Override
+ public String getStringAttribute(String attr) {
+ return this.stringAttributes.get(attr);
+ }
+
+ @Override
+ public String getStringAttribute(String attr, String def) {
+ final String v = getStringAttribute(attr);
+ if (v == null) {
+ return def;
+ } else {
+ return v;
+ }
+ }
+
+ @Override
+ public Map<String, String> getAllStringAttributes() {
+ return Collections.unmodifiableMap(this.stringAttributes);
+ }
+
+ @Override
+ public boolean createStringAttribute(String attr, String value) {
+ if (isFrozen) {
+ throw new IllegalStateException("Tried to modify frozen object: " + this);
+ }
+ Validate.notNull(value, "attribute value cannot be null");
+
+ return this.stringAttributes.putIfAbsent(attr, value) == null;
+ }
+
+ @Override
+ public boolean replaceStringAttribute(String attr, String oldValue, String value) {
+ if (isFrozen) {
+ throw new IllegalStateException("Tried to modify frozen object: " + this);
+ }
+ Validate.notNull(value, "attribute value cannot be null");
+
+ return this.stringAttributes.replace(attr, oldValue, value);
+ }
+
+ @Override
+ public boolean deleteStringAttribute(String attr, String expectedValue) {
+ if (isFrozen) {
+ throw new IllegalStateException("Tried to modify frozen object: " + this);
+ }
+
+ return this.stringAttributes.remove(attr, expectedValue);
+ }
+
+ @Override
+ public void deleteStringAttribute(String attr) {
+ if (isFrozen) {
+ throw new IllegalStateException("Tried to modify frozen object: " + this);
+ }
+
+ this.stringAttributes.remove(attr);
+ }
+}
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 3a07693..b079cae 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyManager.java
@@ -704,7 +704,7 @@
discoveredAddedLinkEvents.get(portEvent.getDpid());
if (oldLinkEvents != null) {
for (LinkEvent linkEvent : new ArrayList<>(oldLinkEvents.values())) {
- if (linkEvent.getDst().equals(portEvent.id)) {
+ if (linkEvent.getDst().equals(portEvent.getSwitchPort())) {
removeLinkDiscoveryEvent(linkEvent);
// XXX If we change our model to allow multiple Link on
// a Port, this loop must be fixed to allow continuing.
@@ -721,7 +721,7 @@
if (oldDeviceEvents != null) {
for (DeviceEvent deviceEvent : new ArrayList<>(oldDeviceEvents.values())) {
for (SwitchPort swp : deviceEvent.getAttachmentPoints()) {
- if (swp.equals(portEvent.id)) {
+ if (swp.equals(portEvent.getSwitchPort())) {
removedDeviceEvents.add(deviceEvent);
}
}
@@ -836,28 +836,31 @@
}
/**
- * Add a switch to the topology.
+ * Add a switch to the topology replica.
*
- * @param switchEvent the Switch Event with the switch to add.
+ * @param switchEvent the SwitchEvent with the switch to add.
*/
@GuardedBy("topology.writeLock")
private void addSwitch(SwitchEvent switchEvent) {
Switch sw = topology.getSwitch(switchEvent.getDpid());
if (sw == null) {
- sw = new SwitchImpl(topology, switchEvent.getDpid());
+ sw = new SwitchImpl(topology, switchEvent);
topology.putSwitch(sw);
} else {
// TODO: Update the switch attributes
- // TODO: Nothing to do for now
log.debug("Update switch attributes");
+ SwitchImpl impl = (SwitchImpl) sw;
+ impl.replaceStringAttributes(switchEvent);
}
apiAddedSwitchEvents.add(switchEvent);
}
/**
- * Remove a switch from the topology.
+ * Remove a switch from the topology replica.
+ * <p/>
+ * It will call {@link #removePort(PortEvent)} for each ports on this switch.
*
- * @param switchEvent the Switch Event with the switch to remove.
+ * @param switchEvent the SwitchEvent with the switch to remove.
*/
@GuardedBy("topology.writeLock")
private void removeSwitch(SwitchEvent switchEvent) {
@@ -887,9 +890,9 @@
}
/**
- * Add a port to the topology.
+ * Add a port to the topology replica.
*
- * @param portEvent the Port Event with the port to add.
+ * @param portEvent the PortEvent with the port to add.
*/
@GuardedBy("topology.writeLock")
private void addPort(PortEvent portEvent) {
@@ -902,21 +905,27 @@
return;
}
+
Port port = sw.getPort(portEvent.getPortNumber());
if (port == null) {
- port = new PortImpl(topology, sw, portEvent.getPortNumber());
+ 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);
}
apiAddedPortEvents.add(portEvent);
}
/**
- * Remove a port from the topology.
+ * Remove a port from the topology replica.
+ * <p/>
+ * It will call {@link #removeDevice(DeviceEvent)} for each hosts on this port
+ * and call {@link #removeLink(LinkEvent)} for each links on this port.
*
- * @param portEvent the Port Event with the port to remove.
+ * @param portEvent the PortEvent with the port to remove.
*/
@GuardedBy("topology.writeLock")
private void removePort(PortEvent portEvent) {
@@ -972,16 +981,17 @@
}
// Remove the Port from the Switch
- SwitchImpl switchImpl = getSwitchImpl(sw);
topology.removePort(port);
apiRemovedPortEvents.add(portEvent);
}
/**
- * Add a link to the topology.
+ * Add a link to the topology replica.
+ * <p/>
+ * It will call {@link #removeDevice(DeviceEvent)} for each hosts on both ports.
*
- * @param linkEvent the Link Event with the link to add.
+ * @param linkEvent the LinkEvent with the link to add.
*/
@GuardedBy("topology.writeLock")
private void addLink(LinkEvent linkEvent) {
@@ -993,6 +1003,7 @@
log.debug("{} reordered because {} port is null", linkEvent,
(srcPort == null) ? "src" : "dst");
+ // XXX domain knowledge: port must be present before link.
// Reordered event: delay the event in local cache
ByteBuffer id = linkEvent.getIDasByteBuffer();
reorderedAddedLinkEvents.put(id, linkEvent);
@@ -1000,10 +1011,14 @@
}
// 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
+ // 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, srcPort, dstPort);
+ link = new LinkImpl(topology, linkEvent);
topology.putLink(link);
// Remove all Devices attached to the Ports
@@ -1015,11 +1030,13 @@
for (Device device : port.getDevices()) {
log.error("Device {} on Port {} should have been removed prior to adding Link {}",
device, port, linkEvent);
+ // FIXME must get Device info from topology, when we add attrs.
DeviceEvent deviceEvent =
new DeviceEvent(device.getMacAddress());
SwitchPort switchPort =
new SwitchPort(port.getSwitch().getDpid(),
port.getNumber());
+ // adding attachment port which needs to be removed
deviceEvent.addAttachmentPoint(switchPort);
devicesToRemove.add(deviceEvent);
}
@@ -1030,15 +1047,17 @@
} else {
// TODO: Update the link attributes
log.debug("Update link attributes");
+ LinkImpl impl = (LinkImpl) link;
+ impl.replaceStringAttributes(linkEvent);
}
apiAddedLinkEvents.add(linkEvent);
}
/**
- * Remove a link from the topology.
+ * Remove a link from the topology replica.
*
- * @param linkEvent the Link Event with the link to remove.
+ * @param linkEvent the LinkEvent with the link to remove.
*/
@GuardedBy("topology.writeLock")
private void removeLink(LinkEvent linkEvent) {
@@ -1080,13 +1099,13 @@
}
/**
- * Add a device to the topology.
+ * Add a device to the topology replica.
* <p/>
* TODO: Device-related work is incomplete.
* TODO: Eventually, we might need to consider reordering
* or addLink() and addDevice() events on the same port.
*
- * @param deviceEvent the Device Event with the device to add.
+ * @param deviceEvent the DeviceEvent with the device to add.
*/
@GuardedBy("topology.writeLock")
private void addDevice(DeviceEvent deviceEvent) {
@@ -1098,11 +1117,14 @@
device = new DeviceImpl(topology, deviceEvent.getMac());
}
- DeviceImpl deviceImpl = getDeviceImpl(device);
+ DeviceImpl deviceImpl = (DeviceImpl) device;
// Process each attachment point
boolean attachmentFound = false;
for (SwitchPort swp : deviceEvent.getAttachmentPoints()) {
+ // XXX domain knowledge: Port must exist before Device
+ // but this knowledge cannot be pushed down to driver.
+
// Attached Ports must exist
Port port = topology.getPort(swp.getDpid(), swp.getPortNumber());
if (port == null) {
@@ -1121,13 +1143,10 @@
}
// Add Device <-> Port attachment
- PortImpl portImpl = getPortImpl(port);
deviceImpl.addAttachmentPoint(port);
attachmentFound = true;
}
- deviceImpl.setLastSeenTime(deviceEvent.getLastSeenTime());
-
// Update the device in the topology
if (attachmentFound) {
log.debug("Storing the device info into the Topology: mac {}", deviceEvent.getMac());
@@ -1137,7 +1156,7 @@
}
/**
- * Remove a device from the topology.
+ * Remove a device from the topology replica.
* <p/>
* TODO: Device-related work is incomplete.
*
@@ -1157,58 +1176,6 @@
}
/**
- * Get the SwitchImpl-casted switch implementation.
- *
- * @param sw the Switch to cast.
- * @return the SwitchImpl-casted switch implementation.
- */
- private SwitchImpl getSwitchImpl(Switch sw) {
- if (sw instanceof SwitchImpl) {
- return (SwitchImpl) sw;
- }
- throw new ClassCastException("SwitchImpl expected, but found: " + sw);
- }
-
- /**
- * Get the PortImpl-casted port implementation.
- *
- * @param port the Port to cast.
- * @return the PortImpl-casted port implementation.
- */
- private PortImpl getPortImpl(Port port) {
- if (port instanceof PortImpl) {
- return (PortImpl) port;
- }
- throw new ClassCastException("PortImpl expected, but found: " + port);
- }
-
- /**
- * Get the LinkImpl-casted link implementation.
- *
- * @param link the Link to cast.
- * @return the LinkImpl-casted link implementation.
- */
- private LinkImpl getLinkImpl(Link link) {
- if (link instanceof LinkImpl) {
- return (LinkImpl) link;
- }
- throw new ClassCastException("LinkImpl expected, but found: " + link);
- }
-
- /**
- * Get the DeviceImpl-casted device implementation.
- *
- * @param device the Device to cast.
- * @return the DeviceImpl-casted device implementation.
- */
- private DeviceImpl getDeviceImpl(Device device) {
- if (device instanceof DeviceImpl) {
- return (DeviceImpl) device;
- }
- throw new ClassCastException("DeviceImpl expected, but found: " + device);
- }
-
- /**
* Read the whole topology from the database.
*
* @return a collection of EventEntry-encapsulated Topology Events for
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 cdf51bd..661f081 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyObject.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyObject.java
@@ -1,5 +1,7 @@
package net.onrc.onos.core.topology;
+import org.apache.commons.lang.Validate;
+
/**
@@ -18,6 +20,7 @@
* @param topology Topology instance this object belongs to
*/
protected TopologyObject(Topology topology) {
+ Validate.notNull(topology);
this.topology = topology;
}
diff --git a/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java b/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
index fbd409f..60b0823 100644
--- a/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
+++ b/src/main/java/net/onrc/onos/core/topology/TopologyPublisher.java
@@ -24,6 +24,7 @@
import net.onrc.onos.core.registry.IControllerRegistryService.ControlChangeCallback;
import net.onrc.onos.core.registry.RegistryException;
import net.onrc.onos.core.util.Dpid;
+import net.onrc.onos.core.util.PortNumber;
import net.onrc.onos.core.util.SwitchPort;
import org.openflow.protocol.OFPhysicalPort;
@@ -133,9 +134,13 @@
@Override
public void linkDiscoveryUpdate(LDUpdate update) {
- LinkEvent linkEvent = new LinkEvent(update.getSrc(),
- (long) update.getSrcPort(), update.getDst(),
- (long) update.getDstPort());
+ LinkEvent linkEvent = new LinkEvent(
+ new SwitchPort(update.getSrc(), update.getSrcPort()),
+ new SwitchPort(update.getDst(), update.getDstPort()));
+ // FIXME should be merging, with existing attrs, etc..
+ // TODO define attr name as constant somewhere.
+ // TODO populate appropriate attributes.
+ linkEvent.freeze();
switch (update.getOperation()) {
case LINK_ADDED:
@@ -168,8 +173,15 @@
@Override
public void switchPortAdded(Long switchId, OFPhysicalPort port) {
+ final Dpid dpid = new Dpid(switchId);
+ PortEvent portEvent = new PortEvent(dpid, new PortNumber(port.getPortNumber()));
+ // FIXME should be merging, with existing attrs, etc..
+ // TODO define attr name as constant somewhere.
+ // TODO populate appropriate attributes.
+ portEvent.createStringAttribute("name", port.getName());
- PortEvent portEvent = new PortEvent(switchId, (long) port.getPortNumber());
+ portEvent.freeze();
+
if (registryService.hasControl(switchId)) {
topologyDiscoveryInterface.putPortDiscoveryEvent(portEvent);
linkDiscovery.removeFromSuppressLLDPs(switchId, port.getPortNumber());
@@ -193,8 +205,15 @@
@Override
public void addedSwitch(IOFSwitch sw) {
+ final Dpid dpid = new Dpid(sw.getId());
+ SwitchEvent switchEvent = new SwitchEvent(dpid);
+ // FIXME should be merging, with existing attrs, etc..
+ // TODO define attr name as constant somewhere.
+ // TODO populate appropriate attributes.
+ switchEvent.createStringAttribute("ConnectedSince",
+ sw.getConnectedSince().toString());
- SwitchEvent switchEvent = new SwitchEvent(sw.getId());
+ switchEvent.freeze();
// TODO Not very robust
if (!registryService.hasControl(sw.getId())) {
@@ -205,7 +224,14 @@
List<PortEvent> portEvents = new ArrayList<PortEvent>();
for (OFPhysicalPort port : sw.getPorts()) {
- portEvents.add(new PortEvent(sw.getId(), (long) port.getPortNumber()));
+ PortEvent portEvent = new PortEvent(dpid, new PortNumber(port.getPortNumber()));
+ // FIXME should be merging, with existing attrs, etc..
+ // TODO define attr name as constant somewhere.
+ // TODO populate appropriate attributes.
+ portEvent.createStringAttribute("name", port.getName());
+
+ portEvent.freeze();
+ portEvents.add(portEvent);
}
topologyDiscoveryInterface
.putSwitchDiscoveryEvent(switchEvent, portEvents);
@@ -314,6 +340,7 @@
event.setAttachmentPoints(spLists);
event.setLastSeenTime(device.getLastSeenTimestamp().getTime());
// Does not use vlan info now.
+ event.freeze();
topologyDiscoveryInterface.putDeviceDiscoveryEvent(event);
}
@@ -322,6 +349,8 @@
public void onosDeviceRemoved(OnosDevice device) {
log.debug("Called onosDeviceRemoved");
DeviceEvent event = new DeviceEvent(device.getMacAddress());
+ // XXX shouldn't we be setting attachment points?
+ event.freeze();
topologyDiscoveryInterface.removeDeviceDiscoveryEvent(event);
}
}
diff --git a/src/main/java/net/onrc/onos/core/topology/UpdateStringAttributes.java b/src/main/java/net/onrc/onos/core/topology/UpdateStringAttributes.java
new file mode 100644
index 0000000..6443b14
--- /dev/null
+++ b/src/main/java/net/onrc/onos/core/topology/UpdateStringAttributes.java
@@ -0,0 +1,47 @@
+package net.onrc.onos.core.topology;
+
+// TODO Need better name
+/**
+ * Update String Attributes.
+ */
+public interface UpdateStringAttributes extends StringAttributes {
+
+ /**
+ * Creates the string attribute.
+ *
+ * @param attr attribute name
+ * @param value new value to replace with
+ * @return true if success, false if the attribute already exist
+ */
+ public boolean createStringAttribute(final String attr,
+ final String value);
+
+ /**
+ * Replaces the existing string attribute.
+ *
+ * @param attr attribute name
+ * @param oldValue old value to replace
+ * @param value new value to replace with
+ * @return true if success
+ */
+ public boolean replaceStringAttribute(final String attr,
+ final String oldValue, final String value);
+
+ /**
+ * Deletes existing string attribute.
+ *
+ * @param attr attribute name
+ * @param expectedValue value expected to be deleted
+ * @return true if success, false if an attribute already exist
+ */
+ public boolean deleteStringAttribute(final String attr,
+ final String expectedValue);
+
+ /**
+ * Deletes string attribute.
+ *
+ * @param attr attribute name
+ */
+ public void deleteStringAttribute(final String attr);
+
+}
diff --git a/src/main/java/net/onrc/onos/core/util/serializers/KryoFactory.java b/src/main/java/net/onrc/onos/core/util/serializers/KryoFactory.java
index a726401..ae1fdb9 100644
--- a/src/main/java/net/onrc/onos/core/util/serializers/KryoFactory.java
+++ b/src/main/java/net/onrc/onos/core/util/serializers/KryoFactory.java
@@ -7,6 +7,7 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
+import java.util.concurrent.ConcurrentHashMap;
import net.floodlightcontroller.util.MACAddress;
import net.onrc.onos.apps.proxyarp.ArpCacheNotification;
@@ -29,6 +30,7 @@
import net.onrc.onos.core.topology.PortEvent;
import net.onrc.onos.core.topology.SwitchEvent;
import net.onrc.onos.core.topology.TopologyEvent;
+import net.onrc.onos.core.topology.TopologyElement;
import net.onrc.onos.core.util.CallerId;
import net.onrc.onos.core.util.DataPath;
import net.onrc.onos.core.util.Dpid;
@@ -199,6 +201,8 @@
kryo.register(PortEvent.class);
kryo.register(SwitchEvent.class);
kryo.register(TopologyEvent.class);
+ kryo.register(TopologyElement.class);
+ kryo.register(ConcurrentHashMap.class);
// Intent-related classes
kryo.register(Path.class);