Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
72 changed files
with
3162 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 | + | ||
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 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 | +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 & 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 | +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 org.junit.After; | ||
4 | +import org.junit.Before; | ||
5 | +import org.junit.Test; | ||
6 | + | ||
7 | +import java.util.ArrayList; | ||
8 | +import java.util.Arrays; | ||
9 | +import java.util.Collections; | ||
10 | +import java.util.Iterator; | ||
11 | +import java.util.List; | ||
12 | + | ||
13 | +import static org.onlab.onos.net.intent.IntentState.*; | ||
14 | +import static org.junit.Assert.*; | ||
15 | + | ||
16 | +// TODO: consider make it categorized as integration test when it become | ||
17 | +// slow test or fragile test | ||
18 | +/** | ||
19 | + * Suite of tests for the intent service contract. | ||
20 | + */ | ||
21 | +public class IntentServiceTest { | ||
22 | + | ||
23 | + public static final IntentId IID = new IntentId(123); | ||
24 | + public static final IntentId INSTALLABLE_IID = new IntentId(234); | ||
25 | + | ||
26 | + protected static final int GRACE_MS = 500; // millis | ||
27 | + | ||
28 | + protected TestableIntentService service; | ||
29 | + protected TestListener listener = new TestListener(); | ||
30 | + | ||
31 | + @Before | ||
32 | + public void setUp() { | ||
33 | + service = createIntentService(); | ||
34 | + service.addListener(listener); | ||
35 | + } | ||
36 | + | ||
37 | + @After | ||
38 | + public void tearDown() { | ||
39 | + service.removeListener(listener); | ||
40 | + } | ||
41 | + | ||
42 | + /** | ||
43 | + * Creates a service instance appropriately instrumented for testing. | ||
44 | + * | ||
45 | + * @return testable intent service | ||
46 | + */ | ||
47 | + protected TestableIntentService createIntentService() { | ||
48 | + return new FakeIntentManager(); | ||
49 | + } | ||
50 | + | ||
51 | + @Test | ||
52 | + public void basics() { | ||
53 | + // Make sure there are no intents | ||
54 | + assertEquals("incorrect intent count", 0, service.getIntents().size()); | ||
55 | + | ||
56 | + // Register a compiler and an installer both setup for success. | ||
57 | + service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID))); | ||
58 | + service.registerInstaller(TestInstallableIntent.class, new TestInstaller(false)); | ||
59 | + | ||
60 | + final Intent intent = new TestIntent(IID); | ||
61 | + service.submit(intent); | ||
62 | + | ||
63 | + // Allow a small window of time until the intent is in the expected state | ||
64 | + TestTools.assertAfter(GRACE_MS, new Runnable() { | ||
65 | + @Override | ||
66 | + public void run() { | ||
67 | + assertEquals("incorrect intent state", INSTALLED, | ||
68 | + service.getIntentState(intent.getId())); | ||
69 | + } | ||
70 | + }); | ||
71 | + | ||
72 | + // Make sure that all expected events have been emitted | ||
73 | + validateEvents(intent, SUBMITTED, COMPILED, INSTALLED); | ||
74 | + | ||
75 | + // Make sure there is just one intent (and is ours) | ||
76 | + assertEquals("incorrect intent count", 1, service.getIntents().size()); | ||
77 | + assertEquals("incorrect intent", intent, service.getIntent(intent.getId())); | ||
78 | + | ||
79 | + // Reset the listener events | ||
80 | + listener.events.clear(); | ||
81 | + | ||
82 | + // Now withdraw the intent | ||
83 | + service.withdraw(intent); | ||
84 | + | ||
85 | + // Allow a small window of time until the event is in the expected state | ||
86 | + TestTools.assertAfter(GRACE_MS, new Runnable() { | ||
87 | + @Override | ||
88 | + public void run() { | ||
89 | + assertEquals("incorrect intent state", WITHDRAWN, | ||
90 | + service.getIntentState(intent.getId())); | ||
91 | + } | ||
92 | + }); | ||
93 | + | ||
94 | + // Make sure that all expected events have been emitted | ||
95 | + validateEvents(intent, WITHDRAWING, WITHDRAWN); | ||
96 | + | ||
97 | + // TODO: discuss what is the fate of intents after they have been withdrawn | ||
98 | + // Make sure that the intent is no longer in the system | ||
99 | +// assertEquals("incorrect intent count", 0, service.getIntents().size()); | ||
100 | +// assertNull("intent should not be found", service.getIntent(intent.getId())); | ||
101 | +// assertNull("intent state should not be found", service.getIntentState(intent.getId())); | ||
102 | + } | ||
103 | + | ||
104 | + @Test | ||
105 | + public void failedCompilation() { | ||
106 | + // Register a compiler programmed for success | ||
107 | + service.registerCompiler(TestIntent.class, new TestCompiler(true)); | ||
108 | + | ||
109 | + // Submit an intent | ||
110 | + final Intent intent = new TestIntent(IID); | ||
111 | + service.submit(intent); | ||
112 | + | ||
113 | + // Allow a small window of time until the intent is in the expected state | ||
114 | + TestTools.assertAfter(GRACE_MS, new Runnable() { | ||
115 | + @Override | ||
116 | + public void run() { | ||
117 | + assertEquals("incorrect intent state", FAILED, | ||
118 | + service.getIntentState(intent.getId())); | ||
119 | + } | ||
120 | + }); | ||
121 | + | ||
122 | + // Make sure that all expected events have been emitted | ||
123 | + validateEvents(intent, SUBMITTED, FAILED); | ||
124 | + } | ||
125 | + | ||
126 | + @Test | ||
127 | + public void failedInstallation() { | ||
128 | + // Register a compiler programmed for success and installer for failure | ||
129 | + service.registerCompiler(TestIntent.class, new TestCompiler(new TestInstallableIntent(INSTALLABLE_IID))); | ||
130 | + service.registerInstaller(TestInstallableIntent.class, new TestInstaller(true)); | ||
131 | + | ||
132 | + // Submit an intent | ||
133 | + final Intent intent = new TestIntent(IID); | ||
134 | + service.submit(intent); | ||
135 | + | ||
136 | + // Allow a small window of time until the intent is in the expected state | ||
137 | + TestTools.assertAfter(GRACE_MS, new Runnable() { | ||
138 | + @Override | ||
139 | + public void run() { | ||
140 | + assertEquals("incorrect intent state", FAILED, | ||
141 | + service.getIntentState(intent.getId())); | ||
142 | + } | ||
143 | + }); | ||
144 | + | ||
145 | + // Make sure that all expected events have been emitted | ||
146 | + validateEvents(intent, SUBMITTED, COMPILED, FAILED); | ||
147 | + } | ||
148 | + | ||
149 | + /** | ||
150 | + * Validates that the test event listener has received the following events | ||
151 | + * for the specified intent. Events received for other intents will not be | ||
152 | + * considered. | ||
153 | + * | ||
154 | + * @param intent intent subject | ||
155 | + * @param states list of states for which events are expected | ||
156 | + */ | ||
157 | + protected void validateEvents(Intent intent, IntentState... states) { | ||
158 | + Iterator<IntentEvent> events = listener.events.iterator(); | ||
159 | + for (IntentState state : states) { | ||
160 | + IntentEvent event = events.hasNext() ? events.next() : null; | ||
161 | + if (event == null) { | ||
162 | + fail("expected event not found: " + state); | ||
163 | + } else if (intent.equals(event.getIntent())) { | ||
164 | + assertEquals("incorrect state", state, event.getState()); | ||
165 | + } | ||
166 | + } | ||
167 | + | ||
168 | + // Remainder of events should not apply to this intent; make sure. | ||
169 | + while (events.hasNext()) { | ||
170 | + assertFalse("unexpected event for intent", | ||
171 | + intent.equals(events.next().getIntent())); | ||
172 | + } | ||
173 | + } | ||
174 | + | ||
175 | + @Test | ||
176 | + public void compilerBasics() { | ||
177 | + // Make sure there are no compilers | ||
178 | + assertEquals("incorrect compiler count", 0, service.getCompilers().size()); | ||
179 | + | ||
180 | + // Add a compiler and make sure that it appears in the map | ||
181 | + IntentCompiler<TestIntent> compiler = new TestCompiler(false); | ||
182 | + service.registerCompiler(TestIntent.class, compiler); | ||
183 | + assertEquals("incorrect compiler", compiler, | ||
184 | + service.getCompilers().get(TestIntent.class)); | ||
185 | + | ||
186 | + // Remove the same and make sure that it no longer appears in the map | ||
187 | + service.unregisterCompiler(TestIntent.class); | ||
188 | + assertNull("compiler should not be registered", | ||
189 | + service.getCompilers().get(TestIntent.class)); | ||
190 | + } | ||
191 | + | ||
192 | + @Test | ||
193 | + public void installerBasics() { | ||
194 | + // Make sure there are no installers | ||
195 | + assertEquals("incorrect installer count", 0, service.getInstallers().size()); | ||
196 | + | ||
197 | + // Add an installer and make sure that it appears in the map | ||
198 | + IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false); | ||
199 | + service.registerInstaller(TestInstallableIntent.class, installer); | ||
200 | + assertEquals("incorrect installer", installer, | ||
201 | + service.getInstallers().get(TestInstallableIntent.class)); | ||
202 | + | ||
203 | + // Remove the same and make sure that it no longer appears in the map | ||
204 | + service.unregisterInstaller(TestInstallableIntent.class); | ||
205 | + assertNull("installer should not be registered", | ||
206 | + service.getInstallers().get(TestInstallableIntent.class)); | ||
207 | + } | ||
208 | + | ||
209 | + @Test | ||
210 | + public void implicitRegistration() { | ||
211 | + // Add a compiler and make sure that it appears in the map | ||
212 | + IntentCompiler<TestIntent> compiler = new TestCompiler(new TestSubclassInstallableIntent(INSTALLABLE_IID)); | ||
213 | + service.registerCompiler(TestIntent.class, compiler); | ||
214 | + assertEquals("incorrect compiler", compiler, | ||
215 | + service.getCompilers().get(TestIntent.class)); | ||
216 | + | ||
217 | + // Add a installer and make sure that it appears in the map | ||
218 | + IntentInstaller<TestInstallableIntent> installer = new TestInstaller(false); | ||
219 | + service.registerInstaller(TestInstallableIntent.class, installer); | ||
220 | + assertEquals("incorrect installer", installer, | ||
221 | + service.getInstallers().get(TestInstallableIntent.class)); | ||
222 | + | ||
223 | + | ||
224 | + // Submit an intent which is a subclass of the one we registered | ||
225 | + final Intent intent = new TestSubclassIntent(IID); | ||
226 | + service.submit(intent); | ||
227 | + | ||
228 | + // Allow some time for the intent to be compiled and installed | ||
229 | + TestTools.assertAfter(GRACE_MS, new Runnable() { | ||
230 | + @Override | ||
231 | + public void run() { | ||
232 | + assertEquals("incorrect intent state", INSTALLED, | ||
233 | + service.getIntentState(intent.getId())); | ||
234 | + } | ||
235 | + }); | ||
236 | + | ||
237 | + // Make sure that now we have an implicit registration of the compiler | ||
238 | + // under the intent subclass | ||
239 | + assertEquals("incorrect compiler", compiler, | ||
240 | + service.getCompilers().get(TestSubclassIntent.class)); | ||
241 | + | ||
242 | + // Make sure that now we have an implicit registration of the installer | ||
243 | + // under the intent subclass | ||
244 | + assertEquals("incorrect installer", installer, | ||
245 | + service.getInstallers().get(TestSubclassInstallableIntent.class)); | ||
246 | + | ||
247 | + // TODO: discuss whether or if implicit registration should require implicit unregistration | ||
248 | + // perhaps unregister by compiler or installer itself, rather than by class would be better | ||
249 | + } | ||
250 | + | ||
251 | + | ||
252 | + // Fixture to track emitted intent events | ||
253 | + protected class TestListener implements IntentEventListener { | ||
254 | + final List<IntentEvent> events = new ArrayList<>(); | ||
255 | + | ||
256 | + @Override | ||
257 | + public void event(IntentEvent event) { | ||
258 | + events.add(event); | ||
259 | + } | ||
260 | + } | ||
261 | + | ||
262 | + // Controllable compiler | ||
263 | + private class TestCompiler implements IntentCompiler<TestIntent> { | ||
264 | + private final boolean fail; | ||
265 | + private final List<Intent> result; | ||
266 | + | ||
267 | + TestCompiler(boolean fail) { | ||
268 | + this.fail = fail; | ||
269 | + this.result = Collections.emptyList(); | ||
270 | + } | ||
271 | + | ||
272 | + TestCompiler(Intent... result) { | ||
273 | + this.fail = false; | ||
274 | + this.result = Arrays.asList(result); | ||
275 | + } | ||
276 | + | ||
277 | + @Override | ||
278 | + public List<Intent> compile(TestIntent intent) { | ||
279 | + if (fail) { | ||
280 | + throw new IntentException("compile failed by design"); | ||
281 | + } | ||
282 | + List<Intent> compiled = new ArrayList<>(result); | ||
283 | + return compiled; | ||
284 | + } | ||
285 | + } | ||
286 | + | ||
287 | + // Controllable installer | ||
288 | + private class TestInstaller implements IntentInstaller<TestInstallableIntent> { | ||
289 | + private final boolean fail; | ||
290 | + | ||
291 | + TestInstaller(boolean fail) { | ||
292 | + this.fail = fail; | ||
293 | + } | ||
294 | + | ||
295 | + @Override | ||
296 | + public void install(TestInstallableIntent intent) { | ||
297 | + if (fail) { | ||
298 | + throw new IntentException("install failed by design"); | ||
299 | + } | ||
300 | + } | ||
301 | + | ||
302 | + @Override | ||
303 | + public void uninstall(TestInstallableIntent intent) { | ||
304 | + if (fail) { | ||
305 | + throw new IntentException("remove failed by design"); | ||
306 | + } | ||
307 | + } | ||
308 | + } | ||
309 | + | ||
310 | +} |
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/MessagingService.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/AsyncResponse.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/EchoHandler.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/InternalMessage.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/LoggingHandler.java
deleted
100644 → 0
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 |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/MessageDecoder.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/MessageEncoder.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/NettyMessagingService.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/SimpleClient.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/main/java/org/onlab/onos/store/messaging/impl/SimpleServer.java
deleted
100644 → 0
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 | -} |
core/store/dist/src/test/java/org/onlab/onos/store/cluster/impl/ClusterCommunicationManagerTest.java
... | @@ -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 | ... | ... |
tools/test/bin/onos-fetch-vms
0 → 100755
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 |
utils/netty/pom.xml
0 → 100644
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> | ... | ... |
-
Please register or login to post a comment