--- /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.sal.connect.netconf;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.concepts.Immutable;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier;
+import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.util.AbstractSchemaContextProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: this should really come from rfc8528-data-util
+final class DeviceMountPointContext extends AbstractSchemaContextProvider implements Immutable, MountPointContext {
+ private static final Logger LOG = LoggerFactory.getLogger(DeviceMountPointContext.class);
+ private static final NodeIdentifier MOUNT_POINT = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "mount-point").intern());
+ private static final NodeIdentifier CONFIG = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "config").intern());
+ private static final NodeIdentifier MODULE = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "module").intern());
+ private static final NodeIdentifier LABEL = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "label").intern());
+ private static final NodeIdentifier SCHEMA_REF = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "schema-ref").intern());
+ private static final NodeIdentifier INLINE = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "inline").intern());
+ private static final NodeIdentifier SHARED_SCHEMA = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "shared-schema").intern());
+ private static final NodeIdentifier PARENT_REFERENCE = NodeIdentifier.create(
+ QName.create(SchemaMountConstants.RFC8528_MODULE, "parent-reference").intern());
+
+ private final ImmutableMap<MountPointIdentifier, NetconfMountPointContextFactory> mountPoints;
+
+ private DeviceMountPointContext(final SchemaContext schemaContext,
+ final Map<MountPointIdentifier, NetconfMountPointContextFactory> mountPoints) {
+ super(schemaContext);
+ this.mountPoints = ImmutableMap.copyOf(mountPoints);
+ }
+
+ static MountPointContext create(final MountPointContext emptyContext, final ContainerNode mountData) {
+ final Optional<DataContainerChild<?, ?>> optMountPoint = mountData.getChild(MOUNT_POINT);
+ if (!optMountPoint.isPresent()) {
+ LOG.debug("mount-point list not present in {}", mountData);
+ return emptyContext;
+ }
+
+ final SchemaContext schemaContext = emptyContext.getSchemaContext();
+ final DataContainerChild<?, ?> mountPoint = optMountPoint.get();
+ checkArgument(mountPoint instanceof MapNode, "mount-point list %s is not a MapNode", mountPoint);
+
+ final Map<MountPointIdentifier, NetconfMountPointContextFactory> mountPoints = new HashMap<>();
+ for (MapEntryNode entry : ((MapNode) mountPoint).getValue()) {
+ final String moduleName = entry.getChild(MODULE).map(mod -> {
+ checkArgument(mod instanceof LeafNode, "Unexpected module leaf %s", mod);
+ final Object value = mod.getValue();
+ checkArgument(value instanceof String, "Unexpected module leaf value %s", value);
+ return (String) value;
+ }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry));
+ final Iterator<Module> it = schemaContext.findModules(moduleName).iterator();
+ checkArgument(it.hasNext(), "Failed to find a module named %s", moduleName);
+ final QNameModule module = it.next().getQNameModule();
+
+ final MountPointIdentifier mountId = MountPointIdentifier.of(QName.create(module,
+ entry.getChild(LABEL).map(lbl -> {
+ checkArgument(lbl instanceof LeafNode, "Unexpected label leaf %s", lbl);
+ final Object value = lbl.getValue();
+ checkArgument(value instanceof String, "Unexpected label leaf value %s", value);
+ return (String) value;
+ }).orElseThrow(() -> new IllegalArgumentException("Mount module missing in " + entry))));
+
+ final DataContainerChild<?, ?> child = entry.getChild(SCHEMA_REF).orElseThrow(
+ () -> new IllegalArgumentException("Missing schema-ref choice in " + entry));
+ checkArgument(child instanceof ChoiceNode, "Unexpected schema-ref choice %s", child);
+ final ChoiceNode schemaRef = (ChoiceNode) child;
+
+ final Optional<DataContainerChild<?, ?>> maybeShared = schemaRef.getChild(SHARED_SCHEMA);
+ if (!maybeShared.isPresent()) {
+ LOG.debug("Ignoring non-shared mountpoint entry {}", entry);
+ continue;
+ }
+
+ mountPoints.put(mountId, new NetconfMountPointContextFactory(schemaContext));
+ }
+
+ return new DeviceMountPointContext(schemaContext, mountPoints);
+ }
+
+ @Override
+ public Optional<MountPointContextFactory> findMountPoint(@NonNull final MountPointIdentifier label) {
+ return Optional.ofNullable(mountPoints.get(requireNonNull(label)));
+ }
+}
import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.requireNonNull;
+import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_NODEID;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import org.opendaylight.yang.gen.v1.urn.opendaylight.netconf.node.topology.rev150114.netconf.node.connection.status.unavailable.capabilities.UnavailableCapability;
import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext;
import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.model.api.SchemaMountConstants;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.repo.api.MissingSchemaSourceException;
import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
justification = "Needed for common logging of related classes")
static final Logger LOG = LoggerFactory.getLogger(NetconfDevice.class);
+ private static final QName RFC8528_SCHEMA_MOUNTS_QNAME = QName.create(
+ SchemaMountConstants.RFC8528_MODULE, "schema-mounts").intern();
+ private static final YangInstanceIdentifier RFC8528_SCHEMA_MOUNTS = YangInstanceIdentifier.create(
+ NodeIdentifier.create(RFC8528_SCHEMA_MOUNTS_QNAME));
+
protected final RemoteDeviceId id;
protected final SchemaContextFactory schemaContextFactory;
protected final SchemaSourceRegistry schemaRegistry;
}
// Set up the SchemaContext for the device
- final ListenableFuture<SchemaContext> futureSchema = Futures.transformAsync(sourceResolverFuture, schemas -> {
- LOG.debug("{}: Resolved device sources to {}", id, schemas);
- addProvidedSourcesToSchemaRegistry(schemas);
- return new SchemaSetup(schemas, remoteSessionCapabilities).startResolution();
- }, processingExecutor);
+ final ListenableFuture<SchemaContext> futureSchema = Futures.transformAsync(sourceResolverFuture,
+ deviceSources -> assembleSchemaContext(deviceSources, remoteSessionCapabilities), processingExecutor);
// Potentially acquire mount point list and interpret it
- final ListenableFuture<MountPointContext> futureContext = Futures.transform(futureSchema, schemaContext -> {
- // FIXME: check if there is RFC8528 schema available
- return new EmptyMountPointContext(schemaContext);
- }, processingExecutor);
+ final ListenableFuture<MountPointContext> futureContext = Futures.transformAsync(futureSchema,
+ schemaContext -> createMountPointContext(schemaContext, baseSchema, listener), processingExecutor);
Futures.addCallback(futureContext, new FutureCallback<MountPointContext>() {
@Override
this.connected = connected;
}
- private void addProvidedSourcesToSchemaRegistry(final DeviceSources deviceSources) {
+ private ListenableFuture<SchemaContext> assembleSchemaContext(final DeviceSources deviceSources,
+ final NetconfSessionPreferences remoteSessionCapabilities) {
+ LOG.debug("{}: Resolved device sources to {}", id, deviceSources);
final SchemaSourceProvider<YangTextSchemaSource> yangProvider = deviceSources.getSourceProvider();
for (final SourceIdentifier sourceId : deviceSources.getProvidedSources()) {
sourceRegistrations.add(schemaRegistry.registerSchemaSource(yangProvider,
- PotentialSchemaSource.create(
- sourceId, YangTextSchemaSource.class, PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
+ PotentialSchemaSource.create(sourceId, YangTextSchemaSource.class,
+ PotentialSchemaSource.Costs.REMOTE_IO.getValue())));
}
+
+ return new SchemaSetup(deviceSources, remoteSessionCapabilities).startResolution();
+ }
+
+ private ListenableFuture<MountPointContext> createMountPointContext(final SchemaContext schemaContext,
+ final BaseSchema baseSchema, final NetconfDeviceCommunicator listener) {
+ final MountPointContext emptyContext = new EmptyMountPointContext(schemaContext);
+ if (!schemaContext.findModule(SchemaMountConstants.RFC8528_MODULE).isPresent()) {
+ return Futures.immediateFuture(emptyContext);
+ }
+
+ // Create a temporary RPC invoker and acquire the mount point tree
+ LOG.debug("{}: Acquiring available mount points", id);
+ final NetconfDeviceRpc deviceRpc = new NetconfDeviceRpc(schemaContext, listener,
+ new NetconfMessageTransformer(emptyContext, false, baseSchema));
+
+ return Futures.transform(deviceRpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_GET_PATH,
+ Builders.containerBuilder().withNodeIdentifier(NETCONF_GET_NODEID)
+ .withChild(NetconfMessageTransformUtil.toFilterStructure(RFC8528_SCHEMA_MOUNTS, schemaContext))
+ .build()), rpcResult -> processSchemaMounts(rpcResult, emptyContext), MoreExecutors.directExecutor());
+ }
+
+ private MountPointContext processSchemaMounts(final DOMRpcResult rpcResult, final MountPointContext emptyContext) {
+ final Collection<? extends RpcError> errors = rpcResult.getErrors();
+ if (!errors.isEmpty()) {
+ LOG.warn("{}: Schema-mounts acquisition resulted in errors {}", id, errors);
+ }
+ final NormalizedNode<?, ?> schemaMounts = rpcResult.getResult();
+ if (schemaMounts == null) {
+ LOG.debug("{}: device does not define any schema mounts", id);
+ return emptyContext;
+ }
+ if (!(schemaMounts instanceof ContainerNode)) {
+ LOG.warn("{}: ignoring non-container schema mounts {}", id, schemaMounts);
+ return emptyContext;
+ }
+
+ return DeviceMountPointContext.create(emptyContext, (ContainerNode) schemaMounts);
}
@Override
--- /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.sal.connect.netconf;
+
+import java.util.Map;
+import org.opendaylight.yangtools.rcf8528.data.util.EmptyMountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointChild;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext;
+import org.opendaylight.yangtools.rfc8528.data.api.MountPointContextFactory;
+import org.opendaylight.yangtools.rfc8528.data.api.YangLibraryConstants.ContainerName;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangParserException;
+
+// TODO: this should really come from mdsal-yanglib-rfc8525
+final class NetconfMountPointContextFactory implements MountPointContextFactory {
+ private final MountPointContext mountPoint;
+
+ NetconfMountPointContextFactory(final SchemaContext schemaContext) {
+ mountPoint = new EmptyMountPointContext(schemaContext);
+ }
+
+ @Override
+ public MountPointContext createContext(final Map<ContainerName, MountPointChild> libraryContainers,
+ final MountPointChild schemaMounts) throws YangParserException {
+ return mountPoint;
+ }
+}