Thomas Vachuska
Committed by Gerrit Code Review

Sketching out what link-state addition would look like; quite easy until we get …

…to the distributed store.
Added unit tests to provide durable-nondurable transitions.
FIxed issue where link could be accidentally activated.
Renamed parameter.

Change-Id: I8aa19a6583ec50dbf28769995f0a8ea9be9a4daa
...@@ -25,6 +25,17 @@ public final class AnnotationKeys { ...@@ -25,6 +25,17 @@ public final class AnnotationKeys {
25 private AnnotationKeys() {} 25 private AnnotationKeys() {}
26 26
27 /** 27 /**
28 + * Annotation key for durable links.
29 + */
30 + public static final String DURABLE = "durable";
31 +
32 + /**
33 + * Annotation key for active/inactive links. Links are implicitly
34 + * considered active unless explicitly marked otherwise.
35 + */
36 + public static final String INACTIVE = "inactive";
37 +
38 + /**
28 * Annotation key for latency. 39 * Annotation key for latency.
29 */ 40 */
30 public static final String LATENCY = "latency"; 41 public static final String LATENCY = "latency";
......
...@@ -20,6 +20,7 @@ import org.onlab.onos.net.provider.ProviderId; ...@@ -20,6 +20,7 @@ import org.onlab.onos.net.provider.ProviderId;
20 import java.util.Objects; 20 import java.util.Objects;
21 21
22 import static com.google.common.base.MoreObjects.toStringHelper; 22 import static com.google.common.base.MoreObjects.toStringHelper;
23 +import static org.onlab.onos.net.Link.State.ACTIVE;
23 24
24 /** 25 /**
25 * Default infrastructure link model implementation. 26 * Default infrastructure link model implementation.
...@@ -29,22 +30,45 @@ public class DefaultLink extends AbstractModel implements Link { ...@@ -29,22 +30,45 @@ public class DefaultLink extends AbstractModel implements Link {
29 private final ConnectPoint src; 30 private final ConnectPoint src;
30 private final ConnectPoint dst; 31 private final ConnectPoint dst;
31 private final Type type; 32 private final Type type;
33 + private final State state;
34 + private final boolean isDurable;
32 35
33 /** 36 /**
34 - * Creates an infrastructure link using the supplied information. 37 + * Creates an active infrastructure link using the supplied information.
35 * 38 *
36 - * @param providerId provider identity 39 + * @param providerId provider identity
37 - * @param src link source 40 + * @param src link source
38 - * @param dst link destination 41 + * @param dst link destination
39 - * @param type link type 42 + * @param type link type
40 * @param annotations optional key/value annotations 43 * @param annotations optional key/value annotations
41 */ 44 */
42 public DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst, 45 public DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst,
43 Type type, Annotations... annotations) { 46 Type type, Annotations... annotations) {
47 + this(providerId, src, dst, type, ACTIVE, false, annotations);
48 + }
49 +
50 + /**
51 + * Creates an infrastructure link using the supplied information.
52 + * Links marked as durable will remain in the inventory when a vanish
53 + * message is received and instead will be marked as inactive.
54 + *
55 + * @param providerId provider identity
56 + * @param src link source
57 + * @param dst link destination
58 + * @param type link type
59 + * @param state link state
60 + * @param isDurable indicates if the link is to be considered durable
61 + * @param annotations optional key/value annotations
62 + */
63 + public DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst,
64 + Type type, State state,
65 + boolean isDurable, Annotations... annotations) {
44 super(providerId, annotations); 66 super(providerId, annotations);
45 this.src = src; 67 this.src = src;
46 this.dst = dst; 68 this.dst = dst;
47 this.type = type; 69 this.type = type;
70 + this.state = state;
71 + this.isDurable = isDurable;
48 } 72 }
49 73
50 @Override 74 @Override
...@@ -63,6 +87,18 @@ public class DefaultLink extends AbstractModel implements Link { ...@@ -63,6 +87,18 @@ public class DefaultLink extends AbstractModel implements Link {
63 } 87 }
64 88
65 @Override 89 @Override
90 + public State state() {
91 + return state;
92 + }
93 +
94 + @Override
95 + public boolean isDurable() {
96 + return isDurable;
97 + }
98 +
99 + // Note: Durability & state are purposefully omitted form equality & hashCode.
100 +
101 + @Override
66 public int hashCode() { 102 public int hashCode() {
67 return Objects.hash(src, dst, type); 103 return Objects.hash(src, dst, type);
68 } 104 }
...@@ -87,6 +123,8 @@ public class DefaultLink extends AbstractModel implements Link { ...@@ -87,6 +123,8 @@ public class DefaultLink extends AbstractModel implements Link {
87 .add("src", src) 123 .add("src", src)
88 .add("dst", dst) 124 .add("dst", dst)
89 .add("type", type) 125 .add("type", type)
126 + .add("state", state)
127 + .add("durable", isDurable)
90 .toString(); 128 .toString();
91 } 129 }
92 130
......
...@@ -55,6 +55,23 @@ public interface Link extends Annotated, Provided, NetworkResource { ...@@ -55,6 +55,23 @@ public interface Link extends Annotated, Provided, NetworkResource {
55 } 55 }
56 56
57 /** 57 /**
58 + * Representation of the link state, which applies primarily only to
59 + * configured durable links, i.e. those that need to remain present,
60 + * but instead be marked as inactive.
61 + */
62 + public enum State {
63 + /**
64 + * Signifies that a link is currently active.
65 + */
66 + ACTIVE,
67 +
68 + /**
69 + * Signifies that a link is currently active.
70 + */
71 + INACTIVE
72 + }
73 +
74 + /**
58 * Returns the link source connection point. 75 * Returns the link source connection point.
59 * 76 *
60 * @return link source connection point 77 * @return link source connection point
...@@ -75,4 +92,16 @@ public interface Link extends Annotated, Provided, NetworkResource { ...@@ -75,4 +92,16 @@ public interface Link extends Annotated, Provided, NetworkResource {
75 */ 92 */
76 Type type(); 93 Type type();
77 94
95 + /**
96 + * Returns the link state.
97 + */
98 + State state();
99 +
100 + /**
101 + * Indicates if the link is to be considered durable.
102 + *
103 + * @return link state
104 + */
105 + boolean isDurable();
106 +
78 } 107 }
......
...@@ -34,9 +34,9 @@ public class DefaultLinkDescription extends AbstractDescription ...@@ -34,9 +34,9 @@ public class DefaultLinkDescription extends AbstractDescription
34 /** 34 /**
35 * Creates a link description using the supplied information. 35 * Creates a link description using the supplied information.
36 * 36 *
37 - * @param src link source 37 + * @param src link source
38 - * @param dst link destination 38 + * @param dst link destination
39 - * @param type link type 39 + * @param type link type
40 * @param annotations optional key/value annotations 40 * @param annotations optional key/value annotations
41 */ 41 */
42 public DefaultLinkDescription(ConnectPoint src, ConnectPoint dst, 42 public DefaultLinkDescription(ConnectPoint src, ConnectPoint dst,
...@@ -64,9 +64,10 @@ public class DefaultLinkDescription extends AbstractDescription ...@@ -64,9 +64,10 @@ public class DefaultLinkDescription extends AbstractDescription
64 64
65 @Override 65 @Override
66 public String toString() { 66 public String toString() {
67 - return MoreObjects.toStringHelper("Link").add("src", src()) 67 + return MoreObjects.toStringHelper(this)
68 - .add("dst", dst()) 68 + .add("src", src())
69 - .add("type", type()).toString(); 69 + .add("dst", dst())
70 + .add("type", type()).toString();
70 } 71 }
71 72
72 } 73 }
......
...@@ -45,6 +45,5 @@ public interface LinkDescription extends Description { ...@@ -45,6 +45,5 @@ public interface LinkDescription extends Description {
45 */ 45 */
46 Link.Type type(); 46 Link.Type type();
47 47
48 -
49 // Add further link attributes 48 // Add further link attributes
50 } 49 }
......
...@@ -33,7 +33,7 @@ public class LinkEvent extends AbstractEvent<LinkEvent.Type, Link> { ...@@ -33,7 +33,7 @@ public class LinkEvent extends AbstractEvent<LinkEvent.Type, Link> {
33 LINK_ADDED, 33 LINK_ADDED,
34 34
35 /** 35 /**
36 - * Signifies that a link has been updated. 36 + * Signifies that a link has been updated or changed state.
37 */ 37 */
38 LINK_UPDATED, 38 LINK_UPDATED,
39 39
......
...@@ -95,6 +95,16 @@ public interface LinkStore extends Store<LinkEvent, LinkStoreDelegate> { ...@@ -95,6 +95,16 @@ public interface LinkStore extends Store<LinkEvent, LinkStoreDelegate> {
95 LinkDescription linkDescription); 95 LinkDescription linkDescription);
96 96
97 /** 97 /**
98 + * Removes the link, or marks it as inactive if the link is durable,
99 + * based on the specified information.
100 + *
101 + * @param src link source
102 + * @param dst link destination
103 + * @return remove or update link event, or null if no change resulted
104 + */
105 + LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst);
106 +
107 + /**
98 * Removes the link based on the specified information. 108 * Removes the link based on the specified information.
99 * 109 *
100 * @param src link source 110 * @param src link source
...@@ -103,4 +113,5 @@ public interface LinkStore extends Store<LinkEvent, LinkStoreDelegate> { ...@@ -103,4 +113,5 @@ public interface LinkStore extends Store<LinkEvent, LinkStoreDelegate> {
103 */ 113 */
104 LinkEvent removeLink(ConnectPoint src, ConnectPoint dst); 114 LinkEvent removeLink(ConnectPoint src, ConnectPoint dst);
105 115
116 +
106 } 117 }
......
...@@ -78,5 +78,4 @@ public class LinkServiceAdapter implements LinkService { ...@@ -78,5 +78,4 @@ public class LinkServiceAdapter implements LinkService {
78 public void removeListener(LinkListener listener) { 78 public void removeListener(LinkListener listener) {
79 } 79 }
80 80
81 -
82 } 81 }
......
...@@ -158,7 +158,7 @@ public class LinkManager ...@@ -158,7 +158,7 @@ public class LinkManager
158 if (deviceService.getRole(connectPoint.deviceId()) != MastershipRole.MASTER) { 158 if (deviceService.getRole(connectPoint.deviceId()) != MastershipRole.MASTER) {
159 return; 159 return;
160 } 160 }
161 - removeLinks(getLinks(connectPoint)); 161 + removeLinks(getLinks(connectPoint), false);
162 } 162 }
163 163
164 @Override 164 @Override
...@@ -166,7 +166,7 @@ public class LinkManager ...@@ -166,7 +166,7 @@ public class LinkManager
166 if (deviceService.getRole(deviceId) != MastershipRole.MASTER) { 166 if (deviceService.getRole(deviceId) != MastershipRole.MASTER) {
167 return; 167 return;
168 } 168 }
169 - removeLinks(getDeviceLinks(deviceId)); 169 + removeLinks(getDeviceLinks(deviceId), false);
170 } 170 }
171 171
172 @Override 172 @Override
...@@ -228,7 +228,7 @@ public class LinkManager ...@@ -228,7 +228,7 @@ public class LinkManager
228 ConnectPoint src = linkDescription.src(); 228 ConnectPoint src = linkDescription.src();
229 ConnectPoint dst = linkDescription.dst(); 229 ConnectPoint dst = linkDescription.dst();
230 230
231 - LinkEvent event = store.removeLink(src, dst); 231 + LinkEvent event = store.removeOrDownLink(src, dst);
232 if (event != null) { 232 if (event != null) {
233 log.info("Link {} vanished", linkDescription); 233 log.info("Link {} vanished", linkDescription);
234 post(event); 234 post(event);
...@@ -242,7 +242,7 @@ public class LinkManager ...@@ -242,7 +242,7 @@ public class LinkManager
242 242
243 log.info("Links for connection point {} vanished", connectPoint); 243 log.info("Links for connection point {} vanished", connectPoint);
244 // FIXME: This will remove links registered by other providers 244 // FIXME: This will remove links registered by other providers
245 - removeLinks(getLinks(connectPoint)); 245 + removeLinks(getLinks(connectPoint), true);
246 } 246 }
247 247
248 @Override 248 @Override
...@@ -251,14 +251,16 @@ public class LinkManager ...@@ -251,14 +251,16 @@ public class LinkManager
251 checkValidity(); 251 checkValidity();
252 252
253 log.info("Links for device {} vanished", deviceId); 253 log.info("Links for device {} vanished", deviceId);
254 - removeLinks(getDeviceLinks(deviceId)); 254 + removeLinks(getDeviceLinks(deviceId), true);
255 } 255 }
256 } 256 }
257 257
258 // Removes all links in the specified set and emits appropriate events. 258 // Removes all links in the specified set and emits appropriate events.
259 - private void removeLinks(Set<Link> links) { 259 + private void removeLinks(Set<Link> links, boolean isSoftRemove) {
260 for (Link link : links) { 260 for (Link link : links) {
261 - LinkEvent event = store.removeLink(link.src(), link.dst()); 261 + LinkEvent event = isSoftRemove ?
262 + store.removeOrDownLink(link.src(), link.dst()) :
263 + store.removeLink(link.src(), link.dst());
262 post(event); 264 post(event);
263 } 265 }
264 } 266 }
......
...@@ -21,7 +21,6 @@ import com.google.common.collect.HashMultimap; ...@@ -21,7 +21,6 @@ import com.google.common.collect.HashMultimap;
21 import com.google.common.collect.ImmutableList; 21 import com.google.common.collect.ImmutableList;
22 import com.google.common.collect.Maps; 22 import com.google.common.collect.Maps;
23 import com.google.common.collect.SetMultimap; 23 import com.google.common.collect.SetMultimap;
24 -
25 import org.apache.commons.lang3.RandomUtils; 24 import org.apache.commons.lang3.RandomUtils;
26 import org.apache.felix.scr.annotations.Activate; 25 import org.apache.felix.scr.annotations.Activate;
27 import org.apache.felix.scr.annotations.Component; 26 import org.apache.felix.scr.annotations.Component;
...@@ -32,6 +31,7 @@ import org.apache.felix.scr.annotations.Service; ...@@ -32,6 +31,7 @@ import org.apache.felix.scr.annotations.Service;
32 import org.onlab.onos.cluster.ClusterService; 31 import org.onlab.onos.cluster.ClusterService;
33 import org.onlab.onos.cluster.ControllerNode; 32 import org.onlab.onos.cluster.ControllerNode;
34 import org.onlab.onos.cluster.NodeId; 33 import org.onlab.onos.cluster.NodeId;
34 +import org.onlab.onos.net.AnnotationKeys;
35 import org.onlab.onos.net.AnnotationsUtil; 35 import org.onlab.onos.net.AnnotationsUtil;
36 import org.onlab.onos.net.ConnectPoint; 36 import org.onlab.onos.net.ConnectPoint;
37 import org.onlab.onos.net.DefaultAnnotations; 37 import org.onlab.onos.net.DefaultAnnotations;
...@@ -65,28 +65,30 @@ import java.util.Collections; ...@@ -65,28 +65,30 @@ import java.util.Collections;
65 import java.util.HashMap; 65 import java.util.HashMap;
66 import java.util.HashSet; 66 import java.util.HashSet;
67 import java.util.Map; 67 import java.util.Map;
68 +import java.util.Map.Entry;
68 import java.util.Objects; 69 import java.util.Objects;
69 import java.util.Set; 70 import java.util.Set;
70 -import java.util.Map.Entry;
71 import java.util.concurrent.ConcurrentHashMap; 71 import java.util.concurrent.ConcurrentHashMap;
72 import java.util.concurrent.ConcurrentMap; 72 import java.util.concurrent.ConcurrentMap;
73 import java.util.concurrent.ScheduledExecutorService; 73 import java.util.concurrent.ScheduledExecutorService;
74 import java.util.concurrent.TimeUnit; 74 import java.util.concurrent.TimeUnit;
75 75
76 +import static com.google.common.base.Preconditions.checkNotNull;
77 +import static com.google.common.base.Predicates.notNull;
78 +import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
76 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor; 79 import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
77 import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId; 80 import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
78 -import static org.onlab.onos.net.DefaultAnnotations.union;
79 import static org.onlab.onos.net.DefaultAnnotations.merge; 81 import static org.onlab.onos.net.DefaultAnnotations.merge;
82 +import static org.onlab.onos.net.DefaultAnnotations.union;
83 +import static org.onlab.onos.net.Link.State.ACTIVE;
84 +import static org.onlab.onos.net.Link.State.INACTIVE;
80 import static org.onlab.onos.net.Link.Type.DIRECT; 85 import static org.onlab.onos.net.Link.Type.DIRECT;
81 import static org.onlab.onos.net.Link.Type.INDIRECT; 86 import static org.onlab.onos.net.Link.Type.INDIRECT;
82 import static org.onlab.onos.net.LinkKey.linkKey; 87 import static org.onlab.onos.net.LinkKey.linkKey;
83 import static org.onlab.onos.net.link.LinkEvent.Type.*; 88 import static org.onlab.onos.net.link.LinkEvent.Type.*;
89 +import static org.onlab.onos.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT;
84 import static org.onlab.util.Tools.namedThreads; 90 import static org.onlab.util.Tools.namedThreads;
85 import static org.slf4j.LoggerFactory.getLogger; 91 import static org.slf4j.LoggerFactory.getLogger;
86 -import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
87 -import static com.google.common.base.Preconditions.checkNotNull;
88 -import static com.google.common.base.Predicates.notNull;
89 -import static org.onlab.onos.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT;
90 92
91 /** 93 /**
92 * Manages inventory of infrastructure links in distributed data store 94 * Manages inventory of infrastructure links in distributed data store
...@@ -261,8 +263,6 @@ public class GossipLinkStore ...@@ -261,8 +263,6 @@ public class GossipLinkStore
261 mergedDesc = map.get(providerId); 263 mergedDesc = map.get(providerId);
262 } 264 }
263 265
264 -
265 -
266 if (event != null) { 266 if (event != null) {
267 log.info("Notifying peers of a link update topology event from providerId: " 267 log.info("Notifying peers of a link update topology event from providerId: "
268 + "{} between src: {} and dst: {}", 268 + "{} between src: {} and dst: {}",
...@@ -278,6 +278,26 @@ public class GossipLinkStore ...@@ -278,6 +278,26 @@ public class GossipLinkStore
278 return event; 278 return event;
279 } 279 }
280 280
281 + @Override
282 + public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
283 + Link link = getLink(src, dst);
284 + if (link == null) {
285 + return null;
286 + }
287 +
288 + if (link.isDurable()) {
289 + // FIXME: this is not the right thing to call for the gossip store; will not sync link state!!!
290 + return link.state() == INACTIVE ? null :
291 + updateLink(linkKey(link.src(), link.dst()), link,
292 + new DefaultLink(link.providerId(),
293 + link.src(), link.dst(),
294 + link.type(), INACTIVE,
295 + link.isDurable(),
296 + link.annotations()));
297 + }
298 + return removeLink(src, dst);
299 + }
300 +
281 private LinkEvent createOrUpdateLinkInternal( 301 private LinkEvent createOrUpdateLinkInternal(
282 ProviderId providerId, 302 ProviderId providerId,
283 Timestamped<LinkDescription> linkDescription) { 303 Timestamped<LinkDescription> linkDescription) {
...@@ -333,7 +353,7 @@ public class GossipLinkStore ...@@ -333,7 +353,7 @@ public class GossipLinkStore
333 } 353 }
334 SparseAnnotations merged = union(existingLinkDescription.value().annotations(), 354 SparseAnnotations merged = union(existingLinkDescription.value().annotations(),
335 linkDescription.value().annotations()); 355 linkDescription.value().annotations());
336 - newLinkDescription = new Timestamped<LinkDescription>( 356 + newLinkDescription = new Timestamped<>(
337 new DefaultLinkDescription( 357 new DefaultLinkDescription(
338 linkDescription.value().src(), 358 linkDescription.value().src(),
339 linkDescription.value().dst(), 359 linkDescription.value().dst(),
...@@ -357,7 +377,8 @@ public class GossipLinkStore ...@@ -357,7 +377,8 @@ public class GossipLinkStore
357 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) { 377 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
358 // Note: INDIRECT -> DIRECT transition only 378 // Note: INDIRECT -> DIRECT transition only
359 // so that BDDP discovered Link will not overwrite LDDP Link 379 // so that BDDP discovered Link will not overwrite LDDP Link
360 - if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) || 380 + if (oldLink.state() != newLink.state() ||
381 + (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
361 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) { 382 !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
362 383
363 links.put(key, newLink); 384 links.put(key, newLink);
...@@ -449,12 +470,8 @@ public class GossipLinkStore ...@@ -449,12 +470,8 @@ public class GossipLinkStore
449 // outdated remove request, ignore 470 // outdated remove request, ignore
450 return null; 471 return null;
451 } 472 }
452 - Link link = links.get(key);
453 - if (isDurable(link)) {
454 - return null;
455 - }
456 removedLinks.put(key, timestamp); 473 removedLinks.put(key, timestamp);
457 - links.remove(key); 474 + Link link = links.remove(key);
458 linkDescriptions.clear(); 475 linkDescriptions.clear();
459 if (link != null) { 476 if (link != null) {
460 srcLinks.remove(link.src().deviceId(), key); 477 srcLinks.remove(link.src().deviceId(), key);
...@@ -465,11 +482,6 @@ public class GossipLinkStore ...@@ -465,11 +482,6 @@ public class GossipLinkStore
465 } 482 }
466 } 483 }
467 484
468 - // Indicates if the link has been marked as durable via annotations.
469 - private boolean isDurable(Link link) {
470 - return link != null && Objects.equals(link.annotations().value("durable"), "true");
471 - }
472 -
473 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() { 485 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
474 return synchronizedSetMultimap(HashMultimap.<K, V>create()); 486 return synchronizedSetMultimap(HashMultimap.<K, V>create());
475 } 487 }
...@@ -518,7 +530,10 @@ public class GossipLinkStore ...@@ -518,7 +530,10 @@ public class GossipLinkStore
518 annotations = merge(annotations, e.getValue().value().annotations()); 530 annotations = merge(annotations, e.getValue().value().annotations());
519 } 531 }
520 532
521 - return new DefaultLink(baseProviderId, src, dst, type, annotations); 533 + boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
534 + boolean isActive = !Objects.equals(annotations.value(AnnotationKeys.INACTIVE), "true");
535 + return new DefaultLink(baseProviderId, src, dst, type,
536 + isActive ? ACTIVE : INACTIVE, isDurable, annotations);
522 } 537 }
523 538
524 private Map<ProviderId, Timestamped<LinkDescription>> getOrCreateLinkDescriptions(LinkKey key) { 539 private Map<ProviderId, Timestamped<LinkDescription>> getOrCreateLinkDescriptions(LinkKey key) {
...@@ -538,9 +553,11 @@ public class GossipLinkStore ...@@ -538,9 +553,11 @@ public class GossipLinkStore
538 } 553 }
539 554
540 private final Function<LinkKey, Link> lookupLink = new LookupLink(); 555 private final Function<LinkKey, Link> lookupLink = new LookupLink();
556 +
541 /** 557 /**
542 * Returns a Function to lookup Link instance using LinkKey from cache. 558 * Returns a Function to lookup Link instance using LinkKey from cache.
543 - * @return 559 + *
560 + * @return lookup link function
544 */ 561 */
545 private Function<LinkKey, Link> lookupLink() { 562 private Function<LinkKey, Link> lookupLink() {
546 return lookupLink; 563 return lookupLink;
......
...@@ -49,6 +49,8 @@ import static com.google.common.collect.ImmutableSetMultimap.Builder; ...@@ -49,6 +49,8 @@ import static com.google.common.collect.ImmutableSetMultimap.Builder;
49 import static org.onlab.graph.GraphPathSearch.Result; 49 import static org.onlab.graph.GraphPathSearch.Result;
50 import static org.onlab.graph.TarjanGraphSearch.SCCResult; 50 import static org.onlab.graph.TarjanGraphSearch.SCCResult;
51 import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID; 51 import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
52 +import static org.onlab.onos.net.Link.State.ACTIVE;
53 +import static org.onlab.onos.net.Link.State.INACTIVE;
52 import static org.onlab.onos.net.Link.Type.INDIRECT; 54 import static org.onlab.onos.net.Link.Type.INDIRECT;
53 55
54 /** 56 /**
...@@ -434,7 +436,8 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -434,7 +436,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
434 public double weight(TopologyEdge edge) { 436 public double weight(TopologyEdge edge) {
435 // To force preference to use direct paths first, make indirect 437 // To force preference to use direct paths first, make indirect
436 // links as expensive as the linear vertex traversal. 438 // links as expensive as the linear vertex traversal.
437 - return edge.link().type() == INDIRECT ? indirectLinkCost : 1; 439 + return edge.link().state() == ACTIVE ?
440 + (edge.link().type() == INDIRECT ? indirectLinkCost : 1) : -1;
438 } 441 }
439 } 442 }
440 443
...@@ -442,7 +445,7 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -442,7 +445,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
442 private static class NoIndirectLinksWeight implements LinkWeight { 445 private static class NoIndirectLinksWeight implements LinkWeight {
443 @Override 446 @Override
444 public double weight(TopologyEdge edge) { 447 public double weight(TopologyEdge edge) {
445 - return edge.link().type() == INDIRECT ? -1 : 1; 448 + return edge.link().state() == INACTIVE || edge.link().type() == INDIRECT ? -1 : 1;
446 } 449 }
447 } 450 }
448 451
......
...@@ -15,15 +15,15 @@ ...@@ -15,15 +15,15 @@
15 */ 15 */
16 package org.onlab.onos.store.serializers; 16 package org.onlab.onos.store.serializers;
17 17
18 -import org.onlab.onos.net.ConnectPoint;
19 -import org.onlab.onos.net.DefaultLink;
20 -import org.onlab.onos.net.Link.Type;
21 -import org.onlab.onos.net.provider.ProviderId;
22 -
23 import com.esotericsoftware.kryo.Kryo; 18 import com.esotericsoftware.kryo.Kryo;
24 import com.esotericsoftware.kryo.Serializer; 19 import com.esotericsoftware.kryo.Serializer;
25 import com.esotericsoftware.kryo.io.Input; 20 import com.esotericsoftware.kryo.io.Input;
26 import com.esotericsoftware.kryo.io.Output; 21 import com.esotericsoftware.kryo.io.Output;
22 +import org.onlab.onos.net.ConnectPoint;
23 +import org.onlab.onos.net.DefaultLink;
24 +import org.onlab.onos.net.Link.State;
25 +import org.onlab.onos.net.Link.Type;
26 +import org.onlab.onos.net.provider.ProviderId;
27 27
28 /** 28 /**
29 * Kryo Serializer for {@link DefaultLink}. 29 * Kryo Serializer for {@link DefaultLink}.
...@@ -44,6 +44,8 @@ public class DefaultLinkSerializer extends Serializer<DefaultLink> { ...@@ -44,6 +44,8 @@ public class DefaultLinkSerializer extends Serializer<DefaultLink> {
44 kryo.writeClassAndObject(output, object.src()); 44 kryo.writeClassAndObject(output, object.src());
45 kryo.writeClassAndObject(output, object.dst()); 45 kryo.writeClassAndObject(output, object.dst());
46 kryo.writeClassAndObject(output, object.type()); 46 kryo.writeClassAndObject(output, object.type());
47 + kryo.writeClassAndObject(output, object.state());
48 + kryo.writeClassAndObject(output, object.isDurable());
47 } 49 }
48 50
49 @Override 51 @Override
...@@ -52,6 +54,8 @@ public class DefaultLinkSerializer extends Serializer<DefaultLink> { ...@@ -52,6 +54,8 @@ public class DefaultLinkSerializer extends Serializer<DefaultLink> {
52 ConnectPoint src = (ConnectPoint) kryo.readClassAndObject(input); 54 ConnectPoint src = (ConnectPoint) kryo.readClassAndObject(input);
53 ConnectPoint dst = (ConnectPoint) kryo.readClassAndObject(input); 55 ConnectPoint dst = (ConnectPoint) kryo.readClassAndObject(input);
54 Type linkType = (Type) kryo.readClassAndObject(input); 56 Type linkType = (Type) kryo.readClassAndObject(input);
55 - return new DefaultLink(providerId, src, dst, linkType); 57 + State state = (State) kryo.readClassAndObject(input);
58 + boolean isDurable = (boolean) kryo.readClassAndObject(input);
59 + return new DefaultLink(providerId, src, dst, linkType, state, isDurable);
56 } 60 }
57 } 61 }
......
...@@ -164,6 +164,7 @@ public final class KryoNamespaces { ...@@ -164,6 +164,7 @@ public final class KryoNamespaces {
164 DefaultPortDescription.class, 164 DefaultPortDescription.class,
165 Element.class, 165 Element.class,
166 Link.Type.class, 166 Link.Type.class,
167 + Link.State.class,
167 Timestamp.class, 168 Timestamp.class,
168 HostId.class, 169 HostId.class,
169 HostDescription.class, 170 HostDescription.class,
......
...@@ -49,6 +49,8 @@ import static com.google.common.collect.ImmutableSetMultimap.Builder; ...@@ -49,6 +49,8 @@ import static com.google.common.collect.ImmutableSetMultimap.Builder;
49 import static org.onlab.graph.GraphPathSearch.Result; 49 import static org.onlab.graph.GraphPathSearch.Result;
50 import static org.onlab.graph.TarjanGraphSearch.SCCResult; 50 import static org.onlab.graph.TarjanGraphSearch.SCCResult;
51 import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID; 51 import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
52 +import static org.onlab.onos.net.Link.State.ACTIVE;
53 +import static org.onlab.onos.net.Link.State.INACTIVE;
52 import static org.onlab.onos.net.Link.Type.INDIRECT; 54 import static org.onlab.onos.net.Link.Type.INDIRECT;
53 55
54 /** 56 /**
...@@ -434,7 +436,8 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -434,7 +436,8 @@ public class DefaultTopology extends AbstractModel implements Topology {
434 public double weight(TopologyEdge edge) { 436 public double weight(TopologyEdge edge) {
435 // To force preference to use direct paths first, make indirect 437 // To force preference to use direct paths first, make indirect
436 // links as expensive as the linear vertex traversal. 438 // links as expensive as the linear vertex traversal.
437 - return edge.link().type() == INDIRECT ? indirectLinkCost : 1; 439 + return edge.link().state() == ACTIVE ?
440 + (edge.link().type() == INDIRECT ? indirectLinkCost : 1) : -1;
438 } 441 }
439 } 442 }
440 443
...@@ -442,7 +445,7 @@ public class DefaultTopology extends AbstractModel implements Topology { ...@@ -442,7 +445,7 @@ public class DefaultTopology extends AbstractModel implements Topology {
442 private static class NoIndirectLinksWeight implements LinkWeight { 445 private static class NoIndirectLinksWeight implements LinkWeight {
443 @Override 446 @Override
444 public double weight(TopologyEdge edge) { 447 public double weight(TopologyEdge edge) {
445 - return edge.link().type() == INDIRECT ? -1 : 1; 448 + return edge.link().state() == INACTIVE || edge.link().type() == INDIRECT ? -1 : 1;
446 } 449 }
447 } 450 }
448 451
......
...@@ -19,20 +19,20 @@ import com.google.common.base.Function; ...@@ -19,20 +19,20 @@ import com.google.common.base.Function;
19 import com.google.common.collect.FluentIterable; 19 import com.google.common.collect.FluentIterable;
20 import com.google.common.collect.HashMultimap; 20 import com.google.common.collect.HashMultimap;
21 import com.google.common.collect.SetMultimap; 21 import com.google.common.collect.SetMultimap;
22 -
23 import org.apache.felix.scr.annotations.Activate; 22 import org.apache.felix.scr.annotations.Activate;
24 import org.apache.felix.scr.annotations.Component; 23 import org.apache.felix.scr.annotations.Component;
25 import org.apache.felix.scr.annotations.Deactivate; 24 import org.apache.felix.scr.annotations.Deactivate;
26 import org.apache.felix.scr.annotations.Service; 25 import org.apache.felix.scr.annotations.Service;
26 +import org.onlab.onos.net.AnnotationKeys;
27 import org.onlab.onos.net.AnnotationsUtil; 27 import org.onlab.onos.net.AnnotationsUtil;
28 import org.onlab.onos.net.ConnectPoint; 28 import org.onlab.onos.net.ConnectPoint;
29 import org.onlab.onos.net.DefaultAnnotations; 29 import org.onlab.onos.net.DefaultAnnotations;
30 import org.onlab.onos.net.DefaultLink; 30 import org.onlab.onos.net.DefaultLink;
31 import org.onlab.onos.net.DeviceId; 31 import org.onlab.onos.net.DeviceId;
32 import org.onlab.onos.net.Link; 32 import org.onlab.onos.net.Link;
33 -import org.onlab.onos.net.SparseAnnotations;
34 import org.onlab.onos.net.Link.Type; 33 import org.onlab.onos.net.Link.Type;
35 import org.onlab.onos.net.LinkKey; 34 import org.onlab.onos.net.LinkKey;
35 +import org.onlab.onos.net.SparseAnnotations;
36 import org.onlab.onos.net.link.DefaultLinkDescription; 36 import org.onlab.onos.net.link.DefaultLinkDescription;
37 import org.onlab.onos.net.link.LinkDescription; 37 import org.onlab.onos.net.link.LinkDescription;
38 import org.onlab.onos.net.link.LinkEvent; 38 import org.onlab.onos.net.link.LinkEvent;
...@@ -46,22 +46,24 @@ import java.util.Collections; ...@@ -46,22 +46,24 @@ import java.util.Collections;
46 import java.util.HashMap; 46 import java.util.HashMap;
47 import java.util.HashSet; 47 import java.util.HashSet;
48 import java.util.Map; 48 import java.util.Map;
49 +import java.util.Map.Entry;
49 import java.util.Objects; 50 import java.util.Objects;
50 import java.util.Set; 51 import java.util.Set;
51 -import java.util.Map.Entry;
52 import java.util.concurrent.ConcurrentHashMap; 52 import java.util.concurrent.ConcurrentHashMap;
53 import java.util.concurrent.ConcurrentMap; 53 import java.util.concurrent.ConcurrentMap;
54 54
55 -import static org.onlab.onos.net.DefaultAnnotations.union; 55 +import static com.google.common.base.Predicates.notNull;
56 +import static com.google.common.base.Verify.verifyNotNull;
57 +import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
56 import static org.onlab.onos.net.DefaultAnnotations.merge; 58 import static org.onlab.onos.net.DefaultAnnotations.merge;
59 +import static org.onlab.onos.net.DefaultAnnotations.union;
60 +import static org.onlab.onos.net.Link.State.ACTIVE;
61 +import static org.onlab.onos.net.Link.State.INACTIVE;
57 import static org.onlab.onos.net.Link.Type.DIRECT; 62 import static org.onlab.onos.net.Link.Type.DIRECT;
58 import static org.onlab.onos.net.Link.Type.INDIRECT; 63 import static org.onlab.onos.net.Link.Type.INDIRECT;
59 import static org.onlab.onos.net.LinkKey.linkKey; 64 import static org.onlab.onos.net.LinkKey.linkKey;
60 import static org.onlab.onos.net.link.LinkEvent.Type.*; 65 import static org.onlab.onos.net.link.LinkEvent.Type.*;
61 import static org.slf4j.LoggerFactory.getLogger; 66 import static org.slf4j.LoggerFactory.getLogger;
62 -import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
63 -import static com.google.common.base.Predicates.notNull;
64 -import static com.google.common.base.Verify.verifyNotNull;
65 67
66 /** 68 /**
67 * Manages inventory of infrastructure links using trivial in-memory structures 69 * Manages inventory of infrastructure links using trivial in-memory structures
...@@ -77,7 +79,7 @@ public class SimpleLinkStore ...@@ -77,7 +79,7 @@ public class SimpleLinkStore
77 79
78 // Link inventory 80 // Link inventory
79 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>> 81 private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
80 - linkDescs = new ConcurrentHashMap<>(); 82 + linkDescs = new ConcurrentHashMap<>();
81 83
82 // Link instance cache 84 // Link instance cache
83 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>(); 85 private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
...@@ -116,9 +118,9 @@ public class SimpleLinkStore ...@@ -116,9 +118,9 @@ public class SimpleLinkStore
116 // lock for iteration 118 // lock for iteration
117 synchronized (srcLinks) { 119 synchronized (srcLinks) {
118 return FluentIterable.from(srcLinks.get(deviceId)) 120 return FluentIterable.from(srcLinks.get(deviceId))
119 - .transform(lookupLink()) 121 + .transform(lookupLink())
120 - .filter(notNull()) 122 + .filter(notNull())
121 - .toSet(); 123 + .toSet();
122 } 124 }
123 } 125 }
124 126
...@@ -127,9 +129,9 @@ public class SimpleLinkStore ...@@ -127,9 +129,9 @@ public class SimpleLinkStore
127 // lock for iteration 129 // lock for iteration
128 synchronized (dstLinks) { 130 synchronized (dstLinks) {
129 return FluentIterable.from(dstLinks.get(deviceId)) 131 return FluentIterable.from(dstLinks.get(deviceId))
130 - .transform(lookupLink()) 132 + .transform(lookupLink())
131 - .filter(notNull()) 133 + .filter(notNull())
132 - .toSet(); 134 + .toSet();
133 } 135 }
134 } 136 }
135 137
...@@ -178,11 +180,30 @@ public class SimpleLinkStore ...@@ -178,11 +180,30 @@ public class SimpleLinkStore
178 } 180 }
179 } 181 }
180 182
183 + @Override
184 + public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
185 + Link link = getLink(src, dst);
186 + if (link == null) {
187 + return null;
188 + }
189 +
190 + if (link.isDurable()) {
191 + return link.state() == INACTIVE ? null :
192 + updateLink(linkKey(link.src(), link.dst()), link,
193 + new DefaultLink(link.providerId(),
194 + link.src(), link.dst(),
195 + link.type(), INACTIVE,
196 + link.isDurable(),
197 + link.annotations()));
198 + }
199 + return removeLink(src, dst);
200 + }
201 +
181 // Guarded by linkDescs value (=locking each Link) 202 // Guarded by linkDescs value (=locking each Link)
182 private LinkDescription createOrUpdateLinkDescription( 203 private LinkDescription createOrUpdateLinkDescription(
183 - Map<ProviderId, LinkDescription> descs, 204 + Map<ProviderId, LinkDescription> descs,
184 - ProviderId providerId, 205 + ProviderId providerId,
185 - LinkDescription linkDescription) { 206 + LinkDescription linkDescription) {
186 207
187 // merge existing attributes and merge 208 // merge existing attributes and merge
188 LinkDescription oldDesc = descs.get(providerId); 209 LinkDescription oldDesc = descs.get(providerId);
...@@ -196,11 +217,10 @@ public class SimpleLinkStore ...@@ -196,11 +217,10 @@ public class SimpleLinkStore
196 newType = linkDescription.type(); 217 newType = linkDescription.type();
197 } 218 }
198 SparseAnnotations merged = union(oldDesc.annotations(), 219 SparseAnnotations merged = union(oldDesc.annotations(),
199 - linkDescription.annotations()); 220 + linkDescription.annotations());
200 - newDesc = new DefaultLinkDescription( 221 + newDesc = new DefaultLinkDescription(linkDescription.src(),
201 - linkDescription.src(), 222 + linkDescription.dst(),
202 - linkDescription.dst(), 223 + newType, merged);
203 - newType, merged);
204 } 224 }
205 return descs.put(providerId, newDesc); 225 return descs.put(providerId, newDesc);
206 } 226 }
...@@ -217,8 +237,9 @@ public class SimpleLinkStore ...@@ -217,8 +237,9 @@ public class SimpleLinkStore
217 // Updates, if necessary the specified link and returns the appropriate event. 237 // Updates, if necessary the specified link and returns the appropriate event.
218 // Guarded by linkDescs value (=locking each Link) 238 // Guarded by linkDescs value (=locking each Link)
219 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) { 239 private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
220 - if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) || 240 + if (oldLink.state() != newLink.state() ||
221 - !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) { 241 + (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
242 + !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
222 243
223 links.put(key, newLink); 244 links.put(key, newLink);
224 // strictly speaking following can be ommitted 245 // strictly speaking following can be ommitted
...@@ -234,11 +255,7 @@ public class SimpleLinkStore ...@@ -234,11 +255,7 @@ public class SimpleLinkStore
234 final LinkKey key = linkKey(src, dst); 255 final LinkKey key = linkKey(src, dst);
235 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key); 256 Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
236 synchronized (descs) { 257 synchronized (descs) {
237 - Link link = links.get(key); 258 + Link link = links.remove(key);
238 - if (isDurable(link)) {
239 - return null;
240 - }
241 - links.remove(key);
242 descs.clear(); 259 descs.clear();
243 if (link != null) { 260 if (link != null) {
244 srcLinks.remove(link.src().deviceId(), key); 261 srcLinks.remove(link.src().deviceId(), key);
...@@ -249,11 +266,6 @@ public class SimpleLinkStore ...@@ -249,11 +266,6 @@ public class SimpleLinkStore
249 } 266 }
250 } 267 }
251 268
252 - // Indicates if the link has been marked as durable via annotations.
253 - private boolean isDurable(Link link) {
254 - return link != null && Objects.equals(link.annotations().value("durable"), "true");
255 - }
256 -
257 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() { 269 private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
258 return synchronizedSetMultimap(HashMultimap.<K, V>create()); 270 return synchronizedSetMultimap(HashMultimap.<K, V>create());
259 } 271 }
...@@ -301,7 +313,10 @@ public class SimpleLinkStore ...@@ -301,7 +313,10 @@ public class SimpleLinkStore
301 annotations = merge(annotations, e.getValue().annotations()); 313 annotations = merge(annotations, e.getValue().annotations());
302 } 314 }
303 315
304 - return new DefaultLink(primary , src, dst, type, annotations); 316 + boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
317 + boolean isActive = !Objects.equals(annotations.value(AnnotationKeys.INACTIVE), "true");
318 + return new DefaultLink(primary, src, dst, type,
319 + isActive ? ACTIVE : INACTIVE, isDurable, annotations);
305 } 320 }
306 321
307 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) { 322 private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
...@@ -321,6 +336,7 @@ public class SimpleLinkStore ...@@ -321,6 +336,7 @@ public class SimpleLinkStore
321 } 336 }
322 337
323 private final Function<LinkKey, Link> lookupLink = new LookupLink(); 338 private final Function<LinkKey, Link> lookupLink = new LookupLink();
339 +
324 private Function<LinkKey, Link> lookupLink() { 340 private Function<LinkKey, Link> lookupLink() {
325 return lookupLink; 341 return lookupLink;
326 } 342 }
......
...@@ -23,6 +23,7 @@ import org.junit.Before; ...@@ -23,6 +23,7 @@ import org.junit.Before;
23 import org.junit.BeforeClass; 23 import org.junit.BeforeClass;
24 import org.junit.Ignore; 24 import org.junit.Ignore;
25 import org.junit.Test; 25 import org.junit.Test;
26 +import org.onlab.onos.net.AnnotationKeys;
26 import org.onlab.onos.net.ConnectPoint; 27 import org.onlab.onos.net.ConnectPoint;
27 import org.onlab.onos.net.DefaultAnnotations; 28 import org.onlab.onos.net.DefaultAnnotations;
28 import org.onlab.onos.net.DeviceId; 29 import org.onlab.onos.net.DeviceId;
...@@ -80,6 +81,23 @@ public class SimpleLinkStoreTest { ...@@ -80,6 +81,23 @@ public class SimpleLinkStoreTest {
80 .set("B4", "b4") 81 .set("B4", "b4")
81 .build(); 82 .build();
82 83
84 + private static final SparseAnnotations DA1 = DefaultAnnotations.builder()
85 + .set("A1", "a1")
86 + .set("B1", "b1")
87 + .set(AnnotationKeys.DURABLE, "true")
88 + .build();
89 + private static final SparseAnnotations DA2 = DefaultAnnotations.builder()
90 + .set("A2", "a2")
91 + .set("B2", "b2")
92 + .set(AnnotationKeys.DURABLE, "true")
93 + .build();
94 + private static final SparseAnnotations NDA1 = DefaultAnnotations.builder()
95 + .set("A1", "a1")
96 + .set("B1", "b1")
97 + .remove(AnnotationKeys.DURABLE)
98 + .build();
99 +
100 +
83 101
84 private SimpleLinkStore simpleLinkStore; 102 private SimpleLinkStore simpleLinkStore;
85 private LinkStore linkStore; 103 private LinkStore linkStore;
...@@ -105,17 +123,19 @@ public class SimpleLinkStoreTest { ...@@ -105,17 +123,19 @@ public class SimpleLinkStoreTest {
105 } 123 }
106 124
107 private void putLink(DeviceId srcId, PortNumber srcNum, 125 private void putLink(DeviceId srcId, PortNumber srcNum,
108 - DeviceId dstId, PortNumber dstNum, Type type, 126 + DeviceId dstId, PortNumber dstNum,
127 + Type type, boolean isDurable,
109 SparseAnnotations... annotations) { 128 SparseAnnotations... annotations) {
110 ConnectPoint src = new ConnectPoint(srcId, srcNum); 129 ConnectPoint src = new ConnectPoint(srcId, srcNum);
111 ConnectPoint dst = new ConnectPoint(dstId, dstNum); 130 ConnectPoint dst = new ConnectPoint(dstId, dstNum);
112 - linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type, annotations)); 131 + linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type,
132 + annotations));
113 } 133 }
114 134
115 private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) { 135 private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) {
116 putLink(key.src().deviceId(), key.src().port(), 136 putLink(key.src().deviceId(), key.src().port(),
117 key.dst().deviceId(), key.dst().port(), 137 key.dst().deviceId(), key.dst().port(),
118 - type, annotations); 138 + type, false, annotations);
119 } 139 }
120 140
121 private static void assertLink(DeviceId srcId, PortNumber srcNum, 141 private static void assertLink(DeviceId srcId, PortNumber srcNum,
...@@ -138,9 +158,9 @@ public class SimpleLinkStoreTest { ...@@ -138,9 +158,9 @@ public class SimpleLinkStoreTest {
138 public final void testGetLinkCount() { 158 public final void testGetLinkCount() {
139 assertEquals("initialy empty", 0, linkStore.getLinkCount()); 159 assertEquals("initialy empty", 0, linkStore.getLinkCount());
140 160
141 - putLink(DID1, P1, DID2, P2, DIRECT); 161 + putLink(DID1, P1, DID2, P2, DIRECT, false);
142 - putLink(DID2, P2, DID1, P1, DIRECT); 162 + putLink(DID2, P2, DID1, P1, DIRECT, false);
143 - putLink(DID1, P1, DID2, P2, DIRECT); 163 + putLink(DID1, P1, DID2, P2, DIRECT, false);
144 164
145 assertEquals("expecting 2 unique link", 2, linkStore.getLinkCount()); 165 assertEquals("expecting 2 unique link", 2, linkStore.getLinkCount());
146 } 166 }
...@@ -360,6 +380,47 @@ public class SimpleLinkStoreTest { ...@@ -360,6 +380,47 @@ public class SimpleLinkStoreTest {
360 380
361 381
362 @Test 382 @Test
383 + public final void testRemoveOrDownLink() {
384 + removeOrDownLink(false);
385 + }
386 +
387 + @Test
388 + public final void testRemoveOrDownLinkDurable() {
389 + removeOrDownLink(true);
390 + }
391 +
392 + private void removeOrDownLink(boolean isDurable) {
393 + final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
394 + final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
395 + LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
396 + LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
397 +
398 + putLink(linkId1, DIRECT, isDurable ? DA1 : A1);
399 + putLink(linkId2, DIRECT, isDurable ? DA2 : A2);
400 +
401 + // DID1,P1 => DID2,P2
402 + // DID2,P2 => DID1,P1
403 + // DID1,P2 => DID2,P3
404 +
405 + LinkEvent event = linkStore.removeOrDownLink(d1P1, d2P2);
406 + assertEquals(isDurable ? LINK_UPDATED : LINK_REMOVED, event.type());
407 + assertAnnotationsEquals(event.subject().annotations(), isDurable ? DA1 : A1);
408 + LinkEvent event2 = linkStore.removeOrDownLink(d1P1, d2P2);
409 + assertNull(event2);
410 +
411 + assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
412 + assertAnnotationsEquals(linkStore.getLink(d2P2, d1P1).annotations(),
413 + isDurable ? DA2 : A2);
414 +
415 + // annotations, etc. should not survive remove
416 + if (!isDurable) {
417 + putLink(linkId1, DIRECT);
418 + assertLink(linkId1, DIRECT, linkStore.getLink(d1P1, d2P2));
419 + assertAnnotationsEquals(linkStore.getLink(d1P1, d2P2).annotations());
420 + }
421 + }
422 +
423 + @Test
363 public final void testRemoveLink() { 424 public final void testRemoveLink() {
364 final ConnectPoint d1P1 = new ConnectPoint(DID1, P1); 425 final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
365 final ConnectPoint d2P2 = new ConnectPoint(DID2, P2); 426 final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
...@@ -402,6 +463,30 @@ public class SimpleLinkStoreTest { ...@@ -402,6 +463,30 @@ public class SimpleLinkStoreTest {
402 assertNotNull(linkStore.getLink(src, dst)); 463 assertNotNull(linkStore.getLink(src, dst));
403 } 464 }
404 465
466 + @Test
467 + public void testDurableToNonDurable() {
468 + final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
469 + final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
470 + LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
471 +
472 + putLink(linkId1, DIRECT, DA1);
473 + assertTrue("should be be durable", linkStore.getLink(d1P1, d2P2).isDurable());
474 + putLink(linkId1, DIRECT, NDA1);
475 + assertFalse("should not be durable", linkStore.getLink(d1P1, d2P2).isDurable());
476 + }
477 +
478 + @Test
479 + public void testNonDurableToDurable() {
480 + final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
481 + final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
482 + LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
483 +
484 + putLink(linkId1, DIRECT, A1);
485 + assertFalse("should not be durable", linkStore.getLink(d1P1, d2P2).isDurable());
486 + putLink(linkId1, DIRECT, DA1);
487 + assertTrue("should be durable", linkStore.getLink(d1P1, d2P2).isDurable());
488 + }
489 +
405 // If Delegates should be called only on remote events, 490 // If Delegates should be called only on remote events,
406 // then Simple* should never call them, thus not test required. 491 // then Simple* should never call them, thus not test required.
407 @Ignore("Ignore until Delegate spec. is clear.") 492 @Ignore("Ignore until Delegate spec. is clear.")
...@@ -451,7 +536,7 @@ public class SimpleLinkStoreTest { ...@@ -451,7 +536,7 @@ public class SimpleLinkStoreTest {
451 536
452 linkStore.unsetDelegate(checkUpdate); 537 linkStore.unsetDelegate(checkUpdate);
453 linkStore.setDelegate(checkRemove); 538 linkStore.setDelegate(checkRemove);
454 - linkStore.removeLink(d1P1, d2P2); 539 + linkStore.removeOrDownLink(d1P1, d2P2);
455 assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS)); 540 assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
456 } 541 }
457 } 542 }
......