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
Showing
16 changed files
with
292 additions
and
73 deletions
| ... | @@ -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,9 +30,11 @@ public class DefaultLink extends AbstractModel implements Link { | ... | @@ -29,9 +30,11 @@ 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 |
| ... | @@ -41,10 +44,31 @@ public class DefaultLink extends AbstractModel implements Link { | ... | @@ -41,10 +44,31 @@ public class DefaultLink extends AbstractModel implements Link { |
| 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 | } | ... | ... |
| ... | @@ -64,7 +64,8 @@ public class DefaultLinkDescription extends AbstractDescription | ... | @@ -64,7 +64,8 @@ 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("src", src()) | ||
| 68 | .add("dst", dst()) | 69 | .add("dst", dst()) |
| 69 | .add("type", type()).toString(); | 70 | .add("type", type()).toString(); |
| 70 | } | 71 | } | ... | ... |
| ... | @@ -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 |
| ... | @@ -178,6 +180,25 @@ public class SimpleLinkStore | ... | @@ -178,6 +180,25 @@ 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, |
| ... | @@ -197,8 +218,7 @@ public class SimpleLinkStore | ... | @@ -197,8 +218,7 @@ public class SimpleLinkStore |
| 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(), | ||
| 202 | linkDescription.dst(), | 222 | linkDescription.dst(), |
| 203 | newType, merged); | 223 | newType, merged); |
| 204 | } | 224 | } |
| ... | @@ -217,7 +237,8 @@ public class SimpleLinkStore | ... | @@ -217,7 +237,8 @@ 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() || |
| 241 | + (oldLink.type() == INDIRECT && newLink.type() == DIRECT) || | ||
| 221 | !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) { | 242 | !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) { |
| 222 | 243 | ||
| 223 | links.put(key, newLink); | 244 | links.put(key, newLink); |
| ... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment