From d86e82a7318555107ed957838b11e6b2ab5d9311 Mon Sep 17 00:00:00 2001 From: Jakub Morvay Date: Tue, 21 Jun 2016 13:23:03 +0200 Subject: [PATCH] Bug 5403 - Support yang-library schema resolution Change-Id: I3599d3cf857cbb7b40c17aafadc9db7c83c4b09d Signed-off-by: Jakub Morvay --- .../topology/AbstractNetconfTopology.java | 8 +- .../netconf/NetconfConnectorModule.java | 8 +- .../sal/connect/api/NetconfDeviceSchemas.java | 21 +++ .../api/NetconfDeviceSchemasResolver.java | 20 +++ .../netconf/LibraryModulesSchemas.java | 168 ++++++++++++++---- .../sal/connect/netconf/NetconfDevice.java | 58 ++++-- .../connect/netconf/NetconfStateSchemas.java | 26 +-- .../NetconfStateSchemasResolverImpl.java | 33 ++++ .../connect/netconf/NetconfDeviceTest.java | 10 +- .../src/test/resources/yang-library-fail.xml | 3 + .../src/test/resources/yang-library.json | 5 +- .../src/test/resources/yang-library.xml | 3 + .../cli/NetconfDeviceConnectionManager.java | 2 +- 13 files changed, 280 insertions(+), 85 deletions(-) create mode 100644 netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemas.java create mode 100644 netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemasResolver.java create mode 100644 netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasResolverImpl.java diff --git a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java index 6d5cdf898b..b75efc080f 100644 --- a/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java +++ b/netconf/netconf-topology/src/main/java/org/opendaylight/netconf/topology/AbstractNetconfTopology.java @@ -48,7 +48,7 @@ import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; -import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas; +import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; import org.opendaylight.netconf.sal.connect.netconf.SchemalessNetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCapabilities; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; @@ -150,7 +150,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin schemaResourcesDTOs.put(DEFAULT_CACHE_DIRECTORY, new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_CONTEXT_FACTORY, - new NetconfStateSchemas.NetconfStateSchemasResolverImpl())); + new NetconfStateSchemasResolverImpl())); DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE); DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener( TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY)); @@ -372,7 +372,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin if (schemaResourcesDTO == null) { schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, - new NetconfStateSchemas.NetconfStateSchemasResolverImpl()); + new NetconfStateSchemasResolverImpl()); } return schemaResourcesDTO; @@ -394,7 +394,7 @@ public abstract class AbstractNetconfTopology implements NetconfTopology, Bindin createDeviceFilesystemCache(moduleSchemaCacheDirectory); repository.registerSchemaSourceListener(deviceCache); return new NetconfDevice.SchemaResourcesDTO(repository, schemaContextFactory, - new NetconfStateSchemas.NetconfStateSchemasResolverImpl()); + new NetconfStateSchemasResolverImpl()); } /** diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java index 79a439c0eb..96cb688e45 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/controller/config/yang/md/sal/connector/netconf/NetconfConnectorModule.java @@ -40,7 +40,7 @@ import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.netconf.sal.connect.netconf.LibraryModulesSchemas; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; -import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas; +import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.listener.UserPreferences; @@ -134,7 +134,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co schemaResourcesDTOs.put(DEFAULT_CACHE_DIRECTORY, new NetconfDevice.SchemaResourcesDTO(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_CONTEXT_FACTORY, - new NetconfStateSchemas.NetconfStateSchemasResolverImpl())); + new NetconfStateSchemasResolverImpl())); DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener(DEFAULT_CACHE); DEFAULT_SCHEMA_REPOSITORY.registerSchemaSourceListener( TextToASTTransformer.create(DEFAULT_SCHEMA_REPOSITORY, DEFAULT_SCHEMA_REPOSITORY)); @@ -284,7 +284,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co if (schemaResourcesDTO == null) { schemaResourcesDTO = new NetconfDevice.SchemaResourcesDTO(schemaRegistry, schemaContextFactory, - new NetconfStateSchemas.NetconfStateSchemasResolverImpl()); + new NetconfStateSchemasResolverImpl()); } final NetconfDevice device = new NetconfDeviceBuilder() @@ -330,7 +330,7 @@ public final class NetconfConnectorModule extends org.opendaylight.controller.co createDeviceFilesystemCache(moduleSchemaCacheDirectory); repository.registerSchemaSourceListener(deviceCache); return new NetconfDevice.SchemaResourcesDTO(repository, schemaContextFactory, - new NetconfStateSchemas.NetconfStateSchemasResolverImpl()); + new NetconfStateSchemasResolverImpl()); } /** diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemas.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemas.java new file mode 100644 index 0000000000..54c413555e --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemas.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.connect.api; + +import java.util.Set; +import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; + +public interface NetconfDeviceSchemas { + + Set getAvailableYangSchemasQNames(); +} diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemasResolver.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemasResolver.java new file mode 100644 index 0000000000..c1a465ef6e --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/api/NetconfDeviceSchemasResolver.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.connect.api; + +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; + +/** + * Factory for netconf device schemas + */ +public interface NetconfDeviceSchemasResolver { + NetconfDeviceSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id); +} diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemas.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemas.java index dd6f855d7a..adb95c5540 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemas.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/LibraryModulesSchemas.java @@ -8,23 +8,36 @@ package org.opendaylight.netconf.sal.connect.netconf; import static javax.xml.bind.DatatypeConverter.printBase64Binary; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DATA_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toId; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; import com.google.gson.stream.JsonReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.MalformedURLException; +import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.util.AbstractMap; import java.util.Collections; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ExecutionException; import org.opendaylight.controller.config.util.xml.XmlUtil; +import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc; +import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160409.ModulesState; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160409.module.list.Module; import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; @@ -39,6 +52,7 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream; import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; @@ -54,28 +68,47 @@ import org.xml.sax.SAXException; * Holds URLs with YANG schema resources for all yang modules reported in * ietf-netconf-yang-library/modules-state/modules node */ -public class LibraryModulesSchemas { +public class LibraryModulesSchemas implements NetconfDeviceSchemas { private static final Logger LOG = LoggerFactory.getLogger(LibraryModulesSchemas.class); - private static SchemaContext libraryContext; - - private final Map availableModels; + private static final SchemaContext LIBRARY_CONTEXT; static { final ModuleInfoBackedContext moduleInfoBackedContext = ModuleInfoBackedContext.create(); moduleInfoBackedContext.registerModuleInfo( org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160409. $YangModuleInfoImpl.getInstance()); - libraryContext = moduleInfoBackedContext.tryToCreateSchemaContext().get(); + LIBRARY_CONTEXT = moduleInfoBackedContext.tryToCreateSchemaContext().get(); + } + + private final Map availableModels; + + private static final YangInstanceIdentifier MODULES_STATE_MODULE_LIST = + YangInstanceIdentifier.builder().node(ModulesState.QNAME).node(Module.QNAME).build(); + + private static final ContainerNode GET_MODULES_STATE_MODULE_LIST_RPC; + + static { + final DataContainerChild filter = + NetconfMessageTransformUtil.toFilterStructure(MODULES_STATE_MODULE_LIST, LIBRARY_CONTEXT); + GET_MODULES_STATE_MODULE_LIST_RPC = + Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_GET_QNAME)).withChild(filter).build(); } - private LibraryModulesSchemas(final Map availableModels) { + private LibraryModulesSchemas(final Map availableModels) { this.availableModels = availableModels; } public Map getAvailableModels() { - return availableModels; + final Map result = Maps.newHashMap(); + for (final Map.Entry entry : availableModels.entrySet()) { + final SourceIdentifier sId = RevisionSourceIdentifier + .create(entry.getKey().getLocalName(), Optional.fromNullable(entry.getKey().getFormattedRevision())); + result.put(sId, entry.getValue()); + } + + return result; } @@ -92,22 +125,89 @@ public class LibraryModulesSchemas { if(connection instanceof HttpURLConnection) { connection.setRequestProperty("Accept", "application/xml"); - String userpass = username + ":" + password; - String basicAuth = "Basic " + printBase64Binary(userpass.getBytes()); + final String userpass = username + ":" + password; + final String basicAuth = "Basic " + printBase64Binary(userpass.getBytes()); connection.setRequestProperty("Authorization", basicAuth); } return createFromURLConnection(connection); - } catch (IOException e) { + } catch (final IOException e) { LOG.warn("Unable to download yang library from {}", url, e); - return new LibraryModulesSchemas(Collections.emptyMap()); + return new LibraryModulesSchemas(Collections.emptyMap()); } } - private static LibraryModulesSchemas createFromURLConnection(URLConnection connection) { + public static LibraryModulesSchemas create(final NetconfDeviceRpc deviceRpc, final RemoteDeviceId deviceId) { + final DOMRpcResult moduleListNodeResult; + try { + moduleListNodeResult = + deviceRpc.invokeRpc(toPath(NETCONF_GET_QNAME), GET_MODULES_STATE_MODULE_LIST_RPC).get(); + } catch (final InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException(deviceId + ": Interrupted while waiting for response to " + + MODULES_STATE_MODULE_LIST, e); + } catch (final ExecutionException e) { + LOG.warn("{}: Unable to detect available schemas, get to {} failed", deviceId, MODULES_STATE_MODULE_LIST, e); + return new LibraryModulesSchemas(Collections.emptyMap()); + } + + if(moduleListNodeResult.getErrors().isEmpty() == false) { + LOG.warn("{}: Unable to detect available schemas, get to {} failed, {}", + deviceId, MODULES_STATE_MODULE_LIST, moduleListNodeResult.getErrors()); + return new LibraryModulesSchemas(Collections.emptyMap()); + } + + + final Optional> modulesStateNode = + findModulesStateNode(moduleListNodeResult.getResult()); + if(modulesStateNode.isPresent()) { + Preconditions.checkState(modulesStateNode.get() instanceof ContainerNode, + "Expecting container containing schemas, but was %s", modulesStateNode.get()); + return create(((ContainerNode) modulesStateNode.get())); + } else { + LOG.warn("{}: Unable to detect available schemas, get to {} was empty", deviceId, toId(ModulesState.QNAME)); + return new LibraryModulesSchemas(Collections.emptyMap()); + } + } + + private static Optional> findModulesStateNode(final NormalizedNode result) { + if(result == null) { + return Optional.absent(); + } + final Optional> dataNode = ((DataContainerNode) result).getChild(toId(NETCONF_DATA_QNAME)); + if(dataNode.isPresent() == false) { + return Optional.absent(); + } + + return ((DataContainerNode) dataNode.get()).getChild(toId(ModulesState.QNAME)); + } + + private static LibraryModulesSchemas create(final ContainerNode modulesStateNode) { + final YangInstanceIdentifier.NodeIdentifier moduleListNodeId = + new YangInstanceIdentifier.NodeIdentifier(Module.QNAME); + final Optional> moduleListNode = + modulesStateNode.getChild(moduleListNodeId); + Preconditions.checkState(moduleListNode.isPresent(), + "Unable to find list: %s in %s", moduleListNodeId, modulesStateNode); + Preconditions.checkState(moduleListNode.get() instanceof MapNode, + "Unexpected structure for container: %s in : %s. Expecting a list", + moduleListNodeId, modulesStateNode); + + final ImmutableMap.Builder schemasMapping = new ImmutableMap.Builder<>(); + for (final MapEntryNode moduleNode : ((MapNode) moduleListNode.get()).getValue()) { + final Optional> schemaMappingEntry = createFromEntry(moduleNode); + if (schemaMappingEntry.isPresent()) { + schemasMapping.put(createFromEntry(moduleNode).get()); + } + } + + return new LibraryModulesSchemas(schemasMapping.build()); + } + + private static LibraryModulesSchemas createFromURLConnection(final URLConnection connection) { String contentType = connection.getContentType(); @@ -124,7 +224,7 @@ public class LibraryModulesSchemas { contentType.equals("application/json") ? readJson(in) : readXml(in); if (!optionalModulesStateNode.isPresent()) { - return new LibraryModulesSchemas(Collections.emptyMap()); + return new LibraryModulesSchemas(Collections.emptyMap()); } final NormalizedNode modulesStateNode = optionalModulesStateNode.get(); @@ -143,18 +243,18 @@ public class LibraryModulesSchemas { "Unexpected structure for container: %s in : %s. Expecting a list", moduleListNodeId, modulesStateNode); - final ImmutableMap.Builder schemasMapping = new ImmutableMap.Builder<>(); + final ImmutableMap.Builder schemasMapping = new ImmutableMap.Builder<>(); for (final MapEntryNode moduleNode : ((MapNode) moduleListNode.get()).getValue()) { - final Optional> schemaMappingEntry = createFromEntry(moduleNode); + final Optional> schemaMappingEntry = createFromEntry(moduleNode); if (schemaMappingEntry.isPresent()) { schemasMapping.put(createFromEntry(moduleNode).get()); } } return new LibraryModulesSchemas(schemasMapping.build()); - } catch (IOException e) { + } catch (final IOException e) { LOG.warn("Unable to download yang library from {}", connection.getURL(), e); - return new LibraryModulesSchemas(Collections.emptyMap()); + return new LibraryModulesSchemas(Collections.emptyMap()); } } @@ -175,9 +275,9 @@ public class LibraryModulesSchemas { return createFromURLConnection(connection); - } catch (IOException e) { + } catch (final IOException e) { LOG.warn("Unable to download yang library from {}", url, e); - return new LibraryModulesSchemas(Collections.emptyMap()); + return new LibraryModulesSchemas(Collections.emptyMap()); } } @@ -195,7 +295,7 @@ public class LibraryModulesSchemas { final NormalizedNodeResult resultHolder = new NormalizedNodeResult(); final NormalizedNodeStreamWriter writer = ImmutableNormalizedNodeStreamWriter.from(resultHolder); - final JsonParserStream jsonParser = JsonParserStream.create(writer, libraryContext); + final JsonParserStream jsonParser = JsonParserStream.create(writer, LIBRARY_CONTEXT); final JsonReader reader = new JsonReader(new InputStreamReader(in)); jsonParser.parse(reader); @@ -207,12 +307,12 @@ public class LibraryModulesSchemas { private static Optional> readXml(final InputStream in) { final DomToNormalizedNodeParserFactory parserFactory = - DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, libraryContext); + DomToNormalizedNodeParserFactory.getInstance(XmlUtils.DEFAULT_XML_CODEC_PROVIDER, LIBRARY_CONTEXT); try { final NormalizedNode parsed = parserFactory.getContainerNodeParser().parse(Collections.singleton(XmlUtil.readXmlToElement(in)), - (ContainerSchemaNode) libraryContext.getDataChildByName(ModulesState.QNAME)); + (ContainerSchemaNode) LIBRARY_CONTEXT.getDataChildByName(ModulesState.QNAME)); return Optional.of(parsed); } catch (IOException|SAXException e) { LOG.warn("Unable to parse yang library xml content", e); @@ -221,7 +321,7 @@ public class LibraryModulesSchemas { return Optional.>absent(); } - private static Optional> createFromEntry(final MapEntryNode moduleNode) { + private static Optional> createFromEntry(final MapEntryNode moduleNode) { Preconditions.checkArgument( moduleNode.getNodeType().equals(Module.QNAME), "Wrong QName %s", moduleNode.getNodeType()); @@ -234,7 +334,7 @@ public class LibraryModulesSchemas { if(revision.isPresent()) { if(!SourceIdentifier.REVISION_PATTERN.matcher(revision.get()).matches()) { LOG.warn("Skipping library schema for {}. Revision {} is in wrong format.", moduleNode, revision.get()); - return Optional.>absent(); + return Optional.>absent(); } } @@ -243,18 +343,20 @@ public class LibraryModulesSchemas { childNodeId = new YangInstanceIdentifier.NodeIdentifier(QName.create(Module.QNAME, "schema")); final Optional schemaUriAsString = getSingleChildNodeValue(moduleNode, childNodeId); + childNodeId = new YangInstanceIdentifier.NodeIdentifier(QName.create(Module.QNAME, "namespace")); + final String moduleNameSpace = getSingleChildNodeValue(moduleNode, childNodeId).get(); - final SourceIdentifier sourceId = revision.isPresent() - ? RevisionSourceIdentifier.create(moduleName, revision.get()) - : RevisionSourceIdentifier.create(moduleName); + final QName moduleQName = revision.isPresent() + ? QName.create(moduleNameSpace, revision.get(), moduleName) + : QName.create(URI.create(moduleNameSpace), null, moduleName); try { - return Optional.>of(new AbstractMap.SimpleImmutableEntry<>( - sourceId, new URL(schemaUriAsString.get()))); - } catch (MalformedURLException e) { + return Optional.>of(new AbstractMap.SimpleImmutableEntry<>( + moduleQName, new URL(schemaUriAsString.get()))); + } catch (final MalformedURLException e) { LOG.warn("Skipping library schema for {}. URL {} representing yang schema resource is not valid", moduleNode, schemaUriAsString.get()); - return Optional.>absent(); + return Optional.>absent(); } } @@ -273,4 +375,8 @@ public class LibraryModulesSchemas { ? Optional.absent() : Optional.of(value.toString().trim()); } + @Override + public Set getAvailableYangSchemasQNames() { + return null; + } } diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java index 4070c96750..69efd2858e 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDevice.java @@ -32,6 +32,8 @@ import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; import org.opendaylight.netconf.api.NetconfMessage; import org.opendaylight.netconf.sal.connect.api.MessageTransformer; +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas; +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver; import org.opendaylight.netconf.sal.connect.api.RemoteDevice; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceCommunicator; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; @@ -40,6 +42,7 @@ import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommun import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc; import org.opendaylight.netconf.sal.connect.netconf.schema.NetconfRemoteSchemaYangSourceProvider; +import org.opendaylight.netconf.sal.connect.netconf.schema.YangLibrarySchemaYangSourceProvider; import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseSchema; import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.NetconfMessageTransformer; import org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil; @@ -56,6 +59,7 @@ import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceRepresentation import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier; import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource; import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource; +import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceProvider; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistration; import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry; import org.slf4j.Logger; @@ -83,7 +87,7 @@ public class NetconfDevice implements RemoteDevice salFacade; private final ListeningExecutorService processingExecutor; protected final SchemaSourceRegistry schemaRegistry; - private final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver; + private final NetconfDeviceSchemasResolver stateSchemasResolver; private final NotificationHandler notificationHandler; protected final List> sourceRegistrations = Lists.newArrayList(); @@ -94,7 +98,7 @@ public class NetconfDevice implements RemoteDevice resolvedSourceCallback = new FutureCallback() { @Override public void onSuccess(final DeviceSources result) { - addProvidedSourcesToSchemaRegistry(initRpc, result); + addProvidedSourcesToSchemaRegistry(result); setUpSchema(result); } @@ -193,7 +197,7 @@ public class NetconfDevice implements RemoteDevice yangProvider = deviceSources.getSourceProvider(); for (final SourceIdentifier sourceId : deviceSources.getProvidedSources()) { sourceRegistrations.add(schemaRegistry.registerSchemaSource(yangProvider, PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue()))); @@ -260,12 +264,14 @@ public class NetconfDevice implements RemoteDevice requiredSources = Sets.newHashSet(remoteSessionCapabilities.getModuleBasedCaps()); @@ -331,7 +337,15 @@ public class NetconfDevice implements RemoteDevice sourceProvider; + if(availableSchemas instanceof LibraryModulesSchemas) { + sourceProvider = new YangLibrarySchemaYangSourceProvider(id, + ((LibraryModulesSchemas) availableSchemas).getAvailableModels()); + } else { + sourceProvider = new NetconfRemoteSchemaYangSourceProvider(id, deviceRpc); + } + + return new DeviceSources(requiredSources, providedSources, sourceProvider); } } @@ -341,10 +355,13 @@ public class NetconfDevice implements RemoteDevice requiredSources; private final Set providedSources; + private final SchemaSourceProvider sourceProvider; - public DeviceSources(final Set requiredSources, final Set providedSources) { + public DeviceSources(final Set requiredSources, final Set providedSources, + final SchemaSourceProvider sourceProvider) { this.requiredSources = requiredSources; this.providedSources = providedSources; + this.sourceProvider = sourceProvider; } public Set getRequiredSourcesQName() { @@ -363,6 +380,9 @@ public class NetconfDevice implements RemoteDevice getSourceProvider() { + return sourceProvider; + } } /** @@ -401,7 +421,7 @@ public class NetconfDevice implements RemoteDevice handleMissingSchemaSourceException(Collection requiredSources, final MissingSchemaSourceException t) { + private Collection handleMissingSchemaSourceException(final Collection requiredSources, final MissingSchemaSourceException t) { // In case source missing, try without it final SourceIdentifier missingSource = t.getSourceId(); LOG.warn("{}: Unable to build schema context, missing source {}, will reattempt without it", id, missingSource); @@ -438,7 +458,7 @@ public class NetconfDevice implements RemoteDevice handleSchemaResolutionException(Collection requiredSources, final SchemaResolutionException resolutionException) { + private Collection handleSchemaResolutionException(final Collection requiredSources, final SchemaResolutionException resolutionException) { // In case resolution error, try only with resolved sources final Set unresolvedSources = resolutionException.getUnsatisfiedImports().keySet(); capabilities.addUnresolvedCapabilities(getQNameFromSourceIdentifiers(unresolvedSources), UnavailableCapability.FailureReason.UnableToResolve); diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java index b738ed69de..7f4ecd96db 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemas.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.Set; import java.util.concurrent.ExecutionException; import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc; import org.opendaylight.netconf.sal.connect.netconf.schema.mapping.BaseSchema; @@ -51,28 +52,10 @@ import org.slf4j.LoggerFactory; /** * Holds QNames for all yang modules reported by ietf-netconf-monitoring/state/schemas */ -public final class NetconfStateSchemas { +public final class NetconfStateSchemas implements NetconfDeviceSchemas { private static final Logger LOG = LoggerFactory.getLogger(NetconfStateSchemas.class); - /** - * Factory for NetconfStateSchemas - */ - public interface NetconfStateSchemasResolver { - NetconfStateSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id); - } - - /** - * Default implementation resolving schemas QNames from netconf-state - */ - public static final class NetconfStateSchemasResolverImpl implements NetconfStateSchemasResolver { - - @Override - public NetconfStateSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { - return NetconfStateSchemas.create(deviceRpc, remoteSessionCapabilities, id); - } - } - public static final NetconfStateSchemas EMPTY = new NetconfStateSchemas(Collections.emptySet()); private static final YangInstanceIdentifier STATE_SCHEMAS_IDENTIFIER = @@ -96,6 +79,7 @@ public final class NetconfStateSchemas { return availableYangSchemas; } + @Override public Set getAvailableYangSchemasQNames() { return Sets.newHashSet(Collections2.transform(getAvailableYangSchemas(), new Function() { @Override @@ -108,7 +92,7 @@ public final class NetconfStateSchemas { /** * Issue get request to remote device and parse response to find all schemas under netconf-state/schemas */ - private static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { + static NetconfStateSchemas create(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { if(remoteSessionCapabilities.isMonitoringSupported() == false) { // TODO - need to search for get-schema support, not just ietf-netconf-monitoring support // issue might be a deviation to ietf-netconf-monitoring where get-schema is unsupported... @@ -199,7 +183,7 @@ public final class NetconfStateSchemas { QName childNode = NetconfMessageTransformUtil.IETF_NETCONF_MONITORING_SCHEMA_FORMAT; - String formatAsString = getSingleChildNodeValue(schemaNode, childNode).get(); + final String formatAsString = getSingleChildNodeValue(schemaNode, childNode).get(); if(formatAsString.equals(Yang.QNAME.toString()) == false) { LOG.debug("{}: Ignoring schema due to unsupported format: {}", id, formatAsString); diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasResolverImpl.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasResolverImpl.java new file mode 100644 index 0000000000..6931cf8733 --- /dev/null +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/NetconfStateSchemasResolverImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.opendaylight.netconf.sal.connect.netconf; + +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas; +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver; +import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; +import org.opendaylight.netconf.sal.connect.netconf.sal.NetconfDeviceRpc; +import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev160409.ModulesState; +import org.opendaylight.yangtools.yang.common.QName; + +/** + * Default implementation resolving schemas QNames from netconf-state or from modules-state + */ +public final class NetconfStateSchemasResolverImpl implements NetconfDeviceSchemasResolver { + + @Override + public NetconfDeviceSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { + if (remoteSessionCapabilities.isMonitoringSupported()) { + return NetconfStateSchemas.create(deviceRpc, remoteSessionCapabilities, id); + } else if (remoteSessionCapabilities.containsModuleCapability(QName.create(ModulesState.QNAME, "ietf-yang-library"))) { + return LibraryModulesSchemas.create(deviceRpc, id); + } + return NetconfStateSchemas.EMPTY; + } +} diff --git a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceTest.java b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceTest.java index 23e86f3aed..c8be625f0b 100644 --- a/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceTest.java +++ b/netconf/sal-netconf-connector/src/test/java/org/opendaylight/netconf/sal/connect/netconf/NetconfDeviceTest.java @@ -44,6 +44,8 @@ import org.opendaylight.controller.md.sal.dom.spi.DefaultDOMRpcResult; import org.opendaylight.netconf.api.NetconfMessage; import org.opendaylight.netconf.api.xml.XmlNetconfConstants; import org.opendaylight.netconf.sal.connect.api.MessageTransformer; +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemas; +import org.opendaylight.netconf.sal.connect.api.NetconfDeviceSchemasResolver; import org.opendaylight.netconf.sal.connect.api.RemoteDeviceHandler; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfSessionPreferences; @@ -101,10 +103,10 @@ public class NetconfDeviceTest { RevisionSourceIdentifier.create(TEST_MODULE + "2", Optional.of(TEST_REVISION)); public static final String TEST_CAPABILITY2 = TEST_NAMESPACE + "?module=" + TEST_MODULE + "2" + "&revision=" + TEST_REVISION; - private static final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver = new NetconfStateSchemas.NetconfStateSchemasResolver() { + private static final NetconfDeviceSchemasResolver stateSchemasResolver = new NetconfDeviceSchemasResolver() { @Override - public NetconfStateSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { + public NetconfDeviceSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { return NetconfStateSchemas.EMPTY; } }; @@ -166,9 +168,9 @@ public class NetconfDeviceTest { } }).when(schemaFactory).createSchemaContext(anyCollectionOf(SourceIdentifier.class)); - final NetconfStateSchemas.NetconfStateSchemasResolver stateSchemasResolver = new NetconfStateSchemas.NetconfStateSchemasResolver() { + final NetconfDeviceSchemasResolver stateSchemasResolver = new NetconfDeviceSchemasResolver() { @Override - public NetconfStateSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { + public NetconfDeviceSchemas resolve(final NetconfDeviceRpc deviceRpc, final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id) { final Module first = Iterables.getFirst(schema.getModules(), null); final QName qName = QName.create(first.getQNameModule(), first.getName()); final NetconfStateSchemas.RemoteYangSchema source1 = new NetconfStateSchemas.RemoteYangSchema(qName); diff --git a/netconf/sal-netconf-connector/src/test/resources/yang-library-fail.xml b/netconf/sal-netconf-connector/src/test/resources/yang-library-fail.xml index ef1feea975..eb54f1cb37 100644 --- a/netconf/sal-netconf-connector/src/test/resources/yang-library-fail.xml +++ b/netconf/sal-netconf-connector/src/test/resources/yang-library-fail.xml @@ -2,6 +2,7 @@ module-with-bad-url + b badurl @@ -9,10 +10,12 @@ module-bad-revision bad-revision + bbb good-ol-module http://www.example.com + bbbds \ No newline at end of file diff --git a/netconf/sal-netconf-connector/src/test/resources/yang-library.json b/netconf/sal-netconf-connector/src/test/resources/yang-library.json index d0f5552821..0b08c0d705 100644 --- a/netconf/sal-netconf-connector/src/test/resources/yang-library.json +++ b/netconf/sal-netconf-connector/src/test/resources/yang-library.json @@ -4,16 +4,19 @@ { "name": "module-with-revision", "revision": "2014-04-08", - "schema": "http://localhost:8181/yanglib/schemas/module-with-revision/2014-04-08" + "schema": "http://localhost:8181/yanglib/schemas/module-with-revision/2014-04-08", + "namespace": "bla" }, { "name": "another-module-with-revision", "revision": "2014-06-08", + "namespace": "test", "schema": "http://localhost:8181/yanglib/schemas/another-module-with-revision/2013-10-21" }, { "name": "module-without-revision", "revision": "", + "namespace": "namespace:module-without-revision", "schema": "http://localhost:8181/yanglib/schemas/module-without-revision/2013-10-21" } ] diff --git a/netconf/sal-netconf-connector/src/test/resources/yang-library.xml b/netconf/sal-netconf-connector/src/test/resources/yang-library.xml index 704ec670a2..c603450933 100644 --- a/netconf/sal-netconf-connector/src/test/resources/yang-library.xml +++ b/netconf/sal-netconf-connector/src/test/resources/yang-library.xml @@ -2,6 +2,7 @@ module-with-revision 2014-04-08 + namespace http://localhost:8181/yanglib/schemas/module-with-revision/2014-04-08 @@ -9,12 +10,14 @@ another-module-with-revision 2013-10-21 + another-module-with-revision:namespace http://localhost:8181/yanglib/schemas/another-module-with-revision/2013-10-21 module-without-revision + module-without-revision:namespace http://localhost:8181/yanglib/schemas/module-without-revision/ diff --git a/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/NetconfDeviceConnectionManager.java b/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/NetconfDeviceConnectionManager.java index 4bbbc11dfc..a3e98b31da 100644 --- a/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/NetconfDeviceConnectionManager.java +++ b/netconf/tools/netconf-cli/src/main/java/org/opendaylight/netconf/cli/NetconfDeviceConnectionManager.java @@ -24,7 +24,7 @@ import org.opendaylight.netconf.client.conf.NetconfClientConfigurationBuilder; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice; import org.opendaylight.netconf.sal.connect.netconf.NetconfDevice.SchemaResourcesDTO; import org.opendaylight.netconf.sal.connect.netconf.NetconfDeviceBuilder; -import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemas.NetconfStateSchemasResolverImpl; +import org.opendaylight.netconf.sal.connect.netconf.NetconfStateSchemasResolverImpl; import org.opendaylight.netconf.sal.connect.netconf.listener.NetconfDeviceCommunicator; import org.opendaylight.netconf.sal.connect.util.RemoteDeviceId; import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory; -- 2.36.6