X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=netconf%2Fsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fnetconf%2Fsal%2Fconnect%2Fnetconf%2Futil%2FNetconfBaseOps.java;h=af09e9f575a8cb735f379608b570ae43639c3645;hb=f3cd4750c64f9002940b3bf20ea6be47a14344c3;hp=3dee33869ede81d72aa3f8572c8ef43d5f77be8c;hpb=3e787780b4682064a31337a41c04749663a5652d;p=netconf.git diff --git a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfBaseOps.java b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfBaseOps.java index 3dee33869e..af09e9f575 100644 --- a/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfBaseOps.java +++ b/netconf/sal-netconf-connector/src/main/java/org/opendaylight/netconf/sal/connect/netconf/util/NetconfBaseOps.java @@ -5,355 +5,434 @@ * 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.util; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME; +import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.COMMIT_RPC_CONTENT; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.EDIT_CONTENT_NODEID; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.GET_RPC_CONTENT; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_NODEID; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_NODEID; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_CONFIG_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_GET_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_LOCK_QNAME; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_SOURCE_QNAME; -import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_NODEID; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_SOURCE_NODEID; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_NODEID; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_UNLOCK_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_UNLOCK_QNAME; +import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_VALIDATE_NODEID; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION; import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toFilterStructure; -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.Function; -import com.google.common.base.Optional; -import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult; -import org.opendaylight.controller.md.sal.dom.api.DOMRpcService; -import org.opendaylight.netconf.sal.connect.netconf.sal.KeepaliveSalFacade.KeepaliveDOMRpcService; -import org.opendaylight.netconf.sal.connect.netconf.sal.SchemalessNetconfDeviceRpc; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.stream.Collectors; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.mdsal.dom.api.DOMRpcResult; +import org.opendaylight.netconf.api.ModifyAction; +import org.opendaylight.netconf.sal.connect.api.NetconfRpcService; +import org.opendaylight.netconf.sal.connect.api.RemoteDeviceServices.Rpcs; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.copy.config.input.target.ConfigTarget; -import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.edit.config.input.EditContent; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.get.config.input.source.ConfigSource; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.ModifyAction; +import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext; +import org.opendaylight.yangtools.yang.common.Empty; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; +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.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; /** - * Provides base operations for netconf e.g. get, get-config, edit-config, (un)lock, commit etc. - * According to RFC-6241 + * Provides base operations for NETCONF e.g. {@code get}, {@code get-config}, {@code edit-config}, {@code commit} etc. + * as per RFC6241 Protocol Operations. */ +// FIXME: turn Optional arguments to @Nullable public final class NetconfBaseOps { + private static final NodeIdentifier CONFIG_SOURCE_NODEID = NodeIdentifier.create(ConfigSource.QNAME); + private static final NodeIdentifier CONFIG_TARGET_NODEID = NodeIdentifier.create(ConfigTarget.QNAME); + private static final LeafNode NETCONF_ERROR_OPTION_ROLLBACK = + ImmutableNodes.leafNode(NETCONF_ERROR_OPTION_NODEID, ROLLBACK_ON_ERROR_OPTION); - private final DOMRpcService rpc; - private final SchemaContext schemaContext; + private final NetconfRpcService rpc; + private final MountPointContext mountContext; private final RpcStructureTransformer transformer; - public NetconfBaseOps(final DOMRpcService rpc, final SchemaContext schemaContext) { - this.rpc = rpc; - this.schemaContext = schemaContext; + public NetconfBaseOps(final Rpcs rpc, final MountPointContext mountContext) { + this.rpc = requireNonNull(rpc); + this.mountContext = requireNonNull(mountContext); - if ((rpc instanceof KeepaliveDOMRpcService) - && (((KeepaliveDOMRpcService) rpc).getDeviceRpc() instanceof SchemalessNetconfDeviceRpc)) { - this.transformer = new SchemalessRpcStructureTransformer(); + if (rpc instanceof Rpcs.Schemaless) { + transformer = new SchemalessRpcStructureTransformer(); + } else if (rpc instanceof Rpcs.Normalized) { + transformer = new NetconfRpcStructureTransformer(mountContext); } else { - this.transformer = new NetconfRpcStructureTransformer(schemaContext); + throw new IllegalStateException("Unhandled rpcs " + rpc); } } - public ListenableFuture lock(final FutureCallback callback, final QName datastore) { - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(datastore); - - final ListenableFuture future = - rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(datastore)); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture lock(final FutureCallback callback, + final NodeIdentifier datastore) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_LOCK_QNAME, getLockContent(datastore))); } - public ListenableFuture lockCandidate(final FutureCallback callback) { - final ListenableFuture future = - rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_CANDIDATE_QNAME)); + private static ListenableFuture addCallback(final FutureCallback callback, + final ListenableFuture future) { Futures.addCallback(future, callback, MoreExecutors.directExecutor()); return future; } - - public ListenableFuture lockRunning(final FutureCallback callback) { - final ListenableFuture future = - rpc.invokeRpc(toPath(NETCONF_LOCK_QNAME), getLockContent(NETCONF_RUNNING_QNAME)); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture lockCandidate(final FutureCallback callback) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_LOCK_QNAME, + getLockContent(NETCONF_CANDIDATE_NODEID))); } - public ListenableFuture unlock(final FutureCallback callback, final QName datastore) { - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(datastore); - - final ListenableFuture future = - rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(datastore)); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture lockRunning(final FutureCallback callback) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_LOCK_QNAME, + getLockContent(NETCONF_RUNNING_NODEID))); } - public ListenableFuture unlockRunning(final FutureCallback callback) { - final ListenableFuture future = - rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_RUNNING_QNAME)); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture unlock(final FutureCallback callback, + final NodeIdentifier datastore) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_UNLOCK_QNAME, + getUnLockContent(datastore))); } - public ListenableFuture unlockCandidate(final FutureCallback callback) { - final ListenableFuture future = - rpc.invokeRpc(toPath(NETCONF_UNLOCK_QNAME), getUnLockContent(NETCONF_CANDIDATE_QNAME)); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture unlockRunning(final FutureCallback callback) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_UNLOCK_QNAME, + getUnLockContent(NETCONF_RUNNING_NODEID))); } - public ListenableFuture discardChanges(final FutureCallback callback) { - Preconditions.checkNotNull(callback); - - final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_DISCARD_CHANGES_QNAME), null); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture unlockCandidate(final FutureCallback callback) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_UNLOCK_QNAME, + getUnLockContent(NETCONF_CANDIDATE_NODEID))); } - public ListenableFuture commit(final FutureCallback callback) { - Preconditions.checkNotNull(callback); - - final ListenableFuture future = rpc.invokeRpc( - toPath(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME), NetconfMessageTransformUtil.COMMIT_RPC_CONTENT); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture discardChanges(final FutureCallback callback) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_DISCARD_CHANGES_QNAME, + DISCARD_CHANGES_RPC_CONTENT)); } - public ListenableFuture validate(final FutureCallback callback, final QName datastore) { - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(datastore); - - final ListenableFuture future = rpc.invokeRpc( - toPath(NetconfMessageTransformUtil.NETCONF_VALIDATE_QNAME), getValidateContent(datastore)); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture commit(final FutureCallback callback) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_COMMIT_QNAME, COMMIT_RPC_CONTENT)); } - public ListenableFuture validateCandidate(final FutureCallback callback) { - return validate(callback, NETCONF_CANDIDATE_QNAME); + public ListenableFuture validate(final FutureCallback callback, + final NodeIdentifier datastore) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_VALIDATE_QNAME, + getValidateContent(requireNonNull(datastore)))); } - - public ListenableFuture validateRunning(final FutureCallback callback) { - return validate(callback, NETCONF_RUNNING_QNAME); + public ListenableFuture validateCandidate(final FutureCallback callback) { + return validate(callback, NETCONF_CANDIDATE_NODEID); } - public ListenableFuture copyConfig(final FutureCallback callback, - final QName source, final QName target) { - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(source); - Preconditions.checkNotNull(target); + public ListenableFuture validateRunning(final FutureCallback callback) { + return validate(callback, NETCONF_RUNNING_NODEID); + } - final ListenableFuture future = rpc.invokeRpc( - toPath(NetconfMessageTransformUtil.NETCONF_COPY_CONFIG_QNAME), getCopyConfigContent(source, target)); - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + public ListenableFuture copyConfig(final FutureCallback callback, + final NodeIdentifier sourceDatastore, final NodeIdentifier targetDatastore) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_COPY_CONFIG_QNAME, + getCopyConfigContent(sourceDatastore, targetDatastore))); } - public ListenableFuture copyRunningToCandidate(final FutureCallback callback) { - return copyConfig(callback, NETCONF_RUNNING_QNAME, NETCONF_CANDIDATE_QNAME); + public ListenableFuture copyRunningToCandidate( + final FutureCallback callback) { + return copyConfig(callback, NETCONF_RUNNING_NODEID, NETCONF_CANDIDATE_NODEID); } - public ListenableFuture getConfig(final FutureCallback callback, final QName datastore, - final Optional filterPath) { - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(datastore); + public ListenableFuture getConfig(final FutureCallback callback, + final NodeIdentifier datastore, final Optional filterPath) { + final var source = getSourceNode(datastore); + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_GET_CONFIG_QNAME, + nonEmptyFilter(filterPath) + .map(path -> NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, source, + transformer.toFilterStructure(path))) + .orElseGet(() -> NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, source)))); + } - final ListenableFuture future; - if (isFilterPresent(filterPath)) { - final DataContainerChild node = transformer.toFilterStructure(filterPath.get()); - future = rpc.invokeRpc(toPath(NETCONF_GET_CONFIG_QNAME), - NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, getSourceNode(datastore), node)); + private ListenableFuture getConfig(final FutureCallback callback, + final NodeIdentifier datastore, final Optional filterPath, + final List fields) { + final ContainerNode rpcInput; + if (nonEmptyFilter(filterPath).isPresent()) { + rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore), + transformer.toFilterStructure(List.of(FieldsFilter.of(filterPath.orElseThrow(), fields)))); + } else if (containsEmptyPath(fields)) { + rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore)); } else { - future = rpc.invokeRpc(toPath(NETCONF_GET_CONFIG_QNAME), - NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_QNAME, getSourceNode(datastore))); + rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, + getSourceNode(datastore), getSubtreeFilterFromRootFields(fields)); } - - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_GET_CONFIG_QNAME, rpcInput)); + } + + /** + * Calling GET-CONFIG RPC with subtree filter that is specified by {@link YangInstanceIdentifier}. + * + * @param callback RPC response callback + * @param filterPath path to requested data + * @return asynchronous completion token with read {@link NormalizedNode} wrapped in {@link Optional} instance + */ + public ListenableFuture> getConfigRunningData(final FutureCallback callback, + final Optional filterPath) { + return extractData(filterPath, getConfigRunning(callback, filterPath)); + } + + /** + * Calling GET-CONFIG RPC with subtree filter tha tis specified by parent {@link YangInstanceIdentifier} and list + * of specific fields that caller would like to read. Field paths are relative to parent path. + * + * @param callback RPC response callback + * @param filterPath parent path to requested data + * @param fields paths to specific fields that are selected under parent path + * @return asynchronous completion token with read {@link NormalizedNode} wrapped in {@link Optional} instance + */ + public ListenableFuture> getConfigRunningData(final FutureCallback callback, + final Optional filterPath, final List fields) { + if (fields.isEmpty()) { + // RFC doesn't allow to build subtree filter that would expect just empty element in response + return Futures.immediateFailedFuture(new IllegalArgumentException( + "Failed to build NETCONF GET-CONFIG RPC: provided list of fields is empty; filter path: " + + filterPath)); + } + return extractData(filterPath, getConfigRunning(callback, filterPath, fields)); + } + + /** + * Calling GET RPC with subtree filter that is specified by {@link YangInstanceIdentifier}. + * + * @param callback RPC response callback + * @param filterPath path to requested data + * @return asynchronous completion token with read {@link NormalizedNode} wrapped in {@link Optional} instance + */ + public ListenableFuture> getData(final FutureCallback callback, + final Optional filterPath) { + return extractData(filterPath, get(callback, filterPath)); + } + + /** + * Calling GET RPC with subtree filter tha tis specified by parent {@link YangInstanceIdentifier} and list + * of specific fields that caller would like to read. Field paths are relative to parent path. + * + * @param callback RPC response callback + * @param filterPath parent path to requested data + * @param fields paths to specific fields that are selected under parent path + * @return asynchronous completion token with read {@link NormalizedNode} wrapped in {@link Optional} instance + */ + public ListenableFuture> getData(final FutureCallback callback, + final Optional filterPath, final List fields) { + if (fields.isEmpty()) { + // RFC doesn't allow to build subtree filter that would expect just empty element in response + return Futures.immediateFailedFuture(new IllegalArgumentException( + "Failed to build NETCONF GET RPC: provided list of fields is empty; filter path: " + filterPath)); + } + return extractData(filterPath, get(callback, filterPath, fields)); } - public ListenableFuture>> getConfigRunningData( - final FutureCallback callback, final Optional filterPath) { - final ListenableFuture configRunning = getConfigRunning(callback, filterPath); - return extractData(filterPath, configRunning); + private ListenableFuture> extractData(final Optional path, + final ListenableFuture configRunning) { + return Futures.transform(configRunning, result -> { + final var errors = result.errors(); + checkArgument(errors.isEmpty(), "Unable to read data: %s, errors: %s", path, errors); + return transformer.selectFromDataStructure(result.value() + .getChildByArg(NetconfMessageTransformUtil.NETCONF_DATA_NODEID), path.orElseThrow()); + }, MoreExecutors.directExecutor()); } - public ListenableFuture>> getData(final FutureCallback callback, - final Optional filterPath) { - final ListenableFuture configRunning = get(callback, filterPath); - return extractData(filterPath, configRunning); + public ListenableFuture getConfigRunning(final FutureCallback callback, + final Optional filterPath) { + return getConfig(callback, NETCONF_RUNNING_NODEID, filterPath); } - private ListenableFuture>> extractData( - final Optional path, final ListenableFuture configRunning) { - return Futures.transform(configRunning, (Function>>) result -> { - Preconditions.checkArgument( - result.getErrors().isEmpty(), "Unable to read data: %s, errors: %s", path, result.getErrors()); - final DataContainerChild dataNode = - ((ContainerNode) result.getResult()).getChild( - NetconfMessageTransformUtil.toId(NetconfMessageTransformUtil.NETCONF_DATA_QNAME)).get(); - return transformer.selectFromDataStructure(dataNode, path.get()); - }); + private ListenableFuture getConfigRunning(final FutureCallback callback, + final Optional filterPath, final List fields) { + return getConfig(callback, NETCONF_RUNNING_NODEID, filterPath, fields); } - public ListenableFuture getConfigRunning(final FutureCallback callback, - final Optional filterPath) { - return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath); + public ListenableFuture getConfigCandidate(final FutureCallback callback, + final Optional filterPath) { + return getConfig(callback, NETCONF_CANDIDATE_NODEID, filterPath); } - public ListenableFuture getConfigCandidate(final FutureCallback callback, - final Optional filterPath) { - return getConfig(callback, NETCONF_CANDIDATE_QNAME, filterPath); + public ListenableFuture get(final FutureCallback callback, + final Optional filterPath) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_GET_QNAME, + nonEmptyFilter(filterPath) + .map(path -> NetconfMessageTransformUtil.wrap(NETCONF_GET_NODEID, + toFilterStructure(path, mountContext.getEffectiveModelContext()))) + .orElse(NetconfMessageTransformUtil.GET_RPC_CONTENT))); } - public ListenableFuture get(final FutureCallback callback, - final Optional filterPath) { - Preconditions.checkNotNull(callback); - - final ListenableFuture future; - - future = isFilterPresent(filterPath) - ? rpc.invokeRpc(toPath(NETCONF_GET_QNAME), - NetconfMessageTransformUtil.wrap(NETCONF_GET_QNAME, toFilterStructure(filterPath.get(), schemaContext))) - : rpc.invokeRpc(toPath(NETCONF_GET_QNAME), NetconfMessageTransformUtil.GET_RPC_CONTENT); + private ListenableFuture get(final FutureCallback callback, + final Optional filterPath, final List fields) { + final ContainerNode rpcInput; + if (nonEmptyFilter(filterPath).isPresent()) { + rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_NODEID, transformer.toFilterStructure( + Collections.singletonList(FieldsFilter.of(filterPath.orElseThrow(), fields)))); + } else if (containsEmptyPath(fields)) { + rpcInput = GET_RPC_CONTENT; + } else { + rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_NODEID, getSubtreeFilterFromRootFields(fields)); + } + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_GET_QNAME, rpcInput)); + } - Futures.addCallback(future, callback); - return future; + private static boolean containsEmptyPath(final List fields) { + return fields.stream().anyMatch(YangInstanceIdentifier::isEmpty); } - private static boolean isFilterPresent(final Optional filterPath) { - return filterPath.isPresent() && !filterPath.get().isEmpty(); + private DataContainerChild getSubtreeFilterFromRootFields(final List fields) { + return transformer.toFilterStructure(fields.stream() + .map(fieldPath -> Map.entry( + YangInstanceIdentifier.create(Iterables.limit(fieldPath.getPathArguments(), 1)), + YangInstanceIdentifier.create(Iterables.skip(fieldPath.getPathArguments(), 1)))) + .collect(Collectors.groupingBy(Entry::getKey, + Collectors.mapping(Entry::getValue, Collectors.toUnmodifiableList()))) + .entrySet().stream() + .map(entry -> FieldsFilter.of(entry.getKey(), entry.getValue())) + .collect(Collectors.toUnmodifiableList())); } - public ListenableFuture editConfigCandidate(final FutureCallback callback, - final DataContainerChild editStructure, - final ModifyAction modifyAction, final boolean rollback) { - return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.of(modifyAction), rollback); + private static Optional nonEmptyFilter(final Optional filterPath) { + return filterPath.filter(path -> !path.isEmpty()); } - public ListenableFuture editConfigCandidate(final FutureCallback callback, - final DataContainerChild editStructure, - final boolean rollback) { - return editConfig(callback, NETCONF_CANDIDATE_QNAME, editStructure, Optional.absent(), rollback); + public ListenableFuture editConfigCandidate( + final FutureCallback callback, final DataContainerChild editStructure, + final ModifyAction modifyAction, final boolean rollback) { + return editConfig(callback, NETCONF_CANDIDATE_NODEID, editStructure, Optional.of(modifyAction), rollback); } - public ListenableFuture editConfigRunning(final FutureCallback callback, - final DataContainerChild editStructure, - final ModifyAction modifyAction, final boolean rollback) { - return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.of(modifyAction), rollback); + public ListenableFuture editConfigCandidate( + final FutureCallback callback, final DataContainerChild editStructure, + final boolean rollback) { + return editConfig(callback, NETCONF_CANDIDATE_NODEID, editStructure, Optional.empty(), rollback); } - public ListenableFuture editConfigRunning(final FutureCallback callback, - final DataContainerChild editStructure, - final boolean rollback) { - return editConfig(callback, NETCONF_RUNNING_QNAME, editStructure, Optional.absent(), rollback); + public ListenableFuture editConfigRunning( + final FutureCallback callback, final DataContainerChild editStructure, + final ModifyAction modifyAction, final boolean rollback) { + return editConfig(callback, NETCONF_RUNNING_NODEID, editStructure, Optional.of(modifyAction), rollback); } - public ListenableFuture editConfig( - final FutureCallback callback, final QName datastore, - final DataContainerChild editStructure, final Optional modifyAction, + public ListenableFuture editConfigRunning( + final FutureCallback callback, final DataContainerChild editStructure, final boolean rollback) { - Preconditions.checkNotNull(editStructure); - Preconditions.checkNotNull(callback); - Preconditions.checkNotNull(datastore); - - final ListenableFuture future = rpc.invokeRpc(toPath(NETCONF_EDIT_CONFIG_QNAME), - getEditConfigContent(datastore, editStructure, modifyAction, rollback)); - - Futures.addCallback(future, callback, MoreExecutors.directExecutor()); - return future; + return editConfig(callback, NETCONF_RUNNING_NODEID, editStructure, Optional.empty(), rollback); } - public DataContainerChild createEditConfigStrcture(final Optional> lastChild, - final Optional operation, - final YangInstanceIdentifier dataPath) { - final AnyXmlNode configContent = transformer.createEditConfigStructure(lastChild, dataPath, operation); - return Builders.choiceBuilder().withNodeIdentifier(toId(EditContent.QNAME)).withChild(configContent).build(); + public ListenableFuture editConfig( + final FutureCallback callback, final NodeIdentifier datastore, + final DataContainerChild editStructure, final Optional modifyAction, + final boolean rollback) { + return addCallback(requireNonNull(callback), rpc.invokeNetconf(NETCONF_EDIT_CONFIG_QNAME, + getEditConfigContent(requireNonNull(datastore), requireNonNull(editStructure), modifyAction, rollback))); } - private static ContainerNode getEditConfigContent( - final QName datastore, final DataContainerChild editStructure, - final Optional defaultOperation, final boolean rollback) { - final DataContainerNodeAttrBuilder editBuilder = - Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_EDIT_CONFIG_QNAME)); + public ChoiceNode createEditConfigStructure(final Optional lastChild, + final Optional operation, final YangInstanceIdentifier dataPath) { + return Builders.choiceBuilder() + .withNodeIdentifier(EDIT_CONTENT_NODEID) + .withChild(transformer.createEditConfigStructure(lastChild, dataPath, operation)) + .build(); + } - // Target - editBuilder.withChild(getTargetNode(datastore)); + private static ContainerNode getEditConfigContent(final NodeIdentifier datastore, + final DataContainerChild editStructure, final Optional defaultOperation, + final boolean rollback) { + final var editBuilder = Builders.containerBuilder() + .withNodeIdentifier(NETCONF_EDIT_CONFIG_NODEID) + // Target + .withChild(getTargetNode(datastore)); // Default operation - if (defaultOperation.isPresent()) { - final String opString = defaultOperation.get().name().toLowerCase(); - editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_DEFAULT_OPERATION_QNAME)) - .withValue(opString).build()); - } + defaultOperation.ifPresent(op -> { + editBuilder.withChild(ImmutableNodes.leafNode(NETCONF_DEFAULT_OPERATION_NODEID, op.xmlValue())); + }); // Error option if (rollback) { - editBuilder.withChild(Builders.leafBuilder().withNodeIdentifier(toId(NETCONF_ERROR_OPTION_QNAME)) - .withValue(ROLLBACK_ON_ERROR_OPTION).build()); + editBuilder.withChild(NETCONF_ERROR_OPTION_ROLLBACK); } // Edit content - editBuilder.withChild(editStructure); - return editBuilder.build(); + return editBuilder.withChild(editStructure).build(); } - public static DataContainerChild getSourceNode(final QName datastore) { - return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_SOURCE_QNAME)) - .withChild( - Builders.choiceBuilder().withNodeIdentifier(toId(ConfigSource.QNAME)).withChild( - Builders.leafBuilder().withNodeIdentifier(toId(datastore)).build()).build() - ).build(); + public static @NonNull ContainerNode getSourceNode(final NodeIdentifier datastore) { + return Builders.containerBuilder() + .withNodeIdentifier(NETCONF_SOURCE_NODEID) + .withChild(Builders.choiceBuilder() + .withNodeIdentifier(CONFIG_SOURCE_NODEID) + .withChild(ImmutableNodes.leafNode(datastore, Empty.value())) + .build()) + .build(); } - public static ContainerNode getLockContent(final QName datastore) { - return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_LOCK_QNAME)) - .withChild(getTargetNode(datastore)).build(); + public static @NonNull ContainerNode getLockContent(final NodeIdentifier datastore) { + return Builders.containerBuilder() + .withNodeIdentifier(NETCONF_LOCK_NODEID) + .withChild(getTargetNode(datastore)) + .build(); } - public static DataContainerChild getTargetNode(final QName datastore) { - return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_TARGET_QNAME)) - .withChild( - Builders.choiceBuilder().withNodeIdentifier(toId(ConfigTarget.QNAME)).withChild( - Builders.leafBuilder().withNodeIdentifier(toId(datastore)).build()).build() - ).build(); + public static @NonNull ContainerNode getTargetNode(final NodeIdentifier datastore) { + return Builders.containerBuilder() + .withNodeIdentifier(NETCONF_TARGET_NODEID) + .withChild(Builders.choiceBuilder() + .withNodeIdentifier(CONFIG_TARGET_NODEID) + .withChild(ImmutableNodes.leafNode(datastore, Empty.value())) + .build()) + .build(); } - public static NormalizedNode getCopyConfigContent(final QName source, final QName target) { - return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_COPY_CONFIG_QNAME)) - .withChild(getTargetNode(target)).withChild(getSourceNode(source)).build(); + public static @NonNull ContainerNode getCopyConfigContent(final NodeIdentifier sourceDatastore, + final NodeIdentifier targetDatastore) { + return Builders.containerBuilder() + .withNodeIdentifier(NETCONF_COPY_CONFIG_NODEID) + .withChild(getTargetNode(targetDatastore)) + .withChild(getSourceNode(sourceDatastore)) + .build(); } - public static NormalizedNode getValidateContent(final QName source) { - return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_VALIDATE_QNAME)) - .withChild(getSourceNode(source)).build(); + public static @NonNull ContainerNode getValidateContent(final NodeIdentifier sourceDatastore) { + return Builders.containerBuilder() + .withNodeIdentifier(NETCONF_VALIDATE_NODEID) + .withChild(getSourceNode(sourceDatastore)) + .build(); } - public static NormalizedNode getUnLockContent(final QName datastore) { - return Builders.containerBuilder().withNodeIdentifier(toId(NETCONF_UNLOCK_QNAME)) - .withChild(getTargetNode(datastore)).build(); + public static @NonNull ContainerNode getUnLockContent(final NodeIdentifier datastore) { + return Builders.containerBuilder() + .withNodeIdentifier(NETCONF_UNLOCK_NODEID) + .withChild(getTargetNode(datastore)) + .build(); } - }