Madan Jampani

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 +/**
4 + * An interface of the class which is assigned to BatchOperation.
5 + */
6 +public interface BatchOperationTarget {
7 +
8 +}
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 intent that can be installed into
5 + * the underlying system without additional compilation.
6 + */
7 +public interface InstallableIntent extends Intent {
8 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Abstraction of an application level intent.
5 + *
6 + * Make sure that an Intent should be immutable when a new type is defined.
7 + */
8 +public interface Intent extends BatchOperationTarget {
9 + /**
10 + * Returns the intent identifier.
11 + *
12 + * @return intent identifier
13 + */
14 + IntentId getId();
15 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * A list of intent operations.
5 + */
6 +public class IntentBatchOperation extends
7 + BatchOperation<BatchOperationEntry<IntentBatchOperation.Operator, ?>> {
8 + /**
9 + * The intent operators.
10 + */
11 + public enum Operator {
12 + ADD,
13 + REMOVE,
14 + }
15 +
16 + /**
17 + * Adds an add-intent operation.
18 + *
19 + * @param intent the intent to be added
20 + * @return the IntentBatchOperation object if succeeded, null otherwise
21 + */
22 + public IntentBatchOperation addAddIntentOperation(Intent intent) {
23 + return (null == super.addOperation(
24 + new BatchOperationEntry<Operator, Intent>(Operator.ADD, intent)))
25 + ? null : this;
26 + }
27 +
28 + /**
29 + * Adds a remove-intent operation.
30 + *
31 + * @param id the ID of intent to be removed
32 + * @return the IntentBatchOperation object if succeeded, null otherwise
33 + */
34 + public IntentBatchOperation addRemoveIntentOperation(IntentId id) {
35 + return (null == super.addOperation(
36 + new BatchOperationEntry<Operator, IntentId>(Operator.REMOVE, id)))
37 + ? null : this;
38 + }
39 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.List;
4 +
5 +/**
6 + * Abstraction of a compiler which is capable of taking an intent
7 + * and translating it to other, potentially installable, intents.
8 + *
9 + * @param <T> the type of intent
10 + */
11 +public interface IntentCompiler<T extends Intent> {
12 + /**
13 + * Compiles the specified intent into other intents.
14 + *
15 + * @param intent intent to be compiled
16 + * @return list of resulting intents
17 + * @throws IntentException if issues are encountered while compiling the intent
18 + */
19 + List<Intent> compile(T intent);
20 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import com.google.common.base.MoreObjects;
8 +
9 +/**
10 + * A class to represent an intent related event.
11 + */
12 +public class IntentEvent {
13 +
14 + // TODO: determine a suitable parent class; if one does not exist, consider introducing one
15 +
16 + private final long time;
17 + private final Intent intent;
18 + private final IntentState state;
19 + private final IntentState previous;
20 +
21 + /**
22 + * Creates an event describing a state change of an intent.
23 + *
24 + * @param intent subject intent
25 + * @param state new intent state
26 + * @param previous previous intent state
27 + * @param time time the event created in milliseconds since start of epoch
28 + * @throws NullPointerException if the intent or state is null
29 + */
30 + public IntentEvent(Intent intent, IntentState state, IntentState previous, long time) {
31 + this.intent = checkNotNull(intent);
32 + this.state = checkNotNull(state);
33 + this.previous = previous;
34 + this.time = time;
35 + }
36 +
37 + /**
38 + * Constructor for serializer.
39 + */
40 + protected IntentEvent() {
41 + this.intent = null;
42 + this.state = null;
43 + this.previous = null;
44 + this.time = 0;
45 + }
46 +
47 + /**
48 + * Returns the state of the intent which caused the event.
49 + *
50 + * @return the state of the intent
51 + */
52 + public IntentState getState() {
53 + return state;
54 + }
55 +
56 + /**
57 + * Returns the previous state of the intent which caused the event.
58 + *
59 + * @return the previous state of the intent
60 + */
61 + public IntentState getPreviousState() {
62 + return previous;
63 + }
64 +
65 + /**
66 + * Returns the intent associated with the event.
67 + *
68 + * @return the intent
69 + */
70 + public Intent getIntent() {
71 + return intent;
72 + }
73 +
74 + /**
75 + * Returns the time at which the event was created.
76 + *
77 + * @return the time in milliseconds since start of epoch
78 + */
79 + public long getTime() {
80 + return time;
81 + }
82 +
83 + @Override
84 + public boolean equals(Object o) {
85 + if (this == o) {
86 + return true;
87 + }
88 + if (o == null || getClass() != o.getClass()) {
89 + return false;
90 + }
91 +
92 + IntentEvent that = (IntentEvent) o;
93 + return Objects.equals(this.intent, that.intent)
94 + && Objects.equals(this.state, that.state)
95 + && Objects.equals(this.previous, that.previous)
96 + && Objects.equals(this.time, that.time);
97 + }
98 +
99 + @Override
100 + public int hashCode() {
101 + return Objects.hash(intent, state, previous, time);
102 + }
103 +
104 + @Override
105 + public String toString() {
106 + return MoreObjects.toStringHelper(getClass())
107 + .add("intent", intent)
108 + .add("state", state)
109 + .add("previous", previous)
110 + .add("time", time)
111 + .toString();
112 + }
113 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Listener for {@link IntentEvent intent events}.
5 + */
6 +public interface IntentEventListener {
7 + /**
8 + * Processes the specified intent event.
9 + *
10 + * @param event the event to process
11 + */
12 + void event(IntentEvent event);
13 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Represents an intent related error.
5 + */
6 +public class IntentException extends RuntimeException {
7 +
8 + private static final long serialVersionUID = 1907263634145241319L;
9 +
10 + /**
11 + * Constructs an exception with no message and no underlying cause.
12 + */
13 + public IntentException() {
14 + }
15 +
16 + /**
17 + * Constructs an exception with the specified message.
18 + *
19 + * @param message the message describing the specific nature of the error
20 + */
21 + public IntentException(String message) {
22 + super(message);
23 + }
24 +
25 + /**
26 + * Constructs an exception with the specified message and the underlying cause.
27 + *
28 + * @param message the message describing the specific nature of the error
29 + * @param cause the underlying cause of this error
30 + */
31 + public IntentException(String message, Throwable cause) {
32 + super(message, cause);
33 + }
34 +
35 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Map;
4 +
5 +/**
6 + * Service for extending the capability of intent framework by
7 + * adding additional compilers or/and installers.
8 + */
9 +public interface IntentExtensionService {
10 + /**
11 + * Registers the specified compiler for the given intent class.
12 + *
13 + * @param cls intent class
14 + * @param compiler intent compiler
15 + * @param <T> the type of intent
16 + */
17 + <T extends Intent> void registerCompiler(Class<T> cls, IntentCompiler<T> compiler);
18 +
19 + /**
20 + * Unregisters the compiler for the specified intent class.
21 + *
22 + * @param cls intent class
23 + * @param <T> the type of intent
24 + */
25 + <T extends Intent> void unregisterCompiler(Class<T> cls);
26 +
27 + /**
28 + * Returns immutable set of bindings of currently registered intent compilers.
29 + *
30 + * @return the set of compiler bindings
31 + */
32 + Map<Class<? extends Intent>, IntentCompiler<? extends Intent>> getCompilers();
33 +
34 + /**
35 + * Registers the specified installer for the given installable intent class.
36 + *
37 + * @param cls installable intent class
38 + * @param installer intent installer
39 + * @param <T> the type of installable intent
40 + */
41 + <T extends InstallableIntent> void registerInstaller(Class<T> cls, IntentInstaller<T> installer);
42 +
43 + /**
44 + * Unregisters the installer for the given installable intent class.
45 + *
46 + * @param cls installable intent class
47 + * @param <T> the type of installable intent
48 + */
49 + <T extends InstallableIntent> void unregisterInstaller(Class<T> cls);
50 +
51 + /**
52 + * Returns immutable set of bindings of currently registered intent installers.
53 + *
54 + * @return the set of installer bindings
55 + */
56 + Map<Class<? extends InstallableIntent>, IntentInstaller<? extends InstallableIntent>> getInstallers();
57 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Intent identifier suitable as an external key.
5 + *
6 + * This class is immutable.
7 + */
8 +public final class IntentId implements BatchOperationTarget {
9 +
10 + private static final int DEC = 10;
11 + private static final int HEX = 16;
12 +
13 + private final long id;
14 +
15 + /**
16 + * Creates an intent identifier from the specified string representation.
17 + *
18 + * @param value long value
19 + * @return intent identifier
20 + */
21 + public static IntentId valueOf(String value) {
22 + long id = value.toLowerCase().startsWith("0x")
23 + ? Long.parseLong(value.substring(2), HEX)
24 + : Long.parseLong(value, DEC);
25 + return new IntentId(id);
26 + }
27 +
28 + /**
29 + * Constructor for serializer.
30 + */
31 + protected IntentId() {
32 + this.id = 0;
33 + }
34 +
35 + /**
36 + * Constructs the ID corresponding to a given long value.
37 + *
38 + * @param id the underlying value of this ID
39 + */
40 + public IntentId(long id) {
41 + this.id = id;
42 + }
43 +
44 + @Override
45 + public int hashCode() {
46 + return (int) (id ^ (id >>> 32));
47 + }
48 +
49 + @Override
50 + public boolean equals(Object obj) {
51 + if (obj == this) {
52 + return true;
53 + }
54 +
55 + if (!(obj instanceof IntentId)) {
56 + return false;
57 + }
58 +
59 + IntentId that = (IntentId) obj;
60 + return this.id == that.id;
61 + }
62 +
63 + @Override
64 + public String toString() {
65 + return "0x" + Long.toHexString(id);
66 + }
67 +
68 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Abstraction of entity capable of installing intents to the environment.
5 + */
6 +public interface IntentInstaller<T extends InstallableIntent> {
7 + /**
8 + * Installs the specified intent to the environment.
9 + *
10 + * @param intent intent to be installed
11 + * @throws IntentException if issues are encountered while installing the intent
12 + */
13 + void install(T intent);
14 +
15 + /**
16 + * Uninstalls the specified intent from the environment.
17 + *
18 + * @param intent intent to be uninstalled
19 + * @throws IntentException if issues are encountered while uninstalling the intent
20 + */
21 + void uninstall(T intent);
22 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * Abstraction of a batch of intent submit/withdraw operations.
5 + */
6 +public interface IntentOperations {
7 +
8 + // TODO: elaborate once the revised BatchOperation scheme is in place
9 +
10 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Set;
4 +
5 +/**
6 + * Service for application submitting or withdrawing their intents.
7 + */
8 +public interface IntentService {
9 + /**
10 + * Submits an intent into the system.
11 + *
12 + * This is an asynchronous request meaning that any compiling
13 + * or installation activities may be done at later time.
14 + *
15 + * @param intent intent to be submitted
16 + */
17 + void submit(Intent intent);
18 +
19 + /**
20 + * Withdraws an intent from the system.
21 + *
22 + * This is an asynchronous request meaning that the environment
23 + * may be affected at later time.
24 + *
25 + * @param intent intent to be withdrawn
26 + */
27 + void withdraw(Intent intent);
28 +
29 + /**
30 + * Submits a batch of submit &amp; withdraw operations. Such a batch is
31 + * assumed to be processed together.
32 + *
33 + * This is an asynchronous request meaning that the environment
34 + * may be affected at later time.
35 + *
36 + * @param operations batch of intent operations
37 + */
38 + void execute(IntentOperations operations);
39 +
40 + /**
41 + * Returns immutable set of intents currently in the system.
42 + *
43 + * @return set of intents
44 + */
45 + Set<Intent> getIntents();
46 +
47 + /**
48 + * Retrieves the intent specified by its identifier.
49 + *
50 + * @param id intent identifier
51 + * @return the intent or null if one with the given identifier is not found
52 + */
53 + Intent getIntent(IntentId id);
54 +
55 + /**
56 + * Retrieves the state of an intent by its identifier.
57 + *
58 + * @param id intent identifier
59 + * @return the intent state or null if one with the given identifier is not found
60 + */
61 + IntentState getIntentState(IntentId id);
62 +
63 + /**
64 + * Adds the specified listener for intent events.
65 + *
66 + * @param listener listener to be added
67 + */
68 + void addListener(IntentEventListener listener);
69 +
70 + /**
71 + * Removes the specified listener for intent events.
72 + *
73 + * @param listener listener to be removed
74 + */
75 + void removeListener(IntentEventListener listener);
76 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +/**
4 + * This class represents the states of an intent.
5 + *
6 + * <p>
7 + * Note: The state is expressed as enum, but there is possibility
8 + * in the future that we define specific class instead of enum to improve
9 + * the extensibility of state definition.
10 + * </p>
11 + */
12 +public enum IntentState {
13 + // FIXME: requires discussion on State vs. EventType and a solid state-transition diagram
14 + // TODO: consider the impact of conflict detection
15 + // TODO: consider the impact that external events affect an installed intent
16 + /**
17 + * The beginning state.
18 + *
19 + * All intent in the runtime take this state first.
20 + */
21 + SUBMITTED,
22 +
23 + /**
24 + * The intent compilation has been completed.
25 + *
26 + * An intent translation graph (tree) is completely created.
27 + * Leaves of the graph are installable intent type.
28 + */
29 + COMPILED,
30 +
31 + /**
32 + * The intent has been successfully installed.
33 + */
34 + INSTALLED,
35 +
36 + /**
37 + * The intent is being withdrawn.
38 + *
39 + * When {@link IntentService#withdraw(Intent)} is called,
40 + * the intent takes this state first.
41 + */
42 + WITHDRAWING,
43 +
44 + /**
45 + * The intent has been successfully withdrawn.
46 + */
47 + WITHDRAWN,
48 +
49 + /**
50 + * The intent has failed to be compiled, installed, or withdrawn.
51 + *
52 + * When the intent failed to be withdrawn, it is still, at least partially installed.
53 + */
54 + FAILED,
55 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkArgument;
4 +import static com.google.common.base.Preconditions.checkNotNull;
5 +
6 +import java.util.Objects;
7 +import java.util.Set;
8 +
9 +import org.onlab.onos.net.ConnectPoint;
10 +import org.onlab.onos.net.flow.TrafficSelector;
11 +import org.onlab.onos.net.flow.TrafficTreatment;
12 +
13 +import com.google.common.base.MoreObjects;
14 +import com.google.common.collect.Sets;
15 +
16 +/**
17 + * Abstraction of multiple source to single destination connectivity intent.
18 + */
19 +public class MultiPointToSinglePointIntent extends ConnectivityIntent {
20 +
21 + private final Set<ConnectPoint> ingressPorts;
22 + private final ConnectPoint egressPort;
23 +
24 + /**
25 + * Creates a new multi-to-single point connectivity intent for the specified
26 + * traffic match and action.
27 + *
28 + * @param id intent identifier
29 + * @param match traffic match
30 + * @param action action
31 + * @param ingressPorts set of ports from which ingress traffic originates
32 + * @param egressPort port to which traffic will egress
33 + * @throws NullPointerException if {@code ingressPorts} or
34 + * {@code egressPort} is null.
35 + * @throws IllegalArgumentException if the size of {@code ingressPorts} is
36 + * not more than 1
37 + */
38 + public MultiPointToSinglePointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
39 + Set<ConnectPoint> ingressPorts, ConnectPoint egressPort) {
40 + super(id, match, action);
41 +
42 + checkNotNull(ingressPorts);
43 + checkArgument(!ingressPorts.isEmpty(),
44 + "there should be at least one ingress port");
45 +
46 + this.ingressPorts = Sets.newHashSet(ingressPorts);
47 + this.egressPort = checkNotNull(egressPort);
48 + }
49 +
50 + /**
51 + * Constructor for serializer.
52 + */
53 + protected MultiPointToSinglePointIntent() {
54 + super();
55 + this.ingressPorts = null;
56 + this.egressPort = null;
57 + }
58 +
59 + /**
60 + * Returns the set of ports on which ingress traffic should be connected to
61 + * the egress port.
62 + *
63 + * @return set of ingress ports
64 + */
65 + public Set<ConnectPoint> getIngressPorts() {
66 + return ingressPorts;
67 + }
68 +
69 + /**
70 + * Returns the port on which the traffic should egress.
71 + *
72 + * @return egress port
73 + */
74 + public ConnectPoint getEgressPort() {
75 + return egressPort;
76 + }
77 +
78 + @Override
79 + public boolean equals(Object o) {
80 + if (this == o) {
81 + return true;
82 + }
83 + if (o == null || getClass() != o.getClass()) {
84 + return false;
85 + }
86 + if (!super.equals(o)) {
87 + return false;
88 + }
89 +
90 + MultiPointToSinglePointIntent that = (MultiPointToSinglePointIntent) o;
91 + return Objects.equals(this.ingressPorts, that.ingressPorts)
92 + && Objects.equals(this.egressPort, that.egressPort);
93 + }
94 +
95 + @Override
96 + public int hashCode() {
97 + return Objects.hash(super.hashCode(), ingressPorts, egressPort);
98 + }
99 +
100 + @Override
101 + public String toString() {
102 + return MoreObjects.toStringHelper(getClass())
103 + .add("id", getId())
104 + .add("match", getTrafficSelector())
105 + .add("action", getTrafficTreatment())
106 + .add("ingressPorts", getIngressPorts())
107 + .add("egressPort", getEgressPort())
108 + .toString();
109 + }
110 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import org.onlab.onos.net.ConnectPoint;
4 +
5 +// TODO: consider if this intent should be sub-class of ConnectivityIntent
6 +/**
7 + * An optical layer Intent for a connectivity from a transponder port to another
8 + * transponder port.
9 + * <p>
10 + * This class doesn't accepts lambda specifier. This class computes path between
11 + * ports and assign lambda automatically. The lambda can be specified using
12 + * OpticalPathFlow class.
13 + */
14 +public class OpticalConnectivityIntent extends AbstractIntent {
15 + protected ConnectPoint srcConnectPoint;
16 + protected ConnectPoint dstConnectPoint;
17 +
18 + /**
19 + * Constructor.
20 + *
21 + * @param id ID for this new Intent object.
22 + * @param srcConnectPoint The source transponder port.
23 + * @param dstConnectPoint The destination transponder port.
24 + */
25 + public OpticalConnectivityIntent(IntentId id,
26 + ConnectPoint srcConnectPoint, ConnectPoint dstConnectPoint) {
27 + super(id);
28 + this.srcConnectPoint = srcConnectPoint;
29 + this.dstConnectPoint = dstConnectPoint;
30 + }
31 +
32 + /**
33 + * Constructor for serializer.
34 + */
35 + protected OpticalConnectivityIntent() {
36 + super();
37 + this.srcConnectPoint = null;
38 + this.dstConnectPoint = null;
39 + }
40 +
41 + /**
42 + * Gets source transponder port.
43 + *
44 + * @return The source transponder port.
45 + */
46 + public ConnectPoint getSrcConnectPoint() {
47 + return srcConnectPoint;
48 + }
49 +
50 + /**
51 + * Gets destination transponder port.
52 + *
53 + * @return The source transponder port.
54 + */
55 + public ConnectPoint getDstConnectPoint() {
56 + return dstConnectPoint;
57 + }
58 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Collection;
4 +import java.util.Collections;
5 +import java.util.HashSet;
6 +import java.util.Set;
7 +
8 +import org.onlab.onos.net.ConnectPoint;
9 +import org.onlab.onos.net.flow.TrafficSelector;
10 +
11 +// TODO: consider if this intent should be sub-class of Connectivity intent
12 +/**
13 + * A packet layer Intent for a connectivity from a set of ports to a set of
14 + * ports.
15 + * <p>
16 + * TODO: Design methods to support the ReactiveForwarding and the SDN-IP. <br>
17 + * NOTE: Should this class support modifier methods? Should this object a
18 + * read-only object?
19 + */
20 +public class PacketConnectivityIntent extends AbstractIntent {
21 + protected Set<ConnectPoint> srcConnectPoints;
22 + protected TrafficSelector selector;
23 + protected Set<ConnectPoint> dstConnectPoints;
24 + protected boolean canSetupOpticalFlow;
25 + protected int idleTimeoutValue;
26 + protected int hardTimeoutValue;
27 +
28 + /**
29 + * Creates a connectivity intent for the packet layer.
30 + * <p>
31 + * When the "canSetupOpticalFlow" option is true, this intent will compute
32 + * the packet/optical converged path, decompose it to the OpticalPathFlow
33 + * and the PacketPathFlow objects, and execute the operations to add them
34 + * considering the dependency between the packet and optical layers.
35 + *
36 + * @param id ID for this new Intent object.
37 + * @param srcConnectPoints The set of source switch ports.
38 + * @param match Traffic specifier for this object.
39 + * @param dstConnectPoints The set of destination switch ports.
40 + * @param canSetupOpticalFlow The flag whether this intent can create
41 + * optical flows if needed.
42 + */
43 + public PacketConnectivityIntent(IntentId id,
44 + Collection<ConnectPoint> srcConnectPoints, TrafficSelector match,
45 + Collection<ConnectPoint> dstConnectPoints, boolean canSetupOpticalFlow) {
46 + super(id);
47 + this.srcConnectPoints = new HashSet<ConnectPoint>(srcConnectPoints);
48 + this.selector = match;
49 + this.dstConnectPoints = new HashSet<ConnectPoint>(dstConnectPoints);
50 + this.canSetupOpticalFlow = canSetupOpticalFlow;
51 + this.idleTimeoutValue = 0;
52 + this.hardTimeoutValue = 0;
53 +
54 + // TODO: check consistency between these parameters.
55 + }
56 +
57 + /**
58 + * Constructor for serializer.
59 + */
60 + protected PacketConnectivityIntent() {
61 + super();
62 + this.srcConnectPoints = null;
63 + this.selector = null;
64 + this.dstConnectPoints = null;
65 + this.canSetupOpticalFlow = false;
66 + this.idleTimeoutValue = 0;
67 + this.hardTimeoutValue = 0;
68 + }
69 +
70 + /**
71 + * Gets the set of source switch ports.
72 + *
73 + * @return the set of source switch ports.
74 + */
75 + public Collection<ConnectPoint> getSrcConnectPoints() {
76 + return Collections.unmodifiableCollection(srcConnectPoints);
77 + }
78 +
79 + /**
80 + * Gets the traffic specifier.
81 + *
82 + * @return The traffic specifier.
83 + */
84 + public TrafficSelector getMatch() {
85 + return selector;
86 + }
87 +
88 + /**
89 + * Gets the set of destination switch ports.
90 + *
91 + * @return the set of destination switch ports.
92 + */
93 + public Collection<ConnectPoint> getDstConnectPoints() {
94 + return Collections.unmodifiableCollection(dstConnectPoints);
95 + }
96 +
97 + /**
98 + * Adds the specified port to the set of source ports.
99 + *
100 + * @param port ConnectPoint object to be added
101 + */
102 + public void addSrcConnectPoint(ConnectPoint port) {
103 + // TODO implement it.
104 + }
105 +
106 + /**
107 + * Adds the specified port to the set of destination ports.
108 + *
109 + * @param port ConnectPoint object to be added
110 + */
111 + public void addDstConnectPoint(ConnectPoint port) {
112 + // TODO implement it.
113 + }
114 +
115 + /**
116 + * Removes the specified port from the set of source ports.
117 + *
118 + * @param port ConnectPoint object to be removed
119 + */
120 + public void removeSrcConnectPoint(ConnectPoint port) {
121 + // TODO implement it.
122 + }
123 +
124 + /**
125 + * Removes the specified port from the set of destination ports.
126 + *
127 + * @param port ConnectPoint object to be removed
128 + */
129 + public void removeDstConnectPoint(ConnectPoint port) {
130 + // TODO implement it.
131 + }
132 +
133 + /**
134 + * Sets idle-timeout value.
135 + *
136 + * @param timeout Idle-timeout value (seconds)
137 + */
138 + public void setIdleTimeout(int timeout) {
139 + idleTimeoutValue = timeout;
140 + }
141 +
142 + /**
143 + * Sets hard-timeout value.
144 + *
145 + * @param timeout Hard-timeout value (seconds)
146 + */
147 + public void setHardTimeout(int timeout) {
148 + hardTimeoutValue = timeout;
149 + }
150 +
151 + /**
152 + * Gets idle-timeout value.
153 + *
154 + * @return Idle-timeout value (seconds)
155 + */
156 + public int getIdleTimeout() {
157 + return idleTimeoutValue;
158 + }
159 +
160 + /**
161 + * Gets hard-timeout value.
162 + *
163 + * @return Hard-timeout value (seconds)
164 + */
165 + public int getHardTimeout() {
166 + return hardTimeoutValue;
167 + }
168 +
169 + /**
170 + * Returns whether this intent can create optical flows if needed.
171 + *
172 + * @return whether this intent can create optical flows.
173 + */
174 + public boolean canSetupOpticalFlow() {
175 + return canSetupOpticalFlow;
176 + }
177 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import java.util.Objects;
4 +
5 +import org.onlab.onos.net.ConnectPoint;
6 +import org.onlab.onos.net.Path;
7 +import org.onlab.onos.net.flow.TrafficSelector;
8 +import org.onlab.onos.net.flow.TrafficTreatment;
9 +
10 +import com.google.common.base.MoreObjects;
11 +
12 +/**
13 + * Abstraction of explicitly path specified connectivity intent.
14 + */
15 +public class PathIntent extends PointToPointIntent {
16 +
17 + private final Path path;
18 +
19 + /**
20 + * Creates a new point-to-point intent with the supplied ingress/egress
21 + * ports and using the specified explicit path.
22 + *
23 + * @param id intent identifier
24 + * @param match traffic match
25 + * @param action action
26 + * @param ingressPort ingress port
27 + * @param egressPort egress port
28 + * @param path traversed links
29 + * @throws NullPointerException {@code path} is null
30 + */
31 + public PathIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
32 + ConnectPoint ingressPort, ConnectPoint egressPort,
33 + Path path) {
34 + super(id, match, action, ingressPort, egressPort);
35 + this.path = path;
36 + }
37 +
38 + protected PathIntent() {
39 + super();
40 + this.path = null;
41 + }
42 +
43 + /**
44 + * Returns the links which the traffic goes along.
45 + *
46 + * @return traversed links
47 + */
48 + public Path getPath() {
49 + return path;
50 + }
51 +
52 + @Override
53 + public boolean equals(Object o) {
54 + if (this == o) {
55 + return true;
56 + }
57 + if (o == null || getClass() != o.getClass()) {
58 + return false;
59 + }
60 + if (!super.equals(o)) {
61 + return false;
62 + }
63 +
64 + PathIntent that = (PathIntent) o;
65 +
66 + if (!path.equals(that.path)) {
67 + return false;
68 + }
69 +
70 + return true;
71 + }
72 +
73 + @Override
74 + public int hashCode() {
75 + return Objects.hash(super.hashCode(), path);
76 + }
77 +
78 + @Override
79 + public String toString() {
80 + return MoreObjects.toStringHelper(getClass())
81 + .add("id", getId())
82 + .add("match", getTrafficSelector())
83 + .add("action", getTrafficTreatment())
84 + .add("ingressPort", getIngressPort())
85 + .add("egressPort", getEgressPort())
86 + .add("path", path)
87 + .toString();
88 + }
89 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkNotNull;
4 +
5 +import java.util.Objects;
6 +
7 +import org.onlab.onos.net.ConnectPoint;
8 +import org.onlab.onos.net.flow.TrafficSelector;
9 +import org.onlab.onos.net.flow.TrafficTreatment;
10 +
11 +import com.google.common.base.MoreObjects;
12 +
13 +/**
14 + * Abstraction of point-to-point connectivity.
15 + */
16 +public class PointToPointIntent extends ConnectivityIntent {
17 +
18 + private final ConnectPoint ingressPort;
19 + private final ConnectPoint egressPort;
20 +
21 + /**
22 + * Creates a new point-to-point intent with the supplied ingress/egress
23 + * ports.
24 + *
25 + * @param id intent identifier
26 + * @param match traffic match
27 + * @param action action
28 + * @param ingressPort ingress port
29 + * @param egressPort egress port
30 + * @throws NullPointerException if {@code ingressPort} or {@code egressPort} is null.
31 + */
32 + public PointToPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
33 + ConnectPoint ingressPort, ConnectPoint egressPort) {
34 + super(id, match, action);
35 + this.ingressPort = checkNotNull(ingressPort);
36 + this.egressPort = checkNotNull(egressPort);
37 + }
38 +
39 + /**
40 + * Constructor for serializer.
41 + */
42 + protected PointToPointIntent() {
43 + super();
44 + this.ingressPort = null;
45 + this.egressPort = null;
46 + }
47 +
48 + /**
49 + * Returns the port on which the ingress traffic should be connected to
50 + * the egress.
51 + *
52 + * @return ingress port
53 + */
54 + public ConnectPoint getIngressPort() {
55 + return ingressPort;
56 + }
57 +
58 + /**
59 + * Returns the port on which the traffic should egress.
60 + *
61 + * @return egress port
62 + */
63 + public ConnectPoint getEgressPort() {
64 + return egressPort;
65 + }
66 +
67 + @Override
68 + public boolean equals(Object o) {
69 + if (this == o) {
70 + return true;
71 + }
72 + if (o == null || getClass() != o.getClass()) {
73 + return false;
74 + }
75 + if (!super.equals(o)) {
76 + return false;
77 + }
78 +
79 + PointToPointIntent that = (PointToPointIntent) o;
80 + return Objects.equals(this.ingressPort, that.ingressPort)
81 + && Objects.equals(this.egressPort, that.egressPort);
82 + }
83 +
84 + @Override
85 + public int hashCode() {
86 + return Objects.hash(super.hashCode(), ingressPort, egressPort);
87 + }
88 +
89 + @Override
90 + public String toString() {
91 + return MoreObjects.toStringHelper(getClass())
92 + .add("id", getId())
93 + .add("match", getTrafficSelector())
94 + .add("action", getTrafficTreatment())
95 + .add("ingressPort", ingressPort)
96 + .add("egressPort", egressPort)
97 + .toString();
98 + }
99 +
100 +}
1 +package org.onlab.onos.net.intent;
2 +
3 +import static com.google.common.base.Preconditions.checkArgument;
4 +import static com.google.common.base.Preconditions.checkNotNull;
5 +
6 +import java.util.Objects;
7 +import java.util.Set;
8 +
9 +import org.onlab.onos.net.ConnectPoint;
10 +import org.onlab.onos.net.flow.TrafficSelector;
11 +import org.onlab.onos.net.flow.TrafficTreatment;
12 +
13 +import com.google.common.base.MoreObjects;
14 +import com.google.common.collect.Sets;
15 +
16 +/**
17 + * Abstraction of single source, multiple destination connectivity intent.
18 + */
19 +public class SinglePointToMultiPointIntent extends ConnectivityIntent {
20 +
21 + private final ConnectPoint ingressPort;
22 + private final Set<ConnectPoint> egressPorts;
23 +
24 + /**
25 + * Creates a new single-to-multi point connectivity intent.
26 + *
27 + * @param id intent identifier
28 + * @param match traffic match
29 + * @param action action
30 + * @param ingressPort port on which traffic will ingress
31 + * @param egressPorts set of ports on which traffic will egress
32 + * @throws NullPointerException if {@code ingressPort} or
33 + * {@code egressPorts} is null
34 + * @throws IllegalArgumentException if the size of {@code egressPorts} is
35 + * not more than 1
36 + */
37 + public SinglePointToMultiPointIntent(IntentId id, TrafficSelector match, TrafficTreatment action,
38 + ConnectPoint ingressPort,
39 + Set<ConnectPoint> egressPorts) {
40 + super(id, match, action);
41 +
42 + checkNotNull(egressPorts);
43 + checkArgument(!egressPorts.isEmpty(),
44 + "there should be at least one egress port");
45 +
46 + this.ingressPort = checkNotNull(ingressPort);
47 + this.egressPorts = Sets.newHashSet(egressPorts);
48 + }
49 +
50 + /**
51 + * Constructor for serializer.
52 + */
53 + protected SinglePointToMultiPointIntent() {
54 + super();
55 + this.ingressPort = null;
56 + this.egressPorts = null;
57 + }
58 +
59 + /**
60 + * Returns the port on which the ingress traffic should be connected to the egress.
61 + *
62 + * @return ingress port
63 + */
64 + public ConnectPoint getIngressPort() {
65 + return ingressPort;
66 + }
67 +
68 + /**
69 + * Returns the set of ports on which the traffic should egress.
70 + *
71 + * @return set of egress ports
72 + */
73 + public Set<ConnectPoint> getEgressPorts() {
74 + return egressPorts;
75 + }
76 +
77 + @Override
78 + public boolean equals(Object o) {
79 + if (this == o) {
80 + return true;
81 + }
82 + if (o == null || getClass() != o.getClass()) {
83 + return false;
84 + }
85 + if (!super.equals(o)) {
86 + return false;
87 + }
88 +
89 + SinglePointToMultiPointIntent that = (SinglePointToMultiPointIntent) o;
90 + return Objects.equals(this.ingressPort, that.ingressPort)
91 + && Objects.equals(this.egressPorts, that.egressPorts);
92 + }
93 +
94 + @Override
95 + public int hashCode() {
96 + return Objects.hash(super.hashCode(), ingressPort, egressPorts);
97 + }
98 +
99 + @Override
100 + public String toString() {
101 + return MoreObjects.toStringHelper(getClass())
102 + .add("id", getId())
103 + .add("match", getTrafficSelector())
104 + .add("action", getTrafficTreatment())
105 + .add("ingressPort", ingressPort)
106 + .add("egressPort", egressPorts)
107 + .toString();
108 + }
109 +
110 +}
1 +/**
2 + * Intent Package. TODO
3 + */
4 +
5 +package org.onlab.onos.net.intent;
...\ No newline at end of file ...\ No newline at end of file
...@@ -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
......