Merge branch 'master' of ssh://gerrit.onlab.us:29418/onos-next
Showing
31 changed files
with
1737 additions
and
34 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 | + | ||
3 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
4 | + | ||
5 | +import java.util.Collections; | ||
6 | +import java.util.LinkedList; | ||
7 | +import java.util.List; | ||
8 | + | ||
9 | +/** | ||
10 | + * A list of BatchOperationEntry. | ||
11 | + * | ||
12 | + * @param <T> the enum of operators <br> | ||
13 | + * This enum must be defined in each sub-classes. | ||
14 | + * | ||
15 | + */ | ||
16 | +public abstract class BatchOperation<T extends BatchOperationEntry<?, ?>> { | ||
17 | + private List<T> ops; | ||
18 | + | ||
19 | + /** | ||
20 | + * Creates new {@link BatchOperation} object. | ||
21 | + */ | ||
22 | + public BatchOperation() { | ||
23 | + ops = new LinkedList<>(); | ||
24 | + } | ||
25 | + | ||
26 | + /** | ||
27 | + * Creates {@link BatchOperation} object from a list of batch operation | ||
28 | + * entries. | ||
29 | + * | ||
30 | + * @param batchOperations the list of batch operation entries. | ||
31 | + */ | ||
32 | + public BatchOperation(List<T> batchOperations) { | ||
33 | + ops = new LinkedList<>(checkNotNull(batchOperations)); | ||
34 | + } | ||
35 | + | ||
36 | + /** | ||
37 | + * Removes all operations maintained in this object. | ||
38 | + */ | ||
39 | + public void clear() { | ||
40 | + ops.clear(); | ||
41 | + } | ||
42 | + | ||
43 | + /** | ||
44 | + * Returns the number of operations in this object. | ||
45 | + * | ||
46 | + * @return the number of operations in this object | ||
47 | + */ | ||
48 | + public int size() { | ||
49 | + return ops.size(); | ||
50 | + } | ||
51 | + | ||
52 | + /** | ||
53 | + * Returns the operations in this object. | ||
54 | + * | ||
55 | + * @return the operations in this object | ||
56 | + */ | ||
57 | + public List<T> getOperations() { | ||
58 | + return Collections.unmodifiableList(ops); | ||
59 | + } | ||
60 | + | ||
61 | + /** | ||
62 | + * Adds an operation. | ||
63 | + * | ||
64 | + * @param entry the operation to be added | ||
65 | + * @return this object if succeeded, null otherwise | ||
66 | + */ | ||
67 | + public BatchOperation<T> addOperation(T entry) { | ||
68 | + return ops.add(entry) ? this : null; | ||
69 | + } | ||
70 | + | ||
71 | + @Override | ||
72 | + public boolean equals(Object o) { | ||
73 | + if (this == o) { | ||
74 | + return true; | ||
75 | + } | ||
76 | + | ||
77 | + if (o == null) { | ||
78 | + return false; | ||
79 | + } | ||
80 | + | ||
81 | + if (getClass() != o.getClass()) { | ||
82 | + return false; | ||
83 | + } | ||
84 | + BatchOperation<?> other = (BatchOperation<?>) o; | ||
85 | + | ||
86 | + return this.ops.equals(other.ops); | ||
87 | + } | ||
88 | + | ||
89 | + @Override | ||
90 | + public int hashCode() { | ||
91 | + return ops.hashCode(); | ||
92 | + } | ||
93 | + | ||
94 | + @Override | ||
95 | + public String toString() { | ||
96 | + return ops.toString(); | ||
97 | + } | ||
98 | +} |
1 | +package org.onlab.onos.net.intent; | ||
2 | + | ||
3 | +import java.util.Objects; | ||
4 | + | ||
5 | +import com.google.common.base.MoreObjects; | ||
6 | + | ||
7 | +/** | ||
8 | + * A super class for batch operation entry classes. | ||
9 | + * <p> | ||
10 | + * This is the interface to classes which are maintained by BatchOperation as | ||
11 | + * its entries. | ||
12 | + */ | ||
13 | +public class BatchOperationEntry<T extends Enum<?>, U extends BatchOperationTarget> { | ||
14 | + private final T operator; | ||
15 | + private final U target; | ||
16 | + | ||
17 | + /** | ||
18 | + * Default constructor for serializer. | ||
19 | + */ | ||
20 | + @Deprecated | ||
21 | + protected BatchOperationEntry() { | ||
22 | + this.operator = null; | ||
23 | + this.target = null; | ||
24 | + } | ||
25 | + | ||
26 | + /** | ||
27 | + * Constructs new instance for the entry of the BatchOperation. | ||
28 | + * | ||
29 | + * @param operator the operator of this operation | ||
30 | + * @param target the target object of this operation | ||
31 | + */ | ||
32 | + public BatchOperationEntry(T operator, U target) { | ||
33 | + this.operator = operator; | ||
34 | + this.target = target; | ||
35 | + } | ||
36 | + | ||
37 | + /** | ||
38 | + * Gets the target object of this operation. | ||
39 | + * | ||
40 | + * @return the target object of this operation | ||
41 | + */ | ||
42 | + public U getTarget() { | ||
43 | + return target; | ||
44 | + } | ||
45 | + | ||
46 | + /** | ||
47 | + * Gets the operator of this operation. | ||
48 | + * | ||
49 | + * @return the operator of this operation | ||
50 | + */ | ||
51 | + public T getOperator() { | ||
52 | + return operator; | ||
53 | + } | ||
54 | + | ||
55 | + @Override | ||
56 | + public boolean equals(Object o) { | ||
57 | + if (this == o) { | ||
58 | + return true; | ||
59 | + } | ||
60 | + if (o == null || getClass() != o.getClass()) { | ||
61 | + return false; | ||
62 | + } | ||
63 | + | ||
64 | + BatchOperationEntry<?, ?> other = (BatchOperationEntry<?, ?>) o; | ||
65 | + return (this.operator == other.operator) && | ||
66 | + Objects.equals(this.target, other.target); | ||
67 | + } | ||
68 | + | ||
69 | + @Override | ||
70 | + public int hashCode() { | ||
71 | + return Objects.hash(operator, target); | ||
72 | + } | ||
73 | + | ||
74 | + @Override | ||
75 | + public String toString() { | ||
76 | + return MoreObjects.toStringHelper(this) | ||
77 | + .add("operator", operator) | ||
78 | + .add("target", target) | ||
79 | + .toString(); | ||
80 | + } | ||
81 | +} |
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 | + | ||
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 | +} |
... | @@ -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 | ... | ... |
-
Please register or login to post a comment