import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
-import java.util.Set;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.stream.XMLStreamException;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.mdsal.binding.runtime.spi.BindingRuntimeHelpers;
import org.opendaylight.mdsal.dom.api.DOMRpcResult;
-import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemas;
import org.opendaylight.netconf.client.mdsal.api.NetconfRpcService;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
import org.opendaylight.netconf.client.mdsal.impl.NetconfMessageTransformUtil;
* Holds URLs with YANG schema resources for all yang modules reported in
* ietf-netconf-yang-library/modules-state/modules node.
*/
-public final class LibraryModulesSchemas implements NetconfDeviceSchemas {
+public final class LibraryModulesSchemas {
private static final Logger LOG = LoggerFactory.getLogger(LibraryModulesSchemas.class);
private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4}-\\d{2}-\\d{2})");
private static final EffectiveModelContext LIBRARY_CONTEXT = BindingRuntimeHelpers.createEffectiveModel(
final String valueStr = node.body().toString();
return Strings.isNullOrEmpty(valueStr) ? Optional.empty() : Optional.of(valueStr.trim());
}
-
- @Override
- public Set<QName> getAvailableYangSchemasQNames() {
- return null;
- }
}
+++ /dev/null
-/*
- * 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.client.mdsal;
-
-import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.ListenableFuture;
-import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemas;
-import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemasResolver;
-import org.opendaylight.netconf.client.mdsal.api.NetconfRpcService;
-import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
-import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
-import org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.library.rev190104.YangModuleInfoImpl;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.Revision;
-import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
-
-/**
- * Default implementation resolving schemas QNames from netconf-state or from modules-state.
- */
-public final class NetconfStateSchemasResolverImpl implements NetconfDeviceSchemasResolver {
- private static final QName RFC8525_YANG_LIBRARY_CAPABILITY = YangModuleInfoImpl.getInstance().getName();
- private static final QName RFC7895_YANG_LIBRARY_CAPABILITY = RFC8525_YANG_LIBRARY_CAPABILITY
- .bindTo(QNameModule.create(RFC8525_YANG_LIBRARY_CAPABILITY.getNamespace(), Revision.of("2016-06-21"))).intern();
-
- @Override
- public ListenableFuture<? extends NetconfDeviceSchemas> resolve(final NetconfRpcService deviceRpc,
- final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id,
- final EffectiveModelContext schemaContext) {
- // FIXME: I think we should prefer YANG library here
- if (remoteSessionCapabilities.isMonitoringSupported()) {
- return NetconfStateSchemas.forDevice(deviceRpc, remoteSessionCapabilities, id, schemaContext);
- }
- if (remoteSessionCapabilities.containsModuleCapability(RFC8525_YANG_LIBRARY_CAPABILITY)
- || remoteSessionCapabilities.containsModuleCapability(RFC7895_YANG_LIBRARY_CAPABILITY)) {
- return LibraryModulesSchemas.forDevice(deviceRpc, id);
- }
-
- return Futures.immediateFuture(NetconfStateSchemas.EMPTY);
- }
-}
*/
package org.opendaylight.netconf.client.mdsal.api;
+import static java.util.Objects.requireNonNull;
+
+import java.util.List;
import java.util.Set;
+import org.eclipse.jdt.annotation.NonNullByDefault;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
-public interface NetconfDeviceSchemas {
- // FIXME: document this method
- Set<QName> getAvailableYangSchemasQNames();
+/**
+ * The specification of the {@link SourceRepresentation}s and how they need to be assembled to form the device's
+ * {@link EffectiveModelContext}.
+ *
+ * @param requiredSources the set of sources that are required to form the accurate model of the device
+ * @param librarySources additional sources that should be presented to YANG parser, typically to resolve submodules
+ * when the originator of this object is Just Not Sure(tm)
+ * @param providedSources {@link ProvidedSources} grouped by their representation
+ */
+// FIXME: this structure will need to be updated for NMDA to support per-datastore model contexts
+@NonNullByDefault
+public record NetconfDeviceSchemas(
+ // FIXME: NETCONF-840: use SourceIdentifier
+ Set<QName> requiredSources,
+ FeatureSet features,
+ // FIXME: NETCONF-840: use SourceIdentifier
+ Set<QName> librarySources,
+ List<ProvidedSources<?>> providedSources) {
+ public NetconfDeviceSchemas {
+ requiredSources = Set.copyOf(requiredSources);
+ requireNonNull(features);
+ librarySources = Set.copyOf(librarySources);
+ providedSources = List.copyOf(providedSources);
+ }
}
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
/**
- * Factory for netconf device schemas.
+ * A component capable of determining the {@link NetconfDeviceSchemas} for a particular device.
*/
public interface NetconfDeviceSchemasResolver {
// FIXME: document this method
- ListenableFuture<? extends NetconfDeviceSchemas> resolve(NetconfRpcService deviceRpc,
- NetconfSessionPreferences remoteSessionCapabilities, RemoteDeviceId id, EffectiveModelContext schemaContext);
+ ListenableFuture<NetconfDeviceSchemas> resolve(RemoteDeviceId deviceId,
+ NetconfSessionPreferences sessionPreferences, NetconfRpcService deviceRpc,
+ // FIXME: this should not be needed
+ EffectiveModelContext baseModelContext);
}
--- /dev/null
+/*
+ * Copyright (c) 2024 PANTHEON.tech, s.r.o. 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.client.mdsal.api;
+
+import static java.util.Objects.requireNonNull;
+
+import java.util.Set;
+import java.util.stream.Stream;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.concepts.Registration;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.source.SourceRepresentation;
+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.SchemaSourceRegistry;
+
+/**
+ * A set of sources provided through a single provider. A NETCONF device can have several of these.
+ *
+ * @param representation the {@link SourceRepresentation} of provided sources
+ * @param provider the {@link SchemaSourceProvider} providing the sources
+ * @param sources provided sources
+ */
+@NonNullByDefault
+public record ProvidedSources<T extends SourceRepresentation>(
+ Class<T> representation,
+ SchemaSourceProvider<T> provider,
+ // FIXME: NETCONF-840: use SourceIdentifier
+ Set<QName> sources) {
+ public ProvidedSources {
+ representation = requireNonNull(representation);
+ provider = requireNonNull(provider);
+ sources = Set.copyOf(sources);
+ }
+
+ public Stream<Registration> registerWith(final SchemaSourceRegistry registry, final int cost) {
+ return sources.stream()
+ .map(qname -> new SourceIdentifier(qname.getLocalName(), qname.getRevision().orElse(null)))
+ .map(sourceId -> registry.registerSchemaSource(provider,
+ PotentialSchemaSource.create(sourceId, representation, cost)));
+ }
+}
import static java.util.Objects.requireNonNull;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.Sets;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.MoreExecutors;
-import java.util.HashSet;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
import org.eclipse.jdt.annotation.NonNull;
-import org.opendaylight.netconf.client.mdsal.LibraryModulesSchemas;
-import org.opendaylight.netconf.client.mdsal.LibrarySchemaSourceProvider;
-import org.opendaylight.netconf.client.mdsal.NetconfStateSchemasResolverImpl;
import org.opendaylight.netconf.client.mdsal.api.BaseNetconfSchema;
import org.opendaylight.netconf.client.mdsal.api.DeviceNetconfSchema;
import org.opendaylight.netconf.client.mdsal.api.DeviceNetconfSchemaProvider;
import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactoryConfiguration;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
+import org.opendaylight.yangtools.yang.model.repo.spi.PotentialSchemaSource.Costs;
import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
import org.opendaylight.yangtools.yang.parser.repo.SharedSchemaRepository;
import org.slf4j.Logger;
private static final Logger LOG = LoggerFactory.getLogger(DefaultDeviceNetconfSchemaProvider.class);
// FIXME: resolver seems to be a useless indirection
- private final NetconfDeviceSchemasResolver resolver;
+ private final NetconfDeviceSchemasResolver resolver = new NetconfStateSchemasResolverImpl();
private final @NonNull EffectiveModelContextFactory contextFactory;
private final @NonNull SchemaSourceRegistry registry;
private final @NonNull SchemaRepository repository;
DefaultDeviceNetconfSchemaProvider(final SharedSchemaRepository repository) {
this(repository, repository,
- repository.createEffectiveModelContextFactory(SchemaContextFactoryConfiguration.getDefault()),
- new NetconfStateSchemasResolverImpl());
+ repository.createEffectiveModelContextFactory(SchemaContextFactoryConfiguration.getDefault()));
}
@VisibleForTesting
public DefaultDeviceNetconfSchemaProvider(final SchemaSourceRegistry registry, final SchemaRepository repository,
- final EffectiveModelContextFactory contextFactory, final NetconfDeviceSchemasResolver resolver) {
+ final EffectiveModelContextFactory contextFactory) {
this.registry = requireNonNull(registry);
this.repository = requireNonNull(repository);
this.contextFactory = requireNonNull(contextFactory);
- this.resolver = requireNonNull(resolver);
}
@Override
public ListenableFuture<DeviceNetconfSchema> deviceNetconfSchemaFor(final RemoteDeviceId deviceId,
final NetconfSessionPreferences sessionPreferences, final NetconfRpcService deviceRpc,
final BaseNetconfSchema baseSchema, final Executor processingExecutor) {
-
- // Acquire schemas
- final var futureSchemas = resolver.resolve(deviceRpc, sessionPreferences, deviceId, baseSchema.modelContext());
-
- // Convert to sources
- final var sourceResolverFuture = Futures.transform(futureSchemas, availableSchemas -> {
- final var providedSources = availableSchemas.getAvailableYangSchemasQNames();
- LOG.debug("{}: Schemas exposed by ietf-netconf-monitoring: {}", deviceId, providedSources);
-
- final var requiredSources = new HashSet<>(sessionPreferences.moduleBasedCaps().keySet());
- final var requiredSourcesNotProvided = Sets.difference(requiredSources, providedSources);
- if (!requiredSourcesNotProvided.isEmpty()) {
- LOG.warn("{}: Netconf device does not provide all yang models reported in hello message capabilities,"
- + " required but not provided: {}", deviceId, requiredSourcesNotProvided);
- LOG.warn("{}: Attempting to build schema context from required sources", deviceId);
- }
-
- // Here all the sources reported in netconf monitoring are merged with those reported in hello.
- // It is necessary to perform this since submodules are not mentioned in hello but still required.
- // This clashes with the option of a user to specify supported yang models manually in configuration
- // for netconf-connector and as a result one is not able to fully override yang models of a device.
- // It is only possible to add additional models.
- final var providedSourcesNotRequired = Sets.difference(providedSources, requiredSources);
- if (!providedSourcesNotRequired.isEmpty()) {
- LOG.warn("{}: Netconf device provides additional yang models not reported in "
- + "hello message capabilities: {}", deviceId, providedSourcesNotRequired);
- LOG.warn("{}: Adding provided but not required sources as required to prevent failures", deviceId);
- LOG.debug("{}: Netconf device reported in hello: {}", deviceId, requiredSources);
- requiredSources.addAll(providedSourcesNotRequired);
- }
-
- // FIXME: this instanceof check is quite bad
- final var sourceProvider = availableSchemas instanceof LibraryModulesSchemas libraryModule
- ? new LibrarySchemaSourceProvider(libraryModule.getAvailableModels())
- : new MonitoringSchemaSourceProvider(deviceId, deviceRpc);
- return new DeviceSources(requiredSources, providedSources, sourceProvider);
- }, MoreExecutors.directExecutor());
+ // Acquire sources
+ final var sourceResolverFuture = resolver.resolve(deviceId, sessionPreferences, deviceRpc,
+ baseSchema.modelContext());
// Set up the EffectiveModelContext for the device
return Futures.transformAsync(sourceResolverFuture, deviceSources -> {
LOG.debug("{}: Resolved device sources to {}", deviceId, deviceSources);
// Register all sources with repository and start resolution
- final var registrations = deviceSources.register(registry);
+ final var registrations = deviceSources.providedSources().stream()
+ .flatMap(sources -> sources.registerWith(registry, Costs.REMOTE_IO.getValue()))
+ .collect(Collectors.toUnmodifiableList());
final var future = new SchemaSetup(repository, contextFactory, deviceId, deviceSources, sessionPreferences)
.startResolution();
+++ /dev/null
-/*
- * Copyright (c) 2019 PANTHEON.tech, s.r.o. 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.client.mdsal.impl;
-
-import static java.util.Objects.requireNonNull;
-
-import java.util.List;
-import java.util.Set;
-import java.util.stream.Collectors;
-import org.opendaylight.yangtools.concepts.Registration;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.Revision;
-import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
-import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
-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.SchemaSourceRegistry;
-
-/**
- * Contains RequiredSources - sources from capabilities.
- */
-final class DeviceSources {
- private final Set<QName> requiredSources;
- private final Set<QName> providedSources;
- private final SchemaSourceProvider<YangTextSource> sourceProvider;
-
- DeviceSources(final Set<QName> requiredSources, final Set<QName> providedSources,
- final SchemaSourceProvider<YangTextSource> sourceProvider) {
- this.requiredSources = requireNonNull(requiredSources);
- this.providedSources = requireNonNull(providedSources);
- this.sourceProvider = requireNonNull(sourceProvider);
- }
-
- Set<QName> getRequiredSourcesQName() {
- return requiredSources;
- }
-
- Set<QName> getProvidedSourcesQName() {
- return providedSources;
- }
-
- List<SourceIdentifier> getRequiredSources() {
- return requiredSources.stream().map(DeviceSources::toSourceId).collect(Collectors.toList());
- }
-
- List<Registration> register(final SchemaSourceRegistry schemaRegistry) {
- return providedSources.stream()
- .map(DeviceSources::toSourceId)
- .map(sourceId -> schemaRegistry.registerSchemaSource(sourceProvider,
- PotentialSchemaSource.create(sourceId, YangTextSource.class,
- PotentialSchemaSource.Costs.REMOTE_IO.getValue())))
- .collect(Collectors.toUnmodifiableList());
- }
-
- private static SourceIdentifier toSourceId(final QName input) {
- return new SourceIdentifier(input.getLocalName(), input.getRevision().map(Revision::toString).orElse(null));
- }
-}
\ No newline at end of file
/*
- * Copyright (c) 2014, 2015 Cisco Systems, Inc. and others. All rights reserved.
+ * 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.client.mdsal;
+package org.opendaylight.netconf.client.mdsal.impl;
import static org.opendaylight.netconf.client.mdsal.impl.NetconfMessageTransformUtil.NETCONF_DATA_NODEID;
import static org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.YangModuleInfoImpl.qnameOf;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.VerifyException;
import com.google.common.collect.ImmutableSet;
-import com.google.common.util.concurrent.FutureCallback;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
-import com.google.common.util.concurrent.SettableFuture;
import java.io.IOException;
import java.net.URISyntaxException;
import java.time.format.DateTimeParseException;
+import java.util.HashSet;
+import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
import javax.xml.stream.XMLStreamException;
import javax.xml.transform.dom.DOMSource;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.netconf.api.NamespaceURN;
import org.opendaylight.netconf.api.xml.XmlUtil;
import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemas;
+import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemasResolver;
import org.opendaylight.netconf.client.mdsal.api.NetconfRpcService;
import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
+import org.opendaylight.netconf.client.mdsal.api.ProvidedSources;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
import org.opendaylight.netconf.common.mdsal.NormalizedDataUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Get;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.schemas.Schema.Location;
import org.opendaylight.yangtools.yang.common.ErrorSeverity;
-import org.opendaylight.yangtools.yang.common.OperationFailedException;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.Revision;
import org.opendaylight.yangtools.yang.common.XMLNamespace;
import org.opendaylight.yangtools.yang.data.api.schema.SystemMapNode;
import org.opendaylight.yangtools.yang.data.spi.node.ImmutableNodes;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
/**
- * Holds QNames for all YANG modules reported by ietf-netconf-monitoring/state/schemas.
+ * Default implementation resolving schemas QNames from netconf-state or from modules-state.
*/
-public final class NetconfStateSchemas implements NetconfDeviceSchemas {
- public static final NetconfStateSchemas EMPTY = new NetconfStateSchemas(ImmutableSet.of());
-
- private static final Logger LOG = LoggerFactory.getLogger(NetconfStateSchemas.class);
+public final class NetconfStateSchemasResolverImpl implements NetconfDeviceSchemasResolver {
+ private static final Logger LOG = LoggerFactory.getLogger(NetconfStateSchemasResolverImpl.class);
private static final String MONITORING_NAMESPACE = NetconfState.QNAME.getNamespace().toString();
private static final @NonNull NodeIdentifier SCHEMA_FORMAT_NODEID = NodeIdentifier.create(qnameOf("format"));
private static final @NonNull NodeIdentifier SCHEMA_LOCATION_NODEID = NodeIdentifier.create(qnameOf("location"));
.build();
}
- private final ImmutableSet<QName> availableYangSchemasQNames;
-
- public NetconfStateSchemas(final Set<QName> availableYangSchemasQNames) {
- this.availableYangSchemasQNames = ImmutableSet.copyOf(availableYangSchemasQNames);
- }
-
@Override
- public Set<QName> getAvailableYangSchemasQNames() {
- return availableYangSchemasQNames;
- }
+ public ListenableFuture<NetconfDeviceSchemas> resolve(final RemoteDeviceId deviceId,
+ final NetconfSessionPreferences sessionPreferences, final NetconfRpcService deviceRpc,
+ final EffectiveModelContext baseModelContext) {
+ // Find all schema sources provided via ietf-netconf-monitoring, if supported
+ final var monitoringFuture = sessionPreferences.isMonitoringSupported()
+ ? resolveMonitoringSources(deviceId, deviceRpc, baseModelContext)
+ : Futures.immediateFuture(List.<ProvidedSources<?>>of());
+
+ final AsyncFunction<List<ProvidedSources<?>>, NetconfDeviceSchemas> function;
+ LOG.debug("{}: resolving YANG 1.0 conformance", deviceId);
+ function = sources -> resolveYang10(deviceId, sessionPreferences, deviceRpc, baseModelContext, sources);
+
+ // FIXME: check for
+ // urn:ietf:params:netconf:capability:yang-library:1.0?revision=<date>&module-set-id=<id>
+ //
+ // and then dispatch to resolveYang11(), which should resolve schemas based on RFC7950's conformance
+ // announcement via I <hello/> message, as defined in
+ // https://www.rfc-editor.org/rfc/rfc6020#section-5.6.4
+ //
+ // if (sessionPreferences.containsNonModuleCapability(CapabilityURN.YANG_LIBRARY)) {
+ // LOG.debug("{}: resolving YANG 1.1 conformance", deviceId);
+ // function = sources -> resolveYang11(deviceId, sessionPreferences, deviceRpc, baseModelContext,
+ // sources);
+ // }
+
+ // FIXME: check for
+ // urn:ietf:params:netconf:capability:yang-library:1.1?revision=<date>&content-id=<content-id-value>
+ // where date is at least 2019-01-04
+ //
+ // and then dispatch to resolveNmda():
+ //
+ // if (sessionPreferences.containsNonModuleCapability(CapabilityURN.YANG_LIBRARY)) {
+ // LOG.debug("{}: resolving YANG 1.1 NMDA conformance", deviceId);
+ // function = sources -> resolveNmda(deviceId, sessionPreferences, deviceRpc, baseModelContext,
+ // sources);
+ // }
- /**
- * Issue get request to remote device and parse response to find all schemas under netconf-state/schemas.
- */
- static ListenableFuture<NetconfStateSchemas> forDevice(final NetconfRpcService deviceRpc,
- final NetconfSessionPreferences remoteSessionCapabilities, final RemoteDeviceId id,
- final EffectiveModelContext modelContext) {
- if (!remoteSessionCapabilities.isMonitoringSupported()) {
- // 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...
- LOG.warn("{}: Netconf monitoring not supported on device, cannot detect provided schemas", id);
- return Futures.immediateFuture(EMPTY);
- }
+ return Futures.transformAsync(monitoringFuture, function, MoreExecutors.directExecutor());
+ }
- final var future = SettableFuture.<NetconfStateSchemas>create();
- Futures.addCallback(deviceRpc.invokeNetconf(Get.QNAME, GET_SCHEMAS_RPC),
- new FutureCallback<DOMRpcResult>() {
- @Override
- public void onSuccess(final DOMRpcResult result) {
- onGetSchemasResult(future, id, modelContext, result);
- }
-
- @Override
- public void onFailure(final Throwable cause) {
- // debug, because we expect this error to be reported by caller
- LOG.debug("{}: Unable to detect available schemas", id, cause);
- future.setException(cause);
- }
- }, MoreExecutors.directExecutor());
- return future;
+ private static ListenableFuture<List<ProvidedSources<?>>> resolveMonitoringSources(
+ final RemoteDeviceId deviceId, final NetconfRpcService deviceRpc,
+ final EffectiveModelContext baseModelContext) {
+ return Futures.transform(deviceRpc.invokeNetconf(Get.QNAME, GET_SCHEMAS_RPC),
+ result -> resolveMonitoringSources(deviceId, deviceRpc, result, baseModelContext),
+ MoreExecutors.directExecutor());
}
- private static void onGetSchemasResult(final SettableFuture<NetconfStateSchemas> future, final RemoteDeviceId id,
- final EffectiveModelContext modelContext, final DOMRpcResult result) {
+ private static List<ProvidedSources<?>> resolveMonitoringSources(final RemoteDeviceId deviceId,
+ final NetconfRpcService deviceRpc, final DOMRpcResult rpcResult,
+ final EffectiveModelContext baseModelContext) {
// Two-pass error reporting: first check if there is a hard error, then log any remaining warnings
- final var errors = result.errors();
+ final var errors = rpcResult.errors();
if (errors.stream().anyMatch(error -> error.getSeverity() == ErrorSeverity.ERROR)) {
- // FIXME: a good exception, which can report the contents of errors?
- future.setException(new OperationFailedException("Failed to get netconf-state", errors));
- return;
+ LOG.warn("{}: failed to get netconf-state", errors);
+ return List.of();
}
for (var error : errors) {
- LOG.info("{}: schema retrieval warning: {}", id, error);
+ LOG.info("{}: schema retrieval warning: {}", deviceId, error);
}
- final var value = result.value();
- if (value == null) {
- LOG.warn("{}: missing RPC output", id);
- future.set(EMPTY);
- return;
+ final var rpcOutput = rpcResult.value();
+ if (rpcOutput == null) {
+ LOG.warn("{}: missing RPC output", deviceId);
+ return List.of();
}
- final var data = value.childByArg(NETCONF_DATA_NODEID);
+ final var data = rpcOutput.childByArg(NETCONF_DATA_NODEID);
if (data == null) {
- LOG.warn("{}: missing RPC data", id);
- future.set(EMPTY);
- return;
+ LOG.warn("{}: missing RPC data", deviceId);
+ return List.of();
}
if (!(data instanceof AnyxmlNode<?> anyxmlData)) {
- future.setException(new VerifyException("Unexpected data " + data.prettyTree()));
- return;
+ LOG.warn("{}: unexpected data {}", deviceId, data.prettyTree());
+ return List.of();
}
final var dataBody = anyxmlData.body();
if (!(dataBody instanceof DOMSource domDataBody)) {
- future.setException(new VerifyException("Unexpected body " + dataBody));
- return;
+ LOG.warn("{}: unexpected body {}", deviceId, dataBody);
+ return List.of();
}
// Server may include additional data which we do not understand. Make sure we trim the input before we try
// Now normalize the anyxml content to the selected model context
final NormalizedNode normalizedData;
try {
- normalizedData = NormalizedDataUtil.transformDOMSourceToNormalizedNode(modelContext, filteredBody)
+ normalizedData = NormalizedDataUtil.transformDOMSourceToNormalizedNode(baseModelContext, filteredBody)
.getResult().data();
} catch (XMLStreamException | URISyntaxException | IOException | SAXException e) {
- LOG.debug("{}: failed to transform {}", id, filteredBody, e);
- future.setException(e);
- return;
+ LOG.warn("{}: failed to transform {}", deviceId, filteredBody, e);
+ return List.of();
}
// The result should be the root of datastore, hence a DataContainerNode
if (!(normalizedData instanceof DataContainerNode root)) {
- future.setException(new VerifyException("Unexpected normalized data " + normalizedData.prettyTree()));
- return;
+ LOG.warn("{}: unexpected normalized data {}", deviceId, normalizedData.prettyTree());
+ return List.of();
}
// container netconf-state
final var netconfState = root.childByArg(new NodeIdentifier(NetconfState.QNAME));
if (netconfState == null) {
- LOG.warn("{}: missing netconf-state", id);
- future.set(EMPTY);
- return;
+ LOG.warn("{}: missing netconf-state", deviceId);
+ return List.of();
}
if (!(netconfState instanceof ContainerNode netconfStateCont)) {
- future.setException(new VerifyException("Unexpected netconf-state " + netconfState.prettyTree()));
- return;
+ LOG.warn("{}: unexpected netconf-state {}", deviceId, netconfState.prettyTree());
+ return List.of();
}
// container schemas
final var schemas = netconfStateCont.childByArg(new NodeIdentifier(Schemas.QNAME));
if (schemas == null) {
- LOG.warn("{}: missing schemas", id);
- future.set(EMPTY);
- return;
+ LOG.warn("{}: missing schemas", deviceId);
+ return List.of();
}
if (!(schemas instanceof ContainerNode schemasNode)) {
- future.setException(new VerifyException("Unexpected schemas " + schemas.prettyTree()));
- return;
+ LOG.warn("{}: unexpected schemas {}", deviceId, schemas.prettyTree());
+ return List.of();
}
- create(future, id, schemasNode);
+ return resolveMonitoringSources(deviceId, deviceRpc, schemasNode);
}
/**
* Parse response of get(netconf-state/schemas) to find all schemas under netconf-state/schemas.
*/
@VisibleForTesting
- static void create(final SettableFuture<NetconfStateSchemas> future, final RemoteDeviceId id,
- final ContainerNode schemasNode) {
+ static List<ProvidedSources<?>> resolveMonitoringSources(final RemoteDeviceId deviceId,
+ final NetconfRpcService deviceRpc, final ContainerNode schemasNode) {
final var child = schemasNode.childByArg(new NodeIdentifier(Schema.QNAME));
if (child == null) {
- LOG.warn("{}: missing schema", id);
- future.set(EMPTY);
- return;
+ LOG.warn("{}: missing schema", deviceId);
+ return List.of();
}
if (!(child instanceof SystemMapNode schemaMap)) {
- future.setException(new VerifyException("Unexpected schemas " + child.prettyTree()));
- return;
+ LOG.warn("{}: unexpected schema {}", deviceId, child.prettyTree());
+ return List.of();
}
// FIXME: we are producing the wrong thing here and simply not handling all the use cases
// translating it to the real world -- for example turning a String into a XMLNamespace or a local name.
final var builder = ImmutableSet.<QName>builderWithExpectedSize(schemaMap.size());
for (var schemaNode : schemaMap.body()) {
- final var qname = createFromNormalizedNode(id, schemaNode);
+ final var qname = createFromNormalizedNode(deviceId, schemaNode);
if (qname != null) {
builder.add(qname);
}
}
- future.set(new NetconfStateSchemas(builder.build()));
+
+ final var sources = builder.build();
+ return sources.isEmpty() ? List.of() : List.of(new ProvidedSources<>(YangTextSource.class,
+ new MonitoringSchemaSourceProvider(deviceId, deviceRpc), sources));
}
- private static @Nullable QName createFromNormalizedNode(final RemoteDeviceId id, final MapEntryNode schemaEntry) {
+ private static @Nullable QName createFromNormalizedNode(final RemoteDeviceId id,
+ final MapEntryNode schemaEntry) {
// These three are mandatory due to 'key "identifier version format"'
final var format = schemaEntry.getChildByArg(SCHEMA_FORMAT_NODEID).body();
// FIXME: we should support Yin as well
}
}
}
+
+ // Resolve schemas based on RFC6020's conformance announcement in <hello/> message, as defined in
+ // https://www.rfc-editor.org/rfc/rfc6020#section-5.6.4
+ private static ListenableFuture<NetconfDeviceSchemas> resolveYang10(final RemoteDeviceId deviceId,
+ final NetconfSessionPreferences sessionPreferences, final NetconfRpcService deviceRpc,
+ final EffectiveModelContext baseModelContext, final List<ProvidedSources<?>> monitoringSources) {
+ final var providedSources = monitoringSources.stream()
+ .flatMap(sources -> sources.sources().stream())
+ .collect(Collectors.toSet());
+ LOG.debug("{}: Schemas exposed by ietf-netconf-monitoring: {}", deviceId, providedSources);
+
+ final var requiredSources = new HashSet<>(sessionPreferences.moduleBasedCaps().keySet());
+ final var requiredSourcesNotProvided = Sets.difference(requiredSources, providedSources);
+ if (!requiredSourcesNotProvided.isEmpty()) {
+ LOG.warn("{}: Netconf device does not provide all yang models reported in hello message capabilities,"
+ + " required but not provided: {}", deviceId, requiredSourcesNotProvided);
+ LOG.warn("{}: Attempting to build schema context from required sources", deviceId);
+ }
+
+ // Here all the sources reported in netconf monitoring are merged with those reported in hello.
+ // It is necessary to perform this since submodules are not mentioned in hello but still required.
+ // This clashes with the option of a user to specify supported yang models manually in configuration
+ // for netconf-connector and as a result one is not able to fully override yang models of a device.
+ // It is only possible to add additional models.
+ final var providedSourcesNotRequired = Sets.difference(providedSources, requiredSources);
+ if (!providedSourcesNotRequired.isEmpty()) {
+ LOG.warn("{}: Netconf device provides additional yang models not reported in "
+ + "hello message capabilities: {}", deviceId, providedSourcesNotRequired);
+ LOG.warn("{}: Adding provided but not required sources as required to prevent failures", deviceId);
+ LOG.debug("{}: Netconf device reported in hello: {}", deviceId, requiredSources);
+ requiredSources.addAll(providedSourcesNotRequired);
+ }
+
+
+ return Futures.immediateFuture(new NetconfDeviceSchemas(requiredSources,
+ // FIXME: determine features
+ FeatureSet.builder().build(),
+ // FIXME: use this instead of adjusted required sources
+ Set.of(),
+ monitoringSources));
+ }
}
import org.opendaylight.netconf.client.mdsal.NetconfDevice.EmptySchemaContextException;
import org.opendaylight.netconf.client.mdsal.NetconfDeviceCapabilities;
import org.opendaylight.netconf.client.mdsal.api.DeviceNetconfSchema;
+import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemas;
import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability;
private final Set<AvailableCapability> nonModuleBasedCapabilities = new HashSet<>();
private final Map<QName, FailureReason> unresolvedCapabilites = new HashMap<>();
private final Set<AvailableCapability> resolvedCapabilities = new HashSet<>();
-
private final RemoteDeviceId deviceId;
- private final DeviceSources deviceSources;
- private final NetconfSessionPreferences remoteSessionCapabilities;
+ private final NetconfDeviceSchemas deviceSchemas;
+ private final Set<QName> deviceRequiredSources;
+ private final NetconfSessionPreferences sessionPreferences;
private final SchemaRepository repository;
private final EffectiveModelContextFactory contextFactory;
private Collection<SourceIdentifier> requiredSources;
SchemaSetup(final SchemaRepository repository, final EffectiveModelContextFactory contextFactory,
- final RemoteDeviceId deviceId, final DeviceSources deviceSources,
- final NetconfSessionPreferences remoteSessionCapabilities) {
+ final RemoteDeviceId deviceId, final NetconfDeviceSchemas deviceSchemas,
+ final NetconfSessionPreferences sessionPreferences) {
this.repository = requireNonNull(repository);
this.contextFactory = requireNonNull(contextFactory);
this.deviceId = requireNonNull(deviceId);
- this.deviceSources = requireNonNull(deviceSources);
- this.remoteSessionCapabilities = requireNonNull(remoteSessionCapabilities);
+ this.deviceSchemas = requireNonNull(deviceSchemas);
+ this.sessionPreferences = requireNonNull(sessionPreferences);
// If device supports notifications and does not contain necessary modules, add them automatically
- if (remoteSessionCapabilities.containsNonModuleCapability(CapabilityURN.NOTIFICATION)) {
- // FIXME: mutable collection modification!
- deviceSources.getRequiredSourcesQName().addAll(List.of(
+ deviceRequiredSources = new HashSet<>(deviceSchemas.requiredSources());
+ if (sessionPreferences.containsNonModuleCapability(CapabilityURN.NOTIFICATION)) {
+ deviceRequiredSources.add(
org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.netconf.notification._1._0.rev080714
- .YangModuleInfoImpl.getInstance().getName(),
+ .YangModuleInfoImpl.getInstance().getName());
+ deviceRequiredSources.add(
org.opendaylight.yang.svc.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715
- .YangModuleInfoImpl.getInstance().getName())
- );
+ .YangModuleInfoImpl.getInstance().getName());
}
- requiredSources = deviceSources.getRequiredSources();
- final var missingSources = filterMissingSources(requiredSources);
+ requiredSources = deviceRequiredSources.stream()
+ .map(qname -> new SourceIdentifier(qname.getLocalName(), qname.getRevision().orElse(null)))
+ .collect(Collectors.toList());
+ final var missingSources = filterMissingSources(requiredSources);
addUnresolvedCapabilities(getQNameFromSourceIdentifiers(missingSources),
UnavailableCapability.FailureReason.MissingSource);
requiredSources.removeAll(missingSources);
public void onSuccess(final EffectiveModelContext result) {
LOG.debug("{}: Schema context built successfully from {}", deviceId, requiredSources);
- final Collection<QName> filteredQNames = Sets.difference(deviceSources.getRequiredSourcesQName(),
- unresolvedCapabilites.keySet());
+ final var filteredQNames = Sets.difference(deviceRequiredSources, unresolvedCapabilites.keySet());
resolvedCapabilities.addAll(filteredQNames.stream()
.map(capability -> new AvailableCapabilityBuilder()
.setCapability(capability.toString())
- .setCapabilityOrigin(remoteSessionCapabilities.capabilityOrigin(capability))
+ .setCapabilityOrigin(sessionPreferences.capabilityOrigin(capability))
.build())
.collect(Collectors.toList()));
- nonModuleBasedCapabilities.addAll(remoteSessionCapabilities.nonModuleCaps().keySet().stream()
+ nonModuleBasedCapabilities.addAll(sessionPreferences.nonModuleCaps().keySet().stream()
.map(capability -> new AvailableCapabilityBuilder()
.setCapability(capability)
- .setCapabilityOrigin(remoteSessionCapabilities.capabilityOrigin(capability))
+ .setCapabilityOrigin(sessionPreferences.capabilityOrigin(capability))
.build())
.collect(Collectors.toList()));
-
resultFuture.set(new DeviceNetconfSchema(new NetconfDeviceCapabilities(
ImmutableMap.copyOf(unresolvedCapabilites), ImmutableSet.copyOf(resolvedCapabilities),
ImmutableSet.copyOf(nonModuleBasedCapabilities)), result));
private QName getQNameFromSourceIdentifier(final SourceIdentifier identifier) {
// Required sources are all required and provided merged in DeviceSourcesResolver
- for (final QName qname : deviceSources.getRequiredSourcesQName()) {
+ for (final QName qname : deviceRequiredSources) {
if (!qname.getLocalName().equals(identifier.name().getLocalName())) {
continue;
}
return qname;
}
}
- LOG.warn("Unable to map identifier to a devices reported capability: {} Available: {}",identifier,
- deviceSources.getRequiredSourcesQName());
+ LOG.warn("Unable to map identifier to a devices reported capability: {} Available: {}", identifier,
+ deviceRequiredSources);
// return null since we cannot find the QName,
// this capability will be removed from required sources and not reported as unresolved-capability
return null;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceServices;
import org.opendaylight.netconf.client.mdsal.impl.DefaultDeviceNetconfSchemaProvider;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Get;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapabilityBuilder;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
final var facade = getFacade();
final var listener = getListener();
- final var deviceSchemaProvider = mockDeviceNetconfSchemaProvider();
+ doReturn(RpcResultBuilder.failed().buildFuture()).when(listener).sendRequest(any(), eq(Get.QNAME));
final var device = new NetconfDeviceBuilder()
.setReconnectOnSchemasChange(true)
- .setDeviceSchemaProvider(deviceSchemaProvider)
+ .setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider())
.setProcessingExecutor(MoreExecutors.directExecutor())
.setId(getId())
.setSalFacade(facade)
final var schemaFuture = SettableFuture.<EffectiveModelContext>create();
doReturn(schemaFuture).when(schemaContextProviderFactory).createEffectiveModelContext(anyCollection());
+ final var listener = getListener();
+ doReturn(RpcResultBuilder.failed().buildFuture()).when(listener).sendRequest(any(), eq(Get.QNAME));
+
final var device = new NetconfDeviceBuilder()
.setReconnectOnSchemasChange(true)
.setDeviceSchemaProvider(mockDeviceNetconfSchemaProvider(getSchemaRepository(),
final var sessionCaps = getSessionCaps(true,
TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION);
- final NetconfDeviceCommunicator listener = getListener();
// session up, start schema resolution
device.onRemoteSessionUp(sessionCaps, listener);
// session down
public void testNetconfDeviceAvailableCapabilitiesBuilding() throws Exception {
final var facade = getFacade();
final var listener = getListener();
+ doReturn(RpcResultBuilder.failed().buildFuture()).when(listener).sendRequest(any(), eq(Get.QNAME));
final var netconfSpy = spy(new NetconfDeviceBuilder()
.setReconnectOnSchemasChange(true)
private DeviceNetconfSchemaProvider mockDeviceNetconfSchemaProvider(final SchemaRepository schemaRepository,
final EffectiveModelContextFactory schemaFactory) {
- return new DefaultDeviceNetconfSchemaProvider(schemaRegistry, schemaRepository, schemaFactory,
- (unused1, unused2, unused3, unused4) -> Futures.immediateFuture(NetconfStateSchemas.EMPTY));
+ return new DefaultDeviceNetconfSchemaProvider(schemaRegistry, schemaRepository, schemaFactory);
}
public RemoteDeviceId getId() {
* 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.client.mdsal;
+package org.opendaylight.netconf.client.mdsal.impl;
import static org.junit.jupiter.api.Assertions.assertFalse;
final var expected = XmlUtil.readXmlToDocument(
NC881Test.class.getResourceAsStream("/nc881/netconf-state-filtered.xml"));
- final var filteredDom = NetconfStateSchemas.ietfMonitoringCopy(new DOMSource(source.getDocumentElement()));
+ final var filteredDom = NetconfStateSchemasResolverImpl.ietfMonitoringCopy(
+ new DOMSource(source.getDocumentElement()));
final var filtered = XmlUtil.newDocument();
filtered.appendChild(filtered.importNode(filteredDom.getNode(), true));
* 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.client.mdsal;
+package org.opendaylight.netconf.client.mdsal.impl;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import com.google.common.util.concurrent.Futures;
-import com.google.common.util.concurrent.SettableFuture;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.junit.MockitoJUnitRunner;
import org.opendaylight.mdsal.dom.api.DOMRpcImplementationNotAvailableException;
import org.opendaylight.mdsal.dom.spi.DefaultDOMRpcResult;
+import org.opendaylight.netconf.client.mdsal.AbstractBaseSchemasTest;
+import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemas;
import org.opendaylight.netconf.client.mdsal.api.NetconfRpcService;
import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
+import org.opendaylight.netconf.client.mdsal.api.ProvidedSources;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
-import org.opendaylight.netconf.client.mdsal.impl.NetconfMessageTransformUtil;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.Get;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.netconf.state.Schemas;
import org.opendaylight.yangtools.util.xml.UntrustedXML;
import org.opendaylight.yangtools.yang.common.ErrorTag;
import org.opendaylight.yangtools.yang.common.ErrorType;
-import org.opendaylight.yangtools.yang.common.OperationFailedException;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
}
@Test
- public void testCreate() throws Exception {
- final var future = SettableFuture.<NetconfStateSchemas>create();
- NetconfStateSchemas.create(future, DEVICE_ID, SCHEMAS_PAYLOAD);
- final var schemas = Futures.getDone(future);
+ public void testTesolveMonitoringSources() throws Exception {
+ final var providedSchemas = NetconfStateSchemasResolverImpl.resolveMonitoringSources(DEVICE_ID, rpc,
+ SCHEMAS_PAYLOAD);
+
+ final var availableYangSchemasQNames = availableYangSchemasQNames(providedSchemas);
- final var availableYangSchemasQNames = schemas.getAvailableYangSchemasQNames();
assertEquals(69, availableYangSchemasQNames.size());
assertThat(availableYangSchemasQNames,
doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(rpcReply))).when(rpc)
.invokeNetconf(eq(Get.QNAME), any());
final var stateSchemas = assertSchemas(CAPS);
- final var availableYangSchemasQNames = stateSchemas.getAvailableYangSchemasQNames();
+ final var availableYangSchemasQNames = availableYangSchemasQNames(stateSchemas.providedSources());
assertEquals(69, availableYangSchemasQNames.size());
assertThat(availableYangSchemasQNames,
@Test
public void testCreateMonitoringNotSupported() throws Exception {
final var stateSchemas = assertSchemas(NetconfSessionPreferences.fromStrings(Set.of()));
- assertEquals(Set.of(), stateSchemas.getAvailableYangSchemasQNames());
+ assertEquals(Set.of(), availableYangSchemasQNames(stateSchemas.providedSources()));
}
@Test
doReturn(Futures.immediateFuture(new DefaultDOMRpcResult(rpcError))).when(rpc)
.invokeNetconf(eq(Get.QNAME), any());
- final var ex = assertInstanceOf(OperationFailedException.class, assertSchemasFailure());
- assertEquals(List.of(rpcError), ex.getErrorList());
+ final var stateSchemas = assertSchemas(CAPS);
+ assertEquals(List.of(), stateSchemas.providedSources());
}
- private NetconfStateSchemas assertSchemas(final NetconfSessionPreferences prefs) {
+ private NetconfDeviceSchemas assertSchemas(final NetconfSessionPreferences prefs) {
try {
- return Futures.getDone(NetconfStateSchemas.forDevice(rpc, prefs, DEVICE_ID, MODEL_CONTEXT));
+ return Futures.getDone(new NetconfStateSchemasResolverImpl().resolve(DEVICE_ID, prefs, rpc, MODEL_CONTEXT));
} catch (ExecutionException e) {
throw new AssertionError(e);
}
}
private Throwable assertSchemasFailure() {
- final var future = NetconfStateSchemas.forDevice(rpc, CAPS, DEVICE_ID, MODEL_CONTEXT);
+ final var future = new NetconfStateSchemasResolverImpl().resolve(DEVICE_ID, CAPS, rpc, MODEL_CONTEXT);
return assertThrows(ExecutionException.class, () -> Futures.getDone(future)).getCause();
}
+
+ private static Set<QName> availableYangSchemasQNames(final List<ProvidedSources<?>> providedSources) {
+ return providedSources.stream()
+ .flatMap(sources -> sources.sources().stream())
+ .collect(Collectors.toSet());
+ }
}
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opendaylight.netconf.client.mdsal.AbstractTestModelTest;
+import org.opendaylight.netconf.client.mdsal.api.NetconfDeviceSchemas;
import org.opendaylight.netconf.client.mdsal.api.NetconfSessionPreferences;
+import org.opendaylight.netconf.client.mdsal.api.ProvidedSources;
import org.opendaylight.netconf.client.mdsal.api.RemoteDeviceId;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapability.CapabilityOrigin;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.device.rev240120.connection.oper.available.capabilities.AvailableCapabilityBuilder;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.source.SourceIdentifier;
import org.opendaylight.yangtools.yang.model.api.source.YangTextSource;
+import org.opendaylight.yangtools.yang.model.api.stmt.FeatureSet;
import org.opendaylight.yangtools.yang.model.repo.api.EffectiveModelContextFactory;
import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
.getSchemaSource(any(), eq(YangTextSource.class));
final var setup = new SchemaSetup(schemaRepository, contextFactory, DEVICE_ID,
- new DeviceSources(Set.of(TEST_QNAME, TEST_QNAME2), Set.of(TEST_QNAME, TEST_QNAME2), sourceProvider),
+ new NetconfDeviceSchemas(Set.of(TEST_QNAME, TEST_QNAME2), FeatureSet.builder().build(), Set.of(),
+ List.of(new ProvidedSources<>(YangTextSource.class, sourceProvider, Set.of(TEST_QNAME, TEST_QNAME2)))),
NetconfSessionPreferences.fromStrings(Set.of()));
final var result = Futures.getDone(setup.startResolution());
.createEffectiveModelContext(anyCollection());
final var setup = new SchemaSetup(schemaRepository, contextFactory, DEVICE_ID,
- new DeviceSources(Set.of(TEST_QNAME, TEST_QNAME2), Set.of(TEST_QNAME, TEST_QNAME2), sourceProvider),
+ new NetconfDeviceSchemas(Set.of(TEST_QNAME, TEST_QNAME2), FeatureSet.builder().build(), Set.of(),
+ List.of(new ProvidedSources<>(YangTextSource.class, sourceProvider, Set.of(TEST_QNAME, TEST_QNAME2)))),
NetconfSessionPreferences.fromStrings(Set.of()));
final var result = Futures.getDone(setup.startResolution());
.createEffectiveModelContext(anyCollection());
final var setup = new SchemaSetup(schemaRepository, contextFactory, DEVICE_ID,
- new DeviceSources(Set.of(TEST_QNAME), Set.of(TEST_QNAME), sourceProvider),
+ new NetconfDeviceSchemas(Set.of(TEST_QNAME), FeatureSet.builder().build(), Set.of(),
+ List.of(new ProvidedSources<>(YangTextSource.class, sourceProvider, Set.of(TEST_QNAME)))),
NetconfSessionPreferences.fromStrings(
Set.of(TEST_NAMESPACE + "?module=" + TEST_MODULE + "&revision=" + TEST_REVISION)));