Ayaka Koshibe

Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next

Showing 72 changed files with 2852 additions and 851 deletions
1 package org.onlab.onos.net; 1 package org.onlab.onos.net;
2 2
3 +import java.util.Collections;
3 import java.util.HashMap; 4 import java.util.HashMap;
4 import java.util.Map; 5 import java.util.Map;
5 import java.util.Objects; 6 import java.util.Objects;
...@@ -71,9 +72,33 @@ public final class DefaultAnnotations implements SparseAnnotations { ...@@ -71,9 +72,33 @@ public final class DefaultAnnotations implements SparseAnnotations {
71 return new DefaultAnnotations(merged); 72 return new DefaultAnnotations(merged);
72 } 73 }
73 74
75 + /**
76 + * Convert Annotations to DefaultAnnotations if needed and merges.
77 + *
78 + * @see #merge(DefaultAnnotations, SparseAnnotations)
79 + *
80 + * @param annotations base annotations
81 + * @param sparseAnnotations additional sparse annotations
82 + * @return combined annotations or the original base annotations if there
83 + * are not additional annotations
84 + */
85 + public static DefaultAnnotations merge(Annotations annotations,
86 + SparseAnnotations sparseAnnotations) {
87 + if (annotations instanceof DefaultAnnotations) {
88 + return merge((DefaultAnnotations) annotations, sparseAnnotations);
89 + }
90 +
91 + DefaultAnnotations.Builder builder = DefaultAnnotations.builder();
92 + for (String key : annotations.keys()) {
93 + builder.set(key, annotations.value(key));
94 + }
95 + return merge(builder.build(), sparseAnnotations);
96 + }
97 +
74 @Override 98 @Override
75 public Set<String> keys() { 99 public Set<String> keys() {
76 - return map.keySet(); 100 + // TODO: unmodifiable to be removed after switching to ImmutableMap;
101 + return Collections.unmodifiableSet(map.keySet());
77 } 102 }
78 103
79 @Override 104 @Override
......
...@@ -45,6 +45,18 @@ public class DefaultDeviceDescription extends AbstractDescription ...@@ -45,6 +45,18 @@ public class DefaultDeviceDescription extends AbstractDescription
45 this.serialNumber = serialNumber; 45 this.serialNumber = serialNumber;
46 } 46 }
47 47
48 + /**
49 + * Creates a device description using the supplied information.
50 + * @param base DeviceDescription to basic information
51 + * @param annotations Annotations to use.
52 + */
53 + public DefaultDeviceDescription(DeviceDescription base,
54 + SparseAnnotations... annotations) {
55 + this(base.deviceURI(), base.type(), base.manufacturer(),
56 + base.hwVersion(), base.swVersion(), base.serialNumber(),
57 + annotations);
58 + }
59 +
48 @Override 60 @Override
49 public URI deviceURI() { 61 public URI deviceURI() {
50 return uri; 62 return uri;
......
1 package org.onlab.onos.net.device; 1 package org.onlab.onos.net.device;
2 2
3 +import org.onlab.onos.net.AbstractDescription;
3 import org.onlab.onos.net.PortNumber; 4 import org.onlab.onos.net.PortNumber;
5 +import org.onlab.onos.net.SparseAnnotations;
4 6
5 /** 7 /**
6 * Default implementation of immutable port description. 8 * Default implementation of immutable port description.
7 */ 9 */
8 -public class DefaultPortDescription implements PortDescription { 10 +public class DefaultPortDescription extends AbstractDescription
11 + implements PortDescription {
9 12
10 private final PortNumber number; 13 private final PortNumber number;
11 private final boolean isEnabled; 14 private final boolean isEnabled;
12 15
13 - public DefaultPortDescription(PortNumber number, boolean isEnabled) { 16 + /**
17 + * Creates a port description using the supplied information.
18 + *
19 + * @param number port number
20 + * @param isEnabled port enabled state
21 + * @param annotations optional key/value annotations map
22 + */
23 + public DefaultPortDescription(PortNumber number, boolean isEnabled,
24 + SparseAnnotations... annotations) {
25 + super(annotations);
14 this.number = number; 26 this.number = number;
15 this.isEnabled = isEnabled; 27 this.isEnabled = isEnabled;
16 } 28 }
17 29
30 + /**
31 + * Creates a port description using the supplied information.
32 + *
33 + * @param base PortDescription to get basic information from
34 + * @param annotations optional key/value annotations map
35 + */
36 + public DefaultPortDescription(PortDescription base,
37 + SparseAnnotations annotations) {
38 + this(base.portNumber(), base.isEnabled(), annotations);
39 + }
40 +
18 @Override 41 @Override
19 public PortNumber portNumber() { 42 public PortNumber portNumber() {
20 return number; 43 return number;
......
1 package org.onlab.onos.net.device; 1 package org.onlab.onos.net.device;
2 2
3 +import org.onlab.onos.net.Description;
3 import org.onlab.onos.net.PortNumber; 4 import org.onlab.onos.net.PortNumber;
4 5
5 /** 6 /**
6 * Information about a port. 7 * Information about a port.
7 */ 8 */
8 -public interface PortDescription { 9 +public interface PortDescription extends Description {
9 10
10 // TODO: possibly relocate this to a common ground so that this can also used by host tracking if required 11 // TODO: possibly relocate this to a common ground so that this can also used by host tracking if required
11 12
......
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Base intent implementation.
5 + */
6 +public abstract class AbstractIntent implements Intent {
7 +
8 + private final IntentId id;
9 +
10 + /**
11 + * Creates a base intent with the specified identifier.
12 + *
13 + * @param id intent identifier
14 + */
15 + protected AbstractIntent(IntentId id) {
16 + this.id = id;
17 + }
18 +
19 + /**
20 + * Constructor for serializer.
21 + */
22 + protected AbstractIntent() {
23 + this.id = null;
24 + }
25 +
26 + @Override
27 + public IntentId getId() {
28 + return id;
29 + }
30 +
31 + @Override
32 + public boolean equals(Object o) {
33 + if (this == o) {
34 + return true;
35 + }
36 + if (o == null || getClass() != o.getClass()) {
37 + return false;
38 + }
39 +
40 + AbstractIntent that = (AbstractIntent) o;
41 + return id.equals(that.id);
42 + }
43 +
44 + @Override
45 + public int hashCode() {
46 + return id.hashCode();
47 + }
48 +
49 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +import static com.google.common.base.Preconditions.checkNotNull;
5 +
6 +import java.util.Collections;
7 +import java.util.LinkedList;
8 +import java.util.List;
9 +
10 +/**
11 + * A list of BatchOperationEntry.
12 + *
13 + * @param <T> the enum of operators <br>
14 + * This enum must be defined in each sub-classes.
15 + *
16 + */
17 +public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> {
18 + private List<T> ops;
19 +
20 + /**
21 + * Creates new {@link BatchOperation} object.
22 + */
23 + public BatchOperation() {
24 + ops = new LinkedList<>();
25 + }
26 +
27 + /**
28 + * Creates {@link BatchOperation} object from a list of batch operation
29 + * entries.
30 + *
31 + * @param batchOperations the list of batch operation entries.
32 + */
33 + public BatchOperation(List<T> batchOperations) {
34 + ops = new LinkedList<>(checkNotNull(batchOperations));
35 + }
36 +
37 + /**
38 + * Removes all operations maintained in this object.
39 + */
40 + public void clear() {
41 + ops.clear();
42 + }
43 +
44 + /**
45 + * Returns the number of operations in this object.
46 + *
47 + * @return the number of operations in this object
48 + */
49 + public int size() {
50 + return ops.size();
51 + }
52 +
53 + /**
54 + * Returns the operations in this object.
55 + *
56 + * @return the operations in this object
57 + */
58 + public List<T> getOperations() {
59 + return Collections.unmodifiableList(ops);
60 + }
61 +
62 + /**
63 + * Adds an operation.
64 + *
65 + * @param entry the operation to be added
66 + * @return this object if succeeded, null otherwise
67 + */
68 + public BatchOperation<T> addOperation(T entry) {
69 + return ops.add(entry) ? this : null;
70 + }
71 +
72 + @Override
73 + public boolean equals(Object o) {
74 + if (this == o) {
75 + return true;
76 + }
77 +
78 + if (o == null) {
79 + return false;
80 + }
81 +
82 + if (getClass() != o.getClass()) {
83 + return false;
84 + }
85 + BatchOperation<?> other = (BatchOperation<?>) o;
86 +
87 + return this.ops.equals(other.ops);
88 + }
89 +
90 + @Override
91 + public int hashCode() {
92 + return ops.hashCode();
93 + }
94 +
95 + @Override
96 + public String toString() {
97 + return ops.toString();
98 + }
99 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +import java.util.Objects;
5 +
6 +import com.google.common.base.MoreObjects;
7 +
8 +/**
9 + * A super class for batch operation entry classes.
10 + * <p>
11 + * This is the interface to classes which are maintained by BatchOperation as
12 + * its entries.
13 + */
14 +public class BatchOperationEntry<T extends Enum<?>, U extends BatchOperationTarget> {
15 + private final T operator;
16 + private final U target;
17 +
18 + /**
19 + * Default constructor for serializer.
20 + */
21 + @Deprecated
22 + protected BatchOperationEntry() {
23 + this.operator = null;
24 + this.target = null;
25 + }
26 +
27 + /**
28 + * Constructs new instance for the entry of the BatchOperation.
29 + *
30 + * @param operator the operator of this operation
31 + * @param target the target object of this operation
32 + */
33 + public BatchOperationEntry(T operator, U target) {
34 + this.operator = operator;
35 + this.target = target;
36 + }
37 +
38 + /**
39 + * Gets the target object of this operation.
40 + *
41 + * @return the target object of this operation
42 + */
43 + public U getTarget() {
44 + return target;
45 + }
46 +
47 + /**
48 + * Gets the operator of this operation.
49 + *
50 + * @return the operator of this operation
51 + */
52 + public T getOperator() {
53 + return operator;
54 + }
55 +
56 + @Override
57 + public boolean equals(Object o) {
58 + if (this == o) {
59 + return true;
60 + }
61 + if (o == null || getClass() != o.getClass()) {
62 + return false;
63 + }
64 +
65 + BatchOperationEntry<?, ?> other = (BatchOperationEntry<?, ?>) o;
66 + return (this.operator == other.operator) &&
67 + Objects.equals(this.target, other.target);
68 + }
69 +
70 + @Override
71 + public int hashCode() {
72 + return Objects.hash(operator, target);
73 + }
74 +
75 + @Override
76 + public String toString() {
77 + return MoreObjects.toStringHelper(this)
78 + .add("operator", operator)
79 + .add("target", target)
80 + .toString();
81 + }
82 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +/**
5 + * An interface of the class which is assigned to BatchOperation.
6 + */
7 +public interface BatchOperationTarget {
8 +
9 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import org.onlab.onos.net.flow.TrafficSelector;
6 +import org.onlab.onos.net.flow.TrafficTreatment;
7 +
8 +import com.google.common.base.Objects;
9 +
10 +/**
11 + * Abstraction of connectivity intent for traffic matching some criteria.
12 + */
13 +public abstract class ConnectivityIntent extends AbstractIntent {
14 +
15 + // TODO: other forms of intents should be considered for this family:
16 + // point-to-point with constraints (waypoints/obstacles)
17 + // multi-to-single point with constraints (waypoints/obstacles)
18 + // single-to-multi point with constraints (waypoints/obstacles)
19 + // concrete path (with alternate)
20 + // ...
21 +
22 + private final TrafficSelector selector;
23 + // TODO: should consider which is better for multiple actions,
24 + // defining compound action class or using list of actions.
25 + private final TrafficTreatment treatment;
26 +
27 + /**
28 + * Creates a connectivity intent that matches on the specified intent
29 + * and applies the specified action.
30 + *
31 + * @param id intent identifier
32 + * @param match traffic match
33 + * @param action action
34 + * @throws NullPointerException if the match or action is null
35 + */
36 + protected ConnectivityIntent(IntentId id, TrafficSelector match, TrafficTreatment action) {
37 + super(id);
38 + this.selector = checkNotNull(match);
39 + this.treatment = checkNotNull(action);
40 + }
41 +
42 + /**
43 + * Constructor for serializer.
44 + */
45 + protected ConnectivityIntent() {
46 + super();
47 + this.selector = null;
48 + this.treatment = null;
49 + }
50 +
51 + /**
52 + * Returns the match specifying the type of traffic.
53 + *
54 + * @return traffic match
55 + */
56 + public TrafficSelector getTrafficSelector() {
57 + return selector;
58 + }
59 +
60 + /**
61 + * Returns the action applied to the traffic.
62 + *
63 + * @return applied action
64 + */
65 + public TrafficTreatment getTrafficTreatment() {
66 + return treatment;
67 + }
68 +
69 + @Override
70 + public boolean equals(Object o) {
71 + if (!super.equals(o)) {
72 + return false;
73 + }
74 + ConnectivityIntent that = (ConnectivityIntent) o;
75 + return Objects.equal(this.selector, that.selector)
76 + && Objects.equal(this.treatment, that.treatment);
77 + }
78 +
79 + @Override
80 + public int hashCode() {
81 + return Objects.hashCode(super.hashCode(), selector, treatment);
82 + }
83 +
84 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +/**
5 + * A generalized interface for ID generation
6 + *
7 + * {@link #getNewId()} generates a globally unique ID instance on
8 + * each invocation.
9 + *
10 + * @param <T> the type of ID
11 + */
12 +// TODO: do we need to define a base marker interface for ID,
13 +// then changed the type parameter to <T extends BaseId> something
14 +// like that?
15 +public interface IdGenerator<T> {
16 + /**
17 + * Returns a globally unique ID instance.
18 + *
19 + * @return globally unique ID instance
20 + */
21 + T getNewId();
22 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Abstraction of an intent that can be installed into
5 + * the underlying system without additional compilation.
6 + */
7 +public interface InstallableIntent extends Intent {
8 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Abstraction of an application level intent.
5 + *
6 + * Make sure that an Intent should be immutable when a new type is defined.
7 + */
8 +public interface Intent extends BatchOperationTarget {
9 + /**
10 + * Returns the intent identifier.
11 + *
12 + * @return intent identifier
13 + */
14 + IntentId getId();
15 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * A list of intent operations.
5 + */
6 +public class IntentBatchOperation extends
7 + BatchOperation<BatchOperationEntry<IntentBatchOperation.Operator, ?>> {
8 + /**
9 + * The intent operators.
10 + */
11 + public enum Operator {
12 + ADD,
13 + REMOVE,
14 + }
15 +
16 + /**
17 + * Adds an add-intent operation.
18 + *
19 + * @param intent the intent to be added
20 + * @return the IntentBatchOperation object if succeeded, null otherwise
21 + */
22 + public IntentBatchOperation addAddIntentOperation(Intent intent) {
23 + return (null == super.addOperation(
24 + new BatchOperationEntry<Operator, Intent>(Operator.ADD, intent)))
25 + ? null : this;
26 + }
27 +
28 + /**
29 + * Adds a remove-intent operation.
30 + *
31 + * @param id the ID of intent to be removed
32 + * @return the IntentBatchOperation object if succeeded, null otherwise
33 + */
34 + public IntentBatchOperation addRemoveIntentOperation(IntentId id) {
35 + return (null == super.addOperation(
36 + new BatchOperationEntry<Operator, IntentId>(Operator.REMOVE, id)))
37 + ? null : this;
38 + }
39 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.List;
4 +
5 +/**
6 + * Abstraction of a compiler which is capable of taking an intent
7 + * and translating it to other, potentially installable, intents.
8 + *
9 + * @param <T> the type of intent
10 + */
11 +public interface IntentCompiler<T extends Intent> {
12 + /**
13 + * Compiles the specified intent into other intents.
14 + *
15 + * @param intent intent to be compiled
16 + * @return list of resulting intents
17 + * @throws IntentException if issues are encountered while compiling the intent
18 + */
19 + List<Intent> compile(T intent);
20 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import com.google.common.base.MoreObjects;
8 +
9 +/**
10 + * A class to represent an intent related event.
11 + */
12 +public class IntentEvent {
13 +
14 + // TODO: determine a suitable parent class; if one does not exist, consider introducing one
15 +
16 + private final long time;
17 + private final Intent intent;
18 + private final IntentState state;
19 + private final IntentState previous;
20 +
21 + /**
22 + * Creates an event describing a state change of an intent.
23 + *
24 + * @param intent subject intent
25 + * @param state new intent state
26 + * @param previous previous intent state
27 + * @param time time the event created in milliseconds since start of epoch
28 + * @throws NullPointerException if the intent or state is null
29 + */
30 + public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) {
31 + this.intent = checkNotNull(intent);
32 + this.state = checkNotNull(state);
33 + this.previous = previous;
34 + this.time = time;
35 + }
36 +
37 + /**
38 + * Constructor for serializer.
39 + */
40 + protected IntentEvent() {
41 + this.intent = null;
42 + this.state = null;
43 + this.previous = null;
44 + this.time = 0;
45 + }
46 +
47 + /**
48 + * Returns the state of the intent which caused the event.
49 + *
50 + * @return the state of the intent
51 + */
52 + public IntentState getState() {
53 + return state;
54 + }
55 +
56 + /**
57 + * Returns the previous state of the intent which caused the event.
58 + *
59 + * @return the previous state of the intent
60 + */
61 + public IntentState getPreviousState() {
62 + return previous;
63 + }
64 +
65 + /**
66 + * Returns the intent associated with the event.
67 + *
68 + * @return the intent
69 + */
70 + public Intent getIntent() {
71 + return intent;
72 + }
73 +
74 + /**
75 + * Returns the time at which the event was created.
76 + *
77 + * @return the time in milliseconds since start of epoch
78 + */
79 + public long getTime() {
80 + return time;
81 + }
82 +
83 + @Override
84 + public boolean equals(Object o) {
85 + if (this == o) {
86 + return true;
87 + }
88 + if (o == null || getClass() != o.getClass()) {
89 + return false;
90 + }
91 +
92 + IntentEvent that = (IntentEvent) o;
93 + return Objects.equals(this.intent, that.intent)
94 + && Objects.equals(this.state, that.state)
95 + && Objects.equals(this.previous, that.previous)
96 + && Objects.equals(this.time, that.time);
97 + }
98 +
99 + @Override
100 + public int hashCode() {
101 + return Objects.hash(intent, state, previous, time);
102 + }
103 +
104 + @Override
105 + public String toString() {
106 + return MoreObjects.toStringHelper(getClass())
107 + .add("intent", intent)
108 + .add("state", state)
109 + .add("previous", previous)
110 + .add("time", time)
111 + .toString();
112 + }
113 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Listener for {@link IntentEvent intent events}.
5 + */
6 +public interface IntentEventListener {
7 + /**
8 + * Processes the specified intent event.
9 + *
10 + * @param event the event to process
11 + */
12 + void event(IntentEvent event);
13 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Represents an intent related error.
5 + */
6 +public class IntentException extends RuntimeException {
7 +
8 + private static final long serialVersionUID = 1907263634145241319L;
9 +
10 + /**
11 + * Constructs an exception with no message and no underlying cause.
12 + */
13 + public IntentException() {
14 + }
15 +
16 + /**
17 + * Constructs an exception with the specified message.
18 + *
19 + * @param message the message describing the specific nature of the error
20 + */
21 + public IntentException(String message) {
22 + super(message);
23 + }
24 +
25 + /**
26 + * Constructs an exception with the specified message and the underlying cause.
27 + *
28 + * @param message the message describing the specific nature of the error
29 + * @param cause the underlying cause of this error
30 + */
31 + public IntentException(String message, Throwable cause) {
32 + super(message, cause);
33 + }
34 +
35 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Map;
4 +
5 +/**
6 + * Service for extending the capability of intent framework by
7 + * adding additional compilers or/and installers.
8 + */
9 +public interface IntentExtensionService {
10 + /**
11 + * Registers the specified compiler for the given intent class.
12 + *
13 + * @param cls intent class
14 + * @param compiler intent compiler
15 + * @param <T> the type of intent
16 + */
17 + <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler);
18 +
19 + /**
20 + * Unregisters the compiler for the specified intent class.
21 + *
22 + * @param cls intent class
23 + * @param <T> the type of intent
24 + */
25 + <T extends Intent> void unregisterCompiler(Class<T> cls);
26 +
27 + /**
28 + * Returns immutable set of bindings of currently registered intent compilers.
29 + *
30 + * @return the set of compiler bindings
31 + */
32 + Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers();
33 +
34 + /**
35 + * Registers the specified installer for the given installable intent class.
36 + *
37 + * @param cls installable intent class
38 + * @param installer intent installer
39 + * @param <T> the type of installable intent
40 + */
41 + <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer);
42 +
43 + /**
44 + * Unregisters the installer for the given installable intent class.
45 + *
46 + * @param cls installable intent class
47 + * @param <T> the type of installable intent
48 + */
49 + <T extends InstallableIntent> void unregisterInstaller(Class<T> cls);
50 +
51 + /**
52 + * Returns immutable set of bindings of currently registered intent installers.
53 + *
54 + * @return the set of installer bindings
55 + */
56 + Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers();
57 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Intent identifier suitable as an external key.
5 + *
6 + * This class is immutable.
7 + */
8 +public final class IntentId implements BatchOperationTarget {
9 +
10 + private static final int DEC = 10;
11 + private static final int HEX = 16;
12 +
13 + private final long id;
14 +
15 + /**
16 + * Creates an intent identifier from the specified string representation.
17 + *
18 + * @param value long value
19 + * @return intent identifier
20 + */
21 + public static IntentId valueOf(String value) {
22 + long id = value.toLowerCase().startsWith("0x")
23 + ? Long.parseLong(value.substring(2), HEX)
24 + : Long.parseLong(value, DEC);
25 + return new IntentId(id);
26 + }
27 +
28 + /**
29 + * Constructor for serializer.
30 + */
31 + protected IntentId() {
32 + this.id = 0;
33 + }
34 +
35 + /**
36 + * Constructs the ID corresponding to a given long value.
37 + *
38 + * @param id the underlying value of this ID
39 + */
40 + public IntentId(long id) {
41 + this.id = id;
42 + }
43 +
44 + @Override
45 + public int hashCode() {
46 + return (int) (id ^ (id >>> 32));
47 + }
48 +
49 + @Override
50 + public boolean equals(Object obj) {
51 + if (obj == this) {
52 + return true;
53 + }
54 +
55 + if (!(obj instanceof IntentId)) {
56 + return false;
57 + }
58 +
59 + IntentId that = (IntentId) obj;
60 + return this.id == that.id;
61 + }
62 +
63 + @Override
64 + public String toString() {
65 + return "0x" + Long.toHexString(id);
66 + }
67 +
68 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Abstraction of entity capable of installing intents to the environment.
5 + */
6 +public interface IntentInstaller<T extends InstallableIntent> {
7 + /**
8 + * Installs the specified intent to the environment.
9 + *
10 + * @param intent intent to be installed
11 + * @throws IntentException if issues are encountered while installing the intent
12 + */
13 + void install(T intent);
14 +
15 + /**
16 + * Uninstalls the specified intent from the environment.
17 + *
18 + * @param intent intent to be uninstalled
19 + * @throws IntentException if issues are encountered while uninstalling the intent
20 + */
21 + void uninstall(T intent);
22 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Abstraction of a batch of intent submit/withdraw operations.
5 + */
6 +public interface IntentOperations {
7 +
8 + // TODO: elaborate once the revised BatchOperation scheme is in place
9 +
10 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Set;
4 +
5 +/**
6 + * Service for application submitting or withdrawing their intents.
7 + */
8 +public interface IntentService {
9 + /**
10 + * Submits an intent into the system.
11 + *
12 + * This is an asynchronous request meaning that any compiling
13 + * or installation activities may be done at later time.
14 + *
15 + * @param intent intent to be submitted
16 + */
17 + void submit(Intent intent);
18 +
19 + /**
20 + * Withdraws an intent from the system.
21 + *
22 + * This is an asynchronous request meaning that the environment
23 + * may be affected at later time.
24 + *
25 + * @param intent intent to be withdrawn
26 + */
27 + void withdraw(Intent intent);
28 +
29 + /**
30 + * Submits a batch of submit &amp; withdraw operations. Such a batch is
31 + * assumed to be processed together.
32 + *
33 + * This is an asynchronous request meaning that the environment
34 + * may be affected at later time.
35 + *
36 + * @param operations batch of intent operations
37 + */
38 + void execute(IntentOperations operations);
39 +
40 + /**
41 + * Returns immutable set of intents currently in the system.
42 + *
43 + * @return set of intents
44 + */
45 + Set<Intent> getIntents();
46 +
47 + /**
48 + * Retrieves the intent specified by its identifier.
49 + *
50 + * @param id intent identifier
51 + * @return the intent or null if one with the given identifier is not found
52 + */
53 + Intent getIntent(IntentId id);
54 +
55 + /**
56 + * Retrieves the state of an intent by its identifier.
57 + *
58 + * @param id intent identifier
59 + * @return the intent state or null if one with the given identifier is not found
60 + */
61 + IntentState getIntentState(IntentId id);
62 +
63 + /**
64 + * Adds the specified listener for intent events.
65 + *
66 + * @param listener listener to be added
67 + */
68 + void addListener(IntentEventListener listener);
69 +
70 + /**
71 + * Removes the specified listener for intent events.
72 + *
73 + * @param listener listener to be removed
74 + */
75 + void removeListener(IntentEventListener listener);
76 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * This class represents the states of an intent.
5 + *
6 + * <p>
7 + * Note: The state is expressed as enum, but there is possibility
8 + * in the future that we define specific class instead of enum to improve
9 + * the extensibility of state definition.
10 + * </p>
11 + */
12 +public enum IntentState {
13 + // FIXME: requires discussion on State vs. EventType and a solid state-transition diagram
14 + // TODO: consider the impact of conflict detection
15 + // TODO: consider the impact that external events affect an installed intent
16 + /**
17 + * The beginning state.
18 + *
19 + * All intent in the runtime take this state first.
20 + */
21 + SUBMITTED,
22 +
23 + /**
24 + * The intent compilation has been completed.
25 + *
26 + * An intent translation graph (tree) is completely created.
27 + * Leaves of the graph are installable intent type.
28 + */
29 + COMPILED,
30 +
31 + /**
32 + * The intent has been successfully installed.
33 + */
34 + INSTALLED,
35 +
36 + /**
37 + * The intent is being withdrawn.
38 + *
39 + * When {@link IntentService#withdraw(Intent)} is called,
40 + * the intent takes this state first.
41 + */
42 + WITHDRAWING,
43 +
44 + /**
45 + * The intent has been successfully withdrawn.
46 + */
47 + WITHDRAWN,
48 +
49 + /**
50 + * The intent has failed to be compiled, installed, or withdrawn.
51 + *
52 + * When the intent failed to be withdrawn, it is still, at least partially installed.
53 + */
54 + FAILED,
55 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkArgument;
4 +import static com.google.common.base.Preconditions.checkNotNull;
5 +
6 +import java.util.Objects;
7 +import java.util.Set;
8 +
9 +import org.onlab.onos.net.ConnectPoint;
10 +import org.onlab.onos.net.flow.TrafficSelector;
11 +import org.onlab.onos.net.flow.TrafficTreatment;
12 +
13 +import com.google.common.base.MoreObjects;
14 +import com.google.common.collect.Sets;
15 +
16 +/**
17 + * Abstraction of multiple source to single destination connectivity intent.
18 + */
19 +public class MultiPointToSinglePointIntent extends ConnectivityIntent {
20 +
21 + private final Set<ConnectPoint> ingressPorts;
22 + private final ConnectPoint egressPort;
23 +
24 + /**
25 + * Creates a new multi-to-single point connectivity intent for the specified
26 + * traffic match and action.
27 + *
28 + * @param id intent identifier
29 + * @param match traffic match
30 + * @param action action
31 + * @param ingressPorts set of ports from which ingress traffic originates
32 + * @param egressPort port to which traffic will egress
33 + * @throws NullPointerException if {@code ingressPorts} or
34 + * {@code egressPort} is null.
35 + * @throws IllegalArgumentException if the size of {@code ingressPorts} is
36 + * not more than 1
37 + */
38 + public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
39 + Set<ConnectPoint> ingressPorts, ConnectPoint egressPort) {
40 + super(id, match, action);
41 +
42 + checkNotNull(ingressPorts);
43 + checkArgument(!ingressPorts.isEmpty(),
44 + "there should be at least one ingress port");
45 +
46 + this.ingressPorts = Sets.newHashSet(ingressPorts);
47 + this.egressPort = checkNotNull(egressPort);
48 + }
49 +
50 + /**
51 + * Constructor for serializer.
52 + */
53 + protected MultiPointToSinglePointIntent() {
54 + super();
55 + this.ingressPorts = null;
56 + this.egressPort = null;
57 + }
58 +
59 + /**
60 + * Returns the set of ports on which ingress traffic should be connected to
61 + * the egress port.
62 + *
63 + * @return set of ingress ports
64 + */
65 + public Set<ConnectPoint> getIngressPorts() {
66 + return ingressPorts;
67 + }
68 +
69 + /**
70 + * Returns the port on which the traffic should egress.
71 + *
72 + * @return egress port
73 + */
74 + public ConnectPoint getEgressPort() {
75 + return egressPort;
76 + }
77 +
78 + @Override
79 + public boolean equals(Object o) {
80 + if (this == o) {
81 + return true;
82 + }
83 + if (o == null || getClass() != o.getClass()) {
84 + return false;
85 + }
86 + if (!super.equals(o)) {
87 + return false;
88 + }
89 +
90 + MultiPointToSinglePointIntent that = (MultiPointToSinglePointIntent) o;
91 + return Objects.equals(this.ingressPorts, that.ingressPorts)
92 + && Objects.equals(this.egressPort, that.egressPort);
93 + }
94 +
95 + @Override
96 + public int hashCode() {
97 + return Objects.hash(super.hashCode(), ingressPorts, egressPort);
98 + }
99 +
100 + @Override
101 + public String toString() {
102 + return MoreObjects.toStringHelper(getClass())
103 + .add("id", getId())
104 + .add("match", getTrafficSelector())
105 + .add("action", getTrafficTreatment())
106 + .add("ingressPorts", getIngressPorts())
107 + .add("egressPort", getEgressPort())
108 + .toString();
109 + }
110 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.onlab.onos.net.ConnectPoint;
4 +
5 +// TODO: consider if this intent should be sub-class of ConnectivityIntent
6 +/**
7 + * An optical layer Intent for a connectivity from a transponder port to another
8 + * transponder port.
9 + * <p>
10 + * This class doesn't accepts lambda specifier. This class computes path between
11 + * ports and assign lambda automatically. The lambda can be specified using
12 + * OpticalPathFlow class.
13 + */
14 +public class OpticalConnectivityIntent extends AbstractIntent {
15 + protected ConnectPoint srcConnectPoint;
16 + protected ConnectPoint dstConnectPoint;
17 +
18 + /**
19 + * Constructor.
20 + *
21 + * @param id ID for this new Intent object.
22 + * @param srcConnectPoint The source transponder port.
23 + * @param dstConnectPoint The destination transponder port.
24 + */
25 + public OpticalConnectivityIntent(IntentId id,
26 + ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
27 + super(id);
28 + this.srcConnectPoint = srcConnectPoint;
29 + this.dstConnectPoint = dstConnectPoint;
30 + }
31 +
32 + /**
33 + * Constructor for serializer.
34 + */
35 + protected OpticalConnectivityIntent() {
36 + super();
37 + this.srcConnectPoint = null;
38 + this.dstConnectPoint = null;
39 + }
40 +
41 + /**
42 + * Gets source transponder port.
43 + *
44 + * @return The source transponder port.
45 + */
46 + public ConnectPoint getSrcConnectPoint() {
47 + return srcConnectPoint;
48 + }
49 +
50 + /**
51 + * Gets destination transponder port.
52 + *
53 + * @return The source transponder port.
54 + */
55 + public ConnectPoint getDstConnectPoint() {
56 + return dstConnectPoint;
57 + }
58 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Collection;
4 +import java.util.Collections;
5 +import java.util.HashSet;
6 +import java.util.Set;
7 +
8 +import org.onlab.onos.net.ConnectPoint;
9 +import org.onlab.onos.net.flow.TrafficSelector;
10 +
11 +// TODO: consider if this intent should be sub-class of Connectivity intent
12 +/**
13 + * A packet layer Intent for a connectivity from a set of ports to a set of
14 + * ports.
15 + * <p>
16 + * TODO: Design methods to support the ReactiveForwarding and the SDN-IP. <br>
17 + * NOTE: Should this class support modifier methods? Should this object a
18 + * read-only object?
19 + */
20 +public class PacketConnectivityIntent extends AbstractIntent {
21 + protected Set<ConnectPoint> srcConnectPoints;
22 + protected TrafficSelector selector;
23 + protected Set<ConnectPoint> dstConnectPoints;
24 + protected boolean canSetupOpticalFlow;
25 + protected int idleTimeoutValue;
26 + protected int hardTimeoutValue;
27 +
28 + /**
29 + * Creates a connectivity intent for the packet layer.
30 + * <p>
31 + * When the "canSetupOpticalFlow" option is true, this intent will compute
32 + * the packet/optical converged path, decompose it to the OpticalPathFlow
33 + * and the PacketPathFlow objects, and execute the operations to add them
34 + * considering the dependency between the packet and optical layers.
35 + *
36 + * @param id ID for this new Intent object.
37 + * @param srcConnectPoints The set of source switch ports.
38 + * @param match Traffic specifier for this object.
39 + * @param dstConnectPoints The set of destination switch ports.
40 + * @param canSetupOpticalFlow The flag whether this intent can create
41 + * optical flows if needed.
42 + */
43 + public PacketConnectivityIntent(IntentId id,
44 + Collection<ConnectPoint> srcConnectPoints, TrafficSelector match,
45 + Collection<ConnectPoint> dstConnectPoints, boolean canSetupOpticalFlow) {
46 + super(id);
47 + this.srcConnectPoints = new HashSet<ConnectPoint>(srcConnectPoints);
48 + this.selector = match;
49 + this.dstConnectPoints = new HashSet<ConnectPoint>(dstConnectPoints);
50 + this.canSetupOpticalFlow = canSetupOpticalFlow;
51 + this.idleTimeoutValue = 0;
52 + this.hardTimeoutValue = 0;
53 +
54 + // TODO: check consistency between these parameters.
55 + }
56 +
57 + /**
58 + * Constructor for serializer.
59 + */
60 + protected PacketConnectivityIntent() {
61 + super();
62 + this.srcConnectPoints = null;
63 + this.selector = null;
64 + this.dstConnectPoints = null;
65 + this.canSetupOpticalFlow = false;
66 + this.idleTimeoutValue = 0;
67 + this.hardTimeoutValue = 0;
68 + }
69 +
70 + /**
71 + * Gets the set of source switch ports.
72 + *
73 + * @return the set of source switch ports.
74 + */
75 + public Collection<ConnectPoint> getSrcConnectPoints() {
76 + return Collections.unmodifiableCollection(srcConnectPoints);
77 + }
78 +
79 + /**
80 + * Gets the traffic specifier.
81 + *
82 + * @return The traffic specifier.
83 + */
84 + public TrafficSelector getMatch() {
85 + return selector;
86 + }
87 +
88 + /**
89 + * Gets the set of destination switch ports.
90 + *
91 + * @return the set of destination switch ports.
92 + */
93 + public Collection<ConnectPoint> getDstConnectPoints() {
94 + return Collections.unmodifiableCollection(dstConnectPoints);
95 + }
96 +
97 + /**
98 + * Adds the specified port to the set of source ports.
99 + *
100 + * @param port ConnectPoint object to be added
101 + */
102 + public void addSrcConnectPoint(ConnectPoint port) {
103 + // TODO implement it.
104 + }
105 +
106 + /**
107 + * Adds the specified port to the set of destination ports.
108 + *
109 + * @param port ConnectPoint object to be added
110 + */
111 + public void addDstConnectPoint(ConnectPoint port) {
112 + // TODO implement it.
113 + }
114 +
115 + /**
116 + * Removes the specified port from the set of source ports.
117 + *
118 + * @param port ConnectPoint object to be removed
119 + */
120 + public void removeSrcConnectPoint(ConnectPoint port) {
121 + // TODO implement it.
122 + }
123 +
124 + /**
125 + * Removes the specified port from the set of destination ports.
126 + *
127 + * @param port ConnectPoint object to be removed
128 + */
129 + public void removeDstConnectPoint(ConnectPoint port) {
130 + // TODO implement it.
131 + }
132 +
133 + /**
134 + * Sets idle-timeout value.
135 + *
136 + * @param timeout Idle-timeout value (seconds)
137 + */
138 + public void setIdleTimeout(int timeout) {
139 + idleTimeoutValue = timeout;
140 + }
141 +
142 + /**
143 + * Sets hard-timeout value.
144 + *
145 + * @param timeout Hard-timeout value (seconds)
146 + */
147 + public void setHardTimeout(int timeout) {
148 + hardTimeoutValue = timeout;
149 + }
150 +
151 + /**
152 + * Gets idle-timeout value.
153 + *
154 + * @return Idle-timeout value (seconds)
155 + */
156 + public int getIdleTimeout() {
157 + return idleTimeoutValue;
158 + }
159 +
160 + /**
161 + * Gets hard-timeout value.
162 + *
163 + * @return Hard-timeout value (seconds)
164 + */
165 + public int getHardTimeout() {
166 + return hardTimeoutValue;
167 + }
168 +
169 + /**
170 + * Returns whether this intent can create optical flows if needed.
171 + *
172 + * @return whether this intent can create optical flows.
173 + */
174 + public boolean canSetupOpticalFlow() {
175 + return canSetupOpticalFlow;
176 + }
177 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Objects;
4 +
5 +import org.onlab.onos.net.ConnectPoint;
6 +import org.onlab.onos.net.Path;
7 +import org.onlab.onos.net.flow.TrafficSelector;
8 +import org.onlab.onos.net.flow.TrafficTreatment;
9 +
10 +import com.google.common.base.MoreObjects;
11 +
12 +/**
13 + * Abstraction of explicitly path specified connectivity intent.
14 + */
15 +public class PathIntent extends PointToPointIntent {
16 +
17 + private final Path path;
18 +
19 + /**
20 + * Creates a new point-to-point intent with the supplied ingress/egress
21 + * ports and using the specified explicit path.
22 + *
23 + * @param id intent identifier
24 + * @param match traffic match
25 + * @param action action
26 + * @param ingressPort ingress port
27 + * @param egressPort egress port
28 + * @param path traversed links
29 + * @throws NullPointerException {@code path} is null
30 + */
31 + public PathIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
32 + ConnectPoint ingressPort, ConnectPoint egressPort,
33 + Path path) {
34 + super(id, match, action, ingressPort, egressPort);
35 + this.path = path;
36 + }
37 +
38 + protected PathIntent() {
39 + super();
40 + this.path = null;
41 + }
42 +
43 + /**
44 + * Returns the links which the traffic goes along.
45 + *
46 + * @return traversed links
47 + */
48 + public Path getPath() {
49 + return path;
50 + }
51 +
52 + @Override
53 + public boolean equals(Object o) {
54 + if (this == o) {
55 + return true;
56 + }
57 + if (o == null || getClass() != o.getClass()) {
58 + return false;
59 + }
60 + if (!super.equals(o)) {
61 + return false;
62 + }
63 +
64 + PathIntent that = (PathIntent) o;
65 +
66 + if (!path.equals(that.path)) {
67 + return false;
68 + }
69 +
70 + return true;
71 + }
72 +
73 + @Override
74 + public int hashCode() {
75 + return Objects.hash(super.hashCode(), path);
76 + }
77 +
78 + @Override
79 + public String toString() {
80 + return MoreObjects.toStringHelper(getClass())
81 + .add("id", getId())
82 + .add("match", getTrafficSelector())
83 + .add("action", getTrafficTreatment())
84 + .add("ingressPort", getIngressPort())
85 + .add("egressPort", getEgressPort())
86 + .add("path", path)
87 + .toString();
88 + }
89 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import org.onlab.onos.net.ConnectPoint;
8 +import org.onlab.onos.net.flow.TrafficSelector;
9 +import org.onlab.onos.net.flow.TrafficTreatment;
10 +
11 +import com.google.common.base.MoreObjects;
12 +
13 +/**
14 + * Abstraction of point-to-point connectivity.
15 + */
16 +public class PointToPointIntent extends ConnectivityIntent {
17 +
18 + private final ConnectPoint ingressPort;
19 + private final ConnectPoint egressPort;
20 +
21 + /**
22 + * Creates a new point-to-point intent with the supplied ingress/egress
23 + * ports.
24 + *
25 + * @param id intent identifier
26 + * @param match traffic match
27 + * @param action action
28 + * @param ingressPort ingress port
29 + * @param egressPort egress port
30 + * @throws NullPointerException if {@code ingressPort} or {@code egressPort} is null.
31 + */
32 + public PointToPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
33 + ConnectPoint ingressPort, ConnectPoint egressPort) {
34 + super(id, match, action);
35 + this.ingressPort = checkNotNull(ingressPort);
36 + this.egressPort = checkNotNull(egressPort);
37 + }
38 +
39 + /**
40 + * Constructor for serializer.
41 + */
42 + protected PointToPointIntent() {
43 + super();
44 + this.ingressPort = null;
45 + this.egressPort = null;
46 + }
47 +
48 + /**
49 + * Returns the port on which the ingress traffic should be connected to
50 + * the egress.
51 + *
52 + * @return ingress port
53 + */
54 + public ConnectPoint getIngressPort() {
55 + return ingressPort;
56 + }
57 +
58 + /**
59 + * Returns the port on which the traffic should egress.
60 + *
61 + * @return egress port
62 + */
63 + public ConnectPoint getEgressPort() {
64 + return egressPort;
65 + }
66 +
67 + @Override
68 + public boolean equals(Object o) {
69 + if (this == o) {
70 + return true;
71 + }
72 + if (o == null || getClass() != o.getClass()) {
73 + return false;
74 + }
75 + if (!super.equals(o)) {
76 + return false;
77 + }
78 +
79 + PointToPointIntent that = (PointToPointIntent) o;
80 + return Objects.equals(this.ingressPort, that.ingressPort)
81 + && Objects.equals(this.egressPort, that.egressPort);
82 + }
83 +
84 + @Override
85 + public int hashCode() {
86 + return Objects.hash(super.hashCode(), ingressPort, egressPort);
87 + }
88 +
89 + @Override
90 + public String toString() {
91 + return MoreObjects.toStringHelper(getClass())
92 + .add("id", getId())
93 + .add("match", getTrafficSelector())
94 + .add("action", getTrafficTreatment())
95 + .add("ingressPort", ingressPort)
96 + .add("egressPort", egressPort)
97 + .toString();
98 + }
99 +
100 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkArgument;
4 +import static com.google.common.base.Preconditions.checkNotNull;
5 +
6 +import java.util.Objects;
7 +import java.util.Set;
8 +
9 +import org.onlab.onos.net.ConnectPoint;
10 +import org.onlab.onos.net.flow.TrafficSelector;
11 +import org.onlab.onos.net.flow.TrafficTreatment;
12 +
13 +import com.google.common.base.MoreObjects;
14 +import com.google.common.collect.Sets;
15 +
16 +/**
17 + * Abstraction of single source, multiple destination connectivity intent.
18 + */
19 +public class SinglePointToMultiPointIntent extends ConnectivityIntent {
20 +
21 + private final ConnectPoint ingressPort;
22 + private final Set<ConnectPoint> egressPorts;
23 +
24 + /**
25 + * Creates a new single-to-multi point connectivity intent.
26 + *
27 + * @param id intent identifier
28 + * @param match traffic match
29 + * @param action action
30 + * @param ingressPort port on which traffic will ingress
31 + * @param egressPorts set of ports on which traffic will egress
32 + * @throws NullPointerException if {@code ingressPort} or
33 + * {@code egressPorts} is null
34 + * @throws IllegalArgumentException if the size of {@code egressPorts} is
35 + * not more than 1
36 + */
37 + public SinglePointToMultiPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
38 + ConnectPoint ingressPort,
39 + Set<ConnectPoint> egressPorts) {
40 + super(id, match, action);
41 +
42 + checkNotNull(egressPorts);
43 + checkArgument(!egressPorts.isEmpty(),
44 + "there should be at least one egress port");
45 +
46 + this.ingressPort = checkNotNull(ingressPort);
47 + this.egressPorts = Sets.newHashSet(egressPorts);
48 + }
49 +
50 + /**
51 + * Constructor for serializer.
52 + */
53 + protected SinglePointToMultiPointIntent() {
54 + super();
55 + this.ingressPort = null;
56 + this.egressPorts = null;
57 + }
58 +
59 + /**
60 + * Returns the port on which the ingress traffic should be connected to the egress.
61 + *
62 + * @return ingress port
63 + */
64 + public ConnectPoint getIngressPort() {
65 + return ingressPort;
66 + }
67 +
68 + /**
69 + * Returns the set of ports on which the traffic should egress.
70 + *
71 + * @return set of egress ports
72 + */
73 + public Set<ConnectPoint> getEgressPorts() {
74 + return egressPorts;
75 + }
76 +
77 + @Override
78 + public boolean equals(Object o) {
79 + if (this == o) {
80 + return true;
81 + }
82 + if (o == null || getClass() != o.getClass()) {
83 + return false;
84 + }
85 + if (!super.equals(o)) {
86 + return false;
87 + }
88 +
89 + SinglePointToMultiPointIntent that = (SinglePointToMultiPointIntent) o;
90 + return Objects.equals(this.ingressPort, that.ingressPort)
91 + && Objects.equals(this.egressPorts, that.egressPorts);
92 + }
93 +
94 + @Override
95 + public int hashCode() {
96 + return Objects.hash(super.hashCode(), ingressPort, egressPorts);
97 + }
98 +
99 + @Override
100 + public String toString() {
101 + return MoreObjects.toStringHelper(getClass())
102 + .add("id", getId())
103 + .add("match", getTrafficSelector())
104 + .add("action", getTrafficTreatment())
105 + .add("ingressPort", ingressPort)
106 + .add("egressPort", egressPorts)
107 + .toString();
108 + }
109 +
110 +}
1 +/**
2 + * Intent Package. TODO
3 + */
4 +
5 +package org.onlab.onos.net.intent;
...\ No newline at end of file ...\ No newline at end of file
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Set;
4 +
5 +import org.onlab.onos.net.ConnectPoint;
6 +import org.onlab.onos.net.DeviceId;
7 +import org.onlab.onos.net.PortNumber;
8 +import org.onlab.onos.net.flow.DefaultTrafficSelector;
9 +import org.onlab.onos.net.flow.DefaultTrafficTreatment;
10 +import org.onlab.onos.net.flow.TrafficSelector;
11 +import org.onlab.onos.net.flow.TrafficTreatment;
12 +
13 +/**
14 + * Base facilities to test various connectivity tests.
15 + */
16 +public abstract class ConnectivityIntentTest extends IntentTest {
17 +
18 + public static final IntentId IID = new IntentId(123);
19 + public static final TrafficSelector MATCH = (new DefaultTrafficSelector.Builder()).build();
20 + public static final TrafficTreatment NOP = (new DefaultTrafficTreatment.Builder()).build();
21 +
22 + public static final ConnectPoint P1 = new ConnectPoint(DeviceId.deviceId("111"), PortNumber.portNumber(0x1));
23 + public static final ConnectPoint P2 = new ConnectPoint(DeviceId.deviceId("222"), PortNumber.portNumber(0x2));
24 + public static final ConnectPoint P3 = new ConnectPoint(DeviceId.deviceId("333"), PortNumber.portNumber(0x3));
25 +
26 + public static final Set<ConnectPoint> PS1 = itemSet(new ConnectPoint[]{P1, P3});
27 + public static final Set<ConnectPoint> PS2 = itemSet(new ConnectPoint[]{P2, P3});
28 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.ArrayList;
4 +import java.util.Collections;
5 +import java.util.HashMap;
6 +import java.util.HashSet;
7 +import java.util.List;
8 +import java.util.Map;
9 +import java.util.Set;
10 +import java.util.concurrent.ExecutorService;
11 +import java.util.concurrent.Executors;
12 +
13 +/**
14 + * Fake implementation of the intent service to assist in developing tests
15 + * of the interface contract.
16 + */
17 +public class FakeIntentManager implements TestableIntentService {
18 +
19 + private final Map<IntentId, Intent> intents = new HashMap<>();
20 + private final Map<IntentId, IntentState> intentStates = new HashMap<>();
21 + private final Map<IntentId, List<InstallableIntent>> installables = new HashMap<>();
22 + private final Set<IntentEventListener> listeners = new HashSet<>();
23 +
24 + private final Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> compilers = new HashMap<>();
25 + private final Map<Class<? extends InstallableIntent>,
26 + IntentInstaller<? extends InstallableIntent>> installers = new HashMap<>();
27 +
28 + private final ExecutorService executor = Executors.newSingleThreadExecutor();
29 + private final List<IntentException> exceptions = new ArrayList<>();
30 +
31 + @Override
32 + public List<IntentException> getExceptions() {
33 + return exceptions;
34 + }
35 +
36 + // Provides an out-of-thread simulation of intent submit life-cycle
37 + private void executeSubmit(final Intent intent) {
38 + registerSubclassCompilerIfNeeded(intent);
39 + executor.execute(new Runnable() {
40 + @Override
41 + public void run() {
42 + try {
43 + List<InstallableIntent> installable = compileIntent(intent);
44 + installIntents(intent, installable);
45 + } catch (IntentException e) {
46 + exceptions.add(e);
47 + }
48 + }
49 + });
50 + }
51 +
52 + // Provides an out-of-thread simulation of intent withdraw life-cycle
53 + private void executeWithdraw(final Intent intent) {
54 + executor.execute(new Runnable() {
55 + @Override
56 + public void run() {
57 + try {
58 + List<InstallableIntent> installable = getInstallable(intent.getId());
59 + uninstallIntents(intent, installable);
60 + } catch (IntentException e) {
61 + exceptions.add(e);
62 + }
63 +
64 + }
65 + });
66 + }
67 +
68 + private <T extends Intent> IntentCompiler<T> getCompiler(T intent) {
69 + @SuppressWarnings("unchecked")
70 + IntentCompiler<T> compiler = (IntentCompiler<T>) compilers.get(intent.getClass());
71 + if (compiler == null) {
72 + throw new IntentException("no compiler for class " + intent.getClass());
73 + }
74 + return compiler;
75 + }
76 +
77 + private <T extends InstallableIntent> IntentInstaller<T> getInstaller(T intent) {
78 + @SuppressWarnings("unchecked")
79 + IntentInstaller<T> installer = (IntentInstaller<T>) installers.get(intent.getClass());
80 + if (installer == null) {
81 + throw new IntentException("no installer for class " + intent.getClass());
82 + }
83 + return installer;
84 + }
85 +
86 + private <T extends Intent> List<InstallableIntent> compileIntent(T intent) {
87 + try {
88 + // For the fake, we compile using a single level pass
89 + List<InstallableIntent> installable = new ArrayList<>();
90 + for (Intent compiled : getCompiler(intent).compile(intent)) {
91 + installable.add((InstallableIntent) compiled);
92 + }
93 + setState(intent, IntentState.COMPILED);
94 + return installable;
95 + } catch (IntentException e) {
96 + setState(intent, IntentState.FAILED);
97 + throw e;
98 + }
99 + }
100 +
101 + private void installIntents(Intent intent, List<InstallableIntent> installable) {
102 + try {
103 + for (InstallableIntent ii : installable) {
104 + registerSubclassInstallerIfNeeded(ii);
105 + getInstaller(ii).install(ii);
106 + }
107 + setState(intent, IntentState.INSTALLED);
108 + putInstallable(intent.getId(), installable);
109 + } catch (IntentException e) {
110 + setState(intent, IntentState.FAILED);
111 + throw e;
112 + }
113 + }
114 +
115 + private void uninstallIntents(Intent intent, List<InstallableIntent> installable) {
116 + try {
117 + for (InstallableIntent ii : installable) {
118 + getInstaller(ii).uninstall(ii);
119 + }
120 + setState(intent, IntentState.WITHDRAWN);
121 + removeInstallable(intent.getId());
122 + } catch (IntentException e) {
123 + setState(intent, IntentState.FAILED);
124 + throw e;
125 + }
126 + }
127 +
128 +
129 + // Sets the internal state for the given intent and dispatches an event
130 + private void setState(Intent intent, IntentState state) {
131 + IntentState previous = intentStates.get(intent.getId());
132 + intentStates.put(intent.getId(), state);
133 + dispatch(new IntentEvent(intent, state, previous, System.currentTimeMillis()));
134 + }
135 +
136 + private void putInstallable(IntentId id, List<InstallableIntent> installable) {
137 + installables.put(id, installable);
138 + }
139 +
140 + private void removeInstallable(IntentId id) {
141 + installables.remove(id);
142 + }
143 +
144 + private List<InstallableIntent> getInstallable(IntentId id) {
145 + List<InstallableIntent> installable = installables.get(id);
146 + if (installable != null) {
147 + return installable;
148 + } else {
149 + return Collections.emptyList();
150 + }
151 + }
152 +
153 + @Override
154 + public void submit(Intent intent) {
155 + intents.put(intent.getId(), intent);
156 + setState(intent, IntentState.SUBMITTED);
157 + executeSubmit(intent);
158 + }
159 +
160 + @Override
161 + public void withdraw(Intent intent) {
162 + intents.remove(intent.getId());
163 + setState(intent, IntentState.WITHDRAWING);
164 + executeWithdraw(intent);
165 + }
166 +
167 + @Override
168 + public void execute(IntentOperations operations) {
169 + // TODO: implement later
170 + }
171 +
172 + @Override
173 + public Set<Intent> getIntents() {
174 + return Collections.unmodifiableSet(new HashSet<>(intents.values()));
175 + }
176 +
177 + @Override
178 + public Intent getIntent(IntentId id) {
179 + return intents.get(id);
180 + }
181 +
182 + @Override
183 + public IntentState getIntentState(IntentId id) {
184 + return intentStates.get(id);
185 + }
186 +
187 + @Override
188 + public void addListener(IntentEventListener listener) {
189 + listeners.add(listener);
190 + }
191 +
192 + @Override
193 + public void removeListener(IntentEventListener listener) {
194 + listeners.remove(listener);
195 + }
196 +
197 + private void dispatch(IntentEvent event) {
198 + for (IntentEventListener listener : listeners) {
199 + listener.event(event);
200 + }
201 + }
202 +
203 + @Override
204 + public <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler) {
205 + compilers.put(cls, compiler);
206 + }
207 +
208 + @Override
209 + public <T extends Intent> void unregisterCompiler(Class<T> cls) {
210 + compilers.remove(cls);
211 + }
212 +
213 + @Override
214 + public Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers() {
215 + return Collections.unmodifiableMap(compilers);
216 + }
217 +
218 + @Override
219 + public <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer) {
220 + installers.put(cls, installer);
221 + }
222 +
223 + @Override
224 + public <T extends InstallableIntent> void unregisterInstaller(Class<T> cls) {
225 + installers.remove(cls);
226 + }
227 +
228 + @Override
229 + public Map<Class<? extends InstallableIntent>,
230 + IntentInstaller<? extends InstallableIntent>> getInstallers() {
231 + return Collections.unmodifiableMap(installers);
232 + }
233 +
234 + private void registerSubclassCompilerIfNeeded(Intent intent) {
235 + if (!compilers.containsKey(intent.getClass())) {
236 + Class<?> cls = intent.getClass();
237 + while (cls != Object.class) {
238 + // As long as we're within the Intent class descendants
239 + if (Intent.class.isAssignableFrom(cls)) {
240 + IntentCompiler<?> compiler = compilers.get(cls);
241 + if (compiler != null) {
242 + compilers.put(intent.getClass(), compiler);
243 + return;
244 + }
245 + }
246 + cls = cls.getSuperclass();
247 + }
248 + }
249 + }
250 +
251 + private void registerSubclassInstallerIfNeeded(InstallableIntent intent) {
252 + if (!installers.containsKey(intent.getClass())) {
253 + Class<?> cls = intent.getClass();
254 + while (cls != Object.class) {
255 + // As long as we're within the InstallableIntent class descendants
256 + if (InstallableIntent.class.isAssignableFrom(cls)) {
257 + IntentInstaller<?> installer = installers.get(cls);
258 + if (installer != null) {
259 + installers.put(intent.getClass(), installer);
260 + return;
261 + }
262 + }
263 + cls = cls.getSuperclass();
264 + }
265 + }
266 + }
267 +
268 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +import org.hamcrest.Description;
5 +import org.hamcrest.StringDescription;
6 +
7 +import java.lang.reflect.Field;
8 +import java.lang.reflect.Method;
9 +import java.lang.reflect.Modifier;
10 +
11 +/**
12 + * Hamcrest style class for verifying that a class follows the
13 + * accepted rules for immutable classes.
14 + *
15 + * The rules that are enforced for immutable classes:
16 + * - the class must be declared final
17 + * - all data members of the class must be declared private and final
18 + * - the class must not define any setter methods
19 + */
20 +
21 +public class ImmutableClassChecker {
22 +
23 + private String failureReason = "";
24 +
25 + /**
26 + * Method to determine if a given class is a properly specified
27 + * immutable class.
28 + *
29 + * @param clazz the class to check
30 + * @return true if the given class is a properly specified immutable class.
31 + */
32 + private boolean isImmutableClass(Class<?> clazz) {
33 + // class must be declared final
34 + if (!Modifier.isFinal(clazz.getModifiers())) {
35 + failureReason = "a class that is not final";
36 + return false;
37 + }
38 +
39 + // class must have only final and private data members
40 + for (final Field field : clazz.getDeclaredFields()) {
41 + if (field.getName().startsWith("__cobertura")) {
42 + // cobertura sticks these fields into classes - ignore them
43 + continue;
44 + }
45 + if (!Modifier.isFinal(field.getModifiers())) {
46 + failureReason = "a field named '" + field.getName() +
47 + "' that is not final";
48 + return false;
49 + }
50 + if (!Modifier.isPrivate(field.getModifiers())) {
51 + //
52 + // NOTE: We relax the recommended rules for defining immutable
53 + // objects and allow "static final" fields that are not
54 + // private. The "final" check was already done above so we
55 + // don't repeat it here.
56 + //
57 + if (!Modifier.isStatic(field.getModifiers())) {
58 + failureReason = "a field named '" + field.getName() +
59 + "' that is not private and is not static";
60 + return false;
61 + }
62 + }
63 + }
64 +
65 + // class must not define any setters
66 + for (final Method method : clazz.getMethods()) {
67 + if (method.getDeclaringClass().equals(clazz)) {
68 + if (method.getName().startsWith("set")) {
69 + failureReason = "a class with a setter named '" + method.getName() + "'";
70 + return false;
71 + }
72 + }
73 + }
74 +
75 + return true;
76 + }
77 +
78 + /**
79 + * Describe why an error was reported. Uses Hamcrest style Description
80 + * interfaces.
81 + *
82 + * @param description the Description object to use for reporting the
83 + * mismatch
84 + */
85 + public void describeMismatch(Description description) {
86 + description.appendText(failureReason);
87 + }
88 +
89 + /**
90 + * Describe the source object that caused an error, using a Hamcrest
91 + * Matcher style interface. In this case, it always returns
92 + * that we are looking for a properly defined utility class.
93 + *
94 + * @param description the Description object to use to report the "to"
95 + * object
96 + */
97 + public void describeTo(Description description) {
98 + description.appendText("a properly defined immutable class");
99 + }
100 +
101 + /**
102 + * Assert that the given class adheres to the utility class rules.
103 + *
104 + * @param clazz the class to check
105 + *
106 + * @throws java.lang.AssertionError if the class is not a valid
107 + * utility class
108 + */
109 + public static void assertThatClassIsImmutable(Class<?> clazz) {
110 + final ImmutableClassChecker checker = new ImmutableClassChecker();
111 + if (!checker.isImmutableClass(clazz)) {
112 + final Description toDescription = new StringDescription();
113 + final Description mismatchDescription = new StringDescription();
114 +
115 + checker.describeTo(toDescription);
116 + checker.describeMismatch(mismatchDescription);
117 + final String reason =
118 + "\n" +
119 + "Expected: is \"" + toDescription.toString() + "\"\n" +
120 + " but : was \"" + mismatchDescription.toString() + "\"";
121 +
122 + throw new AssertionError(reason);
123 + }
124 + }
125 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.junit.Test;
4 +
5 +import static org.junit.Assert.assertEquals;
6 +
7 +/**
8 + * Test of the intent exception.
9 + */
10 +public class IntentExceptionTest {
11 +
12 + @Test
13 + public void basics() {
14 + validate(new IntentException(), null, null);
15 + validate(new IntentException("foo"), "foo", null);
16 +
17 + Throwable cause = new NullPointerException("bar");
18 + validate(new IntentException("foo", cause), "foo", cause);
19 + }
20 +
21 + /**
22 + * Validates that the specified exception has the correct message and cause.
23 + *
24 + * @param e exception to test
25 + * @param message expected message
26 + * @param cause expected cause
27 + */
28 + protected void validate(RuntimeException e, String message, Throwable cause) {
29 + assertEquals("incorrect message", message, e.getMessage());
30 + assertEquals("incorrect cause", cause, e.getCause());
31 + }
32 +
33 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * This interface is for generator of IntentId. It is defined only for
5 + * testing purpose to keep type safety on mock creation.
6 + *
7 + * <p>
8 + * {@link #getNewId()} generates a globally unique {@link IntentId} instance
9 + * on each invocation. Application developers should not generate IntentId
10 + * by themselves. Instead use an implementation of this interface.
11 + * </p>
12 + */
13 +public interface IntentIdGenerator extends IdGenerator<IntentId> {
14 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.junit.Test;
4 +
5 +import static org.hamcrest.Matchers.is;
6 +import static org.hamcrest.Matchers.not;
7 +import static org.junit.Assert.assertEquals;
8 +import static org.junit.Assert.assertThat;
9 +
10 +/**
11 + * This class tests the immutability, equality, and non-equality of
12 + * {@link IntentId}.
13 + */
14 +public class IntentIdTest {
15 + /**
16 + * Tests the immutability of {@link IntentId}.
17 + */
18 + @Test
19 + public void intentIdFollowsGuidelineForImmutableObject() {
20 + ImmutableClassChecker.assertThatClassIsImmutable(IntentId.class);
21 + }
22 +
23 + /**
24 + * Tests equality of {@link IntentId}.
25 + */
26 + @Test
27 + public void testEquality() {
28 + IntentId id1 = new IntentId(1L);
29 + IntentId id2 = new IntentId(1L);
30 +
31 + assertThat(id1, is(id2));
32 + }
33 +
34 + /**
35 + * Tests non-equality of {@link IntentId}.
36 + */
37 + @Test
38 + public void testNonEquality() {
39 + IntentId id1 = new IntentId(1L);
40 + IntentId id2 = new IntentId(2L);
41 +
42 + assertThat(id1, is(not(id2)));
43 + }
44 +
45 + @Test
46 + public void valueOf() {
47 + IntentId id = new IntentId(12345);
48 + assertEquals("incorrect valueOf", id, IntentId.valueOf("12345"));
49 + }
50 +
51 + @Test
52 + public void valueOfHex() {
53 + IntentId id = new IntentId(0xdeadbeefL);
54 + assertEquals("incorrect valueOf", id, IntentId.valueOf(id.toString()));
55 + }
56 +
57 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static org.junit.Assert.assertEquals;
4 +import static org.junit.Assert.assertFalse;
5 +import static org.junit.Assert.assertTrue;
6 +
7 +import java.util.Arrays;
8 +import java.util.HashSet;
9 +import java.util.Set;
10 +
11 +import org.junit.Test;
12 +
13 +/**
14 + * Base facilities to test various intent tests.
15 + */
16 +public abstract class IntentTest {
17 + /**
18 + * Produces a set of items from the supplied items.
19 + *
20 + * @param items items to be placed in set
21 + * @param <T> item type
22 + * @return set of items
23 + */
24 + protected static <T> Set<T> itemSet(T[] items) {
25 + return new HashSet<>(Arrays.asList(items));
26 + }
27 +
28 + @Test
29 + public void equalsAndHashCode() {
30 + Intent one = createOne();
31 + Intent like = createOne();
32 + Intent another = createAnother();
33 +
34 + assertTrue("should be equal", one.equals(like));
35 + assertEquals("incorrect hashCode", one.hashCode(), like.hashCode());
36 +
37 + assertFalse("should not be equal", one.equals(another));
38 +
39 + assertFalse("should not be equal", one.equals(null));
40 + assertFalse("should not be equal", one.equals("foo"));
41 + }
42 +
43 + @Test
44 + public void testToString() {
45 + Intent one = createOne();
46 + Intent like = createOne();
47 + assertEquals("incorrect toString", one.toString(), like.toString());
48 + }
49 +
50 + /**
51 + * Creates a new intent, but always a like intent, i.e. all instances will
52 + * be equal, but should not be the same.
53 + *
54 + * @return intent
55 + */
56 + protected abstract Intent createOne();
57 +
58 + /**
59 + * Creates another intent, not equals to the one created by
60 + * {@link #createOne()} and with a different hash code.
61 + *
62 + * @return another intent
63 + */
64 + protected abstract Intent createAnother();
65 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.junit.Test;
4 +
5 +import static org.junit.Assert.assertEquals;
6 +
7 +/**
8 + * Suite of tests of the multi-to-single point intent descriptor.
9 + */
10 +public class MultiPointToSinglePointIntentTest extends ConnectivityIntentTest {
11 +
12 + @Test
13 + public void basics() {
14 + MultiPointToSinglePointIntent intent = createOne();
15 + assertEquals("incorrect id", IID, intent.getId());
16 + assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
17 + assertEquals("incorrect ingress", PS1, intent.getIngressPorts());
18 + assertEquals("incorrect egress", P2, intent.getEgressPort());
19 + }
20 +
21 + @Override
22 + protected MultiPointToSinglePointIntent createOne() {
23 + return new MultiPointToSinglePointIntent(IID, MATCH, NOP, PS1, P2);
24 + }
25 +
26 + @Override
27 + protected MultiPointToSinglePointIntent createAnother() {
28 + return new MultiPointToSinglePointIntent(IID, MATCH, NOP, PS2, P1);
29 + }
30 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static org.junit.Assert.assertEquals;
4 +
5 +import org.junit.Test;
6 +import org.onlab.onos.net.NetTestTools;
7 +import org.onlab.onos.net.Path;
8 +
9 +public class PathIntentTest extends ConnectivityIntentTest {
10 + // 111:11 --> 222:22
11 + private static final Path PATH1 = NetTestTools.createPath("111", "222");
12 +
13 + // 111:11 --> 333:33
14 + private static final Path PATH2 = NetTestTools.createPath("222", "333");
15 +
16 + @Test
17 + public void basics() {
18 + PathIntent intent = createOne();
19 + assertEquals("incorrect id", IID, intent.getId());
20 + assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
21 + assertEquals("incorrect action", NOP, intent.getTrafficTreatment());
22 + assertEquals("incorrect ingress", P1, intent.getIngressPort());
23 + assertEquals("incorrect egress", P2, intent.getEgressPort());
24 + assertEquals("incorrect path", PATH1, intent.getPath());
25 + }
26 +
27 + @Override
28 + protected PathIntent createOne() {
29 + return new PathIntent(IID, MATCH, NOP, P1, P2, PATH1);
30 + }
31 +
32 + @Override
33 + protected PathIntent createAnother() {
34 + return new PathIntent(IID, MATCH, NOP, P1, P3, PATH2);
35 + }
36 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.junit.Test;
4 +
5 +import static org.junit.Assert.assertEquals;
6 +
7 +/**
8 + * Suite of tests of the point-to-point intent descriptor.
9 + */
10 +public class PointToPointIntentTest extends ConnectivityIntentTest {
11 +
12 + @Test
13 + public void basics() {
14 + PointToPointIntent intent = createOne();
15 + assertEquals("incorrect id", IID, intent.getId());
16 + assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
17 + assertEquals("incorrect ingress", P1, intent.getIngressPort());
18 + assertEquals("incorrect egress", P2, intent.getEgressPort());
19 + }
20 +
21 + @Override
22 + protected PointToPointIntent createOne() {
23 + return new PointToPointIntent(IID, MATCH, NOP, P1, P2);
24 + }
25 +
26 + @Override
27 + protected PointToPointIntent createAnother() {
28 + return new PointToPointIntent(IID, MATCH, NOP, P2, P1);
29 + }
30 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.junit.Test;
4 +
5 +import static org.junit.Assert.assertEquals;
6 +
7 +/**
8 + * Suite of tests of the single-to-multi point intent descriptor.
9 + */
10 +public class SinglePointToMultiPointIntentTest extends ConnectivityIntentTest {
11 +
12 + @Test
13 + public void basics() {
14 + SinglePointToMultiPointIntent intent = createOne();
15 + assertEquals("incorrect id", IID, intent.getId());
16 + assertEquals("incorrect match", MATCH, intent.getTrafficSelector());
17 + assertEquals("incorrect ingress", P1, intent.getIngressPort());
18 + assertEquals("incorrect egress", PS2, intent.getEgressPorts());
19 + }
20 +
21 + @Override
22 + protected SinglePointToMultiPointIntent createOne() {
23 + return new SinglePointToMultiPointIntent(IID, MATCH, NOP, P1, PS2);
24 + }
25 +
26 + @Override
27 + protected SinglePointToMultiPointIntent createAnother() {
28 + return new SinglePointToMultiPointIntent(IID, MATCH, NOP, P2, PS1);
29 + }
30 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +/**
5 + * An installable intent used in the unit test.
6 + *
7 + * FIXME: we don't want to expose this class publicly, but the current Kryo
8 + * serialization mechanism does not allow this class to be private and placed
9 + * on testing directory.
10 + */
11 +public class TestInstallableIntent extends AbstractIntent implements InstallableIntent {
12 + /**
13 + * Constructs an instance with the specified intent ID.
14 + *
15 + * @param id intent ID
16 + */
17 + public TestInstallableIntent(IntentId id) {
18 + super(id);
19 + }
20 +
21 + /**
22 + * Constructor for serializer.
23 + */
24 + protected TestInstallableIntent() {
25 + super();
26 + }
27 +
28 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +/**
5 + * An intent used in the unit test.
6 + *
7 + * FIXME: we don't want to expose this class publicly, but the current Kryo
8 + * serialization mechanism does not allow this class to be private and placed
9 + * on testing directory.
10 + */
11 +public class TestIntent extends AbstractIntent {
12 + /**
13 + * Constructs an instance with the specified intent ID.
14 + *
15 + * @param id intent ID
16 + */
17 + public TestIntent(IntentId id) {
18 + super(id);
19 + }
20 +
21 + /**
22 + * Constructor for serializer.
23 + */
24 + protected TestIntent() {
25 + super();
26 + }
27 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +/**
5 + * An intent used in the unit test.
6 + *
7 + * FIXME: we don't want to expose this class publicly, but the current Kryo
8 + * serialization mechanism does not allow this class to be private and placed
9 + * on testing directory.
10 + */
11 +public class TestSubclassInstallableIntent extends TestInstallableIntent implements InstallableIntent {
12 + /**
13 + * Constructs an instance with the specified intent ID.
14 + *
15 + * @param id intent ID
16 + */
17 + public TestSubclassInstallableIntent(IntentId id) {
18 + super(id);
19 + }
20 +
21 + /**
22 + * Constructor for serializer.
23 + */
24 + protected TestSubclassInstallableIntent() {
25 + super();
26 + }
27 +}
1 +package org.onlab.onos.net.intent;
2 +//TODO is this the right package?
3 +
4 +/**
5 + * An intent used in the unit test.
6 + *
7 + * FIXME: we don't want to expose this class publicly, but the current Kryo
8 + * serialization mechanism does not allow this class to be private and placed
9 + * on testing directory.
10 + */
11 +public class TestSubclassIntent extends TestIntent {
12 + /**
13 + * Constructs an instance with the specified intent ID.
14 + *
15 + * @param id intent ID
16 + */
17 + public TestSubclassIntent(IntentId id) {
18 + super(id);
19 + }
20 +
21 + /**
22 + * Constructor for serializer.
23 + */
24 + protected TestSubclassIntent() {
25 + super();
26 + }
27 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static org.junit.Assert.fail;
4 +
5 +/**
6 + * Set of test tools.
7 + */
8 +public final class TestTools {
9 +
10 + // Disallow construction
11 + private TestTools() {
12 + }
13 +
14 + /**
15 + * Utility method to pause the current thread for the specified number of
16 + * milliseconds.
17 + *
18 + * @param ms number of milliseconds to pause
19 + */
20 + public static void delay(int ms) {
21 + try {
22 + Thread.sleep(ms);
23 + } catch (InterruptedException e) {
24 + fail("unexpected interrupt");
25 + }
26 + }
27 +
28 + /**
29 + * Periodically runs the given runnable, which should contain a series of
30 + * test assertions until all the assertions succeed, in which case it will
31 + * return, or until the the time expires, in which case it will throw the
32 + * first failed assertion error.
33 + *
34 + * @param start start time, in millis since start of epoch from which the
35 + * duration will be measured
36 + * @param delay initial delay (in milliseconds) before the first assertion
37 + * attempt
38 + * @param step delay (in milliseconds) between successive assertion
39 + * attempts
40 + * @param duration number of milliseconds beyond the given start time,
41 + * after which the failed assertions will be propagated and allowed
42 + * to fail the test
43 + * @param assertions runnable housing the test assertions
44 + */
45 + public static void assertAfter(long start, int delay, int step,
46 + int duration, Runnable assertions) {
47 + delay(delay);
48 + while (true) {
49 + try {
50 + assertions.run();
51 + break;
52 + } catch (AssertionError e) {
53 + if (System.currentTimeMillis() - start > duration) {
54 + throw e;
55 + }
56 + }
57 + delay(step);
58 + }
59 + }
60 +
61 + /**
62 + * Periodically runs the given runnable, which should contain a series of
63 + * test assertions until all the assertions succeed, in which case it will
64 + * return, or until the the time expires, in which case it will throw the
65 + * first failed assertion error.
66 + * <p>
67 + * The start of the period is the current time.
68 + *
69 + * @param delay initial delay (in milliseconds) before the first assertion
70 + * attempt
71 + * @param step delay (in milliseconds) between successive assertion
72 + * attempts
73 + * @param duration number of milliseconds beyond the current time time,
74 + * after which the failed assertions will be propagated and allowed
75 + * to fail the test
76 + * @param assertions runnable housing the test assertions
77 + */
78 + public static void assertAfter(int delay, int step, int duration,
79 + Runnable assertions) {
80 + assertAfter(System.currentTimeMillis(), delay, step, duration,
81 + assertions);
82 + }
83 +
84 + /**
85 + * Periodically runs the given runnable, which should contain a series of
86 + * test assertions until all the assertions succeed, in which case it will
87 + * return, or until the the time expires, in which case it will throw the
88 + * first failed assertion error.
89 + * <p>
90 + * The start of the period is the current time and the first assertion
91 + * attempt is delayed by the value of {@code step} parameter.
92 + *
93 + * @param step delay (in milliseconds) between successive assertion
94 + * attempts
95 + * @param duration number of milliseconds beyond the current time time,
96 + * after which the failed assertions will be propagated and allowed
97 + * to fail the test
98 + * @param assertions runnable housing the test assertions
99 + */
100 + public static void assertAfter(int step, int duration,
101 + Runnable assertions) {
102 + assertAfter(step, step, duration, assertions);
103 + }
104 +
105 + /**
106 + * Periodically runs the given runnable, which should contain a series of
107 + * test assertions until all the assertions succeed, in which case it will
108 + * return, or until the the time expires, in which case it will throw the
109 + * first failed assertion error.
110 + * <p>
111 + * The start of the period is the current time and each successive
112 + * assertion attempt is delayed by at least 10 milliseconds unless the
113 + * {@code duration} is less than that, in which case the one and only
114 + * assertion is made after that delay.
115 + *
116 + * @param duration number of milliseconds beyond the current time,
117 + * after which the failed assertions will be propagated and allowed
118 + * to fail the test
119 + * @param assertions runnable housing the test assertions
120 + */
121 + public static void assertAfter(int duration, Runnable assertions) {
122 + int step = Math.min(duration, Math.max(10, duration / 10));
123 + assertAfter(step, duration, assertions);
124 + }
125 +
126 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.List;
4 +
5 +/**
6 + * Abstraction of an extensible intent service enabled for unit tests.
7 + */
8 +public interface TestableIntentService extends IntentService, IntentExtensionService {
9 +
10 + List<IntentException> getExceptions();
11 +
12 +}
...@@ -35,6 +35,12 @@ ...@@ -35,6 +35,12 @@
35 </dependency> 35 </dependency>
36 36
37 <dependency> 37 <dependency>
38 + <groupId>org.onlab.onos</groupId>
39 + <artifactId>onlab-netty</artifactId>
40 + <version>${project.version}</version>
41 + </dependency>
42 +
43 + <dependency>
38 <groupId>com.fasterxml.jackson.core</groupId> 44 <groupId>com.fasterxml.jackson.core</groupId>
39 <artifactId>jackson-databind</artifactId> 45 <artifactId>jackson-databind</artifactId>
40 </dependency> 46 </dependency>
...@@ -51,15 +57,6 @@ ...@@ -51,15 +57,6 @@
51 <groupId>de.javakaffee</groupId> 57 <groupId>de.javakaffee</groupId>
52 <artifactId>kryo-serializers</artifactId> 58 <artifactId>kryo-serializers</artifactId>
53 </dependency> 59 </dependency>
54 - <dependency>
55 - <groupId>io.netty</groupId>
56 - <artifactId>netty-all</artifactId>
57 - </dependency>
58 - <dependency>
59 - <groupId>commons-pool</groupId>
60 - <artifactId>commons-pool</artifactId>
61 - <version>1.6</version>
62 - </dependency>
63 </dependencies> 60 </dependencies>
64 61
65 <build> 62 <build>
......
...@@ -23,10 +23,10 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService; ...@@ -23,10 +23,10 @@ import org.onlab.onos.store.cluster.messaging.ClusterCommunicationService;
23 import org.onlab.onos.store.cluster.messaging.ClusterMessage; 23 import org.onlab.onos.store.cluster.messaging.ClusterMessage;
24 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler; 24 import org.onlab.onos.store.cluster.messaging.ClusterMessageHandler;
25 import org.onlab.onos.store.cluster.messaging.MessageSubject; 25 import org.onlab.onos.store.cluster.messaging.MessageSubject;
26 -import org.onlab.onos.store.messaging.Endpoint; 26 +import org.onlab.netty.Endpoint;
27 -import org.onlab.onos.store.messaging.Message; 27 +import org.onlab.netty.Message;
28 -import org.onlab.onos.store.messaging.MessageHandler; 28 +import org.onlab.netty.MessageHandler;
29 -import org.onlab.onos.store.messaging.MessagingService; 29 +import org.onlab.netty.MessagingService;
30 import org.slf4j.Logger; 30 import org.slf4j.Logger;
31 import org.slf4j.LoggerFactory; 31 import org.slf4j.LoggerFactory;
32 32
......
1 -package org.onlab.onos.store.messaging;
2 -
3 -/**
4 - * Representation of a TCP/UDP communication end point.
5 - */
6 -public class Endpoint {
7 -
8 - private final int port;
9 - private final String host;
10 -
11 - public Endpoint(String host, int port) {
12 - this.host = host;
13 - this.port = port;
14 - }
15 -
16 - public String host() {
17 - return host;
18 - }
19 -
20 - public int port() {
21 - return port;
22 - }
23 -
24 - @Override
25 - public String toString() {
26 - return "Endpoint [port=" + port + ", host=" + host + "]";
27 - }
28 -
29 - @Override
30 - public int hashCode() {
31 - final int prime = 31;
32 - int result = 1;
33 - result = prime * result + ((host == null) ? 0 : host.hashCode());
34 - result = prime * result + port;
35 - return result;
36 - }
37 -
38 - @Override
39 - public boolean equals(Object obj) {
40 - if (this == obj) {
41 - return true;
42 - }
43 - if (obj == null) {
44 - return false;
45 - }
46 - if (getClass() != obj.getClass()) {
47 - return false;
48 - }
49 - Endpoint other = (Endpoint) obj;
50 - if (host == null) {
51 - if (other.host != null) {
52 - return false;
53 - }
54 - } else if (!host.equals(other.host)) {
55 - return false;
56 - }
57 - if (port != other.port) {
58 - return false;
59 - }
60 - return true;
61 - }
62 -}
1 -package org.onlab.onos.store.messaging;
2 -
3 -import java.io.IOException;
4 -
5 -/**
6 - * A unit of communication.
7 - * Has a payload. Also supports a feature to respond back to the sender.
8 - */
9 -public interface Message {
10 -
11 - /**
12 - * Returns the payload of this message.
13 - * @return message payload.
14 - */
15 - public Object payload();
16 -
17 - /**
18 - * Sends a reply back to the sender of this messge.
19 - * @param data payload of the response.
20 - * @throws IOException if there is a communication error.
21 - */
22 - public void respond(Object data) throws IOException;
23 -}
1 -package org.onlab.onos.store.messaging;
2 -
3 -import java.io.IOException;
4 -
5 -/**
6 - * Handler for a message.
7 - */
8 -public interface MessageHandler {
9 -
10 - /**
11 - * Handles the message.
12 - * @param message message.
13 - * @throws IOException.
14 - */
15 - public void handle(Message message) throws IOException;
16 -}
1 -package org.onlab.onos.store.messaging;
2 -
3 -import java.io.IOException;
4 -
5 -/**
6 - * Interface for low level messaging primitives.
7 - */
8 -public interface MessagingService {
9 - /**
10 - * Sends a message asynchronously to the specified communication end point.
11 - * The message is specified using the type and payload.
12 - * @param ep end point to send the message to.
13 - * @param type type of message.
14 - * @param payload message payload.
15 - * @throws IOException
16 - */
17 - public void sendAsync(Endpoint ep, String type, Object payload) throws IOException;
18 -
19 - /**
20 - * Sends a message synchronously and waits for a response.
21 - * @param ep end point to send the message to.
22 - * @param type type of message.
23 - * @param payload message payload.
24 - * @return a response future
25 - * @throws IOException
26 - */
27 - public <T> Response<T> sendAndReceive(Endpoint ep, String type, Object payload) throws IOException;
28 -
29 - /**
30 - * Registers a new message handler for message type.
31 - * @param type message type.
32 - * @param handler message handler
33 - */
34 - public void registerHandler(String type, MessageHandler handler);
35 -
36 - /**
37 - * Unregister current handler, if one exists for message type.
38 - * @param type message type
39 - */
40 - public void unregisterHandler(String type);
41 -}
1 -package org.onlab.onos.store.messaging;
2 -
3 -import java.util.concurrent.TimeUnit;
4 -import java.util.concurrent.TimeoutException;
5 -
6 -/**
7 - * Response object returned when making synchronous requests.
8 - * Can you used to check is a response is ready and/or wait for a response
9 - * to become available.
10 - *
11 - * @param <T> type of response.
12 - */
13 -public interface Response<T> {
14 -
15 - /**
16 - * Gets the response waiting for a designated timeout period.
17 - * @param timeout timeout period (since request was sent out)
18 - * @param tu unit of time.
19 - * @return response
20 - * @throws TimeoutException if the timeout expires before the response arrives.
21 - */
22 - public T get(long timeout, TimeUnit tu) throws TimeoutException;
23 -
24 - /**
25 - * Gets the response waiting for indefinite timeout period.
26 - * @return response
27 - * @throws InterruptedException if the thread is interrupted before the response arrives.
28 - */
29 - public T get() throws InterruptedException;
30 -
31 - /**
32 - * Checks if the response is ready without blocking.
33 - * @return true if response is ready, false otherwise.
34 - */
35 - public boolean isReady();
36 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import java.util.concurrent.TimeUnit;
4 -import java.util.concurrent.TimeoutException;
5 -
6 -import org.onlab.onos.store.messaging.Response;
7 -
8 -/**
9 - * An asynchronous response.
10 - * This class provides a base implementation of Response, with methods to retrieve the
11 - * result and query to see if the result is ready. The result can only be retrieved when
12 - * it is ready and the get methods will block if the result is not ready yet.
13 - * @param <T> type of response.
14 - */
15 -public class AsyncResponse<T> implements Response<T> {
16 -
17 - private T value;
18 - private boolean done = false;
19 - private final long start = System.nanoTime();
20 -
21 - @Override
22 - public T get(long timeout, TimeUnit tu) throws TimeoutException {
23 - timeout = tu.toNanos(timeout);
24 - boolean interrupted = false;
25 - try {
26 - synchronized (this) {
27 - while (!done) {
28 - try {
29 - long timeRemaining = timeout - (System.nanoTime() - start);
30 - if (timeRemaining <= 0) {
31 - throw new TimeoutException("Operation timed out.");
32 - }
33 - TimeUnit.NANOSECONDS.timedWait(this, timeRemaining);
34 - } catch (InterruptedException e) {
35 - interrupted = true;
36 - }
37 - }
38 - }
39 - } finally {
40 - if (interrupted) {
41 - Thread.currentThread().interrupt();
42 - }
43 - }
44 - return value;
45 - }
46 -
47 - @Override
48 - public T get() throws InterruptedException {
49 - throw new UnsupportedOperationException();
50 - }
51 -
52 - @Override
53 - public boolean isReady() {
54 - return done;
55 - }
56 -
57 - /**
58 - * Sets response value and unblocks any thread blocking on the response to become
59 - * available.
60 - * @param data response data.
61 - */
62 - @SuppressWarnings("unchecked")
63 - public synchronized void setResponse(Object data) {
64 - if (!done) {
65 - done = true;
66 - value = (T) data;
67 - this.notifyAll();
68 - }
69 - }
70 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import java.io.IOException;
4 -
5 -import org.onlab.onos.store.messaging.Message;
6 -import org.onlab.onos.store.messaging.MessageHandler;
7 -
8 -/**
9 - * Message handler that echos the message back to the sender.
10 - */
11 -public class EchoHandler implements MessageHandler {
12 -
13 - @Override
14 - public void handle(Message message) throws IOException {
15 - System.out.println("Received: " + message.payload() + ". Echoing it back to the sender.");
16 - message.respond(message.payload());
17 - }
18 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import java.io.IOException;
4 -
5 -import org.onlab.onos.store.messaging.Endpoint;
6 -import org.onlab.onos.store.messaging.Message;
7 -
8 -/**
9 - * Internal message representation with additional attributes
10 - * for supporting, synchronous request/reply behavior.
11 - */
12 -public final class InternalMessage implements Message {
13 -
14 - private long id;
15 - private Endpoint sender;
16 - private String type;
17 - private Object payload;
18 - private transient NettyMessagingService messagingService;
19 - public static final String REPLY_MESSAGE_TYPE = "NETTY_MESSAGIG_REQUEST_REPLY";
20 -
21 - // Must be created using the Builder.
22 - private InternalMessage() {}
23 -
24 - public long id() {
25 - return id;
26 - }
27 -
28 - public String type() {
29 - return type;
30 - }
31 -
32 - public Endpoint sender() {
33 - return sender;
34 - }
35 -
36 - @Override
37 - public Object payload() {
38 - return payload;
39 - }
40 -
41 - @Override
42 - public void respond(Object data) throws IOException {
43 - Builder builder = new Builder(messagingService);
44 - InternalMessage message = builder.withId(this.id)
45 - // FIXME: Sender should be messagingService.localEp.
46 - .withSender(this.sender)
47 - .withPayload(data)
48 - .withType(REPLY_MESSAGE_TYPE)
49 - .build();
50 - messagingService.sendAsync(sender, message);
51 - }
52 -
53 -
54 - /**
55 - * Builder for InternalMessages.
56 - */
57 - public static class Builder {
58 - private InternalMessage message;
59 -
60 - public Builder(NettyMessagingService messagingService) {
61 - message = new InternalMessage();
62 - message.messagingService = messagingService;
63 - }
64 -
65 - public Builder withId(long id) {
66 - message.id = id;
67 - return this;
68 - }
69 -
70 - public Builder withType(String type) {
71 - message.type = type;
72 - return this;
73 - }
74 -
75 - public Builder withSender(Endpoint sender) {
76 - message.sender = sender;
77 - return this;
78 - }
79 - public Builder withPayload(Object payload) {
80 - message.payload = payload;
81 - return this;
82 - }
83 -
84 - public InternalMessage build() {
85 - return message;
86 - }
87 - }
88 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import org.onlab.onos.store.messaging.Message;
4 -import org.onlab.onos.store.messaging.MessageHandler;
5 -
6 -/**
7 - * A MessageHandler that simply logs the information.
8 - */
9 -public class LoggingHandler implements MessageHandler {
10 -
11 - @Override
12 - public void handle(Message message) {
13 - System.out.println("Received: " + message.payload());
14 - }
15 -}
...\ No newline at end of file ...\ No newline at end of file
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import java.util.Arrays;
4 -import java.util.List;
5 -
6 -import static com.google.common.base.Preconditions.checkState;
7 -
8 -import org.onlab.onos.store.cluster.messaging.SerializationService;
9 -import org.onlab.onos.store.messaging.Endpoint;
10 -
11 -import io.netty.buffer.ByteBuf;
12 -import io.netty.channel.ChannelHandlerContext;
13 -import io.netty.handler.codec.ByteToMessageDecoder;
14 -
15 -/**
16 - * Decode bytes into a InrenalMessage.
17 - */
18 -public class MessageDecoder extends ByteToMessageDecoder {
19 -
20 - private final NettyMessagingService messagingService;
21 - private final SerializationService serializationService;
22 -
23 - public MessageDecoder(NettyMessagingService messagingService, SerializationService serializationService) {
24 - this.messagingService = messagingService;
25 - this.serializationService = serializationService;
26 - }
27 -
28 - @Override
29 - protected void decode(ChannelHandlerContext context, ByteBuf in,
30 - List<Object> messages) throws Exception {
31 -
32 - byte[] preamble = in.readBytes(MessageEncoder.PREAMBLE.length).array();
33 - checkState(Arrays.equals(MessageEncoder.PREAMBLE, preamble), "Message has wrong preamble");
34 -
35 - // read message Id.
36 - long id = in.readLong();
37 -
38 - // read message type; first read size and then bytes.
39 - String type = new String(in.readBytes(in.readInt()).array());
40 -
41 - // read sender host name; first read size and then bytes.
42 - String host = new String(in.readBytes(in.readInt()).array());
43 -
44 - // read sender port.
45 - int port = in.readInt();
46 -
47 - Endpoint sender = new Endpoint(host, port);
48 -
49 - // read message payload; first read size and then bytes.
50 - Object payload = serializationService.decode(in.readBytes(in.readInt()).array());
51 -
52 - InternalMessage message = new InternalMessage.Builder(messagingService)
53 - .withId(id)
54 - .withSender(sender)
55 - .withType(type)
56 - .withPayload(payload)
57 - .build();
58 -
59 - messages.add(message);
60 - }
61 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import org.onlab.onos.store.cluster.messaging.SerializationService;
4 -
5 -import io.netty.buffer.ByteBuf;
6 -import io.netty.channel.ChannelHandlerContext;
7 -import io.netty.handler.codec.MessageToByteEncoder;
8 -
9 -/**
10 - * Encode InternalMessage out into a byte buffer.
11 - */
12 -public class MessageEncoder extends MessageToByteEncoder<InternalMessage> {
13 -
14 - // onosiscool in ascii
15 - public static final byte[] PREAMBLE = "onosiscool".getBytes();
16 -
17 - private final SerializationService serializationService;
18 -
19 - public MessageEncoder(SerializationService serializationService) {
20 - this.serializationService = serializationService;
21 - }
22 -
23 - @Override
24 - protected void encode(ChannelHandlerContext context, InternalMessage message,
25 - ByteBuf out) throws Exception {
26 -
27 - // write preamble
28 - out.writeBytes(PREAMBLE);
29 -
30 - // write id
31 - out.writeLong(message.id());
32 -
33 - // write type length
34 - out.writeInt(message.type().length());
35 -
36 - // write type
37 - out.writeBytes(message.type().getBytes());
38 -
39 - // write sender host name size
40 - out.writeInt(message.sender().host().length());
41 -
42 - // write sender host name.
43 - out.writeBytes(message.sender().host().getBytes());
44 -
45 - // write port
46 - out.writeInt(message.sender().port());
47 -
48 - try {
49 - serializationService.encode(message.payload());
50 - } catch (Exception e) {
51 - e.printStackTrace();
52 - }
53 -
54 - byte[] payload = serializationService.encode(message.payload());
55 -
56 - // write payload length.
57 - out.writeInt(payload.length);
58 -
59 - // write payload bytes
60 - out.writeBytes(payload);
61 - }
62 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import java.io.IOException;
4 -import java.net.UnknownHostException;
5 -import java.util.concurrent.ConcurrentHashMap;
6 -import java.util.concurrent.ConcurrentMap;
7 -import java.util.concurrent.TimeUnit;
8 -
9 -import io.netty.bootstrap.Bootstrap;
10 -import io.netty.bootstrap.ServerBootstrap;
11 -import io.netty.buffer.PooledByteBufAllocator;
12 -import io.netty.channel.Channel;
13 -import io.netty.channel.ChannelFuture;
14 -import io.netty.channel.ChannelHandlerContext;
15 -import io.netty.channel.ChannelInitializer;
16 -import io.netty.channel.ChannelOption;
17 -import io.netty.channel.EventLoopGroup;
18 -import io.netty.channel.SimpleChannelInboundHandler;
19 -import io.netty.channel.nio.NioEventLoopGroup;
20 -import io.netty.channel.socket.SocketChannel;
21 -import io.netty.channel.socket.nio.NioServerSocketChannel;
22 -import io.netty.channel.socket.nio.NioSocketChannel;
23 -
24 -import org.apache.commons.lang.math.RandomUtils;
25 -import org.apache.commons.pool.KeyedObjectPool;
26 -import org.apache.commons.pool.KeyedPoolableObjectFactory;
27 -import org.apache.commons.pool.impl.GenericKeyedObjectPool;
28 -import org.apache.felix.scr.annotations.Activate;
29 -import org.apache.felix.scr.annotations.Component;
30 -import org.apache.felix.scr.annotations.Deactivate;
31 -import org.apache.felix.scr.annotations.Reference;
32 -import org.apache.felix.scr.annotations.ReferenceCardinality;
33 -import org.apache.felix.scr.annotations.Service;
34 -import org.onlab.onos.store.cluster.messaging.SerializationService;
35 -import org.onlab.onos.store.messaging.Endpoint;
36 -import org.onlab.onos.store.messaging.MessageHandler;
37 -import org.onlab.onos.store.messaging.MessagingService;
38 -import org.onlab.onos.store.messaging.Response;
39 -import org.slf4j.Logger;
40 -import org.slf4j.LoggerFactory;
41 -
42 -import com.google.common.cache.Cache;
43 -import com.google.common.cache.CacheBuilder;
44 -
45 -/**
46 - * A Netty based implementation of MessagingService.
47 - */
48 -@Component(immediate = true)
49 -@Service
50 -public class NettyMessagingService implements MessagingService {
51 -
52 - private final Logger log = LoggerFactory.getLogger(getClass());
53 -
54 - private KeyedObjectPool<Endpoint, Channel> channels =
55 - new GenericKeyedObjectPool<Endpoint, Channel>(new OnosCommunicationChannelFactory());
56 - private final int port;
57 - private final EventLoopGroup bossGroup = new NioEventLoopGroup();
58 - private final EventLoopGroup workerGroup = new NioEventLoopGroup();
59 - private final ConcurrentMap<String, MessageHandler> handlers = new ConcurrentHashMap<>();
60 - private Cache<Long, AsyncResponse<?>> responseFutures;
61 - private final Endpoint localEp;
62 -
63 - @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
64 - protected SerializationService serializationService;
65 -
66 - public NettyMessagingService() {
67 - // TODO: Default port should be configurable.
68 - this(8080);
69 - }
70 -
71 - // FIXME: Constructor should not throw exceptions.
72 - public NettyMessagingService(int port) {
73 - this.port = port;
74 - try {
75 - localEp = new Endpoint(java.net.InetAddress.getLocalHost().getHostName(), port);
76 - } catch (UnknownHostException e) {
77 - // bailing out.
78 - throw new RuntimeException(e);
79 - }
80 - }
81 -
82 - @Activate
83 - public void activate() throws Exception {
84 - responseFutures = CacheBuilder.newBuilder()
85 - .maximumSize(100000)
86 - .weakValues()
87 - // TODO: Once the entry expires, notify blocking threads (if any).
88 - .expireAfterWrite(10, TimeUnit.MINUTES)
89 - .build();
90 - startAcceptingConnections();
91 - }
92 -
93 - @Deactivate
94 - public void deactivate() throws Exception {
95 - channels.close();
96 - bossGroup.shutdownGracefully();
97 - workerGroup.shutdownGracefully();
98 - }
99 -
100 - @Override
101 - public void sendAsync(Endpoint ep, String type, Object payload) throws IOException {
102 - InternalMessage message = new InternalMessage.Builder(this)
103 - .withId(RandomUtils.nextLong())
104 - .withSender(localEp)
105 - .withType(type)
106 - .withPayload(payload)
107 - .build();
108 - sendAsync(ep, message);
109 - }
110 -
111 - protected void sendAsync(Endpoint ep, InternalMessage message) throws IOException {
112 - Channel channel = null;
113 - try {
114 - channel = channels.borrowObject(ep);
115 - channel.eventLoop().execute(new WriteTask(channel, message));
116 - } catch (Exception e) {
117 - throw new IOException(e);
118 - } finally {
119 - try {
120 - channels.returnObject(ep, channel);
121 - } catch (Exception e) {
122 - log.warn("Error returning object back to the pool", e);
123 - // ignored.
124 - }
125 - }
126 - }
127 -
128 - @Override
129 - public <T> Response<T> sendAndReceive(Endpoint ep, String type, Object payload)
130 - throws IOException {
131 - AsyncResponse<T> futureResponse = new AsyncResponse<T>();
132 - Long messageId = RandomUtils.nextLong();
133 - responseFutures.put(messageId, futureResponse);
134 - InternalMessage message = new InternalMessage.Builder(this)
135 - .withId(messageId)
136 - .withSender(localEp)
137 - .withType(type)
138 - .withPayload(payload)
139 - .build();
140 - sendAsync(ep, message);
141 - return futureResponse;
142 - }
143 -
144 - @Override
145 - public void registerHandler(String type, MessageHandler handler) {
146 - // TODO: Is this the right semantics for handler registration?
147 - handlers.putIfAbsent(type, handler);
148 - }
149 -
150 - public void unregisterHandler(String type) {
151 - handlers.remove(type);
152 - }
153 -
154 - private MessageHandler getMessageHandler(String type) {
155 - return handlers.get(type);
156 - }
157 -
158 - private void startAcceptingConnections() throws InterruptedException {
159 - ServerBootstrap b = new ServerBootstrap();
160 - b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
161 - b.group(bossGroup, workerGroup)
162 - .channel(NioServerSocketChannel.class)
163 - .childHandler(new OnosCommunicationChannelInitializer())
164 - .option(ChannelOption.SO_BACKLOG, 128)
165 - .childOption(ChannelOption.SO_KEEPALIVE, true);
166 -
167 - // Bind and start to accept incoming connections.
168 - b.bind(port).sync();
169 - }
170 -
171 - private class OnosCommunicationChannelFactory
172 - implements KeyedPoolableObjectFactory<Endpoint, Channel> {
173 -
174 - @Override
175 - public void activateObject(Endpoint endpoint, Channel channel)
176 - throws Exception {
177 - }
178 -
179 - @Override
180 - public void destroyObject(Endpoint ep, Channel channel) throws Exception {
181 - channel.close();
182 - }
183 -
184 - @Override
185 - public Channel makeObject(Endpoint ep) throws Exception {
186 - Bootstrap b = new Bootstrap();
187 - b.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
188 - b.group(workerGroup);
189 - // TODO: Make this faster:
190 - // http://normanmaurer.me/presentations/2014-facebook-eng-netty/slides.html#37.0
191 - b.channel(NioSocketChannel.class);
192 - b.option(ChannelOption.SO_KEEPALIVE, true);
193 - b.handler(new OnosCommunicationChannelInitializer());
194 -
195 - // Start the client.
196 - ChannelFuture f = b.connect(ep.host(), ep.port()).sync();
197 - return f.channel();
198 - }
199 -
200 - @Override
201 - public void passivateObject(Endpoint ep, Channel channel)
202 - throws Exception {
203 - }
204 -
205 - @Override
206 - public boolean validateObject(Endpoint ep, Channel channel) {
207 - return channel.isOpen();
208 - }
209 - }
210 -
211 - private class OnosCommunicationChannelInitializer extends ChannelInitializer<SocketChannel> {
212 -
213 - @Override
214 - protected void initChannel(SocketChannel channel) throws Exception {
215 - channel.pipeline()
216 - .addLast(new MessageEncoder(serializationService))
217 - .addLast(new MessageDecoder(NettyMessagingService.this, serializationService))
218 - .addLast(new NettyMessagingService.InboundMessageDispatcher());
219 - }
220 - }
221 -
222 - private class WriteTask implements Runnable {
223 -
224 - private final Object message;
225 - private final Channel channel;
226 -
227 - public WriteTask(Channel channel, Object message) {
228 - this.message = message;
229 - this.channel = channel;
230 - }
231 -
232 - @Override
233 - public void run() {
234 - channel.writeAndFlush(message);
235 - }
236 - }
237 -
238 - private class InboundMessageDispatcher extends SimpleChannelInboundHandler<InternalMessage> {
239 -
240 - @Override
241 - protected void channelRead0(ChannelHandlerContext ctx, InternalMessage message) throws Exception {
242 - String type = message.type();
243 - if (type.equals(InternalMessage.REPLY_MESSAGE_TYPE)) {
244 - try {
245 - AsyncResponse<?> futureResponse =
246 - NettyMessagingService.this.responseFutures.getIfPresent(message.id());
247 - if (futureResponse != null) {
248 - futureResponse.setResponse(message.payload());
249 - }
250 - log.warn("Received a reply. But was unable to locate the request handle");
251 - } finally {
252 - NettyMessagingService.this.responseFutures.invalidate(message.id());
253 - }
254 - return;
255 - }
256 - MessageHandler handler = NettyMessagingService.this.getMessageHandler(type);
257 - handler.handle(message);
258 - }
259 - }
260 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import java.util.concurrent.TimeUnit;
4 -
5 -import org.onlab.onos.store.cluster.impl.MessageSerializer;
6 -import org.onlab.onos.store.messaging.Endpoint;
7 -import org.onlab.onos.store.messaging.Response;
8 -
9 -public final class SimpleClient {
10 - private SimpleClient() {}
11 -
12 - public static void main(String... args) throws Exception {
13 - NettyMessagingService messaging = new TestNettyMessagingService(9081);
14 - messaging.activate();
15 -
16 - messaging.sendAsync(new Endpoint("localhost", 8080), "simple", "Hello World");
17 - Response<String> response = messaging.sendAndReceive(new Endpoint("localhost", 8080), "echo", "Hello World");
18 - System.out.println("Got back:" + response.get(2, TimeUnit.SECONDS));
19 - }
20 -
21 - public static class TestNettyMessagingService extends NettyMessagingService {
22 - public TestNettyMessagingService(int port) throws Exception {
23 - super(port);
24 - MessageSerializer mgr = new MessageSerializer();
25 - mgr.activate();
26 - this.serializationService = mgr;
27 - }
28 - }
29 -}
1 -package org.onlab.onos.store.messaging.impl;
2 -
3 -import org.onlab.onos.store.cluster.impl.MessageSerializer;
4 -
5 -public final class SimpleServer {
6 - private SimpleServer() {}
7 -
8 - public static void main(String... args) throws Exception {
9 - NettyMessagingService server = new TestNettyMessagingService();
10 - server.activate();
11 - server.registerHandler("simple", new LoggingHandler());
12 - server.registerHandler("echo", new EchoHandler());
13 - }
14 -
15 - public static class TestNettyMessagingService extends NettyMessagingService {
16 - protected TestNettyMessagingService() {
17 - MessageSerializer mgr = new MessageSerializer();
18 - mgr.activate();
19 - this.serializationService = mgr;
20 - }
21 - }
22 -}
...@@ -7,7 +7,7 @@ import org.junit.Test; ...@@ -7,7 +7,7 @@ import org.junit.Test;
7 import org.onlab.onos.cluster.DefaultControllerNode; 7 import org.onlab.onos.cluster.DefaultControllerNode;
8 import org.onlab.onos.cluster.NodeId; 8 import org.onlab.onos.cluster.NodeId;
9 import org.onlab.onos.store.cluster.messaging.impl.OnosClusterCommunicationManager; 9 import org.onlab.onos.store.cluster.messaging.impl.OnosClusterCommunicationManager;
10 -import org.onlab.onos.store.messaging.impl.NettyMessagingService; 10 +import org.onlab.netty.NettyMessagingService;
11 import org.onlab.packet.IpPrefix; 11 import org.onlab.packet.IpPrefix;
12 12
13 import java.util.concurrent.CountDownLatch; 13 import java.util.concurrent.CountDownLatch;
......
...@@ -9,6 +9,8 @@ import org.apache.felix.scr.annotations.Activate; ...@@ -9,6 +9,8 @@ import org.apache.felix.scr.annotations.Activate;
9 import org.apache.felix.scr.annotations.Component; 9 import org.apache.felix.scr.annotations.Component;
10 import org.apache.felix.scr.annotations.Deactivate; 10 import org.apache.felix.scr.annotations.Deactivate;
11 import org.apache.felix.scr.annotations.Service; 11 import org.apache.felix.scr.annotations.Service;
12 +import org.onlab.onos.net.Annotations;
13 +import org.onlab.onos.net.DefaultAnnotations;
12 import org.onlab.onos.net.DefaultDevice; 14 import org.onlab.onos.net.DefaultDevice;
13 import org.onlab.onos.net.DefaultPort; 15 import org.onlab.onos.net.DefaultPort;
14 import org.onlab.onos.net.Device; 16 import org.onlab.onos.net.Device;
...@@ -16,6 +18,9 @@ import org.onlab.onos.net.Device.Type; ...@@ -16,6 +18,9 @@ import org.onlab.onos.net.Device.Type;
16 import org.onlab.onos.net.DeviceId; 18 import org.onlab.onos.net.DeviceId;
17 import org.onlab.onos.net.Port; 19 import org.onlab.onos.net.Port;
18 import org.onlab.onos.net.PortNumber; 20 import org.onlab.onos.net.PortNumber;
21 +import org.onlab.onos.net.SparseAnnotations;
22 +import org.onlab.onos.net.device.DefaultDeviceDescription;
23 +import org.onlab.onos.net.device.DefaultPortDescription;
19 import org.onlab.onos.net.device.DeviceDescription; 24 import org.onlab.onos.net.device.DeviceDescription;
20 import org.onlab.onos.net.device.DeviceEvent; 25 import org.onlab.onos.net.device.DeviceEvent;
21 import org.onlab.onos.net.device.DeviceStore; 26 import org.onlab.onos.net.device.DeviceStore;
...@@ -45,6 +50,7 @@ import static com.google.common.base.Predicates.notNull; ...@@ -45,6 +50,7 @@ import static com.google.common.base.Predicates.notNull;
45 import static org.onlab.onos.net.device.DeviceEvent.Type.*; 50 import static org.onlab.onos.net.device.DeviceEvent.Type.*;
46 import static org.slf4j.LoggerFactory.getLogger; 51 import static org.slf4j.LoggerFactory.getLogger;
47 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked; 52 import static org.apache.commons.lang3.concurrent.ConcurrentUtils.createIfAbsentUnchecked;
53 +import static org.onlab.onos.net.DefaultAnnotations.merge;
48 54
49 // TODO: synchronization should be done in more fine-grained manner. 55 // TODO: synchronization should be done in more fine-grained manner.
50 /** 56 /**
...@@ -112,8 +118,8 @@ public class SimpleDeviceStore ...@@ -112,8 +118,8 @@ public class SimpleDeviceStore
112 = createIfAbsentUnchecked(providerDescs, providerId, 118 = createIfAbsentUnchecked(providerDescs, providerId,
113 new InitDeviceDescs(deviceDescription)); 119 new InitDeviceDescs(deviceDescription));
114 120
121 + // update description
115 descs.putDeviceDesc(deviceDescription); 122 descs.putDeviceDesc(deviceDescription);
116 -
117 Device newDevice = composeDevice(deviceId, providerDescs); 123 Device newDevice = composeDevice(deviceId, providerDescs);
118 124
119 if (oldDevice == null) { 125 if (oldDevice == null) {
...@@ -144,7 +150,8 @@ public class SimpleDeviceStore ...@@ -144,7 +150,8 @@ public class SimpleDeviceStore
144 150
145 // We allow only certain attributes to trigger update 151 // We allow only certain attributes to trigger update
146 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) || 152 if (!Objects.equals(oldDevice.hwVersion(), newDevice.hwVersion()) ||
147 - !Objects.equals(oldDevice.swVersion(), newDevice.swVersion())) { 153 + !Objects.equals(oldDevice.swVersion(), newDevice.swVersion()) ||
154 + !isAnnotationsEqual(oldDevice.annotations(), newDevice.annotations())) {
148 155
149 synchronized (this) { 156 synchronized (this) {
150 devices.replace(newDevice.id(), oldDevice, newDevice); 157 devices.replace(newDevice.id(), oldDevice, newDevice);
...@@ -203,7 +210,7 @@ public class SimpleDeviceStore ...@@ -203,7 +210,7 @@ public class SimpleDeviceStore
203 PortNumber number = portDescription.portNumber(); 210 PortNumber number = portDescription.portNumber();
204 Port oldPort = ports.get(number); 211 Port oldPort = ports.get(number);
205 // update description 212 // update description
206 - descs.putPortDesc(number, portDescription); 213 + descs.putPortDesc(portDescription);
207 Port newPort = composePort(device, number, descsMap); 214 Port newPort = composePort(device, number, descsMap);
208 215
209 events.add(oldPort == null ? 216 events.add(oldPort == null ?
...@@ -225,12 +232,14 @@ public class SimpleDeviceStore ...@@ -225,12 +232,14 @@ public class SimpleDeviceStore
225 return new DeviceEvent(PORT_ADDED, device, newPort); 232 return new DeviceEvent(PORT_ADDED, device, newPort);
226 } 233 }
227 234
228 - // CHecks if the specified port requires update and if so, it replaces the 235 + // Checks if the specified port requires update and if so, it replaces the
229 // existing entry in the map and returns corresponding event. 236 // existing entry in the map and returns corresponding event.
230 private DeviceEvent updatePort(Device device, Port oldPort, 237 private DeviceEvent updatePort(Device device, Port oldPort,
231 Port newPort, 238 Port newPort,
232 ConcurrentMap<PortNumber, Port> ports) { 239 ConcurrentMap<PortNumber, Port> ports) {
233 - if (oldPort.isEnabled() != newPort.isEnabled()) { 240 + if (oldPort.isEnabled() != newPort.isEnabled() ||
241 + !isAnnotationsEqual(oldPort.annotations(), newPort.annotations())) {
242 +
234 ports.put(oldPort.number(), newPort); 243 ports.put(oldPort.number(), newPort);
235 return new DeviceEvent(PORT_UPDATED, device, newPort); 244 return new DeviceEvent(PORT_UPDATED, device, newPort);
236 } 245 }
...@@ -272,17 +281,17 @@ public class SimpleDeviceStore ...@@ -272,17 +281,17 @@ public class SimpleDeviceStore
272 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId); 281 checkArgument(descsMap != null, DEVICE_NOT_FOUND, deviceId);
273 282
274 DeviceDescriptions descs = descsMap.get(providerId); 283 DeviceDescriptions descs = descsMap.get(providerId);
284 + // assuming all providers must to give DeviceDescription
275 checkArgument(descs != null, 285 checkArgument(descs != null,
276 "Device description for Device ID %s from Provider %s was not found", 286 "Device description for Device ID %s from Provider %s was not found",
277 deviceId, providerId); 287 deviceId, providerId);
278 288
279 - // TODO: implement multi-provider
280 synchronized (this) { 289 synchronized (this) {
281 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId); 290 ConcurrentMap<PortNumber, Port> ports = getPortMap(deviceId);
282 final PortNumber number = portDescription.portNumber(); 291 final PortNumber number = portDescription.portNumber();
283 Port oldPort = ports.get(number); 292 Port oldPort = ports.get(number);
284 // update description 293 // update description
285 - descs.putPortDesc(number, portDescription); 294 + descs.putPortDesc(portDescription);
286 Port newPort = composePort(device, number, descsMap); 295 Port newPort = composePort(device, number, descsMap);
287 if (oldPort == null) { 296 if (oldPort == null) {
288 return createPort(device, newPort, ports); 297 return createPort(device, newPort, ports);
...@@ -321,6 +330,26 @@ public class SimpleDeviceStore ...@@ -321,6 +330,26 @@ public class SimpleDeviceStore
321 } 330 }
322 } 331 }
323 332
333 + private static boolean isAnnotationsEqual(Annotations lhs, Annotations rhs) {
334 + if (lhs == rhs) {
335 + return true;
336 + }
337 + if (lhs == null || rhs == null) {
338 + return false;
339 + }
340 +
341 + if (!lhs.keys().equals(rhs.keys())) {
342 + return false;
343 + }
344 +
345 + for (String key : lhs.keys()) {
346 + if (!lhs.value(key).equals(rhs.value(key))) {
347 + return false;
348 + }
349 + }
350 + return true;
351 + }
352 +
324 /** 353 /**
325 * Returns a Device, merging description given from multiple Providers. 354 * Returns a Device, merging description given from multiple Providers.
326 * 355 *
...@@ -336,46 +365,67 @@ public class SimpleDeviceStore ...@@ -336,46 +365,67 @@ public class SimpleDeviceStore
336 ProviderId primary = pickPrimaryPID(providerDescs); 365 ProviderId primary = pickPrimaryPID(providerDescs);
337 366
338 DeviceDescriptions desc = providerDescs.get(primary); 367 DeviceDescriptions desc = providerDescs.get(primary);
368 +
369 + // base
339 Type type = desc.getDeviceDesc().type(); 370 Type type = desc.getDeviceDesc().type();
340 String manufacturer = desc.getDeviceDesc().manufacturer(); 371 String manufacturer = desc.getDeviceDesc().manufacturer();
341 String hwVersion = desc.getDeviceDesc().hwVersion(); 372 String hwVersion = desc.getDeviceDesc().hwVersion();
342 String swVersion = desc.getDeviceDesc().swVersion(); 373 String swVersion = desc.getDeviceDesc().swVersion();
343 String serialNumber = desc.getDeviceDesc().serialNumber(); 374 String serialNumber = desc.getDeviceDesc().serialNumber();
375 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
376 + annotations = merge(annotations, desc.getDeviceDesc().annotations());
344 377
345 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { 378 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
346 if (e.getKey().equals(primary)) { 379 if (e.getKey().equals(primary)) {
347 continue; 380 continue;
348 } 381 }
349 - // FIXME: implement attribute merging once we have K-V attributes 382 + // TODO: should keep track of Description timestamp
383 + // and only merge conflicting keys when timestamp is newer
384 + // Currently assuming there will never be a key conflict between
385 + // providers
386 +
387 + // annotation merging. not so efficient, should revisit later
388 + annotations = merge(annotations, e.getValue().getDeviceDesc().annotations());
350 } 389 }
351 390
352 - return new DefaultDevice(primary, deviceId , type, manufacturer, hwVersion, swVersion, serialNumber); 391 + return new DefaultDevice(primary, deviceId , type, manufacturer,
392 + hwVersion, swVersion, serialNumber, annotations);
353 } 393 }
354 394
355 - // probably want composePorts 395 + // probably want composePort"s" also
356 private Port composePort(Device device, PortNumber number, 396 private Port composePort(Device device, PortNumber number,
357 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) { 397 ConcurrentMap<ProviderId, DeviceDescriptions> providerDescs) {
358 398
359 ProviderId primary = pickPrimaryPID(providerDescs); 399 ProviderId primary = pickPrimaryPID(providerDescs);
360 DeviceDescriptions primDescs = providerDescs.get(primary); 400 DeviceDescriptions primDescs = providerDescs.get(primary);
401 + // if no primary, assume not enabled
402 + // TODO: revisit this default port enabled/disabled behavior
403 + boolean isEnabled = false;
404 + DefaultAnnotations annotations = DefaultAnnotations.builder().build();
405 +
361 final PortDescription portDesc = primDescs.getPortDesc(number); 406 final PortDescription portDesc = primDescs.getPortDesc(number);
362 - boolean isEnabled;
363 if (portDesc != null) { 407 if (portDesc != null) {
364 isEnabled = portDesc.isEnabled(); 408 isEnabled = portDesc.isEnabled();
365 - } else { 409 + annotations = merge(annotations, portDesc.annotations());
366 - // if no primary, assume not enabled
367 - // TODO: revisit this port enabled/disabled behavior
368 - isEnabled = false;
369 } 410 }
370 411
371 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) { 412 for (Entry<ProviderId, DeviceDescriptions> e : providerDescs.entrySet()) {
372 if (e.getKey().equals(primary)) { 413 if (e.getKey().equals(primary)) {
373 continue; 414 continue;
374 } 415 }
375 - // FIXME: implement attribute merging once we have K-V attributes 416 + // TODO: should keep track of Description timestamp
417 + // and only merge conflicting keys when timestamp is newer
418 + // Currently assuming there will never be a key conflict between
419 + // providers
420 +
421 + // annotation merging. not so efficient, should revisit later
422 + final PortDescription otherPortDesc = e.getValue().getPortDesc(number);
423 + if (otherPortDesc != null) {
424 + annotations = merge(annotations, otherPortDesc.annotations());
425 + }
376 } 426 }
377 427
378 - return new DefaultPort(device, number, isEnabled); 428 + return new DefaultPort(device, number, isEnabled, annotations);
379 } 429 }
380 430
381 /** 431 /**
...@@ -428,7 +478,7 @@ public class SimpleDeviceStore ...@@ -428,7 +478,7 @@ public class SimpleDeviceStore
428 private final ConcurrentMap<PortNumber, PortDescription> portDescs; 478 private final ConcurrentMap<PortNumber, PortDescription> portDescs;
429 479
430 public DeviceDescriptions(DeviceDescription desc) { 480 public DeviceDescriptions(DeviceDescription desc) {
431 - this.deviceDesc = new AtomicReference<>(desc); 481 + this.deviceDesc = new AtomicReference<>(checkNotNull(desc));
432 this.portDescs = new ConcurrentHashMap<>(); 482 this.portDescs = new ConcurrentHashMap<>();
433 } 483 }
434 484
...@@ -444,12 +494,38 @@ public class SimpleDeviceStore ...@@ -444,12 +494,38 @@ public class SimpleDeviceStore
444 return Collections.unmodifiableCollection(portDescs.values()); 494 return Collections.unmodifiableCollection(portDescs.values());
445 } 495 }
446 496
447 - public DeviceDescription putDeviceDesc(DeviceDescription newDesc) { 497 + /**
448 - return deviceDesc.getAndSet(newDesc); 498 + * Puts DeviceDescription, merging annotations as necessary.
499 + *
500 + * @param newDesc new DeviceDescription
501 + * @return previous DeviceDescription
502 + */
503 + public synchronized DeviceDescription putDeviceDesc(DeviceDescription newDesc) {
504 + DeviceDescription oldOne = deviceDesc.get();
505 + DeviceDescription newOne = newDesc;
506 + if (oldOne != null) {
507 + SparseAnnotations merged = merge(oldOne.annotations(),
508 + newDesc.annotations());
509 + newOne = new DefaultDeviceDescription(newOne, merged);
510 + }
511 + return deviceDesc.getAndSet(newOne);
449 } 512 }
450 513
451 - public PortDescription putPortDesc(PortNumber number, PortDescription newDesc) { 514 + /**
452 - return portDescs.put(number, newDesc); 515 + * Puts PortDescription, merging annotations as necessary.
516 + *
517 + * @param newDesc new PortDescription
518 + * @return previous PortDescription
519 + */
520 + public synchronized PortDescription putPortDesc(PortDescription newDesc) {
521 + PortDescription oldOne = portDescs.get(newDesc.portNumber());
522 + PortDescription newOne = newDesc;
523 + if (oldOne != null) {
524 + SparseAnnotations merged = merge(oldOne.annotations(),
525 + newDesc.annotations());
526 + newOne = new DefaultPortDescription(newOne, merged);
527 + }
528 + return portDescs.put(newOne.portNumber(), newOne);
453 } 529 }
454 } 530 }
455 } 531 }
......
...@@ -22,10 +22,13 @@ import org.junit.Before; ...@@ -22,10 +22,13 @@ import org.junit.Before;
22 import org.junit.BeforeClass; 22 import org.junit.BeforeClass;
23 import org.junit.Ignore; 23 import org.junit.Ignore;
24 import org.junit.Test; 24 import org.junit.Test;
25 +import org.onlab.onos.net.Annotations;
26 +import org.onlab.onos.net.DefaultAnnotations;
25 import org.onlab.onos.net.Device; 27 import org.onlab.onos.net.Device;
26 import org.onlab.onos.net.DeviceId; 28 import org.onlab.onos.net.DeviceId;
27 import org.onlab.onos.net.Port; 29 import org.onlab.onos.net.Port;
28 import org.onlab.onos.net.PortNumber; 30 import org.onlab.onos.net.PortNumber;
31 +import org.onlab.onos.net.SparseAnnotations;
29 import org.onlab.onos.net.device.DefaultDeviceDescription; 32 import org.onlab.onos.net.device.DefaultDeviceDescription;
30 import org.onlab.onos.net.device.DefaultPortDescription; 33 import org.onlab.onos.net.device.DefaultPortDescription;
31 import org.onlab.onos.net.device.DeviceDescription; 34 import org.onlab.onos.net.device.DeviceDescription;
...@@ -57,6 +60,23 @@ public class SimpleDeviceStoreTest { ...@@ -57,6 +60,23 @@ public class SimpleDeviceStoreTest {
57 private static final PortNumber P2 = PortNumber.portNumber(2); 60 private static final PortNumber P2 = PortNumber.portNumber(2);
58 private static final PortNumber P3 = PortNumber.portNumber(3); 61 private static final PortNumber P3 = PortNumber.portNumber(3);
59 62
63 + private static final SparseAnnotations A1 = DefaultAnnotations.builder()
64 + .set("A1", "a1")
65 + .set("B1", "b1")
66 + .build();
67 + private static final SparseAnnotations A1_2 = DefaultAnnotations.builder()
68 + .remove("A1")
69 + .set("B3", "b3")
70 + .build();
71 + private static final SparseAnnotations A2 = DefaultAnnotations.builder()
72 + .set("A2", "a2")
73 + .set("B2", "b2")
74 + .build();
75 + private static final SparseAnnotations A2_2 = DefaultAnnotations.builder()
76 + .remove("A2")
77 + .set("B4", "b4")
78 + .build();
79 +
60 private SimpleDeviceStore simpleDeviceStore; 80 private SimpleDeviceStore simpleDeviceStore;
61 private DeviceStore deviceStore; 81 private DeviceStore deviceStore;
62 82
...@@ -106,6 +126,24 @@ public class SimpleDeviceStoreTest { ...@@ -106,6 +126,24 @@ public class SimpleDeviceStoreTest {
106 assertEquals(SN, device.serialNumber()); 126 assertEquals(SN, device.serialNumber());
107 } 127 }
108 128
129 + /**
130 + * Verifies that Annotations created by merging {@code annotations} is
131 + * equal to actual Annotations.
132 + *
133 + * @param actual Annotations to check
134 + * @param annotations
135 + */
136 + private static void assertAnnotationsEquals(Annotations actual, SparseAnnotations... annotations) {
137 + DefaultAnnotations expected = DefaultAnnotations.builder().build();
138 + for (SparseAnnotations a : annotations) {
139 + expected = DefaultAnnotations.merge(expected, a);
140 + }
141 + assertEquals(expected.keys(), actual.keys());
142 + for (String key : expected.keys()) {
143 + assertEquals(expected.value(key), actual.value(key));
144 + }
145 + }
146 +
109 @Test 147 @Test
110 public final void testGetDeviceCount() { 148 public final void testGetDeviceCount() {
111 assertEquals("initialy empty", 0, deviceStore.getDeviceCount()); 149 assertEquals("initialy empty", 0, deviceStore.getDeviceCount());
...@@ -171,26 +209,41 @@ public class SimpleDeviceStoreTest { ...@@ -171,26 +209,41 @@ public class SimpleDeviceStoreTest {
171 public final void testCreateOrUpdateDeviceAncillary() { 209 public final void testCreateOrUpdateDeviceAncillary() {
172 DeviceDescription description = 210 DeviceDescription description =
173 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 211 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
174 - HW, SW1, SN); 212 + HW, SW1, SN, A2);
175 DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description); 213 DeviceEvent event = deviceStore.createOrUpdateDevice(PIDA, DID1, description);
176 assertEquals(DEVICE_ADDED, event.type()); 214 assertEquals(DEVICE_ADDED, event.type());
177 assertDevice(DID1, SW1, event.subject()); 215 assertDevice(DID1, SW1, event.subject());
178 assertEquals(PIDA, event.subject().providerId()); 216 assertEquals(PIDA, event.subject().providerId());
217 + assertAnnotationsEquals(event.subject().annotations(), A2);
179 assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1)); 218 assertFalse("Ancillary will not bring device up", deviceStore.isAvailable(DID1));
180 219
181 DeviceDescription description2 = 220 DeviceDescription description2 =
182 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR, 221 new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
183 - HW, SW2, SN); 222 + HW, SW2, SN, A1);
184 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2); 223 DeviceEvent event2 = deviceStore.createOrUpdateDevice(PID, DID1, description2);
185 assertEquals(DEVICE_UPDATED, event2.type()); 224 assertEquals(DEVICE_UPDATED, event2.type());
186 assertDevice(DID1, SW2, event2.subject()); 225 assertDevice(DID1, SW2, event2.subject());
187 assertEquals(PID, event2.subject().providerId()); 226 assertEquals(PID, event2.subject().providerId());
227 + assertAnnotationsEquals(event2.subject().annotations(), A1, A2);
188 assertTrue(deviceStore.isAvailable(DID1)); 228 assertTrue(deviceStore.isAvailable(DID1));
189 229
190 assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2)); 230 assertNull("No change expected", deviceStore.createOrUpdateDevice(PID, DID1, description2));
191 231
192 // For now, Ancillary is ignored once primary appears 232 // For now, Ancillary is ignored once primary appears
193 assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description)); 233 assertNull("No change expected", deviceStore.createOrUpdateDevice(PIDA, DID1, description));
234 +
235 + // But, Ancillary annotations will be in effect
236 + DeviceDescription description3 =
237 + new DefaultDeviceDescription(DID1.uri(), SWITCH, MFR,
238 + HW, SW1, SN, A2_2);
239 + DeviceEvent event3 = deviceStore.createOrUpdateDevice(PIDA, DID1, description3);
240 + assertEquals(DEVICE_UPDATED, event3.type());
241 + // basic information will be the one from Primary
242 + assertDevice(DID1, SW2, event3.subject());
243 + assertEquals(PID, event3.subject().providerId());
244 + // but annotation from Ancillary will be merged
245 + assertAnnotationsEquals(event3.subject().annotations(), A1, A2, A2_2);
246 + assertTrue(deviceStore.isAvailable(DID1));
194 } 247 }
195 248
196 249
...@@ -299,27 +352,40 @@ public class SimpleDeviceStoreTest { ...@@ -299,27 +352,40 @@ public class SimpleDeviceStoreTest {
299 putDeviceAncillary(DID1, SW1); 352 putDeviceAncillary(DID1, SW1);
300 putDevice(DID1, SW1); 353 putDevice(DID1, SW1);
301 List<PortDescription> pds = Arrays.<PortDescription>asList( 354 List<PortDescription> pds = Arrays.<PortDescription>asList(
302 - new DefaultPortDescription(P1, true) 355 + new DefaultPortDescription(P1, true, A1)
303 ); 356 );
304 deviceStore.updatePorts(PID, DID1, pds); 357 deviceStore.updatePorts(PID, DID1, pds);
305 358
306 DeviceEvent event = deviceStore.updatePortStatus(PID, DID1, 359 DeviceEvent event = deviceStore.updatePortStatus(PID, DID1,
307 - new DefaultPortDescription(P1, false)); 360 + new DefaultPortDescription(P1, false, A1_2));
308 assertEquals(PORT_UPDATED, event.type()); 361 assertEquals(PORT_UPDATED, event.type());
309 assertDevice(DID1, SW1, event.subject()); 362 assertDevice(DID1, SW1, event.subject());
310 assertEquals(P1, event.port().number()); 363 assertEquals(P1, event.port().number());
364 + assertAnnotationsEquals(event.port().annotations(), A1, A1_2);
311 assertFalse("Port is disabled", event.port().isEnabled()); 365 assertFalse("Port is disabled", event.port().isEnabled());
312 366
313 DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1, 367 DeviceEvent event2 = deviceStore.updatePortStatus(PIDA, DID1,
314 new DefaultPortDescription(P1, true)); 368 new DefaultPortDescription(P1, true));
315 assertNull("Ancillary is ignored if primary exists", event2); 369 assertNull("Ancillary is ignored if primary exists", event2);
316 370
371 + // but, Ancillary annotation update will be notified
317 DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1, 372 DeviceEvent event3 = deviceStore.updatePortStatus(PIDA, DID1,
318 - new DefaultPortDescription(P2, true)); 373 + new DefaultPortDescription(P1, true, A2));
319 - assertEquals(PORT_ADDED, event3.type()); 374 + assertEquals(PORT_UPDATED, event3.type());
320 assertDevice(DID1, SW1, event3.subject()); 375 assertDevice(DID1, SW1, event3.subject());
321 - assertEquals(P2, event3.port().number()); 376 + assertEquals(P1, event3.port().number());
322 - assertFalse("Port is disabled if not given from provider", event3.port().isEnabled()); 377 + assertAnnotationsEquals(event3.port().annotations(), A1, A1_2, A2);
378 + assertFalse("Port is disabled", event3.port().isEnabled());
379 +
380 + // port only reported from Ancillary will be notified as down
381 + DeviceEvent event4 = deviceStore.updatePortStatus(PIDA, DID1,
382 + new DefaultPortDescription(P2, true));
383 + assertEquals(PORT_ADDED, event4.type());
384 + assertDevice(DID1, SW1, event4.subject());
385 + assertEquals(P2, event4.port().number());
386 + assertAnnotationsEquals(event4.port().annotations());
387 + assertFalse("Port is disabled if not given from primary provider",
388 + event4.port().isEnabled());
323 } 389 }
324 390
325 @Test 391 @Test
......
...@@ -48,6 +48,19 @@ ...@@ -48,6 +48,19 @@
48 </dependency> 48 </dependency>
49 49
50 <dependency> 50 <dependency>
51 + <groupId>org.hamcrest</groupId>
52 + <artifactId>hamcrest-core</artifactId>
53 + <version>1.3</version>
54 + <scope>test</scope>
55 + </dependency>
56 + <dependency>
57 + <groupId>org.hamcrest</groupId>
58 + <artifactId>hamcrest-library</artifactId>
59 + <version>1.3</version>
60 + <scope>test</scope>
61 + </dependency>
62 +
63 + <dependency>
51 <groupId>org.slf4j</groupId> 64 <groupId>org.slf4j</groupId>
52 <artifactId>slf4j-api</artifactId> 65 <artifactId>slf4j-api</artifactId>
53 <version>1.7.6</version> 66 <version>1.7.6</version>
...@@ -244,6 +257,14 @@ ...@@ -244,6 +257,14 @@
244 <artifactId>junit</artifactId> 257 <artifactId>junit</artifactId>
245 </dependency> 258 </dependency>
246 <dependency> 259 <dependency>
260 + <groupId>org.hamcrest</groupId>
261 + <artifactId>hamcrest-core</artifactId>
262 + </dependency>
263 + <dependency>
264 + <groupId>org.hamcrest</groupId>
265 + <artifactId>hamcrest-library</artifactId>
266 + </dependency>
267 + <dependency>
247 <groupId>org.slf4j</groupId> 268 <groupId>org.slf4j</groupId>
248 <artifactId>slf4j-jdk14</artifactId> 269 <artifactId>slf4j-jdk14</artifactId>
249 </dependency> 270 </dependency>
...@@ -320,6 +341,35 @@ ...@@ -320,6 +341,35 @@
320 </plugin> 341 </plugin>
321 342
322 <!-- TODO: add findbugs plugin for static code analysis; for explicit invocation only --> 343 <!-- TODO: add findbugs plugin for static code analysis; for explicit invocation only -->
344 + <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
345 + <plugin>
346 + <groupId>org.eclipse.m2e</groupId>
347 + <artifactId>lifecycle-mapping</artifactId>
348 + <version>1.0.0</version>
349 + <configuration>
350 + <lifecycleMappingMetadata>
351 + <pluginExecutions>
352 + <pluginExecution>
353 + <pluginExecutionFilter>
354 + <groupId>org.jacoco</groupId>
355 + <artifactId>
356 + jacoco-maven-plugin
357 + </artifactId>
358 + <versionRange>
359 + [0.7.1.201405082137,)
360 + </versionRange>
361 + <goals>
362 + <goal>prepare-agent</goal>
363 + </goals>
364 + </pluginExecutionFilter>
365 + <action>
366 + <ignore></ignore>
367 + </action>
368 + </pluginExecution>
369 + </pluginExecutions>
370 + </lifecycleMappingMetadata>
371 + </configuration>
372 + </plugin>
323 </plugins> 373 </plugins>
324 </pluginManagement> 374 </pluginManagement>
325 375
......
...@@ -6,5 +6,10 @@ ...@@ -6,5 +6,10 @@
6 <groupId>org.onlab.tools</groupId> 6 <groupId>org.onlab.tools</groupId>
7 <artifactId>onos-build-conf</artifactId> 7 <artifactId>onos-build-conf</artifactId>
8 <version>1.0</version> 8 <version>1.0</version>
9 +
10 + <properties>
11 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
12 + </properties>
13 +
9 </project> 14 </project>
10 15
......
1 +#!/bin/bash
2 +#-------------------------------------------------------------------------------
3 +# Remotely fetches the ONOS test VMs from a local share into ~/Downloads.
4 +#-------------------------------------------------------------------------------
5 +
6 +[ ! -d "$ONOS_ROOT" ] && echo "ONOS_ROOT is not defined" >&2 && exit 1
7 +. $ONOS_ROOT/tools/build/envDefaults
8 +
9 +mkdir -p /tmp/onos
10 +mount -t smbfs smb://guest:@10.254.1.15/onos /tmp/onos
11 +cp /tmp/onos/*.ova ~/Downloads
1 +<?xml version="1.0" encoding="UTF-8"?>
2 +<project xmlns="http://maven.apache.org/POM/4.0.0"
3 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
5 + <modelVersion>4.0.0</modelVersion>
6 +
7 + <parent>
8 + <groupId>org.onlab.onos</groupId>
9 + <artifactId>onlab-utils</artifactId>
10 + <version>1.0.0-SNAPSHOT</version>
11 + <relativePath>../pom.xml</relativePath>
12 + </parent>
13 +
14 + <artifactId>onlab-netty</artifactId>
15 + <packaging>bundle</packaging>
16 +
17 + <description>Network I/O using Netty framework</description>
18 +
19 + <dependencies>
20 + <dependency>
21 + <groupId>com.google.guava</groupId>
22 + <artifactId>guava-testlib</artifactId>
23 + <scope>test</scope>
24 + </dependency>
25 + <dependency>
26 + <groupId>org.onlab.onos</groupId>
27 + <artifactId>onlab-misc</artifactId>
28 + </dependency>
29 + <dependency>
30 + <groupId>org.onlab.onos</groupId>
31 + <artifactId>onlab-junit</artifactId>
32 + <scope>test</scope>
33 + </dependency>
34 + <dependency>
35 + <groupId>de.javakaffee</groupId>
36 + <artifactId>kryo-serializers</artifactId>
37 + </dependency>
38 + <dependency>
39 + <groupId>io.netty</groupId>
40 + <artifactId>netty-all</artifactId>
41 + </dependency>
42 + <dependency>
43 + <groupId>commons-pool</groupId>
44 + <artifactId>commons-pool</artifactId>
45 + <version>1.6</version>
46 + </dependency>
47 + </dependencies>
48 +
49 +</project>
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
19 <modules> 19 <modules>
20 <module>junit</module> 20 <module>junit</module>
21 <module>misc</module> 21 <module>misc</module>
22 + <module>netty</module>
22 <module>nio</module> 23 <module>nio</module>
23 <module>osgi</module> 24 <module>osgi</module>
24 <module>rest</module> 25 <module>rest</module>
......