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.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_QNAME;
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.toFilterStructure;
import static org.opendaylight.netconf.sal.connect.netconf.util.NetconfMessageTransformUtil.toId;
+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 java.util.AbstractMap.SimpleEntry;
+import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
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.mdsal.dom.api.DOMRpcService;
return future;
}
+ private ListenableFuture<? extends DOMRpcResult> getConfig(final FutureCallback<DOMRpcResult> callback,
+ final QName datastore, final Optional<YangInstanceIdentifier> filterPath,
+ final List<YangInstanceIdentifier> fields) {
+ requireNonNull(callback);
+ requireNonNull(datastore);
+
+ final NormalizedNode<?, ?> rpcInput;
+ if (isFilterPresent(filterPath)) {
+ final DataContainerChild<?, ?> node = transformer.toFilterStructure(
+ Collections.singletonList(FieldsFilter.of(filterPath.get(), fields)));
+ rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore), node);
+ } else if (containsEmptyPath(fields)) {
+ rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID, getSourceNode(datastore));
+ } else {
+ final DataContainerChild<?, ?> subtreeFilter = getSubtreeFilterFromRootFields(fields);
+ rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_CONFIG_NODEID,
+ getSourceNode(datastore), subtreeFilter);
+ }
+ final ListenableFuture<? extends DOMRpcResult> response = rpc.invokeRpc(NETCONF_GET_CONFIG_QNAME, rpcInput);
+ Futures.addCallback(response, callback, MoreExecutors.directExecutor());
+ return response;
+ }
+
+ /**
+ * 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<Optional<NormalizedNode<?, ?>>> getConfigRunningData(
final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> 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<Optional<NormalizedNode<?, ?>>> getConfigRunningData(
final FutureCallback<DOMRpcResult> callback, final Optional<YangInstanceIdentifier> filterPath,
final List<YangInstanceIdentifier> fields) {
- // FIXME: implement this method
- throw new UnsupportedOperationException();
+ 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));
+ }
+ final ListenableFuture<? extends DOMRpcResult> response = getConfigRunning(callback, filterPath, fields);
+ return extractData(filterPath, response);
}
+ /**
+ * 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<Optional<NormalizedNode<?, ?>>> getData(final FutureCallback<DOMRpcResult> callback,
final Optional<YangInstanceIdentifier> 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<Optional<NormalizedNode<?, ?>>> getData(final FutureCallback<DOMRpcResult> callback,
- final Optional<YangInstanceIdentifier> path, final List<YangInstanceIdentifier> fields) {
- // FIXME: implement this method
- throw new UnsupportedOperationException();
+ final Optional<YangInstanceIdentifier> filterPath, final List<YangInstanceIdentifier> 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));
+ }
+ final ListenableFuture<? extends DOMRpcResult> response = get(callback, filterPath, fields);
+ return extractData(filterPath, response);
}
private ListenableFuture<Optional<NormalizedNode<?, ?>>> extractData(
return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath);
}
+ private ListenableFuture<? extends DOMRpcResult> getConfigRunning(final FutureCallback<DOMRpcResult> callback,
+ final Optional<YangInstanceIdentifier> filterPath, final List<YangInstanceIdentifier> fields) {
+ return getConfig(callback, NETCONF_RUNNING_QNAME, filterPath, fields);
+ }
+
public ListenableFuture<? extends DOMRpcResult> getConfigCandidate(final FutureCallback<DOMRpcResult> callback,
final Optional<YangInstanceIdentifier> filterPath) {
return getConfig(callback, NETCONF_CANDIDATE_QNAME, filterPath);
return future;
}
+ private ListenableFuture<? extends DOMRpcResult> get(final FutureCallback<DOMRpcResult> callback,
+ final Optional<YangInstanceIdentifier> filterPath, final List<YangInstanceIdentifier> fields) {
+ requireNonNull(callback);
+
+ final NormalizedNode<?, ?> rpcInput;
+ if (isFilterPresent(filterPath)) {
+ rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_NODEID, transformer.toFilterStructure(
+ Collections.singletonList(FieldsFilter.of(filterPath.get(), fields))));
+ } else if (containsEmptyPath(fields)) {
+ rpcInput = GET_RPC_CONTENT;
+ } else {
+ final DataContainerChild<?, ?> subtreeFilter = getSubtreeFilterFromRootFields(fields);
+ rpcInput = NetconfMessageTransformUtil.wrap(NETCONF_GET_NODEID, subtreeFilter);
+ }
+ final ListenableFuture<? extends DOMRpcResult> response = rpc.invokeRpc(NETCONF_GET_QNAME, rpcInput);
+ Futures.addCallback(response, callback, MoreExecutors.directExecutor());
+ return response;
+ }
+
+ private static boolean containsEmptyPath(final List<YangInstanceIdentifier> fields) {
+ return fields.stream().anyMatch(YangInstanceIdentifier::isEmpty);
+ }
+
+ private DataContainerChild<?, ?> getSubtreeFilterFromRootFields(final List<YangInstanceIdentifier> fields) {
+ final Map<YangInstanceIdentifier, List<YangInstanceIdentifier>> getConfigEntries = fields.stream()
+ .map(fieldPath -> {
+ final YangInstanceIdentifier rootPath = YangInstanceIdentifier.create(
+ Iterables.limit(fieldPath.getPathArguments(), 1));
+ final YangInstanceIdentifier updatedFieldPath = YangInstanceIdentifier.create(
+ Iterables.skip(fieldPath.getPathArguments(), 1));
+ return new SimpleEntry<>(rootPath, updatedFieldPath);
+ })
+ .collect(Collectors.groupingBy(SimpleEntry::getKey,
+ Collectors.mapping(SimpleEntry::getValue, Collectors.toList())));
+ final List<FieldsFilter> fieldsFilters = getConfigEntries.keySet().stream()
+ .map(rootPath -> FieldsFilter.of(rootPath, getConfigEntries.get(rootPath)))
+ .collect(Collectors.toList());
+ return transformer.toFilterStructure(fieldsFilters);
+ }
+
private static boolean isFilterPresent(final Optional<YangInstanceIdentifier> filterPath) {
return filterPath.isPresent() && !filterPath.get().isEmpty();
}