X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Fconnect%2Fnetconf%2Flistener%2FNetconfSessionCapabilities.java;h=2642116927cde25de425d8de95884cb5d0ae3b57;hp=82903ea4ec174a373dc0648901cde3c5c02d0348;hb=5b348df8d4e3cd5d051449983f00e579d4e41d77;hpb=63ebb308c97fc06eafe813c347f9c8d7cde541eb diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java index 82903ea4ec..2642116927 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/listener/NetconfSessionCapabilities.java @@ -1,7 +1,16 @@ package org.opendaylight.controller.sal.connect.netconf.listener; -import java.util.Arrays; +import com.google.common.base.Objects; +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Splitter; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; + import java.util.Collection; +import java.util.HashSet; import java.util.Set; import org.opendaylight.controller.netconf.client.NetconfClientSession; @@ -10,102 +19,153 @@ import org.opendaylight.yangtools.yang.common.QName; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Objects; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.FluentIterable; -import com.google.common.collect.Iterables; -import com.google.common.collect.Sets; - public final class NetconfSessionCapabilities { - private static final Logger logger = LoggerFactory.getLogger(NetconfSessionCapabilities.class); + private static final class ParameterMatcher { + private final Predicate predicate; + private final int skipLength; - private final Set capabilities; + ParameterMatcher(final String name) { + predicate = new Predicate() { + @Override + public boolean apply(final String input) { + return input.startsWith(name); + } + }; + + this.skipLength = name.length(); + } + + private String from(final Iterable params) { + final Optional o = Iterables.tryFind(params, predicate); + if (!o.isPresent()) { + return null; + } + + return o.get().substring(skipLength); + } + } + + private static final Logger LOG = LoggerFactory.getLogger(NetconfSessionCapabilities.class); + private static final ParameterMatcher MODULE_PARAM = new ParameterMatcher("module="); + private static final ParameterMatcher REVISION_PARAM = new ParameterMatcher("revision="); + private static final ParameterMatcher BROKEN_REVISON_PARAM = new ParameterMatcher("amp;revision="); + private static final Splitter AMP_SPLITTER = Splitter.on('&'); + private static final Predicate CONTAINS_REVISION = new Predicate() { + @Override + public boolean apply(final String input) { + return input.contains("revision="); + } + }; private final Set moduleBasedCaps; + private final Set nonModuleCaps; - private NetconfSessionCapabilities(final Set capabilities, final Set moduleBasedCaps) { - this.capabilities = capabilities; - this.moduleBasedCaps = moduleBasedCaps; + private NetconfSessionCapabilities(final Set nonModuleCaps, final Set moduleBasedCaps) { + this.nonModuleCaps = Preconditions.checkNotNull(nonModuleCaps); + this.moduleBasedCaps = Preconditions.checkNotNull(moduleBasedCaps); } public Set getModuleBasedCaps() { return moduleBasedCaps; } - public boolean containsCapability(final String capability) { - return capabilities.contains(capability); + public Set getNonModuleCaps() { + return nonModuleCaps; } - public boolean containsCapability(final QName capability) { + public boolean containsNonModuleCapability(final String capability) { + return nonModuleCaps.contains(capability); + } + + public boolean containsModuleCapability(final QName capability) { return moduleBasedCaps.contains(capability); } @Override public String toString() { return Objects.toStringHelper(this) - .add("capabilities", capabilities) + .add("capabilities", nonModuleCaps) + .add("moduleBasedCapabilities", moduleBasedCaps) .add("rollback", isRollbackSupported()) .add("monitoring", isMonitoringSupported()) .toString(); } public boolean isRollbackSupported() { - return containsCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()); + return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_ROLLBACK_ON_ERROR_URI.toString()); + } + + public boolean isCandidateSupported() { + return containsNonModuleCapability(NetconfMessageTransformUtil.NETCONF_CANDIDATE_URI.toString()); } public boolean isMonitoringSupported() { - return containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING) - || containsCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString()); + return containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING) + || containsNonModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING.getNamespace().toString()); + } + + public NetconfSessionCapabilities replaceModuleCaps(final NetconfSessionCapabilities netconfSessionModuleCapabilities) { + final Set moduleBasedCaps = Sets.newHashSet(netconfSessionModuleCapabilities.getModuleBasedCaps()); + + // Preserve monitoring module, since it indicates support for ietf-netconf-monitoring + if(containsModuleCapability(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING)) { + moduleBasedCaps.add(NetconfMessageTransformUtil.IETF_NETCONF_MONITORING); + } + return new NetconfSessionCapabilities(getNonModuleCaps(), moduleBasedCaps); } public static NetconfSessionCapabilities fromNetconfSession(final NetconfClientSession session) { return fromStrings(session.getServerCapabilities()); } + private static final QName cachedQName(String namespace, String revision, String moduleName) { + return QName.cachedReference(QName.create(namespace, revision, moduleName)); + } + public static NetconfSessionCapabilities fromStrings(final Collection capabilities) { - final Set moduleBasedCaps = Sets.newHashSet(); + final Set moduleBasedCaps = new HashSet<>(); + final Set nonModuleCaps = Sets.newHashSet(capabilities); for (final String capability : capabilities) { - if(isModuleBasedCapability(capability)) { - final String[] parts = capability.split("\\?"); - final String namespace = parts[0]; - final FluentIterable queryParams = FluentIterable.from(Arrays.asList(parts[1].split("&"))); - - String revision = getStringAndTransform(queryParams, "revision=", "revision="); - - final String moduleName = getStringAndTransform(queryParams, "module=", "module="); - - if (revision == null) { - logger.debug("Netconf device was not reporting revision correctly, trying to get amp;revision="); - revision = getStringAndTransform(queryParams, "amp;revision=", "amp;revision="); + final int qmark = capability.indexOf('?'); + if (qmark == -1) { + continue; + } - if (revision == null) { - logger.warn("Netconf device returned revision incorrectly escaped for {}", capability); - } - } - moduleBasedCaps.add(QName.create(namespace, revision, moduleName)); + final String namespace = capability.substring(0, qmark); + final Iterable queryParams = AMP_SPLITTER.split(capability.substring(qmark + 1)); + final String moduleName = MODULE_PARAM.from(queryParams); + if (moduleName == null) { + continue; } - } - return new NetconfSessionCapabilities(Sets.newHashSet(capabilities), moduleBasedCaps); - } + String revision = REVISION_PARAM.from(queryParams); + if (revision != null) { + moduleBasedCaps.add(cachedQName(namespace, revision, moduleName)); + nonModuleCaps.remove(capability); + continue; + } - private static boolean isModuleBasedCapability(final String capability) { - return capability.contains("?") && capability.contains("module=") && capability.contains("revision="); - } + /* + * We have seen devices which mis-escape revision, but the revision may not + * even be there. First check if there is a substring that matches revision. + */ + if (!Iterables.any(queryParams, CONTAINS_REVISION)) { + continue; + } - private static String getStringAndTransform(final Iterable queryParams, final String match, - final String substringToRemove) { - final Optional found = Iterables.tryFind(queryParams, new Predicate() { - @Override - public boolean apply(final String input) { - return input.startsWith(match); + LOG.debug("Netconf device was not reporting revision correctly, trying to get amp;revision="); + revision = BROKEN_REVISON_PARAM.from(queryParams); + if (revision == null) { + LOG.warn("Netconf device returned revision incorrectly escaped for {}, ignoring it", capability); } - }); - return found.isPresent() ? found.get().replaceAll(substringToRemove, "") : null; - } + // FIXME: do we really want to continue here? + moduleBasedCaps.add(cachedQName(namespace, revision, moduleName)); + nonModuleCaps.remove(capability); + } + return new NetconfSessionCapabilities(ImmutableSet.copyOf(nonModuleCaps), ImmutableSet.copyOf(moduleBasedCaps)); + } }