Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
diff --git a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
index aaf5350..564a2da 100644
--- a/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
+++ b/apps/fwd/src/main/java/org/onlab/onos/fwd/ReactiveForwarding.java
@@ -184,13 +184,13 @@
// Install the flow rule to handle this type of message from now on.
Ethernet inPkt = context.inPacket().parsed();
- TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
builder.matchEthType(inPkt.getEtherType())
.matchEthSrc(inPkt.getSourceMAC())
.matchEthDst(inPkt.getDestinationMAC())
.matchInport(context.inPacket().receivedFrom().port());
- TrafficTreatment.Builder treat = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
treat.setOutput(portNumber);
FlowRule f = new DefaultFlowRule(context.inPacket().receivedFrom().deviceId(),
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentInstallCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentInstallCommand.java
new file mode 100644
index 0000000..90b7311
--- /dev/null
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentInstallCommand.java
@@ -0,0 +1,57 @@
+package org.onlab.onos.cli.net;
+
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.onos.cli.AbstractShellCommand;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.host.HostService;
+import org.onlab.onos.net.intent.HostToHostIntent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentService;
+
+/**
+ * Lists all shortest-paths paths between the specified source and
+ * destination devices.
+ */
+@Command(scope = "onos", name = "add-intent",
+ description = "Installs HostToHostIntent between the specified source and destination devices")
+public class IntentInstallCommand extends AbstractShellCommand {
+
+ @Argument(index = 0, name = "src", description = "Source device ID",
+ required = true, multiValued = false)
+ String src = null;
+
+ @Argument(index = 1, name = "dst", description = "Destination device ID",
+ required = true, multiValued = false)
+ String dst = null;
+
+ private static long id = 1;
+
+ @Override
+ protected void execute() {
+ IntentService service = get(IntentService.class);
+ HostService hosts = get(HostService.class);
+
+ HostId srcId = HostId.hostId(src);
+ HostId dstId = HostId.hostId(dst);
+
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
+ builder.matchEthSrc(hosts.getHost(srcId).mac())
+ .matchEthDst(hosts.getHost(dstId).mac());
+
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
+
+ HostToHostIntent intent =
+ new HostToHostIntent(new IntentId(id++), srcId, dstId,
+ builder.build(), treat.build());
+
+ log.info("Adding intent {}", intent);
+
+ service.submit(intent);
+ }
+
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 16b5672..a096735 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -57,6 +57,13 @@
</completers>
</command>
<command>
+ <action class="org.onlab.onos.cli.net.IntentInstallCommand"/>
+ <completers>
+ <ref component-id="hostIdCompleter"/>
+ </completers>
+ </command>
+
+ <command>
<action class="org.onlab.onos.cli.net.ClustersListCommand"/>
</command>
<command>
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java b/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
index 6074c67..74991c8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultEdgeLink.java
@@ -18,9 +18,9 @@
* @param providerId provider identity
* @param hostPoint host-side connection point
* @param hostLocation location where host attaches to the network
- * @param isIngress true to indicated host-to-network direction; false
+ * @param isIngress true to indicate host-to-network direction; false
* for network-to-host direction
- * @param annotations optional key/value annotations
+ * @param annotations optional key/value annotations
*/
public DefaultEdgeLink(ProviderId providerId, ConnectPoint hostPoint,
HostLocation hostLocation, boolean isIngress,
@@ -42,4 +42,20 @@
public HostLocation hostLocation() {
return hostLocation;
}
+
+ /**
+ * Creates a phantom edge link, to an unspecified end-station. This link
+ * does not represent any actually discovered link stored in the system.
+ *
+ * @param edgePort network edge port
+ * @param isIngress true to indicate host-to-network direction; false
+ * for network-to-host direction
+ * @return new phantom edge link
+ */
+ public static DefaultEdgeLink createEdgeLink(HostLocation edgePort,
+ boolean isIngress) {
+ return new DefaultEdgeLink(ProviderId.NONE,
+ new ConnectPoint(HostId.NONE, PortNumber.P0),
+ edgePort, isIngress);
+ }
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/HostId.java b/core/api/src/main/java/org/onlab/onos/net/HostId.java
index 1768f24..f2c0303 100644
--- a/core/api/src/main/java/org/onlab/onos/net/HostId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/HostId.java
@@ -10,6 +10,14 @@
*/
public final class HostId extends ElementId {
+ private static final String NIC = "nic";
+
+ /**
+ * Represents either no host, or an unspecified host; used for creating
+ * open ingress/egress edge links.
+ */
+ public static final HostId NONE = hostId(NIC + ":none-0");
+
// Public construction is prohibited
private HostId(URI uri) {
super(uri);
@@ -43,8 +51,7 @@
* @return host identifier
*/
public static HostId hostId(MacAddress mac, VlanId vlanId) {
- // FIXME: use more efficient means of encoding
- return hostId("nic" + ":" + mac + "-" + vlanId);
+ return hostId(NIC + ":" + mac + "-" + vlanId);
}
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/PortNumber.java b/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
index cfb11d5..60c3305 100644
--- a/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
+++ b/core/api/src/main/java/org/onlab/onos/net/PortNumber.java
@@ -9,6 +9,8 @@
*/
public final class PortNumber {
+ public static final PortNumber P0 = portNumber(0);
+
// TODO: revisit the max and the logical port value assignments
private static final long MAX_NUMBER = (2L * Integer.MAX_VALUE) + 1;
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
index d792c7e..31c53a8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficSelector.java
@@ -1,36 +1,43 @@
package org.onlab.onos.net.flow;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Objects;
-import java.util.Set;
-
+import com.google.common.collect.ImmutableSet;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.criteria.Criteria;
import org.onlab.onos.net.flow.criteria.Criterion;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
-import org.slf4j.Logger;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * Default traffic selector implementation.
+ */
public final class DefaultTrafficSelector implements TrafficSelector {
- private final Set<Criterion> selector;
+ private final Set<Criterion> criteria;
- private DefaultTrafficSelector(Set<Criterion> selector) {
- this.selector = Collections.unmodifiableSet(selector);
+ /**
+ * Creates a new traffic selector with the specified criteria.
+ *
+ * @param criteria criteria
+ */
+ private DefaultTrafficSelector(Set<Criterion> criteria) {
+ this.criteria = Collections.unmodifiableSet(criteria);
}
@Override
public Set<Criterion> criteria() {
- return selector;
+ return criteria;
}
@Override
public int hashCode() {
- return Objects.hash(selector);
+ return Objects.hash(criteria);
}
@Override
@@ -40,23 +47,50 @@
}
if (obj instanceof DefaultTrafficSelector) {
DefaultTrafficSelector that = (DefaultTrafficSelector) obj;
- return Objects.equals(selector, that.selector);
+ return Objects.equals(criteria, that.criteria);
}
return false;
}
+ /**
+ * Returns a new traffic selector builder.
+ *
+ * @return traffic selector builder
+ */
+ public static TrafficSelector.Builder builder() {
+ return new Builder();
+ }
+ /**
+ * Returns a new traffic selector builder primed to produce entities
+ * patterned after the supplied selector.
+ *
+ * @return traffic selector builder
+ */
+ public static TrafficSelector.Builder builder(TrafficSelector selector) {
+ return new Builder(selector);
+ }
- public static class Builder implements TrafficSelector.Builder {
+ /**
+ * Builder of traffic selector entities.
+ */
+ public static final class Builder implements TrafficSelector.Builder {
- private final Logger log = getLogger(getClass());
+ private final Map<Criterion.Type, Criterion> selector = new HashMap<>();
- private final Set<Criterion> selector = new HashSet<>();
+ private Builder() {
+ }
+
+ private Builder(TrafficSelector selector) {
+ for (Criterion c : selector.criteria()) {
+ add(c);
+ }
+ }
@Override
public Builder add(Criterion criterion) {
- selector.add(criterion);
+ selector.put(criterion.type(), criterion);
return this;
}
@@ -107,7 +141,7 @@
@Override
public TrafficSelector build() {
- return new DefaultTrafficSelector(selector);
+ return new DefaultTrafficSelector(ImmutableSet.copyOf(selector.values()));
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
index 2ce233f..3f43598 100644
--- a/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
+++ b/core/api/src/main/java/org/onlab/onos/net/flow/DefaultTrafficTreatment.java
@@ -1,11 +1,5 @@
package org.onlab.onos.net.flow;
-import static org.slf4j.LoggerFactory.getLogger;
-
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.flow.instructions.Instruction;
import org.onlab.onos.net.flow.instructions.Instructions;
@@ -14,10 +8,24 @@
import org.onlab.packet.VlanId;
import org.slf4j.Logger;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.slf4j.LoggerFactory.getLogger;
+
+/**
+ * Default traffic treatment implementation.
+ */
public final class DefaultTrafficTreatment implements TrafficTreatment {
private final List<Instruction> instructions;
+ /**
+ * Creates a new traffic treatment from the specified list of instructions.
+ *
+ * @param instructions treatment instructions
+ */
private DefaultTrafficTreatment(List<Instruction> instructions) {
this.instructions = Collections.unmodifiableList(instructions);
}
@@ -28,12 +36,19 @@
}
/**
+ * Returns a new traffic treatment builder.
+ *
+ * @return traffic treatment builder
+ */
+ public static TrafficTreatment.Builder builder() {
+ return new Builder();
+ }
+
+ /**
* Builds a list of treatments following the following order.
* Modifications -> Group -> Output (including drop)
- *
*/
-
- public static class Builder implements TrafficTreatment.Builder {
+ public static final class Builder implements TrafficTreatment.Builder {
private final Logger log = getLogger(getClass());
@@ -47,27 +62,31 @@
// TODO: should be a list of instructions based on modification objects
List<Instruction> modifications = new LinkedList<>();
+ // Creates a new builder
+ private Builder() {
+ }
+
public Builder add(Instruction instruction) {
if (drop) {
return this;
}
switch (instruction.type()) {
- case DROP:
- drop = true;
- break;
- case OUTPUT:
- outputs.add(instruction);
- break;
- case L2MODIFICATION:
- case L3MODIFICATION:
- // TODO: enforce modification order if any
- modifications.add(instruction);
- break;
- case GROUP:
- groups.add(instruction);
- break;
- default:
- log.warn("Unknown instruction type {}", instruction.type());
+ case DROP:
+ drop = true;
+ break;
+ case OUTPUT:
+ outputs.add(instruction);
+ break;
+ case L2MODIFICATION:
+ case L3MODIFICATION:
+ // TODO: enforce modification order if any
+ modifications.add(instruction);
+ break;
+ case GROUP:
+ groups.add(instruction);
+ break;
+ default:
+ log.warn("Unknown instruction type {}", instruction.type());
}
return this;
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java b/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java
index ad34a2c..5d0cbb8 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/BatchOperation.java
@@ -1,20 +1,20 @@
package org.onlab.onos.net.intent;
//TODO is this the right package?
-import static com.google.common.base.Preconditions.checkNotNull;
-
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
+import static com.google.common.base.Preconditions.checkNotNull;
+
/**
* A list of BatchOperationEntry.
*
* @param <T> the enum of operators <br>
- * This enum must be defined in each sub-classes.
- *
+ * This enum must be defined in each sub-classes.
*/
public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
+
private List<T> ops;
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
index 629a9d1..70cec58 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/ConnectivityIntent.java
@@ -1,11 +1,10 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkNotNull;
-
+import com.google.common.base.Objects;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.Objects;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of connectivity intent for traffic matching some criteria.
@@ -26,17 +25,18 @@
/**
* Creates a connectivity intent that matches on the specified intent
- * and applies the specified action.
+ * and applies the specified treatement.
*
- * @param id intent identifier
- * @param match traffic match
- * @param action action
- * @throws NullPointerException if the match or action is null
+ * @param intentId intent identifier
+ * @param selector traffic selector
+ * @param treatement treatement
+ * @throws NullPointerException if the selector or treatement is null
*/
- protected ConnectivityIntent(IntentId id, TrafficSelector match, TrafficTreatment action) {
- super(id);
- this.selector = checkNotNull(match);
- this.treatment = checkNotNull(action);
+ protected ConnectivityIntent(IntentId intentId, TrafficSelector selector,
+ TrafficTreatment treatement) {
+ super(intentId);
+ this.selector = checkNotNull(selector);
+ this.treatment = checkNotNull(treatement);
}
/**
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
new file mode 100644
index 0000000..08063f0
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/HostToHostIntent.java
@@ -0,0 +1,89 @@
+package org.onlab.onos.net.intent;
+
+import com.google.common.base.MoreObjects;
+import org.onlab.onos.net.HostId;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Abstraction of end-station to end-station connectivity.
+ */
+public class HostToHostIntent extends ConnectivityIntent {
+
+ private final HostId src;
+ private final HostId dst;
+
+ /**
+ * Creates a new point-to-point intent with the supplied ingress/egress
+ * ports.
+ *
+ * @param intentId intent identifier
+ * @param selector action
+ * @param treatment ingress port
+ * @throws NullPointerException if {@code ingressPort} or {@code egressPort}
+ * is null.
+ */
+ public HostToHostIntent(IntentId intentId, HostId src, HostId dst,
+ TrafficSelector selector, TrafficTreatment treatment) {
+ super(intentId, selector, treatment);
+ this.src = checkNotNull(src);
+ this.dst = checkNotNull(dst);
+ }
+
+ /**
+ * Returns the port on which the ingress traffic should be connected to the
+ * egress.
+ *
+ * @return ingress port
+ */
+ public HostId getSrc() {
+ return src;
+ }
+
+ /**
+ * Returns the port on which the traffic should egress.
+ *
+ * @return egress port
+ */
+ public HostId getDst() {
+ return dst;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ HostToHostIntent that = (HostToHostIntent) o;
+ return Objects.equals(this.src, that.src)
+ && Objects.equals(this.dst, that.dst);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), src, dst);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("id", getId())
+ .add("selector", getTrafficSelector())
+ .add("treatmetn", getTrafficTreatment())
+ .add("src", src)
+ .add("dst", dst)
+ .toString();
+ }
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
index b239ede..d4c630a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/Intent.java
@@ -2,7 +2,7 @@
/**
* Abstraction of an application level intent.
- *
+ * <p/>
* Make sure that an Intent should be immutable when a new type is defined.
*/
public interface Intent extends BatchOperationTarget {
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
index 27ae834..c98e788 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEvent.java
@@ -1,17 +1,19 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.base.MoreObjects;
+import org.onlab.onos.event.AbstractEvent;
import java.util.Objects;
-import com.google.common.base.MoreObjects;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* A class to represent an intent related event.
*/
-public class IntentEvent {
+public class IntentEvent extends AbstractEvent<IntentState, Intent> {
- // TODO: determine a suitable parent class; if one does not exist, consider introducing one
+ // TODO: determine a suitable parent class; if one does not exist, consider
+ // introducing one
private final long time;
private final Intent intent;
@@ -21,13 +23,14 @@
/**
* Creates an event describing a state change of an intent.
*
- * @param intent subject intent
- * @param state new intent state
+ * @param intent subject intent
+ * @param state new intent state
* @param previous previous intent state
- * @param time time the event created in milliseconds since start of epoch
+ * @param time time the event created in milliseconds since start of epoch
* @throws NullPointerException if the intent or state is null
*/
public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) {
+ super(state, intent);
this.intent = checkNotNull(intent);
this.state = checkNotNull(state);
this.previous = previous;
@@ -35,16 +38,6 @@
}
/**
- * Constructor for serializer.
- */
- protected IntentEvent() {
- this.intent = null;
- this.state = null;
- this.previous = null;
- this.time = 0;
- }
-
- /**
* Returns the state of the intent which caused the event.
*
* @return the state of the intent
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEventListener.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentEventListener.java
deleted file mode 100644
index f59ecfc..0000000
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentEventListener.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package org.onlab.onos.net.intent;
-
-/**
- * Listener for {@link IntentEvent intent events}.
- */
-public interface IntentEventListener {
- /**
- * Processes the specified intent event.
- *
- * @param event the event to process
- */
- void event(IntentEvent event);
-}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java
index 4148dea..fff55be 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentException.java
@@ -26,7 +26,7 @@
* Constructs an exception with the specified message and the underlying cause.
*
* @param message the message describing the specific nature of the error
- * @param cause the underlying cause of this error
+ * @param cause the underlying cause of this error
*/
public IntentException(String message, Throwable cause) {
super(message, cause);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java
index c6338a7f..8deb372 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentExtensionService.java
@@ -10,9 +10,9 @@
/**
* Registers the specified compiler for the given intent class.
*
- * @param cls intent class
+ * @param cls intent class
* @param compiler intent compiler
- * @param <T> the type of intent
+ * @param <T> the type of intent
*/
<T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler);
@@ -34,9 +34,9 @@
/**
* Registers the specified installer for the given installable intent class.
*
- * @param cls installable intent class
+ * @param cls installable intent class
* @param installer intent installer
- * @param <T> the type of installable intent
+ * @param <T> the type of installable intent
*/
<T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
index 798e00c..8f132c0 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentId.java
@@ -2,7 +2,7 @@
/**
* Intent identifier suitable as an external key.
- *
+ * <p/>
* This class is immutable.
*/
public final class IntentId implements BatchOperationTarget {
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentListener.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentListener.java
new file mode 100644
index 0000000..c00c1f6
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentListener.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.event.EventListener;
+
+/**
+ * Listener for {@link IntentEvent intent events}.
+ */
+public interface IntentListener extends EventListener<IntentEvent> {
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
index 2b4fb59..c3aae54 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
@@ -1,6 +1,5 @@
package org.onlab.onos.net.intent;
-import java.util.Set;
/**
* Service for application submitting or withdrawing their intents.
@@ -8,9 +7,9 @@
public interface IntentService {
/**
* Submits an intent into the system.
- *
- * This is an asynchronous request meaning that any compiling
- * or installation activities may be done at later time.
+ * <p/>
+ * This is an asynchronous request meaning that any compiling or
+ * installation activities may be done at later time.
*
* @param intent intent to be submitted
*/
@@ -18,9 +17,9 @@
/**
* Withdraws an intent from the system.
- *
- * This is an asynchronous request meaning that the environment
- * may be affected at later time.
+ * <p/>
+ * This is an asynchronous request meaning that the environment may be
+ * affected at later time.
*
* @param intent intent to be withdrawn
*/
@@ -29,20 +28,27 @@
/**
* Submits a batch of submit & withdraw operations. Such a batch is
* assumed to be processed together.
- *
- * This is an asynchronous request meaning that the environment
- * may be affected at later time.
+ * <p/>
+ * This is an asynchronous request meaning that the environment may be
+ * affected at later time.
*
* @param operations batch of intent operations
*/
void execute(IntentOperations operations);
/**
- * Returns immutable set of intents currently in the system.
+ * Returns an iterable of intents currently in the system.
*
* @return set of intents
*/
- Set<Intent> getIntents();
+ Iterable<Intent> getIntents();
+
+ /**
+ * Returns the number of intents currently in the system.
+ *
+ * @return number of intents
+ */
+ long getIntentCount();
/**
* Retrieves the intent specified by its identifier.
@@ -56,7 +62,8 @@
* Retrieves the state of an intent by its identifier.
*
* @param id intent identifier
- * @return the intent state or null if one with the given identifier is not found
+ * @return the intent state or null if one with the given identifier is not
+ * found
*/
IntentState getIntentState(IntentId id);
@@ -65,12 +72,12 @@
*
* @param listener listener to be added
*/
- void addListener(IntentEventListener listener);
+ void addListener(IntentListener listener);
/**
* Removes the specified listener for intent events.
*
* @param listener listener to be removed
*/
- void removeListener(IntentEventListener listener);
+ void removeListener(IntentListener listener);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java
new file mode 100644
index 0000000..037f179
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStore.java
@@ -0,0 +1,96 @@
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.store.Store;
+
+import java.util.List;
+
+/**
+ * Manages inventory of end-station intents; not intended for direct use.
+ */
+public interface IntentStore extends Store<IntentEvent, IntentStoreDelegate> {
+
+ /**
+ * Creates a new intent.
+ *
+ * @param intent intent
+ * @return appropriate event or null if no change resulted
+ */
+ IntentEvent createIntent(Intent intent);
+
+ /**
+ * Removes the specified intent from the inventory.
+ *
+ * @param intentId intent identification
+ * @return removed state transition event or null if intent was not found
+ */
+ IntentEvent removeIntent(IntentId intentId);
+
+ /**
+ * Returns the number of intents in the store.
+ */
+ long getIntentCount();
+
+ /**
+ * Returns a collection of all intents in the store.
+ *
+ * @return iterable collection of all intents
+ */
+ Iterable<Intent> getIntents();
+
+ /**
+ * Returns the intent with the specified identifer.
+ *
+ * @param intentId intent identification
+ * @return intent or null if not found
+ */
+ Intent getIntent(IntentId intentId);
+
+ /**
+ * Returns the state of the specified intent.
+ *
+ * @param intentId intent identification
+ * @return current intent state
+ */
+ IntentState getIntentState(IntentId intentId);
+
+ /**
+ * Sets the state of the specified intent to the new state.
+ *
+ * @param intent intent whose state is to be changed
+ * @param newState new state
+ * @return state transition event
+ */
+ IntentEvent setState(Intent intent, IntentState newState);
+
+ /**
+ * Adds the installable intents which resulted from compilation of the
+ * specified original intent.
+ *
+ * @param intentId original intent identifier
+ * @param installableIntents compiled installable intents
+ * @return compiled state transition event
+ */
+ IntentEvent addInstallableIntents(IntentId intentId,
+ List<InstallableIntent> installableIntents);
+
+ /**
+ * Returns the list of the installable events associated with the specified
+ * original intent.
+ *
+ * @param intentId original intent identifier
+ * @return compiled installable intents
+ */
+ List<InstallableIntent> getInstallableIntents(IntentId intentId);
+
+ // TODO: this should be triggered from with the store as a result of removeIntent call
+
+ /**
+ * Removes any installable intents which resulted from compilation of the
+ * specified original intent.
+ *
+ * @param intentId original intent identifier
+ * @return compiled state transition event
+ */
+ void removeInstalledIntents(IntentId intentId);
+
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentStoreDelegate.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStoreDelegate.java
new file mode 100644
index 0000000..6c37db8
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentStoreDelegate.java
@@ -0,0 +1,9 @@
+package org.onlab.onos.net.intent;
+
+import org.onlab.onos.store.StoreDelegate;
+
+/**
+ * Intent store delegate abstraction.
+ */
+public interface IntentStoreDelegate extends StoreDelegate<IntentEvent> {
+}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
index 1e421ab..af1e84b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/MultiPointToSinglePointIntent.java
@@ -30,18 +30,20 @@
* @param action action
* @param ingressPorts set of ports from which ingress traffic originates
* @param egressPort port to which traffic will egress
- * @throws NullPointerException if {@code ingressPorts} or
- * {@code egressPort} is null.
+ * @throws NullPointerException if {@code ingressPorts} or
+ * {@code egressPort} is null.
* @throws IllegalArgumentException if the size of {@code ingressPorts} is
- * not more than 1
+ * not more than 1
*/
- public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
- Set<ConnectPoint> ingressPorts, ConnectPoint egressPort) {
+ public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match,
+ TrafficTreatment action,
+ Set<ConnectPoint> ingressPorts,
+ ConnectPoint egressPort) {
super(id, match, action);
checkNotNull(ingressPorts);
checkArgument(!ingressPorts.isEmpty(),
- "there should be at least one ingress port");
+ "there should be at least one ingress port");
this.ingressPorts = Sets.newHashSet(ingressPorts);
this.egressPort = checkNotNull(egressPort);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
index d11dc7c..b99eb70 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/OpticalConnectivityIntent.java
@@ -3,30 +3,30 @@
import org.onlab.onos.net.ConnectPoint;
// TODO: consider if this intent should be sub-class of ConnectivityIntent
+
/**
* An optical layer Intent for a connectivity from a transponder port to another
* transponder port.
- * <p>
+ * <p/>
* This class doesn't accepts lambda specifier. This class computes path between
* ports and assign lambda automatically. The lambda can be specified using
* OpticalPathFlow class.
*/
public class OpticalConnectivityIntent extends AbstractIntent {
- protected ConnectPoint srcConnectPoint;
- protected ConnectPoint dstConnectPoint;
+ protected ConnectPoint src;
+ protected ConnectPoint dst;
/**
* Constructor.
*
- * @param id ID for this new Intent object.
- * @param srcConnectPoint The source transponder port.
- * @param dstConnectPoint The destination transponder port.
+ * @param id ID for this new Intent object.
+ * @param src The source transponder port.
+ * @param dst The destination transponder port.
*/
- public OpticalConnectivityIntent(IntentId id,
- ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
+ public OpticalConnectivityIntent(IntentId id, ConnectPoint src, ConnectPoint dst) {
super(id);
- this.srcConnectPoint = srcConnectPoint;
- this.dstConnectPoint = dstConnectPoint;
+ this.src = src;
+ this.dst = dst;
}
/**
@@ -34,8 +34,8 @@
*/
protected OpticalConnectivityIntent() {
super();
- this.srcConnectPoint = null;
- this.dstConnectPoint = null;
+ this.src = null;
+ this.dst = null;
}
/**
@@ -44,7 +44,7 @@
* @return The source transponder port.
*/
public ConnectPoint getSrcConnectPoint() {
- return srcConnectPoint;
+ return src;
}
/**
@@ -52,7 +52,7 @@
*
* @return The source transponder port.
*/
- public ConnectPoint getDstConnectPoint() {
- return dstConnectPoint;
+ public ConnectPoint getDst() {
+ return dst;
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
index 39ad011..6809ce2 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PathIntent.java
@@ -12,7 +12,7 @@
/**
* Abstraction of explicitly path specified connectivity intent.
*/
-public class PathIntent extends PointToPointIntent {
+public class PathIntent extends PointToPointIntent implements InstallableIntent {
private final Path path;
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
index b1d18ee..4c86bae 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/PointToPointIntent.java
@@ -1,14 +1,13 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-
+import com.google.common.base.MoreObjects;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.MoreObjects;
+import java.util.Objects;
+
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of point-to-point connectivity.
@@ -23,15 +22,17 @@
* ports.
*
* @param id intent identifier
- * @param match traffic match
- * @param action action
+ * @param selector traffic selector
+ * @param treatment treatment
* @param ingressPort ingress port
* @param egressPort egress port
* @throws NullPointerException if {@code ingressPort} or {@code egressPort} is null.
*/
- public PointToPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
- ConnectPoint ingressPort, ConnectPoint egressPort) {
- super(id, match, action);
+ public PointToPointIntent(IntentId id, TrafficSelector selector,
+ TrafficTreatment treatment,
+ ConnectPoint ingressPort,
+ ConnectPoint egressPort) {
+ super(id, selector, treatment);
this.ingressPort = checkNotNull(ingressPort);
this.egressPort = checkNotNull(egressPort);
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java b/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
index e69a740..af2616b 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/SinglePointToMultiPointIntent.java
@@ -1,17 +1,16 @@
package org.onlab.onos.net.intent;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import java.util.Objects;
-import java.util.Set;
-
+import com.google.common.base.MoreObjects;
+import com.google.common.collect.Sets;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.flow.TrafficSelector;
import org.onlab.onos.net.flow.TrafficTreatment;
-import com.google.common.base.MoreObjects;
-import com.google.common.collect.Sets;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* Abstraction of single source, multiple destination connectivity intent.
@@ -25,23 +24,24 @@
* Creates a new single-to-multi point connectivity intent.
*
* @param id intent identifier
- * @param match traffic match
- * @param action action
+ * @param selector traffic selector
+ * @param treatment treatment
* @param ingressPort port on which traffic will ingress
* @param egressPorts set of ports on which traffic will egress
- * @throws NullPointerException if {@code ingressPort} or
- * {@code egressPorts} is null
+ * @throws NullPointerException if {@code ingressPort} or
+ * {@code egressPorts} is null
* @throws IllegalArgumentException if the size of {@code egressPorts} is
- * not more than 1
+ * not more than 1
*/
- public SinglePointToMultiPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
+ public SinglePointToMultiPointIntent(IntentId id, TrafficSelector selector,
+ TrafficTreatment treatment,
ConnectPoint ingressPort,
Set<ConnectPoint> egressPorts) {
- super(id, match, action);
+ super(id, selector, treatment);
checkNotNull(egressPorts);
checkArgument(!egressPorts.isEmpty(),
- "there should be at least one egress port");
+ "there should be at least one egress port");
this.ingressPort = checkNotNull(ingressPort);
this.egressPorts = Sets.newHashSet(egressPorts);
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java b/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java
index e1e6782..2517067 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/package-info.java
@@ -1,5 +1,8 @@
/**
- * Intent Package. TODO
+ * Set of abstractions for conveying high-level intents for treatment of
+ * selected network traffic by allowing applications to express the
+ * <em>what</em> rather than the <em>how</em>. This makes such instructions
+ * largely independent of topology and device specifics, thus allowing them to
+ * survive topology mutations.
*/
-
package org.onlab.onos.net.intent;
\ No newline at end of file
diff --git a/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java
index 6f1b708..75100c6 100644
--- a/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java
+++ b/core/api/src/main/java/org/onlab/onos/net/packet/DefaultPacketContext.java
@@ -24,7 +24,7 @@
this.inPkt = inPkt;
this.outPkt = outPkt;
this.block = new AtomicBoolean(block);
- this.builder = new DefaultTrafficTreatment.Builder();
+ this.builder = DefaultTrafficTreatment.builder();
}
@Override
diff --git a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
index afaecbe..c2a3133 100644
--- a/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
+++ b/core/api/src/main/java/org/onlab/onos/net/provider/ProviderId.java
@@ -3,6 +3,7 @@
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
/**
* External identity of a {@link org.onlab.onos.net.provider.Provider} family.
@@ -19,10 +20,22 @@
*/
public class ProviderId {
+ /**
+ * Represents no provider ID.
+ */
+ public static final ProviderId NONE = new ProviderId();
+
private final String scheme;
private final String id;
private final boolean ancillary;
+ // For serialization
+ private ProviderId() {
+ scheme = null;
+ id = null;
+ ancillary = false;
+ }
+
/**
* Creates a new primary provider identifier from the specified string.
* The providers are expected to follow the reverse DNS convention, e.g.
@@ -45,8 +58,8 @@
* @param ancillary ancillary provider indicator
*/
public ProviderId(String scheme, String id, boolean ancillary) {
- this.scheme = scheme;
- this.id = id;
+ this.scheme = checkNotNull(scheme, "Scheme cannot be null");
+ this.id = checkNotNull(id, "ID cannot be null");
this.ancillary = ancillary;
}
diff --git a/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java b/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java
new file mode 100644
index 0000000..759b62a
--- /dev/null
+++ b/core/api/src/main/java/org/onlab/onos/store/ClockProviderService.java
@@ -0,0 +1,19 @@
+package org.onlab.onos.store;
+
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.net.DeviceId;
+
+//TODO: Consider renaming to DeviceClockProviderService?
+/**
+* Interface for feeding term information to a logical clock service
+* that vends per device timestamps.
+*/
+public interface ClockProviderService {
+
+ /**
+ * Updates the mastership term for the specified deviceId.
+ * @param deviceId device identifier.
+ * @param term mastership term.
+ */
+ public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
+}
diff --git a/core/api/src/main/java/org/onlab/onos/store/ClockService.java b/core/api/src/main/java/org/onlab/onos/store/ClockService.java
index 2446ab7..20549e8 100644
--- a/core/api/src/main/java/org/onlab/onos/store/ClockService.java
+++ b/core/api/src/main/java/org/onlab/onos/store/ClockService.java
@@ -1,6 +1,5 @@
package org.onlab.onos.store;
-import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.net.DeviceId;
// TODO: Consider renaming to DeviceClockService?
@@ -15,12 +14,4 @@
* @return timestamp.
*/
public Timestamp getTimestamp(DeviceId deviceId);
-
- // TODO: Should this be here or separate as Admin service, etc.?
- /**
- * Updates the mastership term for the specified deviceId.
- * @param deviceId device identifier.
- * @param term mastership term.
- */
- public void setMastershipTerm(DeviceId deviceId, MastershipTerm term);
}
diff --git a/core/api/src/main/java/org/onlab/onos/store/Timestamp.java b/core/api/src/main/java/org/onlab/onos/store/Timestamp.java
index b9d3648..b3caf85 100644
--- a/core/api/src/main/java/org/onlab/onos/store/Timestamp.java
+++ b/core/api/src/main/java/org/onlab/onos/store/Timestamp.java
@@ -2,7 +2,15 @@
/**
* Opaque version structure.
+ * <p>
+ * Classes implementing this interface must also implement
+ * {@link #hashCode()} and {@link #equals(Object)}.
*/
public interface Timestamp extends Comparable<Timestamp> {
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java
index fb1efee..10a0069 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/ConnectivityIntentTest.java
@@ -16,8 +16,8 @@
public abstract class ConnectivityIntentTest extends IntentTest {
public static final IntentId IID = new IntentId(123);
- public static final TrafficSelector MATCH = (new DefaultTrafficSelector.Builder()).build();
- public static final TrafficTreatment NOP = (new DefaultTrafficTreatment.Builder()).build();
+ public static final TrafficSelector MATCH = DefaultTrafficSelector.builder().build();
+ public static final TrafficTreatment NOP = DefaultTrafficTreatment.builder().build();
public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1));
public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2));
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
index df46ec5..349749e 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
@@ -11,19 +11,19 @@
import java.util.concurrent.Executors;
/**
- * Fake implementation of the intent service to assist in developing tests
- * of the interface contract.
+ * Fake implementation of the intent service to assist in developing tests of
+ * the interface contract.
*/
public class FakeIntentManager implements TestableIntentService {
private final Map<IntentId, Intent> intents = new HashMap<>();
private final Map<IntentId, IntentState> intentStates = new HashMap<>();
private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>();
- private final Set<IntentEventListener> listeners = new HashSet<>();
+ private final Set<IntentListener> listeners = new HashSet<>();
private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
- private final Map<Class<? extends InstallableIntent>,
- IntentInstaller<? extends InstallableIntent>> installers = new HashMap<>();
+ private final Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> installers
+ = new HashMap<>();
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final List<IntentException> exceptions = new ArrayList<>();
@@ -76,7 +76,8 @@
private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
@SuppressWarnings("unchecked")
- IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
+ IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent
+ .getClass());
if (installer == null) {
throw new IntentException("no installer for class " + intent.getClass());
}
@@ -125,7 +126,6 @@
}
}
-
// Sets the internal state for the given intent and dispatches an event
private void setState(Intent intent, IntentState state) {
IntentState previous = intentStates.get(intent.getId());
@@ -175,6 +175,11 @@
}
@Override
+ public long getIntentCount() {
+ return intents.size();
+ }
+
+ @Override
public Intent getIntent(IntentId id) {
return intents.get(id);
}
@@ -185,23 +190,24 @@
}
@Override
- public void addListener(IntentEventListener listener) {
+ public void addListener(IntentListener listener) {
listeners.add(listener);
}
@Override
- public void removeListener(IntentEventListener listener) {
+ public void removeListener(IntentListener listener) {
listeners.remove(listener);
}
private void dispatch(IntentEvent event) {
- for (IntentEventListener listener : listeners) {
+ for (IntentListener listener : listeners) {
listener.event(event);
}
}
@Override
- public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+ public <T extends Intent> void registerCompiler(Class<T> cls,
+ IntentCompiler<T> compiler) {
compilers.put(cls, compiler);
}
@@ -216,7 +222,8 @@
}
@Override
- public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+ public <T extends InstallableIntent> void registerInstaller(Class<T> cls,
+ IntentInstaller<T> installer) {
installers.put(cls, installer);
}
@@ -227,7 +234,7 @@
@Override
public Map<Class<? extends InstallableIntent>,
- IntentInstaller<? extends InstallableIntent>> getInstallers() {
+ IntentInstaller<? extends InstallableIntent>> getInstallers() {
return Collections.unmodifiableMap(installers);
}
@@ -252,7 +259,8 @@
if (!installers.containsKey(intent.getClass())) {
Class<?> cls = intent.getClass();
while (cls != Object.class) {
- // As long as we're within the InstallableIntent class descendants
+ // As long as we're within the InstallableIntent class
+ // descendants
if (InstallableIntent.class.isAssignableFrom(cls)) {
IntentInstaller<?> installer = installers.get(cls);
if (installer != null) {
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
index c7682b1..825be86 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/IntentServiceTest.java
@@ -51,7 +51,7 @@
@Test
public void basics() {
// Make sure there are no intents
- assertEquals("incorrect intent count", 0, service.getIntents().size());
+ assertEquals("incorrect intent count", 0, service.getIntentCount());
// Register a compiler and an installer both setup for success.
service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID)));
@@ -73,8 +73,7 @@
validateEvents(intent, SUBMITTED, COMPILED, INSTALLED);
// Make sure there is just one intent (and is ours)
- assertEquals("incorrect intent count", 1, service.getIntents().size());
- assertEquals("incorrect intent", intent, service.getIntent(intent.getId()));
+ assertEquals("incorrect intent count", 1, service.getIntentCount());
// Reset the listener events
listener.events.clear();
@@ -250,7 +249,7 @@
// Fixture to track emitted intent events
- protected class TestListener implements IntentEventListener {
+ protected class TestListener implements IntentListener {
final List<IntentEvent> events = new ArrayList<>();
@Override
diff --git a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
index b61afd2..3c4a885 100644
--- a/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/device/impl/DeviceManager.java
@@ -38,7 +38,7 @@
import org.onlab.onos.net.device.PortDescription;
import org.onlab.onos.net.provider.AbstractProviderRegistry;
import org.onlab.onos.net.provider.AbstractProviderService;
-import org.onlab.onos.store.ClockService;
+import org.onlab.onos.store.ClockProviderService;
import org.slf4j.Logger;
/**
@@ -80,7 +80,7 @@
protected MastershipTermService termService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ClockService clockService;
+ protected ClockProviderService clockProviderService;
@Activate
public void activate() {
@@ -274,7 +274,7 @@
if (event.master().equals(clusterService.getLocalNode().id())) {
MastershipTerm term = mastershipService.requestTermService()
.getMastershipTerm(event.subject());
- clockService.setMastershipTerm(event.subject(), term);
+ clockProviderService.setMastershipTerm(event.subject(), term);
applyRole(event.subject(), MastershipRole.MASTER);
} else {
applyRole(event.subject(), MastershipRole.STANDBY);
diff --git a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
index 00619b3..db82eed 100644
--- a/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/flow/impl/FlowRuleManager.java
@@ -242,15 +242,16 @@
}
private boolean checkRuleLiveness(FlowRule swRule, FlowRule storedRule) {
- int timeout = storedRule.timeout();
- if (storedRule.packets() != swRule.packets()) {
- deadRounds.get(swRule).set(0);
- return true;
- }
-
- return (deadRounds.get(swRule).getAndIncrement() *
- FlowRuleProvider.POLL_INTERVAL) <= timeout;
-
+ return true;
+// int timeout = storedRule.timeout();
+// if (storedRule.packets() != swRule.packets()) {
+// deadRounds.get(swRule).set(0);
+// return true;
+// }
+//
+// return (deadRounds.get(swRule).getAndIncrement() *
+// FlowRuleProvider.POLL_INTERVAL) <= timeout;
+//
}
// Posts the specified event to the local event dispatcher.
diff --git a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
index 9f8dd48..628f424 100644
--- a/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
+++ b/core/net/src/main/java/org/onlab/onos/net/host/impl/HostMonitor.java
@@ -4,9 +4,9 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;
import org.jboss.netty.util.Timeout;
@@ -59,15 +59,14 @@
private final Set<IpAddress> monitoredAddresses;
- private final Map<ProviderId, HostProvider> hostProviders;
+ private final ConcurrentMap<ProviderId, HostProvider> hostProviders;
- private final long probeRate;
+ private static final long DEFAULT_PROBE_RATE = 30000; // milliseconds
+ private long probeRate = DEFAULT_PROBE_RATE;
private final Timeout timeout;
- public HostMonitor(
- DeviceService deviceService,
- PacketService packetService,
+ public HostMonitor(DeviceService deviceService, PacketService packetService,
HostManager hostService) {
this.deviceService = deviceService;
@@ -77,15 +76,7 @@
monitoredAddresses = new HashSet<>();
hostProviders = new ConcurrentHashMap<>();
- probeRate = 30000; // milliseconds
-
timeout = Timer.getTimer().newTimeout(this, 0, TimeUnit.MILLISECONDS);
-
- addDefaultAddresses();
- }
-
- private void addDefaultAddresses() {
- //monitoredAddresses.add(IpAddress.valueOf("10.0.0.1"));
}
void addMonitoringFor(IpAddress ip) {
@@ -104,10 +95,6 @@
hostProviders.put(provider.id(), provider);
}
- void unregisterHostProvider(HostProvider provider) {
- // TODO find out how to call this
- }
-
@Override
public void run(Timeout timeout) throws Exception {
for (IpAddress ip : monitoredAddresses) {
@@ -121,7 +108,9 @@
} else {
for (Host host : hosts) {
HostProvider provider = hostProviders.get(host.providerId());
- if (provider != null) {
+ if (provider == null) {
+ hostProviders.remove(host.providerId(), null);
+ } else {
provider.triggerProbe(host);
}
}
@@ -161,7 +150,7 @@
List<Instruction> instructions = new ArrayList<>();
instructions.add(Instructions.createOutput(port.number()));
- TrafficTreatment treatment = new DefaultTrafficTreatment.Builder()
+ TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(port.number())
.build();
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java
new file mode 100644
index 0000000..00b64da
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/AbstractBlockAllocatorBasedIdGenerator.java
@@ -0,0 +1,42 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IdGenerator;
+
+/**
+ * Base class of {@link IdGenerator} implementations which use {@link IdBlockAllocator} as
+ * backend.
+ *
+ * @param <T> the type of ID
+ */
+public abstract class AbstractBlockAllocatorBasedIdGenerator<T> implements IdGenerator<T> {
+ protected final IdBlockAllocator allocator;
+ protected IdBlock idBlock;
+
+ /**
+ * Constructs an ID generator which use {@link IdBlockAllocator} as backend.
+ *
+ * @param allocator
+ */
+ protected AbstractBlockAllocatorBasedIdGenerator(IdBlockAllocator allocator) {
+ this.allocator = allocator;
+ this.idBlock = allocator.allocateUniqueIdBlock();
+ }
+
+ @Override
+ public synchronized T getNewId() {
+ try {
+ return convertFrom(idBlock.getNextId());
+ } catch (UnavailableIdException e) {
+ idBlock = allocator.allocateUniqueIdBlock();
+ return convertFrom(idBlock.getNextId());
+ }
+ }
+
+ /**
+ * Returns an ID instance of {@code T} type from the long value.
+ *
+ * @param value original long value
+ * @return ID instance
+ */
+ protected abstract T convertFrom(long value);
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java
new file mode 100644
index 0000000..f331aa2
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/DummyIdBlockAllocator.java
@@ -0,0 +1,31 @@
+package org.onlab.onos.net.intent.impl;
+
+public class DummyIdBlockAllocator implements IdBlockAllocator {
+ private long blockTop;
+ private static final long BLOCK_SIZE = 0x1000000L;
+
+ /**
+ * Returns a block of IDs which are unique and unused.
+ * Range of IDs is fixed size and is assigned incrementally as this method
+ * called.
+ *
+ * @return an IdBlock containing a set of unique IDs
+ */
+ @Override
+ public IdBlock allocateUniqueIdBlock() {
+ synchronized (this) {
+ long blockHead = blockTop;
+ long blockTail = blockTop + BLOCK_SIZE;
+
+ IdBlock block = new IdBlock(blockHead, BLOCK_SIZE);
+ blockTop = blockTail;
+
+ return block;
+ }
+ }
+
+ @Override
+ public IdBlock allocateUniqueIdBlock(long range) {
+ throw new UnsupportedOperationException("Not supported yet");
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
new file mode 100644
index 0000000..a8bea2e
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/HostToHostIntentCompiler.java
@@ -0,0 +1,67 @@
+package org.onlab.onos.net.intent.impl;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Path;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.intent.HostToHostIntent;
+import org.onlab.onos.net.intent.IdGenerator;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentCompiler;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.PathIntent;
+import org.onlab.onos.net.topology.PathService;
+
+/**
+ * A intent compiler for {@link HostToHostIntent}.
+ */
+@Component(immediate = true)
+public class HostToHostIntentCompiler
+ implements IntentCompiler<HostToHostIntent> {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected PathService pathService;
+
+ private IdGenerator<IntentId> intentIdGenerator;
+
+ @Activate
+ public void activate() {
+ IdBlockAllocator idBlockAllocator = new DummyIdBlockAllocator();
+ intentIdGenerator = new IdBlockAllocatorBasedIntentIdGenerator(idBlockAllocator);
+ intentManager.registerCompiler(HostToHostIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterCompiler(HostToHostIntent.class);
+ }
+
+ @Override
+ public List<Intent> compile(HostToHostIntent intent) {
+ Set<Path> paths = pathService.getPaths(intent.getSrc(), intent.getDst());
+ if (paths.isEmpty()) {
+ throw new PathNotFoundException();
+ }
+ Path path = paths.iterator().next();
+
+ return Arrays.asList((Intent) new PathIntent(
+ intentIdGenerator.getNewId(),
+ intent.getTrafficSelector(),
+ intent.getTrafficTreatment(),
+ new ConnectPoint(intent.getSrc(), PortNumber.ALL),
+ new ConnectPoint(intent.getDst(), PortNumber.ALL),
+ path));
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java
new file mode 100644
index 0000000..ce418ea
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlock.java
@@ -0,0 +1,111 @@
+package org.onlab.onos.net.intent.impl;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import java.util.Objects;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.google.common.base.MoreObjects;
+
+/**
+ * A class representing an ID space.
+ */
+public final class IdBlock {
+ private final long start;
+ private final long size;
+
+ private final AtomicLong currentId;
+
+ /**
+ * Constructs a new ID block with the specified size and initial value.
+ *
+ * @param start initial value of the block
+ * @param size size of the block
+ * @throws IllegalArgumentException if the size is less than or equal to 0
+ */
+ public IdBlock(long start, long size) {
+ checkArgument(size > 0, "size should be more than 0, but %s", size);
+
+ this.start = start;
+ this.size = size;
+
+ this.currentId = new AtomicLong(start);
+ }
+
+ // TODO: consider if this method is needed or not
+ /**
+ * Returns the initial value.
+ *
+ * @return initial value
+ */
+ public long getStart() {
+ return start;
+ }
+
+ // TODO: consider if this method is needed or not
+ /**
+ * Returns the last value.
+ *
+ * @return last value
+ */
+ public long getEnd() {
+ return start + size - 1;
+ }
+
+ /**
+ * Returns the block size.
+ *
+ * @return block size
+ */
+ public long getSize() {
+ return size;
+ }
+
+ /**
+ * Returns the next ID in the block.
+ *
+ * @return next ID
+ * @throws UnavailableIdException if there is no available ID in the block.
+ */
+ public long getNextId() {
+ final long id = currentId.getAndIncrement();
+ if (id > getEnd()) {
+ throw new UnavailableIdException(String.format(
+ "used all IDs in allocated space (size: %d, end: %d, current: %d)",
+ size, getEnd(), id
+ ));
+ }
+
+ return id;
+ }
+
+ // TODO: Do we really need equals and hashCode? Should it contain currentId?
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ IdBlock that = (IdBlock) o;
+ return Objects.equals(this.start, that.start)
+ && Objects.equals(this.size, that.size)
+ && Objects.equals(this.currentId.get(), that.currentId.get());
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(start, size, currentId);
+ }
+
+ @Override
+ public String toString() {
+ return MoreObjects.toStringHelper(getClass())
+ .add("start", start)
+ .add("size", size)
+ .add("currentId", currentId)
+ .toString();
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java
new file mode 100644
index 0000000..1adac02
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocator.java
@@ -0,0 +1,21 @@
+package org.onlab.onos.net.intent.impl;
+
+/**
+ * An interface that gives unique ID spaces.
+ */
+public interface IdBlockAllocator {
+ /**
+ * Allocates a unique Id Block.
+ *
+ * @return Id Block.
+ */
+ IdBlock allocateUniqueIdBlock();
+
+ /**
+ * Allocates next unique id and retrieve a new range of ids if needed.
+ *
+ * @param range range to use for the identifier
+ * @return Id Block.
+ */
+ IdBlock allocateUniqueIdBlock(long range);
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java
new file mode 100644
index 0000000..9620e59
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IdBlockAllocatorBasedIntentIdGenerator.java
@@ -0,0 +1,25 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentId;
+
+/**
+ * An implementation of {@link org.onlab.onos.net.intent.IdGenerator} of intent ID,
+ * which uses {@link IdBlockAllocator}.
+ */
+public class IdBlockAllocatorBasedIntentIdGenerator extends AbstractBlockAllocatorBasedIdGenerator<IntentId> {
+
+ /**
+ * Constructs an intent ID generator, which uses the specified ID block allocator
+ * to generate a global unique intent ID.
+ *
+ * @param allocator the ID block allocator to use for generating intent IDs
+ */
+ public IdBlockAllocatorBasedIntentIdGenerator(IdBlockAllocator allocator) {
+ super(allocator);
+ }
+
+ @Override
+ protected IntentId convertFrom(long value) {
+ return new IntentId(value);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java
new file mode 100644
index 0000000..bf739df
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentCompilationException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when a intent compilation fails.
+ */
+public class IntentCompilationException extends IntentException {
+ private static final long serialVersionUID = 235237603018210810L;
+
+ public IntentCompilationException() {
+ super();
+ }
+
+ public IntentCompilationException(String message) {
+ super(message);
+ }
+
+ public IntentCompilationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java
new file mode 100644
index 0000000..3b17cf1
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentInstallationException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when intent installation fails.
+ */
+public class IntentInstallationException extends IntentException {
+ private static final long serialVersionUID = 3720268258616014168L;
+
+ public IntentInstallationException() {
+ super();
+ }
+
+ public IntentInstallationException(String message) {
+ super(message);
+ }
+
+ public IntentInstallationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
new file mode 100644
index 0000000..f1c9de3
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -0,0 +1,363 @@
+package org.onlab.onos.net.intent.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.onlab.onos.net.intent.IntentState.FAILED;
+import static org.onlab.onos.net.intent.IntentState.INSTALLED;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWING;
+import static org.onlab.onos.net.intent.IntentState.WITHDRAWN;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.event.AbstractListenerRegistry;
+import org.onlab.onos.event.EventDeliveryService;
+import org.onlab.onos.net.intent.InstallableIntent;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentCompiler;
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentException;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentInstaller;
+import org.onlab.onos.net.intent.IntentListener;
+import org.onlab.onos.net.intent.IntentOperations;
+import org.onlab.onos.net.intent.IntentService;
+import org.onlab.onos.net.intent.IntentState;
+import org.onlab.onos.net.intent.IntentStore;
+import org.onlab.onos.net.intent.IntentStoreDelegate;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableMap;
+
+/**
+ * An implementation of Intent Manager.
+ */
+@Component(immediate = true)
+@Service
+public class IntentManager
+ implements IntentService, IntentExtensionService {
+ private final Logger log = getLogger(getClass());
+
+ public static final String INTENT_NULL = "Intent cannot be null";
+ public static final String INTENT_ID_NULL = "Intent ID cannot be null";
+
+ // Collections for compiler, installer, and listener are ONOS instance local
+ private final ConcurrentMap<Class<? extends Intent>,
+ IntentCompiler<? extends Intent>> compilers = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Class<? extends InstallableIntent>,
+ IntentInstaller<? extends InstallableIntent>> installers = new ConcurrentHashMap<>();
+ private final CopyOnWriteArrayList<IntentListener> listeners = new CopyOnWriteArrayList<>();
+
+ private final AbstractListenerRegistry<IntentEvent, IntentListener>
+ listenerRegistry = new AbstractListenerRegistry<>();
+
+ private final IntentStoreDelegate delegate = new InternalStoreDelegate();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentStore store;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected EventDeliveryService eventDispatcher;
+
+ @Activate
+ public void activate() {
+ store.setDelegate(delegate);
+ eventDispatcher.addSink(IntentEvent.class, listenerRegistry);
+
+// this.intentEvents = new IntentMap<>("intentState", IntentEvent.class, collectionsService);
+// this.installableIntents =
+// new IntentMap<>("installableIntents", IntentCompilationResult.class, collectionsService);
+//
+//
+// this.intentEvents.addListener(new InternalEntryListener(new InternalIntentEventListener()));
+
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ store.unsetDelegate(delegate);
+ eventDispatcher.removeSink(IntentEvent.class);
+ log.info("Stopped");
+ }
+
+ @Override
+ public void submit(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ registerSubclassCompilerIfNeeded(intent);
+ IntentEvent event = store.createIntent(intent);
+ eventDispatcher.post(event);
+ processStoreEvent(event);
+ }
+
+ @Override
+ public void withdraw(Intent intent) {
+ checkNotNull(intent, INTENT_NULL);
+ IntentEvent event = store.setState(intent, WITHDRAWING);
+ eventDispatcher.post(event);
+ processStoreEvent(event);
+ }
+
+ // FIXME: implement this method
+ @Override
+ public void execute(IntentOperations operations) {
+ throw new UnsupportedOperationException("execute() is not implemented yet");
+ }
+
+ @Override
+ public Iterable<Intent> getIntents() {
+ return store.getIntents();
+ }
+
+ @Override
+ public long getIntentCount() {
+ return store.getIntentCount();
+ }
+
+ @Override
+ public Intent getIntent(IntentId id) {
+ checkNotNull(id, INTENT_ID_NULL);
+ return store.getIntent(id);
+ }
+
+ @Override
+ public IntentState getIntentState(IntentId id) {
+ checkNotNull(id, INTENT_ID_NULL);
+ return store.getIntentState(id);
+ }
+
+ @Override
+ public void addListener(IntentListener listener) {
+ listenerRegistry.addListener(listener);
+ }
+
+ @Override
+ public void removeListener(IntentListener listener) {
+ listenerRegistry.removeListener(listener);
+ }
+
+ @Override
+ public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
+ compilers.put(cls, compiler);
+ }
+
+ @Override
+ public <T extends Intent> void unregisterCompiler(Class<T> cls) {
+ compilers.remove(cls);
+ }
+
+ @Override
+ public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
+ return ImmutableMap.copyOf(compilers);
+ }
+
+ @Override
+ public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
+ installers.put(cls, installer);
+ }
+
+ @Override
+ public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
+ installers.remove(cls);
+ }
+
+ @Override
+ public Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers() {
+ return ImmutableMap.copyOf(installers);
+ }
+
+ /**
+ * Invokes all of registered intent event listener.
+ *
+ * @param event event supplied to a listener as an argument
+ */
+ private void invokeListeners(IntentEvent event) {
+ for (IntentListener listener : listeners) {
+ listener.event(event);
+ }
+ }
+
+ /**
+ * Returns the corresponding intent compiler to the specified intent.
+ *
+ * @param intent intent
+ * @param <T> the type of intent
+ * @return intent compiler corresponding to the specified intent
+ */
+ private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
+ @SuppressWarnings("unchecked")
+ IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
+ if (compiler == null) {
+ throw new IntentException("no compiler for class " + intent.getClass());
+ }
+ return compiler;
+ }
+
+ /**
+ * Returns the corresponding intent installer to the specified installable intent.
+ * @param intent intent
+ * @param <T> the type of installable intent
+ * @return intent installer corresponding to the specified installable intent
+ */
+ private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
+ @SuppressWarnings("unchecked")
+ IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
+ if (installer == null) {
+ throw new IntentException("no installer for class " + intent.getClass());
+ }
+ return installer;
+ }
+
+ /**
+ * Compiles an intent.
+ *
+ * @param intent intent
+ */
+ private void compileIntent(Intent intent) {
+ // FIXME: To make SDN-IP workable ASAP, only single level compilation is implemented
+ // TODO: implement compilation traversing tree structure
+ List<InstallableIntent> installable = new ArrayList<>();
+ for (Intent compiled : getCompiler(intent).compile(intent)) {
+ installable.add((InstallableIntent) compiled);
+ }
+ IntentEvent event = store.addInstallableIntents(intent.getId(), installable);
+ eventDispatcher.post(event);
+ processStoreEvent(event);
+ }
+
+ /**
+ * Installs an intent.
+ *
+ * @param intent intent
+ */
+ private void installIntent(Intent intent) {
+ for (InstallableIntent installable : store.getInstallableIntents(intent.getId())) {
+ registerSubclassInstallerIfNeeded(installable);
+ getInstaller(installable).install(installable);
+ }
+
+ IntentEvent event = store.setState(intent, INSTALLED);
+ eventDispatcher.post(event);
+ processStoreEvent(event);
+
+ }
+
+ /**
+ * Uninstalls an intent.
+ *
+ * @param intent intent
+ */
+ private void uninstallIntent(Intent intent) {
+ for (InstallableIntent installable : store.getInstallableIntents(intent.getId())) {
+ getInstaller(installable).uninstall(installable);
+ }
+
+ store.removeInstalledIntents(intent.getId());
+ store.setState(intent, WITHDRAWN);
+ }
+
+ /**
+ * Registers an intent compiler of the specified intent if an intent compiler
+ * for the intent is not registered. This method traverses the class hierarchy of
+ * the intent. Once an intent compiler for a parent type is found, this method
+ * registers the found intent compiler.
+ *
+ * @param intent intent
+ */
+ private void registerSubclassCompilerIfNeeded(Intent intent) {
+ if (!compilers.containsKey(intent.getClass())) {
+ Class<?> cls = intent.getClass();
+ while (cls != Object.class) {
+ // As long as we're within the Intent class descendants
+ if (Intent.class.isAssignableFrom(cls)) {
+ IntentCompiler<?> compiler = compilers.get(cls);
+ if (compiler != null) {
+ compilers.put(intent.getClass(), compiler);
+ return;
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ }
+ }
+
+ /**
+ * Registers an intent installer of the specified intent if an intent installer
+ * for the intent is not registered. This method traverses the class hierarchy of
+ * the intent. Once an intent installer for a parent type is found, this method
+ * registers the found intent installer.
+ *
+ * @param intent intent
+ */
+ private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
+ if (!installers.containsKey(intent.getClass())) {
+ Class<?> cls = intent.getClass();
+ while (cls != Object.class) {
+ // As long as we're within the InstallableIntent class descendants
+ if (InstallableIntent.class.isAssignableFrom(cls)) {
+ IntentInstaller<?> installer = installers.get(cls);
+ if (installer != null) {
+ installers.put(intent.getClass(), installer);
+ return;
+ }
+ }
+ cls = cls.getSuperclass();
+ }
+ }
+ }
+
+ /**
+ * Handles state transition of submitted intents.
+ */
+ private void processStoreEvent(IntentEvent event) {
+ invokeListeners(event);
+ Intent intent = event.getIntent();
+
+ try {
+ switch (event.getState()) {
+ case SUBMITTED:
+ compileIntent(intent);
+ break;
+ case COMPILED:
+ installIntent(intent);
+ break;
+ case INSTALLED:
+ break;
+ case WITHDRAWING:
+ uninstallIntent(intent);
+ break;
+ case WITHDRAWN:
+ break;
+ case FAILED:
+ break;
+ default:
+ throw new IllegalStateException(
+ "the state of IntentEvent is illegal: " + event.getState());
+ }
+ } catch (IntentException e) {
+ store.setState(intent, FAILED);
+ }
+
+ }
+
+ // Store delegate to re-post events emitted from the store.
+ private class InternalStoreDelegate implements IntentStoreDelegate {
+ @Override
+ public void notify(IntentEvent event) {
+ processStoreEvent(event);
+ eventDispatcher.post(event);
+ }
+ }
+
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java
new file mode 100644
index 0000000..5ee4ee4
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentRemovalException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when intent removal failed.
+ */
+public class IntentRemovalException extends IntentException {
+ private static final long serialVersionUID = -5259226322037891951L;
+
+ public IntentRemovalException() {
+ super();
+ }
+
+ public IntentRemovalException(String message) {
+ super(message);
+ }
+
+ public IntentRemovalException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
new file mode 100644
index 0000000..6aa9f66
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathIntentInstaller.java
@@ -0,0 +1,75 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.onlab.onos.ApplicationId;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.flow.DefaultFlowRule;
+import org.onlab.onos.net.flow.DefaultTrafficSelector;
+import org.onlab.onos.net.flow.DefaultTrafficTreatment;
+import org.onlab.onos.net.flow.FlowRule;
+import org.onlab.onos.net.flow.FlowRuleService;
+import org.onlab.onos.net.flow.TrafficSelector;
+import org.onlab.onos.net.flow.TrafficTreatment;
+import org.onlab.onos.net.intent.IntentExtensionService;
+import org.onlab.onos.net.intent.IntentInstaller;
+import org.onlab.onos.net.intent.PathIntent;
+
+import java.util.Iterator;
+
+/**
+ * Installer for {@link PathIntent path connectivity intents}.
+ */
+@Component(immediate = true)
+public class PathIntentInstaller implements IntentInstaller<PathIntent> {
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected IntentExtensionService intentManager;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected FlowRuleService flowRuleService;
+
+ private final ApplicationId appId = ApplicationId.getAppId();
+
+ @Activate
+ public void activate() {
+ intentManager.registerInstaller(PathIntent.class, this);
+ }
+
+ @Deactivate
+ public void deactivate() {
+ intentManager.unregisterInstaller(PathIntent.class);
+ }
+
+ @Override
+ public void install(PathIntent intent) {
+ TrafficSelector.Builder builder =
+ DefaultTrafficSelector.builder(intent.getTrafficSelector());
+ Iterator<Link> links = intent.getPath().links().iterator();
+ ConnectPoint prev = links.next().dst();
+ while (links.hasNext()) {
+ builder.matchInport(prev.port());
+ Link link = links.next();
+
+ TrafficTreatment.Builder treat = DefaultTrafficTreatment.builder();
+ treat.setOutput(link.src().port());
+
+ FlowRule rule = new DefaultFlowRule(link.src().deviceId(),
+ builder.build(), treat.build(),
+ 0, appId, 30);
+ flowRuleService.applyFlowRules(rule);
+
+ prev = link.dst();
+ }
+
+ }
+
+ @Override
+ public void uninstall(PathIntent intent) {
+ //TODO
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java
new file mode 100644
index 0000000..a1fd63a
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/PathNotFoundException.java
@@ -0,0 +1,22 @@
+package org.onlab.onos.net.intent.impl;
+
+import org.onlab.onos.net.intent.IntentException;
+
+/**
+ * An exception thrown when a path is not found.
+ */
+public class PathNotFoundException extends IntentException {
+ private static final long serialVersionUID = -2087045731049914733L;
+
+ public PathNotFoundException() {
+ super();
+ }
+
+ public PathNotFoundException(String message) {
+ super(message);
+ }
+
+ public PathNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java
new file mode 100644
index 0000000..fd4f122
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/UnavailableIdException.java
@@ -0,0 +1,34 @@
+package org.onlab.onos.net.intent.impl;
+
+/**
+ * Represents that there is no available IDs.
+ */
+public class UnavailableIdException extends RuntimeException {
+
+ private static final long serialVersionUID = -2287403908433720122L;
+
+ /**
+ * Constructs an exception with no message and no underlying cause.
+ */
+ public UnavailableIdException() {
+ }
+
+ /**
+ * Constructs an exception with the specified message.
+ *
+ * @param message the message describing the specific nature of the error
+ */
+ public UnavailableIdException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructs an exception with the specified message and the underlying cause.
+ *
+ * @param message the message describing the specific nature of the error
+ * @param cause the underlying cause of this error
+ */
+ public UnavailableIdException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java
new file mode 100644
index 0000000..3f00271
--- /dev/null
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Core subsystem for tracking high-level intents for treatment of selected
+ * network traffic.
+ */
+package org.onlab.onos.net.intent.impl;
\ No newline at end of file
diff --git a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
index 6e07c3e..3570948 100644
--- a/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/proxyarp/impl/ProxyArpManager.java
@@ -43,7 +43,6 @@
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
-
@Component(immediate = true)
@Service
public class ProxyArpManager implements ProxyArpService {
@@ -128,7 +127,7 @@
Ethernet arpReply = buildArpReply(dst, eth);
// TODO: check send status with host service.
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(src.location().port());
packetService.emit(new DefaultOutboundPacket(src.location().deviceId(),
builder.build(), ByteBuffer.wrap(arpReply.serialize())));
@@ -148,7 +147,7 @@
if (h == null) {
flood(eth);
} else {
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
builder.setOutput(h.location().port());
packetService.emit(new DefaultOutboundPacket(h.location().deviceId(),
builder.build(), ByteBuffer.wrap(eth.serialize())));
@@ -166,7 +165,7 @@
synchronized (externalPorts) {
for (Entry<Device, PortNumber> entry : externalPorts.entries()) {
- builder = new DefaultTrafficTreatment.Builder();
+ builder = DefaultTrafficTreatment.builder();
builder.setOutput(entry.getValue());
packetService.emit(new DefaultOutboundPacket(entry.getKey().id(),
builder.build(), buf));
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/MessageSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/MessageSerializer.java
index 98e80f7..10368aa 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/MessageSerializer.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/impl/MessageSerializer.java
@@ -1,46 +1,16 @@
package org.onlab.onos.store.cluster.impl;
-import de.javakaffee.kryoserializers.URISerializer;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.ControllerNode;
-import org.onlab.onos.cluster.DefaultControllerNode;
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DefaultDevice;
-import org.onlab.onos.net.DefaultLink;
-import org.onlab.onos.net.DefaultPort;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Element;
-import org.onlab.onos.net.Link;
-import org.onlab.onos.net.LinkKey;
-import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.provider.ProviderId;
import org.onlab.onos.store.cluster.messaging.MessageSubject;
import org.onlab.onos.store.cluster.messaging.SerializationService;
-import org.onlab.onos.store.serializers.ConnectPointSerializer;
-import org.onlab.onos.store.serializers.DefaultLinkSerializer;
-import org.onlab.onos.store.serializers.DefaultPortSerializer;
-import org.onlab.onos.store.serializers.DeviceIdSerializer;
-import org.onlab.onos.store.serializers.IpPrefixSerializer;
-import org.onlab.onos.store.serializers.LinkKeySerializer;
-import org.onlab.onos.store.serializers.NodeIdSerializer;
-import org.onlab.onos.store.serializers.PortNumberSerializer;
-import org.onlab.onos.store.serializers.ProviderIdSerializer;
-import org.onlab.packet.IpPrefix;
+import org.onlab.onos.store.serializers.KryoPoolUtil;
import org.onlab.util.KryoPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.HashMap;
-
/**
* Factory for parsing messages sent between cluster members.
*/
@@ -72,34 +42,10 @@
* Sets up the common serialzers pool.
*/
protected void setupKryoPool() {
- // FIXME Slice out types used in common to separate pool/namespace.
serializerPool = KryoPool.newBuilder()
- .register(ArrayList.class,
- HashMap.class,
-
- ControllerNode.State.class,
- Device.Type.class,
-
- DefaultControllerNode.class,
- DefaultDevice.class,
- MastershipRole.class,
- Port.class,
- Element.class,
-
- Link.Type.class,
-
- MessageSubject.class
- )
- .register(IpPrefix.class, new IpPrefixSerializer())
- .register(URI.class, new URISerializer())
- .register(NodeId.class, new NodeIdSerializer())
- .register(ProviderId.class, new ProviderIdSerializer())
- .register(DeviceId.class, new DeviceIdSerializer())
- .register(PortNumber.class, new PortNumberSerializer())
- .register(DefaultPort.class, new DefaultPortSerializer())
- .register(LinkKey.class, new LinkKeySerializer())
- .register(ConnectPoint.class, new ConnectPointSerializer())
- .register(DefaultLink.class, new DefaultLinkSerializer())
+ .register(KryoPoolUtil.API)
+ // TODO: Should MessageSubject be in API bundle?
+ .register(MessageSubject.class)
.build()
.populate(1);
}
@@ -114,4 +60,4 @@
public byte[] encode(Object payload) {
return serializerPool.serialize(payload);
}
-}
\ No newline at end of file
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/package-info.java
new file mode 100644
index 0000000..6c1e71b
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/cluster/messaging/impl/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Implementation of the cluster messaging mechanism.
+ */
+package org.onlab.onos.store.cluster.messaging.impl;
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/impl/OnosTimestamp.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
similarity index 73%
rename from core/store/dist/src/main/java/org/onlab/onos/store/impl/OnosTimestamp.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
index 2005582..0f4f894 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/impl/OnosTimestamp.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/MastershipBasedTimestamp.java
@@ -1,4 +1,4 @@
-package org.onlab.onos.store.impl;
+package org.onlab.onos.store.common.impl;
import static com.google.common.base.Preconditions.checkArgument;
@@ -9,12 +9,11 @@
import com.google.common.base.MoreObjects;
import com.google.common.collect.ComparisonChain;
-// If it is store specific, implement serializable interfaces?
/**
* Default implementation of Timestamp.
* TODO: Better documentation.
*/
-public final class OnosTimestamp implements Timestamp {
+public final class MastershipBasedTimestamp implements Timestamp {
private final int termNumber;
private final int sequenceNumber;
@@ -25,15 +24,16 @@
* @param termNumber the mastership termNumber
* @param sequenceNumber the sequenceNumber number within the termNumber
*/
- public OnosTimestamp(int termNumber, int sequenceNumber) {
+ public MastershipBasedTimestamp(int termNumber, int sequenceNumber) {
this.termNumber = termNumber;
this.sequenceNumber = sequenceNumber;
}
@Override
public int compareTo(Timestamp o) {
- checkArgument(o instanceof OnosTimestamp, "Must be OnosTimestamp", o);
- OnosTimestamp that = (OnosTimestamp) o;
+ checkArgument(o instanceof MastershipBasedTimestamp,
+ "Must be MastershipBasedTimestamp", o);
+ MastershipBasedTimestamp that = (MastershipBasedTimestamp) o;
return ComparisonChain.start()
.compare(this.termNumber, that.termNumber)
@@ -51,10 +51,10 @@
if (this == obj) {
return true;
}
- if (!(obj instanceof OnosTimestamp)) {
+ if (!(obj instanceof MastershipBasedTimestamp)) {
return false;
}
- OnosTimestamp that = (OnosTimestamp) obj;
+ MastershipBasedTimestamp that = (MastershipBasedTimestamp) obj;
return Objects.equals(this.termNumber, that.termNumber) &&
Objects.equals(this.sequenceNumber, that.sequenceNumber);
}
@@ -84,4 +84,11 @@
public int sequenceNumber() {
return sequenceNumber;
}
+
+ // Default constructor for serialization
+ @Deprecated
+ protected MastershipBasedTimestamp() {
+ this.termNumber = -1;
+ this.sequenceNumber = -1;
+ }
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java
new file mode 100644
index 0000000..2908b16
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/Timestamped.java
@@ -0,0 +1,79 @@
+package org.onlab.onos.store.common.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import java.util.Objects;
+
+import org.onlab.onos.store.Timestamp;
+
+/**
+ * Wrapper class to store Timestamped value.
+ * @param <T>
+ */
+public final class Timestamped<T> {
+
+ private final Timestamp timestamp;
+ private final T value;
+
+ /**
+ * Creates a time stamped value.
+ *
+ * @param value to be timestamp
+ * @param timestamp the timestamp
+ */
+ public Timestamped(T value, Timestamp timestamp) {
+ this.value = checkNotNull(value);
+ this.timestamp = checkNotNull(timestamp);
+ }
+
+ /**
+ * Returns the value.
+ * @return value
+ */
+ public T value() {
+ return value;
+ }
+
+ /**
+ * Returns the time stamp.
+ * @return time stamp
+ */
+ public Timestamp timestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Tests if this timestamped value is newer than the other.
+ *
+ * @param other timestamped value
+ * @return true if this instance is newer.
+ */
+ public boolean isNewer(Timestamped<T> other) {
+ return this.timestamp.compareTo(checkNotNull(other).timestamp()) > 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return timestamp.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof Timestamped)) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ Timestamped<T> that = (Timestamped<T>) obj;
+ return Objects.equals(this.timestamp, that.timestamp);
+ }
+
+ // Default constructor for serialization
+ @Deprecated
+ protected Timestamped() {
+ this.value = null;
+ this.timestamp = null;
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/package-info.java
new file mode 100644
index 0000000..992fd49
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/common/impl/package-info.java
@@ -0,0 +1,5 @@
+/**
+ * Common abstractions and facilities for implementing distributed store
+ * using gossip protocol.
+ */
+package org.onlab.onos.store.common.impl;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosClockService.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
similarity index 81%
rename from core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosClockService.java
rename to core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
index a99482f..e1e3692 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosClockService.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/DeviceClockManager.java
@@ -12,14 +12,18 @@
import org.apache.felix.scr.annotations.Service;
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.ClockProviderService;
import org.onlab.onos.store.ClockService;
import org.onlab.onos.store.Timestamp;
-import org.onlab.onos.store.impl.OnosTimestamp;
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
import org.slf4j.Logger;
+/**
+ * Clock service to issue Timestamp based on Device Mastership.
+ */
@Component(immediate = true)
@Service
-public class OnosClockService implements ClockService {
+public class DeviceClockManager implements ClockService, ClockProviderService {
private final Logger log = getLogger(getClass());
@@ -43,7 +47,7 @@
if (term == null) {
throw new IllegalStateException("Requesting timestamp for a deviceId without mastership");
}
- return new OnosTimestamp(term.termNumber(), ticker.incrementAndGet());
+ return new MastershipBasedTimestamp(term.termNumber(), ticker.incrementAndGet());
}
@Override
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
new file mode 100644
index 0000000..0edbc21
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/GossipDeviceStore.java
@@ -0,0 +1,652 @@
+package org.onlab.onos.store.device.impl;
+
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableList;
+
+import org.apache.commons.lang3.concurrent.ConcurrentException;
+import org.apache.commons.lang3.concurrent.ConcurrentInitializer;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.net.Annotations;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.DefaultDevice;
+import org.onlab.onos.net.DefaultPort;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.Device.Type;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.device.DefaultDeviceDescription;
+import org.onlab.onos.net.device.DefaultPortDescription;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceStore;
+import org.onlab.onos.net.device.DeviceStoreDelegate;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.AbstractStore;
+import org.onlab.onos.store.ClockService;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.common.impl.Timestamped;
+import org.slf4j.Logger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.notNull;
+import static org.onlab.onos.net.device.DeviceEvent.Type.*;
+import static org.slf4j.LoggerFactory.getLogger;
+import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
+import static org.onlab.onos.net.DefaultAnnotations.merge;
+import static com.google.common.base.Verify.verify;
+
+// TODO: implement remove event handling and call *Internal
+/**
+ * Manages inventory of infrastructure devices using gossip protocol to distribute
+ * information.
+ */
+@Component(immediate = true)
+@Service
+public class GossipDeviceStore
+ extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
+ implements DeviceStore {
+
+ private final Logger log = getLogger(getClass());
+
+ public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
+
+ // TODO: Check if inner Map can be replaced with plain Map
+ // innerMap is used to lock a Device, thus instance should never be replaced.
+ // collection of Description given from various providers
+ private final ConcurrentMap<DeviceId,
+ ConcurrentMap<ProviderId, DeviceDescriptions>>
+ deviceDescs = new ConcurrentHashMap<>();
+
+ // cache of Device and Ports generated by compositing descriptions from providers
+ private final ConcurrentMap<DeviceId, Device> devices = new ConcurrentHashMap<>();
+ private final ConcurrentMap<DeviceId, ConcurrentMap<PortNumber, Port>> devicePorts = new ConcurrentHashMap<>();
+
+ // available(=UP) devices
+ private final Set<DeviceId> availableDevices = new HashSet<>();
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected ClockService clockService;
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ deviceDescs.clear();
+ devices.clear();
+ devicePorts.clear();
+ availableDevices.clear();
+ log.info("Stopped");
+ }
+
+ @Override
+ public int getDeviceCount() {
+ return devices.size();
+ }
+
+ @Override
+ public Iterable<Device> getDevices() {
+ return Collections.unmodifiableCollection(devices.values());
+ }
+
+ @Override
+ public Device getDevice(DeviceId deviceId) {
+ return devices.get(deviceId);
+ }
+
+ @Override
+ public synchronized DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
+ DeviceDescription deviceDescription) {
+ Timestamp newTimestamp = clockService.getTimestamp(deviceId);
+ final Timestamped<DeviceDescription> deltaDesc = new Timestamped<>(deviceDescription, newTimestamp);
+ DeviceEvent event = createOrUpdateDeviceInternal(providerId, deviceId, deltaDesc);
+ if (event != null) {
+ // FIXME: broadcast deltaDesc, UP
+ log.debug("broadcast deltaDesc");
+ }
+ return event;
+ }
+
+ private DeviceEvent createOrUpdateDeviceInternal(ProviderId providerId, DeviceId deviceId,
+ Timestamped<DeviceDescription> deltaDesc) {
+
+ // Collection of DeviceDescriptions for a Device
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
+ = createIfAbsentUnchecked(deviceDescs, deviceId,
+ new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
+
+
+ DeviceDescriptions descs
+ = createIfAbsentUnchecked(providerDescs, providerId,
+ new InitDeviceDescs(deltaDesc));
+
+ // update description
+ synchronized (providerDescs) {
+ // locking per device
+
+ final Device oldDevice = devices.get(deviceId);
+ final Device newDevice;
+
+ if (deltaDesc == descs.getDeviceDesc() ||
+ deltaDesc.isNewer(descs.getDeviceDesc())) {
+ // on new device or valid update
+ descs.putDeviceDesc(deltaDesc);
+ newDevice = composeDevice(deviceId, providerDescs);
+ } else {
+ // outdated event, ignored.
+ return null;
+ }
+ if (oldDevice == null) {
+ // ADD
+ return createDevice(providerId, newDevice);
+ } else {
+ // UPDATE or ignore (no change or stale)
+ return updateDevice(providerId, oldDevice, newDevice);
+ }
+ }
+ }
+
+ // Creates the device and returns the appropriate event if necessary.
+ // Guarded by deviceDescs value (=locking Device)
+ private DeviceEvent createDevice(ProviderId providerId,
+ Device newDevice) {
+
+ // update composed device cache
+ Device oldDevice = devices.putIfAbsent(newDevice.id(), newDevice);
+ verify(oldDevice == null,
+ "Unexpected Device in cache. PID:%s [old=%s, new=%s]",
+ providerId, oldDevice, newDevice);
+
+ if (!providerId.isAncillary()) {
+ availableDevices.add(newDevice.id());
+ }
+
+ return new DeviceEvent(DeviceEvent.Type.DEVICE_ADDED, newDevice, null);
+ }
+
+ // Updates the device and returns the appropriate event if necessary.
+ // Guarded by deviceDescs value (=locking Device)
+ private DeviceEvent updateDevice(ProviderId providerId,
+ Device oldDevice, Device newDevice) {
+
+ // We allow only certain attributes to trigger update
+ if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
+ !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
+ !isAnnotationsEqual(oldDevice.annotations(), newDevice.annotations())) {
+
+ boolean replaced = devices.replace(newDevice.id(), oldDevice, newDevice);
+ if (!replaced) {
+ verify(replaced,
+ "Replacing devices cache failed. PID:%s [expected:%s, found:%s, new=%s]",
+ providerId, oldDevice, devices.get(newDevice.id())
+ , newDevice);
+ }
+ if (!providerId.isAncillary()) {
+ availableDevices.add(newDevice.id());
+ }
+ return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, newDevice, null);
+ }
+
+ // Otherwise merely attempt to change availability if primary provider
+ if (!providerId.isAncillary()) {
+ boolean added = availableDevices.add(newDevice.id());
+ return !added ? null :
+ new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, newDevice, null);
+ }
+ return null;
+ }
+
+ @Override
+ public DeviceEvent markOffline(DeviceId deviceId) {
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs
+ = createIfAbsentUnchecked(deviceDescs, deviceId,
+ new InitConcurrentHashMap<ProviderId, DeviceDescriptions>());
+
+ // locking device
+ synchronized (providerDescs) {
+ Device device = devices.get(deviceId);
+ if (device == null) {
+ return null;
+ }
+ boolean removed = availableDevices.remove(deviceId);
+ if (removed) {
+ // TODO: broadcast ... DOWN only?
+ return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
+
+ }
+ return null;
+ }
+ }
+
+ @Override
+ public synchronized List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
+ List<PortDescription> portDescriptions) {
+ Timestamp newTimestamp = clockService.getTimestamp(deviceId);
+
+ List<Timestamped<PortDescription>> deltaDescs = new ArrayList<>(portDescriptions.size());
+ for (PortDescription e : portDescriptions) {
+ deltaDescs.add(new Timestamped<PortDescription>(e, newTimestamp));
+ }
+
+ List<DeviceEvent> events = updatePortsInternal(providerId, deviceId, deltaDescs);
+ if (!events.isEmpty()) {
+ // FIXME: broadcast deltaDesc, UP
+ log.debug("broadcast deltaDesc");
+ }
+ return events;
+
+ }
+
+ private List<DeviceEvent> updatePortsInternal(ProviderId providerId, DeviceId deviceId,
+ List<Timestamped<PortDescription>> deltaDescs) {
+
+ Device device = devices.get(deviceId);
+ checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
+
+ ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
+
+ DeviceDescriptions descs = descsMap.get(providerId);
+ // every provider must provide DeviceDescription.
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
+
+ List<DeviceEvent> events = new ArrayList<>();
+ synchronized (descsMap) {
+ Map<PortNumber, Port> ports = getPortMap(deviceId);
+
+ // Add new ports
+ Set<PortNumber> processed = new HashSet<>();
+ for (Timestamped<PortDescription> deltaDesc : deltaDescs) {
+ final PortNumber number = deltaDesc.value().portNumber();
+ final Port oldPort = ports.get(number);
+ final Port newPort;
+
+ final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
+ if (existingPortDesc == null ||
+ deltaDesc == existingPortDesc ||
+ deltaDesc.isNewer(existingPortDesc)) {
+ // on new port or valid update
+ // update description
+ descs.putPortDesc(deltaDesc);
+ newPort = composePort(device, number, descsMap);
+ } else {
+ // outdated event, ignored.
+ continue;
+ }
+
+ events.add(oldPort == null ?
+ createPort(device, newPort, ports) :
+ updatePort(device, oldPort, newPort, ports));
+ processed.add(number);
+ }
+
+ events.addAll(pruneOldPorts(device, ports, processed));
+ }
+ return FluentIterable.from(events).filter(notNull()).toList();
+ }
+
+ // Creates a new port based on the port description adds it to the map and
+ // Returns corresponding event.
+ // Guarded by deviceDescs value (=locking Device)
+ private DeviceEvent createPort(Device device, Port newPort,
+ Map<PortNumber, Port> ports) {
+ ports.put(newPort.number(), newPort);
+ return new DeviceEvent(PORT_ADDED, device, newPort);
+ }
+
+ // Checks if the specified port requires update and if so, it replaces the
+ // existing entry in the map and returns corresponding event.
+ // Guarded by deviceDescs value (=locking Device)
+ private DeviceEvent updatePort(Device device, Port oldPort,
+ Port newPort,
+ Map<PortNumber, Port> ports) {
+ if (oldPort.isEnabled() != newPort.isEnabled() ||
+ !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) {
+
+ ports.put(oldPort.number(), newPort);
+ return new DeviceEvent(PORT_UPDATED, device, newPort);
+ }
+ return null;
+ }
+
+ // Prunes the specified list of ports based on which ports are in the
+ // processed list and returns list of corresponding events.
+ // Guarded by deviceDescs value (=locking Device)
+ private List<DeviceEvent> pruneOldPorts(Device device,
+ Map<PortNumber, Port> ports,
+ Set<PortNumber> processed) {
+ List<DeviceEvent> events = new ArrayList<>();
+ Iterator<PortNumber> iterator = ports.keySet().iterator();
+ while (iterator.hasNext()) {
+ PortNumber portNumber = iterator.next();
+ if (!processed.contains(portNumber)) {
+ events.add(new DeviceEvent(PORT_REMOVED, device,
+ ports.get(portNumber)));
+ iterator.remove();
+ }
+ }
+ return events;
+ }
+
+ // Gets the map of ports for the specified device; if one does not already
+ // exist, it creates and registers a new one.
+ private ConcurrentMap<PortNumber, Port> getPortMap(DeviceId deviceId) {
+ return createIfAbsentUnchecked(devicePorts, deviceId,
+ new InitConcurrentHashMap<PortNumber, Port>());
+ }
+
+ @Override
+ public synchronized DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
+ PortDescription portDescription) {
+ Timestamp newTimestamp = clockService.getTimestamp(deviceId);
+ final Timestamped<PortDescription> deltaDesc = new Timestamped<>(portDescription, newTimestamp);
+ DeviceEvent event = updatePortStatusInternal(providerId, deviceId, deltaDesc);
+ if (event != null) {
+ // FIXME: broadcast deltaDesc
+ log.debug("broadcast deltaDesc");
+ }
+ return event;
+ }
+
+ private DeviceEvent updatePortStatusInternal(ProviderId providerId, DeviceId deviceId,
+ Timestamped<PortDescription> deltaDesc) {
+
+ Device device = devices.get(deviceId);
+ checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
+
+ ConcurrentMap<ProviderId, DeviceDescriptions> descsMap = deviceDescs.get(deviceId);
+ checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
+
+ DeviceDescriptions descs = descsMap.get(providerId);
+ // assuming all providers must to give DeviceDescription
+ checkArgument(descs != null,
+ "Device description for Device ID %s from Provider %s was not found",
+ deviceId, providerId);
+
+ synchronized (descsMap) {
+ ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
+ final PortNumber number = deltaDesc.value().portNumber();
+ final Port oldPort = ports.get(number);
+ final Port newPort;
+
+ final Timestamped<PortDescription> existingPortDesc = descs.getPortDesc(number);
+ if (existingPortDesc == null ||
+ deltaDesc == existingPortDesc ||
+ deltaDesc.isNewer(existingPortDesc)) {
+ // on new port or valid update
+ // update description
+ descs.putPortDesc(deltaDesc);
+ newPort = composePort(device, number, descsMap);
+ } else {
+ // outdated event, ignored.
+ return null;
+ }
+
+ if (oldPort == null) {
+ return createPort(device, newPort, ports);
+ } else {
+ return updatePort(device, oldPort, newPort, ports);
+ }
+ }
+ }
+
+ @Override
+ public List<Port> getPorts(DeviceId deviceId) {
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ if (ports == null) {
+ return Collections.emptyList();
+ }
+ return ImmutableList.copyOf(ports.values());
+ }
+
+ @Override
+ public Port getPort(DeviceId deviceId, PortNumber portNumber) {
+ Map<PortNumber, Port> ports = devicePorts.get(deviceId);
+ return ports == null ? null : ports.get(portNumber);
+ }
+
+ @Override
+ public boolean isAvailable(DeviceId deviceId) {
+ return availableDevices.contains(deviceId);
+ }
+
+ @Override
+ public DeviceEvent removeDevice(DeviceId deviceId) {
+ synchronized (this) {
+ Device device = devices.remove(deviceId);
+ return device == null ? null :
+ new DeviceEvent(DEVICE_REMOVED, device, null);
+ }
+ }
+
+ private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) {
+ if (lhs == rhs) {
+ return true;
+ }
+ if (lhs == null || rhs == null) {
+ return false;
+ }
+
+ if (!lhs.keys().equals(rhs.keys())) {
+ return false;
+ }
+
+ for (String key : lhs.keys()) {
+ if (!lhs.value(key).equals(rhs.value(key))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a Device, merging description given from multiple Providers.
+ *
+ * @param deviceId device identifier
+ * @param providerDescs Collection of Descriptions from multiple providers
+ * @return Device instance
+ */
+ private Device composeDevice(DeviceId deviceId,
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+
+ checkArgument(!providerDescs.isEmpty(), "No Device descriptions supplied");
+
+ ProviderId primary = pickPrimaryPID(providerDescs);
+
+ DeviceDescriptions desc = providerDescs.get(primary);
+
+ DeviceDescription base = desc.getDeviceDesc().value();
+ Type type = base.type();
+ String manufacturer = base.manufacturer();
+ String hwVersion = base.hwVersion();
+ String swVersion = base.swVersion();
+ String serialNumber = base.serialNumber();
+ DefaultAnnotations annotations = DefaultAnnotations.builder().build();
+ annotations = merge(annotations, base.annotations());
+
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (e.getKey().equals(primary)) {
+ continue;
+ }
+ // TODO: should keep track of Description timestamp
+ // and only merge conflicting keys when timestamp is newer
+ // Currently assuming there will never be a key conflict between
+ // providers
+
+ // annotation merging. not so efficient, should revisit later
+ annotations = merge(annotations, e.getValue().getDeviceDesc().value().annotations());
+ }
+
+ return new DefaultDevice(primary, deviceId , type, manufacturer,
+ hwVersion, swVersion, serialNumber, annotations);
+ }
+
+ /**
+ * Returns a Port, merging description given from multiple Providers.
+ *
+ * @param device device the port is on
+ * @param number port number
+ * @param providerDescs Collection of Descriptions from multiple providers
+ * @return Port instance
+ */
+ private Port composePort(Device device, PortNumber number,
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+
+ ProviderId primary = pickPrimaryPID(providerDescs);
+ DeviceDescriptions primDescs = providerDescs.get(primary);
+ // if no primary, assume not enabled
+ // TODO: revisit this default port enabled/disabled behavior
+ boolean isEnabled = false;
+ DefaultAnnotations annotations = DefaultAnnotations.builder().build();
+
+ final Timestamped<PortDescription> portDesc = primDescs.getPortDesc(number);
+ if (portDesc != null) {
+ isEnabled = portDesc.value().isEnabled();
+ annotations = merge(annotations, portDesc.value().annotations());
+ }
+
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (e.getKey().equals(primary)) {
+ continue;
+ }
+ // TODO: should keep track of Description timestamp
+ // and only merge conflicting keys when timestamp is newer
+ // Currently assuming there will never be a key conflict between
+ // providers
+
+ // annotation merging. not so efficient, should revisit later
+ final Timestamped<PortDescription> otherPortDesc = e.getValue().getPortDesc(number);
+ if (otherPortDesc != null) {
+ annotations = merge(annotations, otherPortDesc.value().annotations());
+ }
+ }
+
+ return new DefaultPort(device, number, isEnabled, annotations);
+ }
+
+ /**
+ * @return primary ProviderID, or randomly chosen one if none exists
+ */
+ private ProviderId pickPrimaryPID(
+ ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
+ ProviderId fallBackPrimary = null;
+ for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
+ if (!e.getKey().isAncillary()) {
+ return e.getKey();
+ } else if (fallBackPrimary == null) {
+ // pick randomly as a fallback in case there is no primary
+ fallBackPrimary = e.getKey();
+ }
+ }
+ return fallBackPrimary;
+ }
+
+ private static final class InitConcurrentHashMap<K, V> implements
+ ConcurrentInitializer<ConcurrentMap<K, V>> {
+ @Override
+ public ConcurrentMap<K, V> get() throws ConcurrentException {
+ return new ConcurrentHashMap<>();
+ }
+ }
+
+ public static final class InitDeviceDescs
+ implements ConcurrentInitializer<DeviceDescriptions> {
+
+ private final Timestamped<DeviceDescription> deviceDesc;
+
+ public InitDeviceDescs(Timestamped<DeviceDescription> deviceDesc) {
+ this.deviceDesc = checkNotNull(deviceDesc);
+ }
+ @Override
+ public DeviceDescriptions get() throws ConcurrentException {
+ return new DeviceDescriptions(deviceDesc);
+ }
+ }
+
+
+ /**
+ * Collection of Description of a Device and it's Ports given from a Provider.
+ */
+ public static class DeviceDescriptions {
+
+ private final AtomicReference<Timestamped<DeviceDescription>> deviceDesc;
+ private final ConcurrentMap<PortNumber, Timestamped<PortDescription>> portDescs;
+
+ public DeviceDescriptions(Timestamped<DeviceDescription> desc) {
+ this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
+ this.portDescs = new ConcurrentHashMap<>();
+ }
+
+ public Timestamped<DeviceDescription> getDeviceDesc() {
+ return deviceDesc.get();
+ }
+
+ public Timestamped<PortDescription> getPortDesc(PortNumber number) {
+ return portDescs.get(number);
+ }
+
+ /**
+ * Puts DeviceDescription, merging annotations as necessary.
+ *
+ * @param newDesc new DeviceDescription
+ * @return previous DeviceDescription
+ */
+ public synchronized Timestamped<DeviceDescription> putDeviceDesc(Timestamped<DeviceDescription> newDesc) {
+ Timestamped<DeviceDescription> oldOne = deviceDesc.get();
+ Timestamped<DeviceDescription> newOne = newDesc;
+ if (oldOne != null) {
+ SparseAnnotations merged = merge(oldOne.value().annotations(),
+ newDesc.value().annotations());
+ newOne = new Timestamped<DeviceDescription>(
+ new DefaultDeviceDescription(newDesc.value(), merged),
+ newDesc.timestamp());
+ }
+ return deviceDesc.getAndSet(newOne);
+ }
+
+ /**
+ * Puts PortDescription, merging annotations as necessary.
+ *
+ * @param newDesc new PortDescription
+ * @return previous PortDescription
+ */
+ public synchronized Timestamped<PortDescription> putPortDesc(Timestamped<PortDescription> newDesc) {
+ Timestamped<PortDescription> oldOne = portDescs.get(newDesc.value().portNumber());
+ Timestamped<PortDescription> newOne = newDesc;
+ if (oldOne != null) {
+ SparseAnnotations merged = merge(oldOne.value().annotations(),
+ newDesc.value().annotations());
+ newOne = new Timestamped<PortDescription>(
+ new DefaultPortDescription(newDesc.value(), merged),
+ newDesc.timestamp());
+ }
+ return portDescs.put(newOne.value().portNumber(), newOne);
+ }
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java
deleted file mode 100644
index d912983..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/OnosDistributedDeviceStore.java
+++ /dev/null
@@ -1,339 +0,0 @@
-package org.onlab.onos.store.device.impl;
-
-import static com.google.common.base.Predicates.notNull;
-import static com.google.common.base.Preconditions.checkState;
-
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.net.DefaultDevice;
-import org.onlab.onos.net.DefaultPort;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.device.DeviceDescription;
-import org.onlab.onos.net.device.DeviceEvent;
-import org.onlab.onos.net.device.DeviceStore;
-import org.onlab.onos.net.device.DeviceStoreDelegate;
-import org.onlab.onos.net.device.PortDescription;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.onos.store.AbstractStore;
-import org.onlab.onos.store.ClockService;
-import org.onlab.onos.store.Timestamp;
-import org.slf4j.Logger;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static org.onlab.onos.net.device.DeviceEvent.Type.*;
-import static org.slf4j.LoggerFactory.getLogger;
-
-/**
- * Manages inventory of infrastructure devices using a protocol that takes into consideration
- * the order in which device events occur.
- */
-@Component(immediate = true)
-@Service
-public class OnosDistributedDeviceStore
- extends AbstractStore<DeviceEvent, DeviceStoreDelegate>
- implements DeviceStore {
-
- private final Logger log = getLogger(getClass());
-
- public static final String DEVICE_NOT_FOUND = "Device with ID %s not found";
-
- private ConcurrentMap<DeviceId, VersionedValue<Device>> devices;
- private ConcurrentMap<DeviceId, Map<PortNumber, VersionedValue<Port>>> devicePorts;
-
- @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
- protected ClockService clockService;
-
- @Activate
- public void activate() {
-
- devices = new ConcurrentHashMap<>();
- devicePorts = new ConcurrentHashMap<>();
-
- log.info("Started");
- }
-
- @Deactivate
- public void deactivate() {
- log.info("Stopped");
- }
-
- @Override
- public int getDeviceCount() {
- return devices.size();
- }
-
- @Override
- public Iterable<Device> getDevices() {
- Builder<Device> builder = ImmutableSet.builder();
- synchronized (this) {
- for (VersionedValue<Device> device : devices.values()) {
- builder.add(device.entity());
- }
- return builder.build();
- }
- }
-
- @Override
- public Device getDevice(DeviceId deviceId) {
- VersionedValue<Device> device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- return device.entity();
- }
-
- @Override
- public DeviceEvent createOrUpdateDevice(ProviderId providerId, DeviceId deviceId,
- DeviceDescription deviceDescription) {
- Timestamp newTimestamp = clockService.getTimestamp(deviceId);
- VersionedValue<Device> device = devices.get(deviceId);
-
- if (device == null) {
- return createDevice(providerId, deviceId, deviceDescription, newTimestamp);
- }
-
- checkState(newTimestamp.compareTo(device.timestamp()) > 0,
- "Existing device has a timestamp in the future!");
-
- return updateDevice(providerId, device.entity(), deviceDescription, newTimestamp);
- }
-
- // Creates the device and returns the appropriate event if necessary.
- private DeviceEvent createDevice(ProviderId providerId, DeviceId deviceId,
- DeviceDescription desc, Timestamp timestamp) {
- Device device = new DefaultDevice(providerId, deviceId, desc.type(),
- desc.manufacturer(),
- desc.hwVersion(), desc.swVersion(),
- desc.serialNumber());
-
- devices.put(deviceId, new VersionedValue<>(device, true, timestamp));
- // TODO,FIXME: broadcast a message telling peers of a device event.
- return new DeviceEvent(DEVICE_ADDED, device, null);
- }
-
- // Updates the device and returns the appropriate event if necessary.
- private DeviceEvent updateDevice(ProviderId providerId, Device device,
- DeviceDescription desc, Timestamp timestamp) {
- // We allow only certain attributes to trigger update
- if (!Objects.equals(device.hwVersion(), desc.hwVersion()) ||
- !Objects.equals(device.swVersion(), desc.swVersion())) {
-
- Device updated = new DefaultDevice(providerId, device.id(),
- desc.type(),
- desc.manufacturer(),
- desc.hwVersion(),
- desc.swVersion(),
- desc.serialNumber());
- devices.put(device.id(), new VersionedValue<Device>(updated, true, timestamp));
- // FIXME: broadcast a message telling peers of a device event.
- return new DeviceEvent(DeviceEvent.Type.DEVICE_UPDATED, updated, null);
- }
-
- // Otherwise merely attempt to change availability
- Device updated = new DefaultDevice(providerId, device.id(),
- desc.type(),
- desc.manufacturer(),
- desc.hwVersion(),
- desc.swVersion(),
- desc.serialNumber());
-
- VersionedValue<Device> oldDevice = devices.put(device.id(),
- new VersionedValue<Device>(updated, true, timestamp));
- if (!oldDevice.isUp()) {
- return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device, null);
- } else {
- return null;
- }
- }
-
- @Override
- public DeviceEvent markOffline(DeviceId deviceId) {
- VersionedValue<Device> device = devices.get(deviceId);
- boolean willRemove = device != null && device.isUp();
- if (!willRemove) {
- return null;
- }
- Timestamp timestamp = clockService.getTimestamp(deviceId);
- if (replaceIfLatest(device.entity(), false, timestamp)) {
- return new DeviceEvent(DEVICE_AVAILABILITY_CHANGED, device.entity(), null);
- }
- return null;
- }
-
- // Replace existing value if its timestamp is older.
- private synchronized boolean replaceIfLatest(Device device, boolean isUp, Timestamp timestamp) {
- VersionedValue<Device> existingValue = devices.get(device.id());
- if (timestamp.compareTo(existingValue.timestamp()) > 0) {
- devices.put(device.id(), new VersionedValue<Device>(device, isUp, timestamp));
- return true;
- }
- return false;
- }
-
- @Override
- public List<DeviceEvent> updatePorts(ProviderId providerId, DeviceId deviceId,
- List<PortDescription> portDescriptions) {
- List<DeviceEvent> events = new ArrayList<>();
- synchronized (this) {
- VersionedValue<Device> device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- Map<PortNumber, VersionedValue<Port>> ports = getPortMap(deviceId);
- Timestamp newTimestamp = clockService.getTimestamp(deviceId);
-
- // Add new ports
- Set<PortNumber> processed = new HashSet<>();
- for (PortDescription portDescription : portDescriptions) {
- VersionedValue<Port> port = ports.get(portDescription.portNumber());
- if (port == null) {
- events.add(createPort(device, portDescription, ports, newTimestamp));
- }
- checkState(newTimestamp.compareTo(port.timestamp()) > 0,
- "Existing port state has a timestamp in the future!");
- events.add(updatePort(device.entity(), port.entity(), portDescription, ports, newTimestamp));
- processed.add(portDescription.portNumber());
- }
-
- updatePortMap(deviceId, ports);
-
- events.addAll(pruneOldPorts(device.entity(), ports, processed));
- }
- return FluentIterable.from(events).filter(notNull()).toList();
- }
-
- // Creates a new port based on the port description adds it to the map and
- // Returns corresponding event.
- //@GuardedBy("this")
- private DeviceEvent createPort(VersionedValue<Device> device, PortDescription portDescription,
- Map<PortNumber, VersionedValue<Port>> ports, Timestamp timestamp) {
- Port port = new DefaultPort(device.entity(), portDescription.portNumber(),
- portDescription.isEnabled());
- ports.put(port.number(), new VersionedValue<Port>(port, true, timestamp));
- updatePortMap(device.entity().id(), ports);
- return new DeviceEvent(PORT_ADDED, device.entity(), port);
- }
-
- // Checks if the specified port requires update and if so, it replaces the
- // existing entry in the map and returns corresponding event.
- //@GuardedBy("this")
- private DeviceEvent updatePort(Device device, Port port,
- PortDescription portDescription,
- Map<PortNumber, VersionedValue<Port>> ports,
- Timestamp timestamp) {
- if (port.isEnabled() != portDescription.isEnabled()) {
- VersionedValue<Port> updatedPort = new VersionedValue<Port>(
- new DefaultPort(device, portDescription.portNumber(),
- portDescription.isEnabled()),
- portDescription.isEnabled(),
- timestamp);
- ports.put(port.number(), updatedPort);
- updatePortMap(device.id(), ports);
- return new DeviceEvent(PORT_UPDATED, device, updatedPort.entity());
- }
- return null;
- }
-
- // Prunes the specified list of ports based on which ports are in the
- // processed list and returns list of corresponding events.
- //@GuardedBy("this")
- private List<DeviceEvent> pruneOldPorts(Device device,
- Map<PortNumber, VersionedValue<Port>> ports,
- Set<PortNumber> processed) {
- List<DeviceEvent> events = new ArrayList<>();
- Iterator<PortNumber> iterator = ports.keySet().iterator();
- while (iterator.hasNext()) {
- PortNumber portNumber = iterator.next();
- if (!processed.contains(portNumber)) {
- events.add(new DeviceEvent(PORT_REMOVED, device,
- ports.get(portNumber).entity()));
- iterator.remove();
- }
- }
- if (!events.isEmpty()) {
- updatePortMap(device.id(), ports);
- }
- return events;
- }
-
- // Gets the map of ports for the specified device; if one does not already
- // exist, it creates and registers a new one.
- // WARN: returned value is a copy, changes made to the Map
- // needs to be written back using updatePortMap
- //@GuardedBy("this")
- private Map<PortNumber, VersionedValue<Port>> getPortMap(DeviceId deviceId) {
- Map<PortNumber, VersionedValue<Port>> ports = devicePorts.get(deviceId);
- if (ports == null) {
- ports = new HashMap<>();
- // this probably is waste of time in most cases.
- updatePortMap(deviceId, ports);
- }
- return ports;
- }
-
- //@GuardedBy("this")
- private void updatePortMap(DeviceId deviceId, Map<PortNumber, VersionedValue<Port>> ports) {
- devicePorts.put(deviceId, ports);
- }
-
- @Override
- public DeviceEvent updatePortStatus(ProviderId providerId, DeviceId deviceId,
- PortDescription portDescription) {
- VersionedValue<Device> device = devices.get(deviceId);
- checkArgument(device != null, DEVICE_NOT_FOUND, deviceId);
- Map<PortNumber, VersionedValue<Port>> ports = getPortMap(deviceId);
- VersionedValue<Port> port = ports.get(portDescription.portNumber());
- Timestamp timestamp = clockService.getTimestamp(deviceId);
- return updatePort(device.entity(), port.entity(), portDescription, ports, timestamp);
- }
-
- @Override
- public List<Port> getPorts(DeviceId deviceId) {
- Map<PortNumber, VersionedValue<Port>> versionedPorts = devicePorts.get(deviceId);
- if (versionedPorts == null) {
- return Collections.emptyList();
- }
- List<Port> ports = new ArrayList<>();
- for (VersionedValue<Port> port : versionedPorts.values()) {
- ports.add(port.entity());
- }
- return ports;
- }
-
- @Override
- public Port getPort(DeviceId deviceId, PortNumber portNumber) {
- Map<PortNumber, VersionedValue<Port>> ports = devicePorts.get(deviceId);
- return ports == null ? null : ports.get(portNumber).entity();
- }
-
- @Override
- public boolean isAvailable(DeviceId deviceId) {
- return devices.get(deviceId).isUp();
- }
-
- @Override
- public DeviceEvent removeDevice(DeviceId deviceId) {
- VersionedValue<Device> previousDevice = devices.remove(deviceId);
- return previousDevice == null ? null :
- new DeviceEvent(DEVICE_REMOVED, previousDevice.entity(), null);
- }
-}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java
index aa644db..c1f5aad 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/device/impl/package-info.java
@@ -1,4 +1,4 @@
/**
* Implementation of device store using distributed distributed p2p synchronization protocol.
*/
-package org.onlab.onos.store.device.impl;
\ No newline at end of file
+package org.onlab.onos.store.device.impl;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
index 5df25b4..a59b151 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/OnosDistributedLinkStore.java
@@ -42,6 +42,7 @@
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
+//TODO: Add support for multiple provider and annotations
/**
* Manages inventory of infrastructure links using a protocol that takes into consideration
* the order in which events occur.
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java
index 127dc84..c675f84 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/package-info.java
@@ -1,4 +1,4 @@
/**
* Implementation of link store using distributed p2p synchronization protocol.
*/
-package org.onlab.onos.store.link.impl;
\ No newline at end of file
+package org.onlab.onos.store.link.impl;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java
new file mode 100644
index 0000000..9250076
--- /dev/null
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/MastershipBasedTimestampSerializer.java
@@ -0,0 +1,36 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.onos.store.common.impl.MastershipBasedTimestamp;
+
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+// To be used if Timestamp ever needs to cross bundle boundary.
+/**
+ * Kryo Serializer for {@link MastershipBasedTimestamp}.
+ */
+public class MastershipBasedTimestampSerializer extends Serializer<MastershipBasedTimestamp> {
+
+ /**
+ * Default constructor.
+ */
+ public MastershipBasedTimestampSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output, MastershipBasedTimestamp object) {
+ output.writeInt(object.termNumber());
+ output.writeInt(object.sequenceNumber());
+ }
+
+ @Override
+ public MastershipBasedTimestamp read(Kryo kryo, Input input, Class<MastershipBasedTimestamp> type) {
+ final int term = input.readInt();
+ final int sequence = input.readInt();
+ return new MastershipBasedTimestamp(term, sequence);
+ }
+}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/OnosTimestampSerializer.java b/core/store/dist/src/main/java/org/onlab/onos/store/serializers/OnosTimestampSerializer.java
deleted file mode 100644
index 192e035..0000000
--- a/core/store/dist/src/main/java/org/onlab/onos/store/serializers/OnosTimestampSerializer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.onlab.onos.store.serializers;
-
-import org.onlab.onos.store.impl.OnosTimestamp;
-
-import com.esotericsoftware.kryo.Kryo;
-import com.esotericsoftware.kryo.Serializer;
-import com.esotericsoftware.kryo.io.Input;
-import com.esotericsoftware.kryo.io.Output;
-
-/**
- * Kryo Serializer for {@link OnosTimestamp}.
- */
-public class OnosTimestampSerializer extends Serializer<OnosTimestamp> {
-
- /**
- * Default constructor.
- */
- public OnosTimestampSerializer() {
- // non-null, immutable
- super(false, true);
- }
-
- @Override
- public void write(Kryo kryo, Output output, OnosTimestamp object) {
- output.writeInt(object.termNumber());
- output.writeInt(object.sequenceNumber());
- }
-
- @Override
- public OnosTimestamp read(Kryo kryo, Input input, Class<OnosTimestamp> type) {
- final int term = input.readInt();
- final int sequence = input.readInt();
- return new OnosTimestamp(term, sequence);
- }
-}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java
new file mode 100644
index 0000000..ea63ef8
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/MastershipBasedTimestampTest.java
@@ -0,0 +1,95 @@
+package org.onlab.onos.store.common.impl;
+
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Test;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.onos.store.serializers.MastershipBasedTimestampSerializer;
+import org.onlab.util.KryoPool;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Test of {@link MastershipBasedTimestamp}.
+ */
+public class MastershipBasedTimestampTest {
+
+ private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
+ private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
+ private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
+ private static final Timestamp TS_2_2 = new MastershipBasedTimestamp(2, 2);
+
+ @Test
+ public final void testBasic() {
+ final int termNumber = 5;
+ final int sequenceNumber = 6;
+ MastershipBasedTimestamp ts = new MastershipBasedTimestamp(termNumber,
+ sequenceNumber);
+
+ assertEquals(termNumber, ts.termNumber());
+ assertEquals(sequenceNumber, ts.sequenceNumber());
+ }
+
+ @Test
+ public final void testCompareTo() {
+ assertTrue(TS_1_1.compareTo(TS_1_1) == 0);
+ assertTrue(TS_1_1.compareTo(new MastershipBasedTimestamp(1, 1)) == 0);
+
+ assertTrue(TS_1_1.compareTo(TS_1_2) < 0);
+ assertTrue(TS_1_2.compareTo(TS_1_1) > 0);
+
+ assertTrue(TS_1_2.compareTo(TS_2_1) < 0);
+ assertTrue(TS_1_2.compareTo(TS_2_2) < 0);
+ assertTrue(TS_2_1.compareTo(TS_1_1) > 0);
+ assertTrue(TS_2_2.compareTo(TS_1_1) > 0);
+ }
+
+ @Test
+ public final void testEqualsObject() {
+ new EqualsTester()
+ .addEqualityGroup(new MastershipBasedTimestamp(1, 1),
+ new MastershipBasedTimestamp(1, 1), TS_1_1)
+ .addEqualityGroup(new MastershipBasedTimestamp(1, 2),
+ new MastershipBasedTimestamp(1, 2), TS_1_2)
+ .addEqualityGroup(new MastershipBasedTimestamp(2, 1),
+ new MastershipBasedTimestamp(2, 1), TS_2_1)
+ .addEqualityGroup(new MastershipBasedTimestamp(2, 2),
+ new MastershipBasedTimestamp(2, 2), TS_2_2)
+ .testEquals();
+ }
+
+ @Test
+ public final void testKryoSerializable() {
+ final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
+ final KryoPool kryos = KryoPool.newBuilder()
+ .register(MastershipBasedTimestamp.class)
+ .build();
+
+ kryos.serialize(TS_2_1, buffer);
+ buffer.flip();
+ Timestamp copy = kryos.deserialize(buffer);
+
+ new EqualsTester()
+ .addEqualityGroup(TS_2_1, copy)
+ .testEquals();
+ }
+
+ @Test
+ public final void testKryoSerializableWithHandcraftedSerializer() {
+ final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
+ final KryoPool kryos = KryoPool.newBuilder()
+ .register(MastershipBasedTimestamp.class, new MastershipBasedTimestampSerializer())
+ .build();
+
+ kryos.serialize(TS_1_2, buffer);
+ buffer.flip();
+ Timestamp copy = kryos.deserialize(buffer);
+
+ new EqualsTester()
+ .addEqualityGroup(TS_1_2, copy)
+ .testEquals();
+ }
+
+}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java
new file mode 100644
index 0000000..4b44d40
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/common/impl/TimestampedTest.java
@@ -0,0 +1,94 @@
+package org.onlab.onos.store.common.impl;
+
+import static org.junit.Assert.*;
+
+import java.nio.ByteBuffer;
+
+import org.junit.Test;
+import org.onlab.onos.store.Timestamp;
+import org.onlab.util.KryoPool;
+
+import com.google.common.testing.EqualsTester;
+
+/**
+ * Test of {@link Timestamped}.
+ */
+public class TimestampedTest {
+
+ private static final Timestamp TS_1_1 = new MastershipBasedTimestamp(1, 1);
+ private static final Timestamp TS_1_2 = new MastershipBasedTimestamp(1, 2);
+ private static final Timestamp TS_2_1 = new MastershipBasedTimestamp(2, 1);
+
+ @Test
+ public final void testHashCode() {
+ Timestamped<String> a = new Timestamped<>("a", TS_1_1);
+ Timestamped<String> b = new Timestamped<>("b", TS_1_1);
+ assertTrue("value does not impact hashCode",
+ a.hashCode() == b.hashCode());
+ }
+
+ @Test
+ public final void testEquals() {
+ Timestamped<String> a = new Timestamped<>("a", TS_1_1);
+ Timestamped<String> b = new Timestamped<>("b", TS_1_1);
+ assertTrue("value does not impact equality",
+ a.equals(b));
+
+ new EqualsTester()
+ .addEqualityGroup(new Timestamped<>("a", TS_1_1),
+ new Timestamped<>("b", TS_1_1),
+ new Timestamped<>("c", TS_1_1))
+ .addEqualityGroup(new Timestamped<>("a", TS_1_2),
+ new Timestamped<>("b", TS_1_2),
+ new Timestamped<>("c", TS_1_2))
+ .addEqualityGroup(new Timestamped<>("a", TS_2_1),
+ new Timestamped<>("b", TS_2_1),
+ new Timestamped<>("c", TS_2_1))
+ .testEquals();
+
+ }
+
+ @Test
+ public final void testValue() {
+ final Integer n = Integer.valueOf(42);
+ Timestamped<Integer> tsv = new Timestamped<>(n, TS_1_1);
+ assertSame(n, tsv.value());
+
+ }
+
+ @Test(expected = NullPointerException.class)
+ public final void testValueNonNull() {
+ new Timestamped<>(null, TS_1_1);
+ }
+
+ @Test(expected = NullPointerException.class)
+ public final void testTimestampNonNull() {
+ new Timestamped<>("Foo", null);
+ }
+
+ @Test
+ public final void testIsNewer() {
+ Timestamped<String> a = new Timestamped<>("a", TS_1_2);
+ Timestamped<String> b = new Timestamped<>("b", TS_1_1);
+ assertTrue(a.isNewer(b));
+ assertFalse(b.isNewer(a));
+ }
+
+ @Test
+ public final void testKryoSerializable() {
+ final ByteBuffer buffer = ByteBuffer.allocate(1 * 1024 * 1024);
+ final KryoPool kryos = KryoPool.newBuilder()
+ .register(Timestamped.class,
+ MastershipBasedTimestamp.class)
+ .build();
+
+ Timestamped<String> original = new Timestamped<>("foobar", TS_1_1);
+ kryos.serialize(original, buffer);
+ buffer.flip();
+ Timestamped<String> copy = kryos.deserialize(buffer);
+
+ new EqualsTester()
+ .addEqualityGroup(original, copy)
+ .testEquals();
+ }
+}
diff --git a/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
new file mode 100644
index 0000000..09c3098
--- /dev/null
+++ b/core/store/dist/src/test/java/org/onlab/onos/store/device/impl/GossipDeviceStoreTest.java
@@ -0,0 +1,525 @@
+package org.onlab.onos.store.device.impl;
+
+import static org.junit.Assert.*;
+import static org.onlab.onos.net.Device.Type.SWITCH;
+import static org.onlab.onos.net.DeviceId.deviceId;
+import static org.onlab.onos.net.device.DeviceEvent.Type.*;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.Annotations;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.SparseAnnotations;
+import org.onlab.onos.net.device.DefaultDeviceDescription;
+import org.onlab.onos.net.device.DefaultPortDescription;
+import org.onlab.onos.net.device.DeviceDescription;
+import org.onlab.onos.net.device.DeviceEvent;
+import org.onlab.onos.net.device.DeviceStore;
+import org.onlab.onos.net.device.DeviceStoreDelegate;
+import org.onlab.onos.net.device.PortDescription;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.onos.store.ClockService;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+
+
+// TODO add tests for remote replication
+/**
+ * Test of the gossip based distributed DeviceStore implementation.
+ */
+public class GossipDeviceStoreTest {
+
+ private static final ProviderId PID = new ProviderId("of", "foo");
+ private static final ProviderId PIDA = new ProviderId("of", "bar", true);
+ private static final DeviceId DID1 = deviceId("of:foo");
+ private static final DeviceId DID2 = deviceId("of:bar");
+ private static final String MFR = "whitebox";
+ private static final String HW = "1.1.x";
+ private static final String SW1 = "3.8.1";
+ private static final String SW2 = "3.9.5";
+ private static final String SN = "43311-12345";
+
+ private static final PortNumber P1 = PortNumber.portNumber(1);
+ private static final PortNumber P2 = PortNumber.portNumber(2);
+ private static final PortNumber P3 = PortNumber.portNumber(3);
+
+ private static final SparseAnnotations A1 = DefaultAnnotations.builder()
+ .set("A1", "a1")
+ .set("B1", "b1")
+ .build();
+ private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
+ .remove("A1")
+ .set("B3", "b3")
+ .build();
+ private static final SparseAnnotations A2 = DefaultAnnotations.builder()
+ .set("A2", "a2")
+ .set("B2", "b2")
+ .build();
+ private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
+ .remove("A2")
+ .set("B4", "b4")
+ .build();
+
+ private static final NodeId MYSELF = new NodeId("myself");
+
+ private GossipDeviceStore gossipDeviceStore;
+ private DeviceStore deviceStore;
+
+ private DeviceClockManager deviceClockManager;
+ private ClockService clockService;
+
+ @BeforeClass
+ public static void setUpBeforeClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownAfterClass() throws Exception {
+ }
+
+
+ @Before
+ public void setUp() throws Exception {
+ deviceClockManager = new DeviceClockManager();
+ deviceClockManager.activate();
+ clockService = deviceClockManager;
+
+ deviceClockManager.setMastershipTerm(DID1, MastershipTerm.of(MYSELF, 1));
+ deviceClockManager.setMastershipTerm(DID2, MastershipTerm.of(MYSELF, 2));
+
+ gossipDeviceStore = new TestGossipDeviceStore(clockService);
+ gossipDeviceStore.activate();
+ deviceStore = gossipDeviceStore;
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ gossipDeviceStore.deactivate();
+ deviceClockManager.deactivate();
+ }
+
+ private void putDevice(DeviceId deviceId, String swVersion) {
+ DeviceDescription description =
+ new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
+ HW, swVersion, SN);
+ deviceStore.createOrUpdateDevice(PID, deviceId, description);
+ }
+
+ private void putDeviceAncillary(DeviceId deviceId, String swVersion) {
+ DeviceDescription description =
+ new DefaultDeviceDescription(deviceId.uri(), SWITCH, MFR,
+ HW, swVersion, SN);
+ deviceStore.createOrUpdateDevice(PIDA, deviceId, description);
+ }
+
+ private static void assertDevice(DeviceId id, String swVersion, Device device) {
+ assertNotNull(device);
+ assertEquals(id, device.id());
+ assertEquals(MFR, device.manufacturer());
+ assertEquals(HW, device.hwVersion());
+ assertEquals(swVersion, device.swVersion());
+ assertEquals(SN, device.serialNumber());
+ }
+
+ /**
+ * Verifies that Annotations created by merging {@code annotations} is
+ * equal to actual Annotations.
+ *
+ * @param actual Annotations to check
+ * @param annotations
+ */
+ private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
+ DefaultAnnotations expected = DefaultAnnotations.builder().build();
+ for (SparseAnnotations a : annotations) {
+ expected = DefaultAnnotations.merge(expected, a);
+ }
+ assertEquals(expected.keys(), actual.keys());
+ for (String key : expected.keys()) {
+ assertEquals(expected.value(key), actual.value(key));
+ }
+ }
+
+ @Test
+ public final void testGetDeviceCount() {
+ assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
+
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW2);
+ putDevice(DID1, SW1);
+
+ assertEquals("expect 2 uniq devices", 2, deviceStore.getDeviceCount());
+ }
+
+ @Test
+ public final void testGetDevices() {
+ assertEquals("initialy empty", 0, Iterables.size(deviceStore.getDevices()));
+
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW2);
+ putDevice(DID1, SW1);
+
+ assertEquals("expect 2 uniq devices",
+ 2, Iterables.size(deviceStore.getDevices()));
+
+ Map<DeviceId, Device> devices = new HashMap<>();
+ for (Device device : deviceStore.getDevices()) {
+ devices.put(device.id(), device);
+ }
+
+ assertDevice(DID1, SW1, devices.get(DID1));
+ assertDevice(DID2, SW2, devices.get(DID2));
+
+ // add case for new node?
+ }
+
+ @Test
+ public final void testGetDevice() {
+
+ putDevice(DID1, SW1);
+
+ assertDevice(DID1, SW1, deviceStore.getDevice(DID1));
+ assertNull("DID2 shouldn't be there", deviceStore.getDevice(DID2));
+ }
+
+ @Test
+ public final void testCreateOrUpdateDevice() {
+ DeviceDescription description =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN);
+ DeviceEvent event = deviceStore.createOrUpdateDevice(PID, DID1, description);
+ assertEquals(DEVICE_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+
+ DeviceDescription description2 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW2, SN);
+ DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
+ assertEquals(DEVICE_UPDATED, event2.type());
+ assertDevice(DID1, SW2, event2.subject());
+
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
+ }
+
+ @Test
+ public final void testCreateOrUpdateDeviceAncillary() {
+ DeviceDescription description =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN, A2);
+ DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
+ assertEquals(DEVICE_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(PIDA, event.subject().providerId());
+ assertAnnotationsEquals(event.subject().annotations(), A2);
+ assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
+
+ DeviceDescription description2 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW2, SN, A1);
+ DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
+ assertEquals(DEVICE_UPDATED, event2.type());
+ assertDevice(DID1, SW2, event2.subject());
+ assertEquals(PID, event2.subject().providerId());
+ assertAnnotationsEquals(event2.subject().annotations(), A1, A2);
+ assertTrue(deviceStore.isAvailable(DID1));
+
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
+
+ // For now, Ancillary is ignored once primary appears
+ assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
+
+ // But, Ancillary annotations will be in effect
+ DeviceDescription description3 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN, A2_2);
+ DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
+ assertEquals(DEVICE_UPDATED, event3.type());
+ // basic information will be the one from Primary
+ assertDevice(DID1, SW2, event3.subject());
+ assertEquals(PID, event3.subject().providerId());
+ // but annotation from Ancillary will be merged
+ assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2);
+ assertTrue(deviceStore.isAvailable(DID1));
+ }
+
+
+ @Test
+ public final void testMarkOffline() {
+
+ putDevice(DID1, SW1);
+ assertTrue(deviceStore.isAvailable(DID1));
+
+ DeviceEvent event = deviceStore.markOffline(DID1);
+ assertEquals(DEVICE_AVAILABILITY_CHANGED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertFalse(deviceStore.isAvailable(DID1));
+
+ DeviceEvent event2 = deviceStore.markOffline(DID1);
+ assertNull("No change, no event", event2);
+}
+
+ @Test
+ public final void testUpdatePorts() {
+ putDevice(DID1, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true),
+ new DefaultPortDescription(P2, true)
+ );
+
+ List<DeviceEvent> events = deviceStore.updatePorts(PID, DID1, pds);
+
+ Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
+ for (DeviceEvent event : events) {
+ assertEquals(PORT_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertTrue("PortNumber is one of expected",
+ expectedPorts.remove(event.port().number()));
+ assertTrue("Port is enabled", event.port().isEnabled());
+ }
+ assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
+
+
+ List<PortDescription> pds2 = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, false),
+ new DefaultPortDescription(P2, true),
+ new DefaultPortDescription(P3, true)
+ );
+
+ events = deviceStore.updatePorts(PID, DID1, pds2);
+ assertFalse("event should be triggered", events.isEmpty());
+ for (DeviceEvent event : events) {
+ PortNumber num = event.port().number();
+ if (P1.equals(num)) {
+ assertEquals(PORT_UPDATED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertFalse("Port is disabled", event.port().isEnabled());
+ } else if (P2.equals(num)) {
+ fail("P2 event not expected.");
+ } else if (P3.equals(num)) {
+ assertEquals(PORT_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertTrue("Port is enabled", event.port().isEnabled());
+ } else {
+ fail("Unknown port number encountered: " + num);
+ }
+ }
+
+ List<PortDescription> pds3 = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, false),
+ new DefaultPortDescription(P2, true)
+ );
+ events = deviceStore.updatePorts(PID, DID1, pds3);
+ assertFalse("event should be triggered", events.isEmpty());
+ for (DeviceEvent event : events) {
+ PortNumber num = event.port().number();
+ if (P1.equals(num)) {
+ fail("P1 event not expected.");
+ } else if (P2.equals(num)) {
+ fail("P2 event not expected.");
+ } else if (P3.equals(num)) {
+ assertEquals(PORT_REMOVED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertTrue("Port was enabled", event.port().isEnabled());
+ } else {
+ fail("Unknown port number encountered: " + num);
+ }
+ }
+
+ }
+
+ @Test
+ public final void testUpdatePortStatus() {
+ putDevice(DID1, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
+ new DefaultPortDescription(P1, false));
+ assertEquals(PORT_UPDATED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(P1, event.port().number());
+ assertFalse("Port is disabled", event.port().isEnabled());
+
+ }
+ @Test
+ public final void testUpdatePortStatusAncillary() {
+ putDeviceAncillary(DID1, SW1);
+ putDevice(DID1, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true, A1)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
+ new DefaultPortDescription(P1, false, A1_2));
+ assertEquals(PORT_UPDATED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ assertEquals(P1, event.port().number());
+ assertAnnotationsEquals(event.port().annotations(), A1, A1_2);
+ assertFalse("Port is disabled", event.port().isEnabled());
+
+ DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P1, true));
+ assertNull("Ancillary is ignored if primary exists", event2);
+
+ // but, Ancillary annotation update will be notified
+ DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P1, true, A2));
+ assertEquals(PORT_UPDATED, event3.type());
+ assertDevice(DID1, SW1, event3.subject());
+ assertEquals(P1, event3.port().number());
+ assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2);
+ assertFalse("Port is disabled", event3.port().isEnabled());
+
+ // port only reported from Ancillary will be notified as down
+ DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1,
+ new DefaultPortDescription(P2, true));
+ assertEquals(PORT_ADDED, event4.type());
+ assertDevice(DID1, SW1, event4.subject());
+ assertEquals(P2, event4.port().number());
+ assertAnnotationsEquals(event4.port().annotations());
+ assertFalse("Port is disabled if not given from primary provider",
+ event4.port().isEnabled());
+ }
+
+ @Test
+ public final void testGetPorts() {
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true),
+ new DefaultPortDescription(P2, true)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ Set<PortNumber> expectedPorts = Sets.newHashSet(P1, P2);
+ List<Port> ports = deviceStore.getPorts(DID1);
+ for (Port port : ports) {
+ assertTrue("Port is enabled", port.isEnabled());
+ assertTrue("PortNumber is one of expected",
+ expectedPorts.remove(port.number()));
+ }
+ assertTrue("Event for all expectedport appeared", expectedPorts.isEmpty());
+
+
+ assertTrue("DID2 has no ports", deviceStore.getPorts(DID2).isEmpty());
+ }
+
+ @Test
+ public final void testGetPort() {
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW1);
+ List<PortDescription> pds = Arrays.<PortDescription>asList(
+ new DefaultPortDescription(P1, true),
+ new DefaultPortDescription(P2, false)
+ );
+ deviceStore.updatePorts(PID, DID1, pds);
+
+ Port port1 = deviceStore.getPort(DID1, P1);
+ assertEquals(P1, port1.number());
+ assertTrue("Port is enabled", port1.isEnabled());
+
+ Port port2 = deviceStore.getPort(DID1, P2);
+ assertEquals(P2, port2.number());
+ assertFalse("Port is disabled", port2.isEnabled());
+
+ Port port3 = deviceStore.getPort(DID1, P3);
+ assertNull("P3 not expected", port3);
+ }
+
+ @Test
+ public final void testRemoveDevice() {
+ putDevice(DID1, SW1);
+ putDevice(DID2, SW1);
+
+ assertEquals(2, deviceStore.getDeviceCount());
+
+ DeviceEvent event = deviceStore.removeDevice(DID1);
+ assertEquals(DEVICE_REMOVED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+
+ assertEquals(1, deviceStore.getDeviceCount());
+ }
+
+ // If Delegates should be called only on remote events,
+ // then Simple* should never call them, thus not test required.
+ // TODO add test for Port events when we have them
+ @Ignore("Ignore until Delegate spec. is clear.")
+ @Test
+ public final void testEvents() throws InterruptedException {
+ final CountDownLatch addLatch = new CountDownLatch(1);
+ DeviceStoreDelegate checkAdd = new DeviceStoreDelegate() {
+ @Override
+ public void notify(DeviceEvent event) {
+ assertEquals(DEVICE_ADDED, event.type());
+ assertDevice(DID1, SW1, event.subject());
+ addLatch.countDown();
+ }
+ };
+ final CountDownLatch updateLatch = new CountDownLatch(1);
+ DeviceStoreDelegate checkUpdate = new DeviceStoreDelegate() {
+ @Override
+ public void notify(DeviceEvent event) {
+ assertEquals(DEVICE_UPDATED, event.type());
+ assertDevice(DID1, SW2, event.subject());
+ updateLatch.countDown();
+ }
+ };
+ final CountDownLatch removeLatch = new CountDownLatch(1);
+ DeviceStoreDelegate checkRemove = new DeviceStoreDelegate() {
+ @Override
+ public void notify(DeviceEvent event) {
+ assertEquals(DEVICE_REMOVED, event.type());
+ assertDevice(DID1, SW2, event.subject());
+ removeLatch.countDown();
+ }
+ };
+
+ DeviceDescription description =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW1, SN);
+ deviceStore.setDelegate(checkAdd);
+ deviceStore.createOrUpdateDevice(PID, DID1, description);
+ assertTrue("Add event fired", addLatch.await(1, TimeUnit.SECONDS));
+
+
+ DeviceDescription description2 =
+ new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
+ HW, SW2, SN);
+ deviceStore.unsetDelegate(checkAdd);
+ deviceStore.setDelegate(checkUpdate);
+ deviceStore.createOrUpdateDevice(PID, DID1, description2);
+ assertTrue("Update event fired", updateLatch.await(1, TimeUnit.SECONDS));
+
+ deviceStore.unsetDelegate(checkUpdate);
+ deviceStore.setDelegate(checkRemove);
+ deviceStore.removeDevice(DID1);
+ assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
+ }
+
+ private static final class TestGossipDeviceStore extends GossipDeviceStore {
+
+ public TestGossipDeviceStore(ClockService clockService) {
+ this.clockService = clockService;
+ }
+ }
+}
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
index 5feb1ba..2ecd525 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/DistributedDeviceStore.java
@@ -47,6 +47,7 @@
import static org.onlab.onos.net.device.DeviceEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
+//TODO: Add support for multiple provider and annotations
/**
* Manages inventory of infrastructure devices using Hazelcast-backed map.
*/
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java
new file mode 100644
index 0000000..b68620a
--- /dev/null
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockProviderService.java
@@ -0,0 +1,20 @@
+package org.onlab.onos.store.device.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.ClockProviderService;
+
+// FIXME: Code clone in onos-core-trivial, onos-core-hz-net
+/**
+ * Dummy implementation of {@link ClockProviderService}.
+ */
+@Component(immediate = true)
+@Service
+public class NoOpClockProviderService implements ClockProviderService {
+
+ @Override
+ public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
+ }
+}
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockService.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockService.java
deleted file mode 100644
index 2c443e9..0000000
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/device/impl/NoOpClockService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.onlab.onos.store.device.impl;
-
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.MastershipTerm;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.store.ClockService;
-import org.onlab.onos.store.Timestamp;
-
-// FIXME: Code clone in onos-core-trivial, onos-core-hz-net
-/**
- * Dummy implementation of {@link ClockService}.
- */
-@Component(immediate = true)
-@Service
-public class NoOpClockService implements ClockService {
-
- @Override
- public Timestamp getTimestamp(DeviceId deviceId) {
- return new Timestamp() {
-
- @Override
- public int compareTo(Timestamp o) {
- throw new IllegalStateException("Never expected to be used.");
- }
- };
- }
-
- @Override
- public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
- }
-}
diff --git a/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java b/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
index 5161f2f..5f5184f 100644
--- a/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
+++ b/core/store/hz/net/src/main/java/org/onlab/onos/store/link/impl/DistributedLinkStore.java
@@ -38,6 +38,7 @@
import com.google.common.collect.ImmutableSet.Builder;
import com.hazelcast.core.IMap;
+//TODO: Add support for multiple provider and annotations
/**
* Manages inventory of infrastructure links using Hazelcast-backed map.
*/
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java
index 46badcb..14a64d2 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ConnectPointSerializer.java
@@ -3,7 +3,6 @@
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.ElementId;
import org.onlab.onos.net.PortNumber;
-
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
@@ -15,7 +14,7 @@
public class ConnectPointSerializer extends Serializer<ConnectPoint> {
/**
- * Default constructor.
+ * Creates {@link ConnectPointSerializer} serializer instance.
*/
public ConnectPointSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
index 5ee273d..06d01b5 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
@@ -16,7 +16,7 @@
public class DefaultLinkSerializer extends Serializer<DefaultLink> {
/**
- * Default constructor.
+ * Creates {@link DefaultLink} serializer instance.
*/
public DefaultLinkSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java
index 8455e80..5dc310b 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultPortSerializer.java
@@ -16,7 +16,7 @@
Serializer<DefaultPort> {
/**
- * Default constructor.
+ * Creates {@link DefaultPort} serializer instance.
*/
public DefaultPortSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java
index c63b676..36d0a21 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DeviceIdSerializer.java
@@ -14,6 +14,14 @@
*/
public final class DeviceIdSerializer extends Serializer<DeviceId> {
+ /**
+ * Creates {@link DeviceId} serializer instance.
+ */
+ public DeviceIdSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public void write(Kryo kryo, Output output, DeviceId object) {
kryo.writeObject(output, object.uri());
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
index 244cc57..734033f 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableMapSerializer.java
@@ -19,6 +19,9 @@
private final MapSerializer mapSerializer = new MapSerializer();
+ /**
+ * Creates {@link ImmutableMap} serializer instance.
+ */
public ImmutableMapSerializer() {
// non-null, immutable
super(false, true);
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
index c08bf9a..051a843 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ImmutableSetSerializer.java
@@ -18,6 +18,9 @@
private final CollectionSerializer serializer = new CollectionSerializer();
+ /**
+ * Creates {@link ImmutableSet} serializer instance.
+ */
public ImmutableSetSerializer() {
// non-null, immutable
super(false, true);
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java
new file mode 100644
index 0000000..b923df7
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpAddressSerializer.java
@@ -0,0 +1,41 @@
+package org.onlab.onos.store.serializers;
+
+import org.onlab.packet.IpAddress;
+import com.esotericsoftware.kryo.Kryo;
+import com.esotericsoftware.kryo.Serializer;
+import com.esotericsoftware.kryo.io.Input;
+import com.esotericsoftware.kryo.io.Output;
+
+/**
+ * Kryo Serializer for {@link IpAddress}.
+ */
+public class IpAddressSerializer extends Serializer<IpAddress> {
+
+ /**
+ * Creates {@link IpAddress} serializer instance.
+ */
+ public IpAddressSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
+ @Override
+ public void write(Kryo kryo, Output output,
+ IpAddress object) {
+ byte[] octs = object.toOctets();
+ output.writeInt(octs.length);
+ output.writeBytes(octs);
+ output.writeInt(object.prefixLength());
+ }
+
+ @Override
+ public IpAddress read(Kryo kryo, Input input,
+ Class<IpAddress> type) {
+ int octLen = input.readInt();
+ byte[] octs = new byte[octLen];
+ input.read(octs);
+ int prefLen = input.readInt();
+ return IpAddress.valueOf(octs, prefLen);
+ }
+
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
index 2dbec57..2e92692 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/IpPrefixSerializer.java
@@ -13,7 +13,7 @@
public final class IpPrefixSerializer extends Serializer<IpPrefix> {
/**
- * Default constructor.
+ * Creates {@link IpPrefix} serializer instance.
*/
public IpPrefixSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java
new file mode 100644
index 0000000..f1a12fe
--- /dev/null
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoPoolUtil.java
@@ -0,0 +1,79 @@
+package org.onlab.onos.store.serializers;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.onlab.onos.cluster.ControllerNode;
+import org.onlab.onos.cluster.DefaultControllerNode;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultAnnotations;
+import org.onlab.onos.net.DefaultDevice;
+import org.onlab.onos.net.DefaultLink;
+import org.onlab.onos.net.DefaultPort;
+import org.onlab.onos.net.Device;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.net.Element;
+import org.onlab.onos.net.Link;
+import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.MastershipRole;
+import org.onlab.onos.net.Port;
+import org.onlab.onos.net.PortNumber;
+import org.onlab.onos.net.provider.ProviderId;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
+import org.onlab.util.KryoPool;
+
+import de.javakaffee.kryoserializers.URISerializer;
+
+public final class KryoPoolUtil {
+
+ /**
+ * KryoPool which can serialize ON.lab misc classes.
+ */
+ public static final KryoPool MISC = KryoPool.newBuilder()
+ .register(IpPrefix.class, new IpPrefixSerializer())
+ .register(IpAddress.class, new IpAddressSerializer())
+ .build();
+
+ // TODO: Populate other classes
+ /**
+ * KryoPool which can serialize API bundle classes.
+ */
+ public static final KryoPool API = KryoPool.newBuilder()
+ .register(MISC)
+ .register(
+ //
+ ArrayList.class,
+ HashMap.class,
+ //
+ ControllerNode.State.class,
+ Device.Type.class,
+ DefaultAnnotations.class,
+ DefaultControllerNode.class,
+ DefaultDevice.class,
+ MastershipRole.class,
+ Port.class,
+ Element.class,
+ Link.Type.class
+ )
+ .register(URI.class, new URISerializer())
+ .register(NodeId.class, new NodeIdSerializer())
+ .register(ProviderId.class, new ProviderIdSerializer())
+ .register(DeviceId.class, new DeviceIdSerializer())
+ .register(PortNumber.class, new PortNumberSerializer())
+ .register(DefaultPort.class, new DefaultPortSerializer())
+ .register(LinkKey.class, new LinkKeySerializer())
+ .register(ConnectPoint.class, new ConnectPointSerializer())
+ .register(DefaultLink.class, new DefaultLinkSerializer())
+ .register(MastershipTerm.class, new MastershipTermSerializer())
+ .register(MastershipRole.class, new MastershipRoleSerializer())
+
+ .build();
+
+
+ // not to be instantiated
+ private KryoPoolUtil() {}
+}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
index 04d1a88..1b5cac4 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoSerializationManager.java
@@ -1,36 +1,14 @@
package org.onlab.onos.store.serializers;
-import de.javakaffee.kryoserializers.URISerializer;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.ControllerNode;
-import org.onlab.onos.cluster.DefaultControllerNode;
-import org.onlab.onos.cluster.NodeId;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DefaultAnnotations;
-import org.onlab.onos.net.DefaultDevice;
-import org.onlab.onos.net.DefaultLink;
-import org.onlab.onos.net.DefaultPort;
-import org.onlab.onos.net.Device;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.net.Element;
-import org.onlab.onos.net.Link;
-import org.onlab.onos.net.LinkKey;
-import org.onlab.onos.net.MastershipRole;
-import org.onlab.onos.net.Port;
-import org.onlab.onos.net.PortNumber;
-import org.onlab.onos.net.provider.ProviderId;
-import org.onlab.packet.IpPrefix;
import org.onlab.util.KryoPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.net.URI;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
/**
* Serialization service using Kryo.
@@ -58,33 +36,8 @@
* Sets up the common serialzers pool.
*/
protected void setupKryoPool() {
- // FIXME Slice out types used in common to separate pool/namespace.
serializerPool = KryoPool.newBuilder()
- .register(ArrayList.class,
- HashMap.class,
-
- ControllerNode.State.class,
- Device.Type.class,
-
- DefaultAnnotations.class,
- DefaultControllerNode.class,
- DefaultDevice.class,
- MastershipRole.class,
- Port.class,
- Element.class,
-
- Link.Type.class
- )
- .register(IpPrefix.class, new IpPrefixSerializer())
- .register(URI.class, new URISerializer())
- .register(NodeId.class, new NodeIdSerializer())
- .register(ProviderId.class, new ProviderIdSerializer())
- .register(DeviceId.class, new DeviceIdSerializer())
- .register(PortNumber.class, new PortNumberSerializer())
- .register(DefaultPort.class, new DefaultPortSerializer())
- .register(LinkKey.class, new LinkKeySerializer())
- .register(ConnectPoint.class, new ConnectPointSerializer())
- .register(DefaultLink.class, new DefaultLinkSerializer())
+ .register(KryoPoolUtil.API)
.build()
.populate(1);
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java
index f635f3c..bafee4f 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/LinkKeySerializer.java
@@ -2,6 +2,7 @@
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.LinkKey;
+
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
@@ -13,7 +14,7 @@
public class LinkKeySerializer extends Serializer<LinkKey> {
/**
- * Default constructor.
+ * Creates {@link LinkKey} serializer instance.
*/
public LinkKeySerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
index 3903491..dab5aa8 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipRoleSerializer.java
@@ -12,6 +12,14 @@
*/
public class MastershipRoleSerializer extends Serializer<MastershipRole> {
+ /**
+ * Creates {@link MastershipRole} serializer instance.
+ */
+ public MastershipRoleSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public MastershipRole read(Kryo kryo, Input input, Class<MastershipRole> type) {
final String role = kryo.readObject(input, String.class);
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
index a5d6198..e4cb999 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/MastershipTermSerializer.java
@@ -2,7 +2,6 @@
import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.cluster.NodeId;
-
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
@@ -13,6 +12,14 @@
*/
public class MastershipTermSerializer extends Serializer<MastershipTerm> {
+ /**
+ * Creates {@link MastershipTerm} serializer instance.
+ */
+ public MastershipTermSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public MastershipTerm read(Kryo kryo, Input input, Class<MastershipTerm> type) {
final NodeId node = new NodeId(kryo.readObject(input, String.class));
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java
index ef9d3f1..57c9c16 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/NodeIdSerializer.java
@@ -4,6 +4,7 @@
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
+
import org.onlab.onos.cluster.NodeId;
/**
@@ -11,6 +12,14 @@
*/
public final class NodeIdSerializer extends Serializer<NodeId> {
+ /**
+ * Creates {@link NodeId} serializer instance.
+ */
+ public NodeIdSerializer() {
+ // non-null, immutable
+ super(false, true);
+ }
+
@Override
public void write(Kryo kryo, Output output, NodeId object) {
kryo.writeObject(output, object.toString());
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java
index 02805bb..3792966 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/PortNumberSerializer.java
@@ -14,7 +14,7 @@
Serializer<PortNumber> {
/**
- * Default constructor.
+ * Creates {@link PortNumber} serializer instance.
*/
public PortNumberSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java
index 1a1c6f6..f546f63 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/ProviderIdSerializer.java
@@ -13,7 +13,7 @@
public class ProviderIdSerializer extends Serializer<ProviderId> {
/**
- * Default constructor.
+ * Creates {@link ProviderId} serializer instance.
*/
public ProviderIdSerializer() {
// non-null, immutable
diff --git a/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java
index c972d1a..fd05aac 100644
--- a/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java
+++ b/core/store/serializers/src/test/java/org/onlab/onos/store/serializers/KryoSerializerTests.java
@@ -3,16 +3,11 @@
import static org.onlab.onos.net.DeviceId.deviceId;
import static org.onlab.onos.net.PortNumber.portNumber;
-import java.net.URI;
import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.HashMap;
-
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
-import org.onlab.onos.cluster.MastershipTerm;
import org.onlab.onos.cluster.NodeId;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultDevice;
@@ -22,7 +17,6 @@
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
import org.onlab.onos.net.LinkKey;
-import org.onlab.onos.net.MastershipRole;
import org.onlab.onos.net.PortNumber;
import org.onlab.onos.net.provider.ProviderId;
import org.onlab.packet.IpPrefix;
@@ -32,8 +26,6 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.testing.EqualsTester;
-import de.javakaffee.kryoserializers.URISerializer;
-
public class KryoSerializerTests {
private static final ProviderId PID = new ProviderId("of", "foo");
private static final DeviceId DID1 = deviceId("of:foo");
@@ -54,38 +46,12 @@
@BeforeClass
public static void setUpBeforeClass() throws Exception {
kryos = KryoPool.newBuilder()
- .register(
- ArrayList.class,
- HashMap.class
- )
- .register(
- Device.Type.class,
- Link.Type.class
-
-// ControllerNode.State.class,
-// DefaultControllerNode.class,
-// MastershipRole.class,
-// Port.class,
-// Element.class,
- )
- .register(ConnectPoint.class, new ConnectPointSerializer())
- .register(DefaultLink.class, new DefaultLinkSerializer())
- .register(DefaultPort.class, new DefaultPortSerializer())
- .register(DeviceId.class, new DeviceIdSerializer())
+ .register(KryoPoolUtil.API)
.register(ImmutableMap.class, new ImmutableMapSerializer())
.register(ImmutableSet.class, new ImmutableSetSerializer())
- .register(IpPrefix.class, new IpPrefixSerializer())
- .register(LinkKey.class, new LinkKeySerializer())
- .register(NodeId.class, new NodeIdSerializer())
- .register(PortNumber.class, new PortNumberSerializer())
- .register(ProviderId.class, new ProviderIdSerializer())
- .register(DefaultDevice.class)
- .register(URI.class, new URISerializer())
- .register(MastershipRole.class, new MastershipRoleSerializer())
- .register(MastershipTerm.class, new MastershipTermSerializer())
.build();
}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java
new file mode 100644
index 0000000..ff4b31a
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockProviderService.java
@@ -0,0 +1,20 @@
+package org.onlab.onos.store.trivial.impl;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.cluster.MastershipTerm;
+import org.onlab.onos.net.DeviceId;
+import org.onlab.onos.store.ClockProviderService;
+
+//FIXME: Code clone in onos-core-trivial, onos-core-hz-net
+/**
+ * Dummy implementation of {@link ClockProviderService}.
+ */
+@Component(immediate = true)
+@Service
+public class NoOpClockProviderService implements ClockProviderService {
+
+ @Override
+ public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
+ }
+}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java
deleted file mode 100644
index b3f8320..0000000
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/NoOpClockService.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package org.onlab.onos.store.trivial.impl;
-
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Service;
-import org.onlab.onos.cluster.MastershipTerm;
-import org.onlab.onos.net.DeviceId;
-import org.onlab.onos.store.ClockService;
-import org.onlab.onos.store.Timestamp;
-
-//FIXME: Code clone in onos-core-trivial, onos-core-hz-net
-/**
- * Dummy implementation of {@link ClockService}.
- */
-@Component(immediate = true)
-@Service
-public class NoOpClockService implements ClockService {
-
- @Override
- public Timestamp getTimestamp(DeviceId deviceId) {
- return new Timestamp() {
-
- @Override
- public int compareTo(Timestamp o) {
- throw new IllegalStateException("Never expected to be used.");
- }
- };
- }
-
- @Override
- public void setMastershipTerm(DeviceId deviceId, MastershipTerm term) {
- }
-}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java
new file mode 100644
index 0000000..4143548
--- /dev/null
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleIntentStore.java
@@ -0,0 +1,107 @@
+package org.onlab.onos.store.trivial.impl;
+
+import static org.onlab.onos.net.intent.IntentState.COMPILED;
+import static org.slf4j.LoggerFactory.getLogger;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.net.intent.InstallableIntent;
+import org.onlab.onos.net.intent.Intent;
+import org.onlab.onos.net.intent.IntentEvent;
+import org.onlab.onos.net.intent.IntentId;
+import org.onlab.onos.net.intent.IntentState;
+import org.onlab.onos.net.intent.IntentStore;
+import org.onlab.onos.net.intent.IntentStoreDelegate;
+import org.onlab.onos.store.AbstractStore;
+import org.slf4j.Logger;
+
+import com.google.common.collect.ImmutableSet;
+
+@Component(immediate = true)
+@Service
+public class SimpleIntentStore
+ extends AbstractStore<IntentEvent, IntentStoreDelegate>
+ implements IntentStore {
+
+ private final Logger log = getLogger(getClass());
+ private final Map<IntentId, Intent> intents = new HashMap<>();
+ private final Map<IntentId, IntentState> states = new HashMap<>();
+ private final Map<IntentId, List<InstallableIntent>> installable = new HashMap<>();
+
+ @Activate
+ public void activate() {
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ log.info("Stopped");
+ }
+
+ @Override
+ public IntentEvent createIntent(Intent intent) {
+ intents.put(intent.getId(), intent);
+ return this.setState(intent, IntentState.SUBMITTED);
+ }
+
+ @Override
+ public IntentEvent removeIntent(IntentId intentId) {
+ Intent intent = intents.remove(intentId);
+ installable.remove(intentId);
+ IntentEvent event = this.setState(intent, IntentState.WITHDRAWN);
+ states.remove(intentId);
+ return event;
+ }
+
+ @Override
+ public long getIntentCount() {
+ return intents.size();
+ }
+
+ @Override
+ public Iterable<Intent> getIntents() {
+ return ImmutableSet.copyOf(intents.values());
+ }
+
+ @Override
+ public Intent getIntent(IntentId intentId) {
+ return intents.get(intentId);
+ }
+
+ @Override
+ public IntentState getIntentState(IntentId id) {
+ return states.get(id);
+ }
+
+ // TODO return dispatch event here... replace with state transition methods
+ @Override
+ public IntentEvent setState(Intent intent, IntentState newState) {
+ IntentId id = intent.getId();
+ IntentState oldState = states.get(id);
+ states.put(id, newState);
+ return new IntentEvent(intent, newState, oldState, System.currentTimeMillis());
+ }
+
+ @Override
+ public IntentEvent addInstallableIntents(IntentId intentId, List<InstallableIntent> result) {
+ installable.put(intentId, result);
+ return this.setState(intents.get(intentId), COMPILED);
+ }
+
+ @Override
+ public List<InstallableIntent> getInstallableIntents(IntentId intentId) {
+ return installable.get(intentId);
+ }
+
+ @Override
+ public void removeInstalledIntents(IntentId intentId) {
+ installable.remove(intentId);
+ }
+
+}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
index a0e569d..6b96bc7 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
@@ -32,6 +32,7 @@
import static org.onlab.onos.net.link.LinkEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
+// TODO: Add support for multiple provider and annotations
/**
* Manages inventory of infrastructure links using trivial in-memory structures
* implementation.
diff --git a/pom.xml b/pom.xml
index cb00f32..665d1b0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -480,7 +480,7 @@
<group>
<title>Core Subsystems</title>
<packages>
- org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*
+ org.onlab.onos.cluster.impl:org.onlab.onos.net.device.impl:org.onlab.onos.net.link.impl:org.onlab.onos.net.host.impl:org.onlab.onos.net.topology.impl:org.onlab.onos.net.packet.impl:org.onlab.onos.net.flow.impl:org.onlab.onos.store.trivial.*:org.onlab.onos.net.*.impl:org.onlab.onos.event.impl:org.onlab.onos.store.*:org.onlab.onos.net.intent.impl
</packages>
</group>
<group>
diff --git a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
index f3655f7..876bc27 100644
--- a/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
+++ b/providers/openflow/flow/src/main/java/org/onlab/onos/provider/of/flow/impl/FlowRuleBuilder.java
@@ -113,7 +113,7 @@
}
private TrafficTreatment buildTreatment() {
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
// If this is a drop rule
if (actions.size() == 0) {
builder.drop();
@@ -198,7 +198,7 @@
}
private TrafficSelector buildSelector() {
- TrafficSelector.Builder builder = new DefaultTrafficSelector.Builder();
+ TrafficSelector.Builder builder = DefaultTrafficSelector.builder();
for (MatchField<?> field : match.getMatchFields()) {
switch (field.id) {
case IN_PORT:
diff --git a/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java b/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
index 030563c..37971e3 100644
--- a/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
+++ b/providers/openflow/packet/src/test/java/org/onlab/onos/provider/of/packet/impl/OpenFlowPacketProviderTest.java
@@ -181,7 +181,7 @@
}
private static TrafficTreatment treatment(Instruction ... insts) {
- TrafficTreatment.Builder builder = new DefaultTrafficTreatment.Builder();
+ TrafficTreatment.Builder builder = DefaultTrafficTreatment.builder();
for (Instruction i : insts) {
builder.add(i);
}
diff --git a/tools/dev/bash_profile b/tools/dev/bash_profile
index 8332a47..3d80228 100644
--- a/tools/dev/bash_profile
+++ b/tools/dev/bash_profile
@@ -21,7 +21,7 @@
# e.g. 'o api', 'o dev', 'o'
function o {
cd $(find $ONOS_ROOT/ -type d | egrep -v '\.git|target' | \
- egrep "${1:-$ONOS_ROOT}" | head -n 1)
+ egrep "${1:-$ONOS_ROOT}" | egrep -v "$ONOS_ROOT/.+/src/" | head -n 1)
}
# Short-hand for 'mvn clean install' for us lazy folk
diff --git a/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java b/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
index 7a97b08..75c1018 100644
--- a/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
+++ b/utils/misc/src/main/java/org/onlab/metrics/MetricsFeature.java
@@ -11,7 +11,7 @@
*
* @param newName name of the Feature
*/
- MetricsFeature(final String newName) {
+ public MetricsFeature(final String newName) {
name = newName;
}
diff --git a/utils/netty/src/main/java/org/onlab/netty/SimpleClient.java b/utils/netty/src/main/java/org/onlab/netty/SimpleClient.java
index 1573780..5ce8f2e 100644
--- a/utils/netty/src/main/java/org/onlab/netty/SimpleClient.java
+++ b/utils/netty/src/main/java/org/onlab/netty/SimpleClient.java
@@ -2,16 +2,43 @@
import java.util.concurrent.TimeUnit;
+import org.onlab.metrics.MetricsComponent;
+import org.onlab.metrics.MetricsFeature;
+import org.onlab.metrics.MetricsManager;
+
+import com.codahale.metrics.Timer;
+
public final class SimpleClient {
- private SimpleClient() {}
+ private SimpleClient() {
+ }
public static void main(String... args) throws Exception {
NettyMessagingService messaging = new TestNettyMessagingService(9081);
+ MetricsManager metrics = new MetricsManager();
messaging.activate();
+ metrics.activate();
+ MetricsFeature feature = new MetricsFeature("timers");
+ MetricsComponent component = metrics.registerComponent("NettyMessaging");
+ Timer sendAsyncTimer = metrics.createTimer(component, feature, "AsyncSender");
+ final int warmup = 100;
+ for (int i = 0; i < warmup; i++) {
+ Timer.Context context = sendAsyncTimer.time();
+ messaging.sendAsync(new Endpoint("localhost", 8080), "simple", "Hello World");
+ context.stop();
+ }
+ metrics.registerMetric(component, feature, "AsyncTimer", sendAsyncTimer);
- messaging.sendAsync(new Endpoint("localhost", 8080), "simple", "Hello World");
- Response<String> response = messaging.sendAndReceive(new Endpoint("localhost", 8080), "echo", "Hello World");
- System.out.println("Got back:" + response.get(2, TimeUnit.SECONDS));
+ Timer sendAndReceiveTimer = metrics.createTimer(component, feature, "SendAndReceive");
+ final int iterations = 1000000;
+ for (int i = 0; i < iterations; i++) {
+ Timer.Context context = sendAndReceiveTimer.time();
+ Response<String> response = messaging
+ .sendAndReceive(new Endpoint("localhost", 8080), "echo",
+ "Hello World");
+ System.out.println("Got back:" + response.get(2, TimeUnit.SECONDS));
+ context.stop();
+ }
+ metrics.registerMetric(component, feature, "AsyncTimer", sendAndReceiveTimer);
}
public static class TestNettyMessagingService extends NettyMessagingService {