Ayaka Koshibe
Committed by Gerrit Code Review

Link config enhancements:

o Links can be injected using configs
o fix for AllowedEntity check on links
o config for link durability
o Link operator methods for creating Link descriptions

Change-Id: I8a1fabc688a2e7eeb579feced451a6c1ba7e3232
......@@ -17,6 +17,7 @@ package org.onosproject.net.config.basics;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import com.fasterxml.jackson.databind.JsonNode;
import java.time.Duration;
......@@ -28,6 +29,7 @@ public class BasicLinkConfig extends AllowedEntityConfig<LinkKey> {
public static final String TYPE = "type";
public static final String LATENCY = "latency";
public static final String BANDWIDTH = "bandwidth";
public static final String IS_DURABLE = "durable";
/**
* Returns the link type.
......@@ -87,4 +89,26 @@ public class BasicLinkConfig extends AllowedEntityConfig<LinkKey> {
return (BasicLinkConfig) setOrClear(BANDWIDTH, bandwidth);
}
/**
* Returns if link is durable in the network model or not.
*
* @return true for durable, false otherwise
*/
public Boolean isDurable() {
JsonNode res = object.path(IS_DURABLE);
if (res.isMissingNode()) {
return null;
}
return res.asBoolean();
}
/**
* Sets durability for this link.
*
* @param isDurable true for durable, false otherwise
* @return this BasicLinkConfig
*/
public BasicLinkConfig isDurable(Boolean isDurable) {
return (BasicLinkConfig) setOrClear(IS_DURABLE, isDurable);
}
}
......
......@@ -16,12 +16,14 @@
package org.onosproject.net.link.impl;
import static org.slf4j.LoggerFactory.getLogger;
import static com.google.common.base.Preconditions.checkNotNull;
import java.time.Duration;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.config.ConfigOperator;
import org.onosproject.net.config.basics.BasicLinkConfig;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Link;
import org.onosproject.net.SparseAnnotations;
......@@ -81,6 +83,46 @@ public final class BasicLinkOperator implements ConfigOperator {
if (cfg.bandwidth() != DEF_BANDWIDTH) {
b.set(AnnotationKeys.BANDWIDTH, String.valueOf(cfg.bandwidth()));
}
if (cfg.isDurable() != null) {
b.set(AnnotationKeys.DURABLE, String.valueOf(cfg.isDurable()));
}
return DefaultAnnotations.union(an, b.build());
}
/**
* Generates a link description from a link description entity. The endpoints
* must be specified to indicate directionality.
*
* @param src the source ConnectPoint
* @param dst the destination ConnectPoint
* @param link the link config entity
* @return a linkDescription based on the config
*/
public static LinkDescription descriptionOf(
ConnectPoint src, ConnectPoint dst, Link link) {
checkNotNull(src, "Must supply a source endpoint");
checkNotNull(dst, "Must supply a destination endpoint");
checkNotNull(link, "Must supply a link");
return new DefaultLinkDescription(
src, dst, link.type(), (SparseAnnotations) link.annotations());
}
/**
* Generates a link description from a link config entity. This is for
* links that cannot be discovered and has to be injected. The endpoints
* must be specified to indicate directionality.
*
* @param src the source ConnectPoint
* @param dst the destination ConnectPoint
* @param link the link config entity
* @return a linkDescription based on the config
*/
public static LinkDescription descriptionOf(
ConnectPoint src, ConnectPoint dst, BasicLinkConfig link) {
checkNotNull(src, "Must supply a source endpoint");
checkNotNull(dst, "Must supply a destination endpoint");
checkNotNull(link, "Must supply a link config");
return new DefaultLinkDescription(
src, dst, link.type(), combine(link, DefaultAnnotations.EMPTY));
}
}
......
......@@ -18,6 +18,7 @@ package org.onosproject.net.link.impl;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.Sets;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
......@@ -25,6 +26,7 @@ import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.onosproject.net.provider.AbstractListenerProviderRegistry;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigService;
......@@ -54,7 +56,6 @@ import org.slf4j.Logger;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static org.onosproject.net.LinkKey.linkKey;
import static org.onosproject.security.AppGuard.checkPermission;
import static org.slf4j.LoggerFactory.getLogger;
......@@ -207,6 +208,10 @@ public class LinkManager
post(store.removeLink(src, dst));
}
private boolean isAllowed(BasicLinkConfig cfg) {
return (cfg == null || cfg.isAllowed());
}
// Auxiliary interceptor for device remove events to prune links that
// are associated with the removed device or its port.
private class InternalDeviceListener implements DeviceListener {
......@@ -240,13 +245,14 @@ public class LinkManager
checkNotNull(linkDescription, LINK_DESC_NULL);
checkValidity();
linkDescription = validateLink(linkDescription);
LinkEvent event = store.createOrUpdateLink(provider().id(),
linkDescription);
if (linkDescription != null) {
LinkEvent event = store.createOrUpdateLink(provider().id(), linkDescription);
if (event != null) {
log.info("Link {} detected", linkDescription);
post(event);
}
}
}
// returns a LinkDescription made from the union of the BasicLinkConfig
// annotations if it exists
......@@ -258,11 +264,12 @@ public class LinkManager
BasicLinkConfig cfgTwo = networkConfigService.getConfig(linkKey(linkDescription.dst(),
linkDescription.src()),
BasicLinkConfig.class);
checkState(cfg == null || cfg.isAllowed(), "Link " + linkDescription.toString() + " is not allowed");
checkState(cfgTwo == null || cfgTwo.isAllowed(), "Link " + linkDescription.toString() + " is not allowed");
if (isAllowed(cfg) && isAllowed(cfgTwo)) {
return BasicLinkOperator.combine(cfg, linkDescription);
} else {
log.trace("Link " + linkDescription.toString() + " is not allowed");
return null;
}
}
@Override
......@@ -324,20 +331,41 @@ public class LinkManager
// listens for NetworkConfigEvents of type BasicLinkConfig and removes
// links that the config does not allow
private class InternalNetworkConfigListener implements NetworkConfigListener {
@Override
public boolean isRelevant(NetworkConfigEvent event) {
return event.configClass().equals(BasicLinkConfig.class)
&& (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED
|| event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
}
@Override
public void event(NetworkConfigEvent event) {
if ((event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) &&
event.configClass().equals(BasicLinkConfig.class)) {
log.info("Detected Link network config event {}", event.type());
LinkKey lk = (LinkKey) event.subject();
BasicLinkConfig cfg = networkConfigService.getConfig(lk, BasicLinkConfig.class);
if (cfg != null && !cfg.isAllowed()) {
if (!isAllowed(cfg)) {
log.info("Kicking out links between {} and {}", lk.src(), lk.dst());
removeLink(lk.src(), lk.dst());
removeLink(lk.dst(), lk.src());
return;
}
Link link = getLink(lk.src(), lk.dst());
LinkDescription fldesc;
LinkDescription rldesc;
if (link == null) {
fldesc = BasicLinkOperator.descriptionOf(lk.src(), lk.dst(), cfg);
rldesc = BasicLinkOperator.descriptionOf(lk.dst(), lk.src(), cfg);
} else {
fldesc = BasicLinkOperator.combine(cfg,
BasicLinkOperator.descriptionOf(lk.src(), lk.dst(), link));
rldesc = BasicLinkOperator.combine(cfg,
BasicLinkOperator.descriptionOf(lk.dst(), lk.src(), link));
}
// XXX think of sane way to fetch the LinkProvider
store.createOrUpdateLink(ProviderId.NONE, fldesc);
store.createOrUpdateLink(ProviderId.NONE, rldesc);
}
}
}
......
......@@ -211,7 +211,10 @@ public class ECLinkStore
// otherwise signal the actual master.
if (clusterService.getLocalNode().id().equals(dstNodeId)) {
LinkKey linkKey = linkKey(linkDescription.src(), linkDescription.dst());
Provided<LinkKey> internalLinkKey = new Provided<>(linkKey, providerId);
Provided<LinkKey> internalLinkKey = getProvided(linkKey, providerId);
if (internalLinkKey == null) {
return null;
}
linkDescriptions.compute(internalLinkKey, (k, v) -> createOrUpdateLinkInternal(v , linkDescription));
return refreshLinkCache(linkKey);
} else {
......@@ -226,6 +229,18 @@ public class ECLinkStore
}
}
private Provided<LinkKey> getProvided(LinkKey linkKey, ProviderId provId) {
ProviderId bpid = getBaseProviderId(linkKey);
if (provId == null) {
// The LinkService didn't know who this LinkKey belongs to.
// A fix is to either modify the getProvider() in LinkService classes
// or expose the contents of linkDescriptions to the LinkService.
return (bpid == null) ? null : new Provided<>(linkKey, bpid);
} else {
return new Provided<>(linkKey, provId);
}
}
private LinkDescription createOrUpdateLinkInternal(LinkDescription current, LinkDescription updated) {
if (current != null) {
// we only allow transition from INDIRECT -> DIRECT
......