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
diff --git a/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java b/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
index af74043..de18fc0 100644
--- a/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
+++ b/core/api/src/main/java/org/onlab/onos/net/AnnotationKeys.java
@@ -25,6 +25,17 @@
private AnnotationKeys() {}
/**
+ * Annotation key for durable links.
+ */
+ public static final String DURABLE = "durable";
+
+ /**
+ * Annotation key for active/inactive links. Links are implicitly
+ * considered active unless explicitly marked otherwise.
+ */
+ public static final String INACTIVE = "inactive";
+
+ /**
* Annotation key for latency.
*/
public static final String LATENCY = "latency";
diff --git a/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java b/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
index a63de3b..6ebd52d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
+++ b/core/api/src/main/java/org/onlab/onos/net/DefaultLink.java
@@ -20,6 +20,7 @@
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
+import static org.onlab.onos.net.Link.State.ACTIVE;
/**
* Default infrastructure link model implementation.
@@ -29,22 +30,45 @@
private final ConnectPoint src;
private final ConnectPoint dst;
private final Type type;
+ private final State state;
+ private final boolean isDurable;
/**
- * Creates an infrastructure link using the supplied information.
+ * Creates an active infrastructure link using the supplied information.
*
- * @param providerId provider identity
- * @param src link source
- * @param dst link destination
- * @param type link type
+ * @param providerId provider identity
+ * @param src link source
+ * @param dst link destination
+ * @param type link type
* @param annotations optional key/value annotations
*/
public DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst,
Type type, Annotations... annotations) {
+ this(providerId, src, dst, type, ACTIVE, false, annotations);
+ }
+
+ /**
+ * Creates an infrastructure link using the supplied information.
+ * Links marked as durable will remain in the inventory when a vanish
+ * message is received and instead will be marked as inactive.
+ *
+ * @param providerId provider identity
+ * @param src link source
+ * @param dst link destination
+ * @param type link type
+ * @param state link state
+ * @param isDurable indicates if the link is to be considered durable
+ * @param annotations optional key/value annotations
+ */
+ public DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst,
+ Type type, State state,
+ boolean isDurable, Annotations... annotations) {
super(providerId, annotations);
this.src = src;
this.dst = dst;
this.type = type;
+ this.state = state;
+ this.isDurable = isDurable;
}
@Override
@@ -63,6 +87,18 @@
}
@Override
+ public State state() {
+ return state;
+ }
+
+ @Override
+ public boolean isDurable() {
+ return isDurable;
+ }
+
+ // Note: Durability & state are purposefully omitted form equality & hashCode.
+
+ @Override
public int hashCode() {
return Objects.hash(src, dst, type);
}
@@ -87,6 +123,8 @@
.add("src", src)
.add("dst", dst)
.add("type", type)
+ .add("state", state)
+ .add("durable", isDurable)
.toString();
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/Link.java b/core/api/src/main/java/org/onlab/onos/net/Link.java
index cdd0f23..cce34fa 100644
--- a/core/api/src/main/java/org/onlab/onos/net/Link.java
+++ b/core/api/src/main/java/org/onlab/onos/net/Link.java
@@ -55,6 +55,23 @@
}
/**
+ * Representation of the link state, which applies primarily only to
+ * configured durable links, i.e. those that need to remain present,
+ * but instead be marked as inactive.
+ */
+ public enum State {
+ /**
+ * Signifies that a link is currently active.
+ */
+ ACTIVE,
+
+ /**
+ * Signifies that a link is currently active.
+ */
+ INACTIVE
+ }
+
+ /**
* Returns the link source connection point.
*
* @return link source connection point
@@ -75,4 +92,16 @@
*/
Type type();
+ /**
+ * Returns the link state.
+ */
+ State state();
+
+ /**
+ * Indicates if the link is to be considered durable.
+ *
+ * @return link state
+ */
+ boolean isDurable();
+
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java b/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
index fc3f335..986eb0a 100644
--- a/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/link/DefaultLinkDescription.java
@@ -34,9 +34,9 @@
/**
* Creates a link description using the supplied information.
*
- * @param src link source
- * @param dst link destination
- * @param type link type
+ * @param src link source
+ * @param dst link destination
+ * @param type link type
* @param annotations optional key/value annotations
*/
public DefaultLinkDescription(ConnectPoint src, ConnectPoint dst,
@@ -64,9 +64,10 @@
@Override
public String toString() {
- return MoreObjects.toStringHelper("Link").add("src", src())
- .add("dst", dst())
- .add("type", type()).toString();
+ return MoreObjects.toStringHelper(this)
+ .add("src", src())
+ .add("dst", dst())
+ .add("type", type()).toString();
}
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java b/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java
index 3343451..3078243 100644
--- a/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java
+++ b/core/api/src/main/java/org/onlab/onos/net/link/LinkDescription.java
@@ -45,6 +45,5 @@
*/
Link.Type type();
-
// Add further link attributes
}
diff --git a/core/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java b/core/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
index b164b9e..763e95e 100644
--- a/core/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
+++ b/core/api/src/main/java/org/onlab/onos/net/link/LinkEvent.java
@@ -33,7 +33,7 @@
LINK_ADDED,
/**
- * Signifies that a link has been updated.
+ * Signifies that a link has been updated or changed state.
*/
LINK_UPDATED,
diff --git a/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java b/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java
index 95962e7..3af4f9f 100644
--- a/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java
+++ b/core/api/src/main/java/org/onlab/onos/net/link/LinkStore.java
@@ -95,6 +95,16 @@
LinkDescription linkDescription);
/**
+ * Removes the link, or marks it as inactive if the link is durable,
+ * based on the specified information.
+ *
+ * @param src link source
+ * @param dst link destination
+ * @return remove or update link event, or null if no change resulted
+ */
+ LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst);
+
+ /**
* Removes the link based on the specified information.
*
* @param src link source
@@ -103,4 +113,5 @@
*/
LinkEvent removeLink(ConnectPoint src, ConnectPoint dst);
+
}
diff --git a/core/api/src/test/java/org/onlab/onos/net/link/LinkServiceAdapter.java b/core/api/src/test/java/org/onlab/onos/net/link/LinkServiceAdapter.java
index 17a570a..0e25e2a 100644
--- a/core/api/src/test/java/org/onlab/onos/net/link/LinkServiceAdapter.java
+++ b/core/api/src/test/java/org/onlab/onos/net/link/LinkServiceAdapter.java
@@ -78,5 +78,4 @@
public void removeListener(LinkListener listener) {
}
-
}
diff --git a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
index 23c7362..15fc4a1 100644
--- a/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/link/impl/LinkManager.java
@@ -158,7 +158,7 @@
if (deviceService.getRole(connectPoint.deviceId()) != MastershipRole.MASTER) {
return;
}
- removeLinks(getLinks(connectPoint));
+ removeLinks(getLinks(connectPoint), false);
}
@Override
@@ -166,7 +166,7 @@
if (deviceService.getRole(deviceId) != MastershipRole.MASTER) {
return;
}
- removeLinks(getDeviceLinks(deviceId));
+ removeLinks(getDeviceLinks(deviceId), false);
}
@Override
@@ -228,7 +228,7 @@
ConnectPoint src = linkDescription.src();
ConnectPoint dst = linkDescription.dst();
- LinkEvent event = store.removeLink(src, dst);
+ LinkEvent event = store.removeOrDownLink(src, dst);
if (event != null) {
log.info("Link {} vanished", linkDescription);
post(event);
@@ -242,7 +242,7 @@
log.info("Links for connection point {} vanished", connectPoint);
// FIXME: This will remove links registered by other providers
- removeLinks(getLinks(connectPoint));
+ removeLinks(getLinks(connectPoint), true);
}
@Override
@@ -251,14 +251,16 @@
checkValidity();
log.info("Links for device {} vanished", deviceId);
- removeLinks(getDeviceLinks(deviceId));
+ removeLinks(getDeviceLinks(deviceId), true);
}
}
// Removes all links in the specified set and emits appropriate events.
- private void removeLinks(Set<Link> links) {
+ private void removeLinks(Set<Link> links, boolean isSoftRemove) {
for (Link link : links) {
- LinkEvent event = store.removeLink(link.src(), link.dst());
+ LinkEvent event = isSoftRemove ?
+ store.removeOrDownLink(link.src(), link.dst()) :
+ store.removeLink(link.src(), link.dst());
post(event);
}
}
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
index 9810d7a..d496ebe1 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/link/impl/GossipLinkStore.java
@@ -21,7 +21,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
-
import org.apache.commons.lang3.RandomUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
@@ -32,6 +31,7 @@
import org.onlab.onos.cluster.ClusterService;
import org.onlab.onos.cluster.ControllerNode;
import org.onlab.onos.cluster.NodeId;
+import org.onlab.onos.net.AnnotationKeys;
import org.onlab.onos.net.AnnotationsUtil;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultAnnotations;
@@ -65,28 +65,30 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Predicates.notNull;
+import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.onos.cluster.ControllerNodeToNodeId.toNodeId;
-import static org.onlab.onos.net.DefaultAnnotations.union;
import static org.onlab.onos.net.DefaultAnnotations.merge;
+import static org.onlab.onos.net.DefaultAnnotations.union;
+import static org.onlab.onos.net.Link.State.ACTIVE;
+import static org.onlab.onos.net.Link.State.INACTIVE;
import static org.onlab.onos.net.Link.Type.DIRECT;
import static org.onlab.onos.net.Link.Type.INDIRECT;
import static org.onlab.onos.net.LinkKey.linkKey;
import static org.onlab.onos.net.link.LinkEvent.Type.*;
+import static org.onlab.onos.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT;
import static org.onlab.util.Tools.namedThreads;
import static org.slf4j.LoggerFactory.getLogger;
-import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Predicates.notNull;
-import static org.onlab.onos.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT;
/**
* Manages inventory of infrastructure links in distributed data store
@@ -261,8 +263,6 @@
mergedDesc = map.get(providerId);
}
-
-
if (event != null) {
log.info("Notifying peers of a link update topology event from providerId: "
+ "{} between src: {} and dst: {}",
@@ -278,6 +278,26 @@
return event;
}
+ @Override
+ public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
+ Link link = getLink(src, dst);
+ if (link == null) {
+ return null;
+ }
+
+ if (link.isDurable()) {
+ // FIXME: this is not the right thing to call for the gossip store; will not sync link state!!!
+ return link.state() == INACTIVE ? null :
+ updateLink(linkKey(link.src(), link.dst()), link,
+ new DefaultLink(link.providerId(),
+ link.src(), link.dst(),
+ link.type(), INACTIVE,
+ link.isDurable(),
+ link.annotations()));
+ }
+ return removeLink(src, dst);
+ }
+
private LinkEvent createOrUpdateLinkInternal(
ProviderId providerId,
Timestamped<LinkDescription> linkDescription) {
@@ -333,7 +353,7 @@
}
SparseAnnotations merged = union(existingLinkDescription.value().annotations(),
linkDescription.value().annotations());
- newLinkDescription = new Timestamped<LinkDescription>(
+ newLinkDescription = new Timestamped<>(
new DefaultLinkDescription(
linkDescription.value().src(),
linkDescription.value().dst(),
@@ -357,7 +377,8 @@
private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
// Note: INDIRECT -> DIRECT transition only
// so that BDDP discovered Link will not overwrite LDDP Link
- if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
+ if (oldLink.state() != newLink.state() ||
+ (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
!AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
links.put(key, newLink);
@@ -449,12 +470,8 @@
// outdated remove request, ignore
return null;
}
- Link link = links.get(key);
- if (isDurable(link)) {
- return null;
- }
removedLinks.put(key, timestamp);
- links.remove(key);
+ Link link = links.remove(key);
linkDescriptions.clear();
if (link != null) {
srcLinks.remove(link.src().deviceId(), key);
@@ -465,11 +482,6 @@
}
}
- // Indicates if the link has been marked as durable via annotations.
- private boolean isDurable(Link link) {
- return link != null && Objects.equals(link.annotations().value("durable"), "true");
- }
-
private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
return synchronizedSetMultimap(HashMultimap.<K, V>create());
}
@@ -518,7 +530,10 @@
annotations = merge(annotations, e.getValue().value().annotations());
}
- return new DefaultLink(baseProviderId, src, dst, type, annotations);
+ boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
+ boolean isActive = !Objects.equals(annotations.value(AnnotationKeys.INACTIVE), "true");
+ return new DefaultLink(baseProviderId, src, dst, type,
+ isActive ? ACTIVE : INACTIVE, isDurable, annotations);
}
private Map<ProviderId, Timestamped<LinkDescription>> getOrCreateLinkDescriptions(LinkKey key) {
@@ -538,9 +553,11 @@
}
private final Function<LinkKey, Link> lookupLink = new LookupLink();
+
/**
* Returns a Function to lookup Link instance using LinkKey from cache.
- * @return
+ *
+ * @return lookup link function
*/
private Function<LinkKey, Link> lookupLink() {
return lookupLink;
diff --git a/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java b/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java
index 1850fa4..4aafa35 100644
--- a/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java
+++ b/core/store/dist/src/main/java/org/onlab/onos/store/topology/impl/DefaultTopology.java
@@ -49,6 +49,8 @@
import static org.onlab.graph.GraphPathSearch.Result;
import static org.onlab.graph.TarjanGraphSearch.SCCResult;
import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
+import static org.onlab.onos.net.Link.State.ACTIVE;
+import static org.onlab.onos.net.Link.State.INACTIVE;
import static org.onlab.onos.net.Link.Type.INDIRECT;
/**
@@ -434,7 +436,8 @@
public double weight(TopologyEdge edge) {
// To force preference to use direct paths first, make indirect
// links as expensive as the linear vertex traversal.
- return edge.link().type() == INDIRECT ? indirectLinkCost : 1;
+ return edge.link().state() == ACTIVE ?
+ (edge.link().type() == INDIRECT ? indirectLinkCost : 1) : -1;
}
}
@@ -442,7 +445,7 @@
private static class NoIndirectLinksWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
- return edge.link().type() == INDIRECT ? -1 : 1;
+ return edge.link().state() == INACTIVE || edge.link().type() == INDIRECT ? -1 : 1;
}
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
index a79e4d4..a787012 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/DefaultLinkSerializer.java
@@ -15,15 +15,15 @@
*/
package org.onlab.onos.store.serializers;
-import org.onlab.onos.net.ConnectPoint;
-import org.onlab.onos.net.DefaultLink;
-import org.onlab.onos.net.Link.Type;
-import org.onlab.onos.net.provider.ProviderId;
-
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
+import org.onlab.onos.net.ConnectPoint;
+import org.onlab.onos.net.DefaultLink;
+import org.onlab.onos.net.Link.State;
+import org.onlab.onos.net.Link.Type;
+import org.onlab.onos.net.provider.ProviderId;
/**
* Kryo Serializer for {@link DefaultLink}.
@@ -44,6 +44,8 @@
kryo.writeClassAndObject(output, object.src());
kryo.writeClassAndObject(output, object.dst());
kryo.writeClassAndObject(output, object.type());
+ kryo.writeClassAndObject(output, object.state());
+ kryo.writeClassAndObject(output, object.isDurable());
}
@Override
@@ -52,6 +54,8 @@
ConnectPoint src = (ConnectPoint) kryo.readClassAndObject(input);
ConnectPoint dst = (ConnectPoint) kryo.readClassAndObject(input);
Type linkType = (Type) kryo.readClassAndObject(input);
- return new DefaultLink(providerId, src, dst, linkType);
+ State state = (State) kryo.readClassAndObject(input);
+ boolean isDurable = (boolean) kryo.readClassAndObject(input);
+ return new DefaultLink(providerId, src, dst, linkType, state, isDurable);
}
}
diff --git a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
index 07db4a6..7043a61 100644
--- a/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
+++ b/core/store/serializers/src/main/java/org/onlab/onos/store/serializers/KryoNamespaces.java
@@ -164,6 +164,7 @@
DefaultPortDescription.class,
Element.class,
Link.Type.class,
+ Link.State.class,
Timestamp.class,
HostId.class,
HostDescription.class,
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopology.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopology.java
index 2cae2c3..5478806 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopology.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/DefaultTopology.java
@@ -49,6 +49,8 @@
import static org.onlab.graph.GraphPathSearch.Result;
import static org.onlab.graph.TarjanGraphSearch.SCCResult;
import static org.onlab.onos.core.CoreService.CORE_PROVIDER_ID;
+import static org.onlab.onos.net.Link.State.ACTIVE;
+import static org.onlab.onos.net.Link.State.INACTIVE;
import static org.onlab.onos.net.Link.Type.INDIRECT;
/**
@@ -434,7 +436,8 @@
public double weight(TopologyEdge edge) {
// To force preference to use direct paths first, make indirect
// links as expensive as the linear vertex traversal.
- return edge.link().type() == INDIRECT ? indirectLinkCost : 1;
+ return edge.link().state() == ACTIVE ?
+ (edge.link().type() == INDIRECT ? indirectLinkCost : 1) : -1;
}
}
@@ -442,7 +445,7 @@
private static class NoIndirectLinksWeight implements LinkWeight {
@Override
public double weight(TopologyEdge edge) {
- return edge.link().type() == INDIRECT ? -1 : 1;
+ return edge.link().state() == INACTIVE || edge.link().type() == INDIRECT ? -1 : 1;
}
}
diff --git a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
index 50f4e06..a383167 100644
--- a/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
+++ b/core/store/trivial/src/main/java/org/onlab/onos/store/trivial/impl/SimpleLinkStore.java
@@ -19,20 +19,20 @@
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.SetMultimap;
-
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Service;
+import org.onlab.onos.net.AnnotationKeys;
import org.onlab.onos.net.AnnotationsUtil;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DefaultLink;
import org.onlab.onos.net.DeviceId;
import org.onlab.onos.net.Link;
-import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.Link.Type;
import org.onlab.onos.net.LinkKey;
+import org.onlab.onos.net.SparseAnnotations;
import org.onlab.onos.net.link.DefaultLinkDescription;
import org.onlab.onos.net.link.LinkDescription;
import org.onlab.onos.net.link.LinkEvent;
@@ -46,22 +46,24 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
-import static org.onlab.onos.net.DefaultAnnotations.union;
+import static com.google.common.base.Predicates.notNull;
+import static com.google.common.base.Verify.verifyNotNull;
+import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
import static org.onlab.onos.net.DefaultAnnotations.merge;
+import static org.onlab.onos.net.DefaultAnnotations.union;
+import static org.onlab.onos.net.Link.State.ACTIVE;
+import static org.onlab.onos.net.Link.State.INACTIVE;
import static org.onlab.onos.net.Link.Type.DIRECT;
import static org.onlab.onos.net.Link.Type.INDIRECT;
import static org.onlab.onos.net.LinkKey.linkKey;
import static org.onlab.onos.net.link.LinkEvent.Type.*;
import static org.slf4j.LoggerFactory.getLogger;
-import static com.google.common.collect.Multimaps.synchronizedSetMultimap;
-import static com.google.common.base.Predicates.notNull;
-import static com.google.common.base.Verify.verifyNotNull;
/**
* Manages inventory of infrastructure links using trivial in-memory structures
@@ -77,7 +79,7 @@
// Link inventory
private final ConcurrentMap<LinkKey, Map<ProviderId, LinkDescription>>
- linkDescs = new ConcurrentHashMap<>();
+ linkDescs = new ConcurrentHashMap<>();
// Link instance cache
private final ConcurrentMap<LinkKey, Link> links = new ConcurrentHashMap<>();
@@ -116,9 +118,9 @@
// lock for iteration
synchronized (srcLinks) {
return FluentIterable.from(srcLinks.get(deviceId))
- .transform(lookupLink())
- .filter(notNull())
- .toSet();
+ .transform(lookupLink())
+ .filter(notNull())
+ .toSet();
}
}
@@ -127,9 +129,9 @@
// lock for iteration
synchronized (dstLinks) {
return FluentIterable.from(dstLinks.get(deviceId))
- .transform(lookupLink())
- .filter(notNull())
- .toSet();
+ .transform(lookupLink())
+ .filter(notNull())
+ .toSet();
}
}
@@ -178,11 +180,30 @@
}
}
+ @Override
+ public LinkEvent removeOrDownLink(ConnectPoint src, ConnectPoint dst) {
+ Link link = getLink(src, dst);
+ if (link == null) {
+ return null;
+ }
+
+ if (link.isDurable()) {
+ return link.state() == INACTIVE ? null :
+ updateLink(linkKey(link.src(), link.dst()), link,
+ new DefaultLink(link.providerId(),
+ link.src(), link.dst(),
+ link.type(), INACTIVE,
+ link.isDurable(),
+ link.annotations()));
+ }
+ return removeLink(src, dst);
+ }
+
// Guarded by linkDescs value (=locking each Link)
private LinkDescription createOrUpdateLinkDescription(
- Map<ProviderId, LinkDescription> descs,
- ProviderId providerId,
- LinkDescription linkDescription) {
+ Map<ProviderId, LinkDescription> descs,
+ ProviderId providerId,
+ LinkDescription linkDescription) {
// merge existing attributes and merge
LinkDescription oldDesc = descs.get(providerId);
@@ -196,11 +217,10 @@
newType = linkDescription.type();
}
SparseAnnotations merged = union(oldDesc.annotations(),
- linkDescription.annotations());
- newDesc = new DefaultLinkDescription(
- linkDescription.src(),
- linkDescription.dst(),
- newType, merged);
+ linkDescription.annotations());
+ newDesc = new DefaultLinkDescription(linkDescription.src(),
+ linkDescription.dst(),
+ newType, merged);
}
return descs.put(providerId, newDesc);
}
@@ -217,8 +237,9 @@
// Updates, if necessary the specified link and returns the appropriate event.
// Guarded by linkDescs value (=locking each Link)
private LinkEvent updateLink(LinkKey key, Link oldLink, Link newLink) {
- if ((oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
- !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
+ if (oldLink.state() != newLink.state() ||
+ (oldLink.type() == INDIRECT && newLink.type() == DIRECT) ||
+ !AnnotationsUtil.isEqual(oldLink.annotations(), newLink.annotations())) {
links.put(key, newLink);
// strictly speaking following can be ommitted
@@ -234,11 +255,7 @@
final LinkKey key = linkKey(src, dst);
Map<ProviderId, LinkDescription> descs = getOrCreateLinkDescriptions(key);
synchronized (descs) {
- Link link = links.get(key);
- if (isDurable(link)) {
- return null;
- }
- links.remove(key);
+ Link link = links.remove(key);
descs.clear();
if (link != null) {
srcLinks.remove(link.src().deviceId(), key);
@@ -249,11 +266,6 @@
}
}
- // Indicates if the link has been marked as durable via annotations.
- private boolean isDurable(Link link) {
- return link != null && Objects.equals(link.annotations().value("durable"), "true");
- }
-
private static <K, V> SetMultimap<K, V> createSynchronizedHashMultiMap() {
return synchronizedSetMultimap(HashMultimap.<K, V>create());
}
@@ -301,7 +313,10 @@
annotations = merge(annotations, e.getValue().annotations());
}
- return new DefaultLink(primary , src, dst, type, annotations);
+ boolean isDurable = Objects.equals(annotations.value(AnnotationKeys.DURABLE), "true");
+ boolean isActive = !Objects.equals(annotations.value(AnnotationKeys.INACTIVE), "true");
+ return new DefaultLink(primary, src, dst, type,
+ isActive ? ACTIVE : INACTIVE, isDurable, annotations);
}
private Map<ProviderId, LinkDescription> getOrCreateLinkDescriptions(LinkKey key) {
@@ -321,6 +336,7 @@
}
private final Function<LinkKey, Link> lookupLink = new LookupLink();
+
private Function<LinkKey, Link> lookupLink() {
return lookupLink;
}
diff --git a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
index 735f99c..cfd9d24 100644
--- a/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
+++ b/core/store/trivial/src/test/java/org/onlab/onos/store/trivial/impl/SimpleLinkStoreTest.java
@@ -23,6 +23,7 @@
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
+import org.onlab.onos.net.AnnotationKeys;
import org.onlab.onos.net.ConnectPoint;
import org.onlab.onos.net.DefaultAnnotations;
import org.onlab.onos.net.DeviceId;
@@ -80,6 +81,23 @@
.set("B4", "b4")
.build();
+ private static final SparseAnnotations DA1 = DefaultAnnotations.builder()
+ .set("A1", "a1")
+ .set("B1", "b1")
+ .set(AnnotationKeys.DURABLE, "true")
+ .build();
+ private static final SparseAnnotations DA2 = DefaultAnnotations.builder()
+ .set("A2", "a2")
+ .set("B2", "b2")
+ .set(AnnotationKeys.DURABLE, "true")
+ .build();
+ private static final SparseAnnotations NDA1 = DefaultAnnotations.builder()
+ .set("A1", "a1")
+ .set("B1", "b1")
+ .remove(AnnotationKeys.DURABLE)
+ .build();
+
+
private SimpleLinkStore simpleLinkStore;
private LinkStore linkStore;
@@ -105,17 +123,19 @@
}
private void putLink(DeviceId srcId, PortNumber srcNum,
- DeviceId dstId, PortNumber dstNum, Type type,
+ DeviceId dstId, PortNumber dstNum,
+ Type type, boolean isDurable,
SparseAnnotations... annotations) {
ConnectPoint src = new ConnectPoint(srcId, srcNum);
ConnectPoint dst = new ConnectPoint(dstId, dstNum);
- linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type, annotations));
+ linkStore.createOrUpdateLink(PID, new DefaultLinkDescription(src, dst, type,
+ annotations));
}
private void putLink(LinkKey key, Type type, SparseAnnotations... annotations) {
putLink(key.src().deviceId(), key.src().port(),
key.dst().deviceId(), key.dst().port(),
- type, annotations);
+ type, false, annotations);
}
private static void assertLink(DeviceId srcId, PortNumber srcNum,
@@ -138,9 +158,9 @@
public final void testGetLinkCount() {
assertEquals("initialy empty", 0, linkStore.getLinkCount());
- putLink(DID1, P1, DID2, P2, DIRECT);
- putLink(DID2, P2, DID1, P1, DIRECT);
- putLink(DID1, P1, DID2, P2, DIRECT);
+ putLink(DID1, P1, DID2, P2, DIRECT, false);
+ putLink(DID2, P2, DID1, P1, DIRECT, false);
+ putLink(DID1, P1, DID2, P2, DIRECT, false);
assertEquals("expecting 2 unique link", 2, linkStore.getLinkCount());
}
@@ -360,6 +380,47 @@
@Test
+ public final void testRemoveOrDownLink() {
+ removeOrDownLink(false);
+ }
+
+ @Test
+ public final void testRemoveOrDownLinkDurable() {
+ removeOrDownLink(true);
+ }
+
+ private void removeOrDownLink(boolean isDurable) {
+ final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
+ final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
+ LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
+ LinkKey linkId2 = LinkKey.linkKey(d2P2, d1P1);
+
+ putLink(linkId1, DIRECT, isDurable ? DA1 : A1);
+ putLink(linkId2, DIRECT, isDurable ? DA2 : A2);
+
+ // DID1,P1 => DID2,P2
+ // DID2,P2 => DID1,P1
+ // DID1,P2 => DID2,P3
+
+ LinkEvent event = linkStore.removeOrDownLink(d1P1, d2P2);
+ assertEquals(isDurable ? LINK_UPDATED : LINK_REMOVED, event.type());
+ assertAnnotationsEquals(event.subject().annotations(), isDurable ? DA1 : A1);
+ LinkEvent event2 = linkStore.removeOrDownLink(d1P1, d2P2);
+ assertNull(event2);
+
+ assertLink(linkId2, DIRECT, linkStore.getLink(d2P2, d1P1));
+ assertAnnotationsEquals(linkStore.getLink(d2P2, d1P1).annotations(),
+ isDurable ? DA2 : A2);
+
+ // annotations, etc. should not survive remove
+ if (!isDurable) {
+ putLink(linkId1, DIRECT);
+ assertLink(linkId1, DIRECT, linkStore.getLink(d1P1, d2P2));
+ assertAnnotationsEquals(linkStore.getLink(d1P1, d2P2).annotations());
+ }
+ }
+
+ @Test
public final void testRemoveLink() {
final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
@@ -402,6 +463,30 @@
assertNotNull(linkStore.getLink(src, dst));
}
+ @Test
+ public void testDurableToNonDurable() {
+ final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
+ final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
+ LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
+
+ putLink(linkId1, DIRECT, DA1);
+ assertTrue("should be be durable", linkStore.getLink(d1P1, d2P2).isDurable());
+ putLink(linkId1, DIRECT, NDA1);
+ assertFalse("should not be durable", linkStore.getLink(d1P1, d2P2).isDurable());
+ }
+
+ @Test
+ public void testNonDurableToDurable() {
+ final ConnectPoint d1P1 = new ConnectPoint(DID1, P1);
+ final ConnectPoint d2P2 = new ConnectPoint(DID2, P2);
+ LinkKey linkId1 = LinkKey.linkKey(d1P1, d2P2);
+
+ putLink(linkId1, DIRECT, A1);
+ assertFalse("should not be durable", linkStore.getLink(d1P1, d2P2).isDurable());
+ putLink(linkId1, DIRECT, DA1);
+ assertTrue("should be durable", linkStore.getLink(d1P1, d2P2).isDurable());
+ }
+
// If Delegates should be called only on remote events,
// then Simple* should never call them, thus not test required.
@Ignore("Ignore until Delegate spec. is clear.")
@@ -451,7 +536,7 @@
linkStore.unsetDelegate(checkUpdate);
linkStore.setDelegate(checkRemove);
- linkStore.removeLink(d1P1, d2P2);
+ linkStore.removeOrDownLink(d1P1, d2P2);
assertTrue("Remove event fired", removeLatch.await(1, TimeUnit.SECONDS));
}
}