2 * Copyright (c) 2020 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.restconf.nb.rfc8040.rests.transactions;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.util.concurrent.FutureCallback;
14 import com.google.common.util.concurrent.Futures;
15 import com.google.common.util.concurrent.ListenableFuture;
16 import com.google.common.util.concurrent.MoreExecutors;
17 import com.google.common.util.concurrent.SettableFuture;
18 import java.util.List;
19 import java.util.Optional;
20 import org.eclipse.jdt.annotation.NonNull;
21 import org.eclipse.jdt.annotation.Nullable;
22 import org.opendaylight.mdsal.common.api.CommitInfo;
23 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
24 import org.opendaylight.mdsal.common.api.ReadFailedException;
25 import org.opendaylight.mdsal.dom.api.DOMActionService;
26 import org.opendaylight.mdsal.dom.api.DOMMountPointService;
27 import org.opendaylight.mdsal.dom.api.DOMRpcService;
28 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
29 import org.opendaylight.mdsal.dom.api.DOMYangTextSourceProvider;
30 import org.opendaylight.netconf.dom.api.NetconfDataTreeService;
31 import org.opendaylight.restconf.api.query.ContentParam;
32 import org.opendaylight.restconf.api.query.WithDefaultsParam;
33 import org.opendaylight.restconf.common.errors.RestconfFuture;
34 import org.opendaylight.restconf.common.errors.SettableRestconfFuture;
35 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
36 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
37 import org.opendaylight.restconf.nb.rfc8040.utils.parser.NetconfFieldsTranslator;
38 import org.opendaylight.restconf.server.api.DataGetParams;
39 import org.opendaylight.restconf.server.api.DatabindContext;
40 import org.opendaylight.restconf.server.spi.ApiPathNormalizer.DataPath;
41 import org.opendaylight.yangtools.yang.common.Empty;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
43 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46 * Implementation of RESTCONF operations on top of a raw NETCONF backend.
48 * @see NetconfDataTreeService
50 public final class NetconfRestconfStrategy extends RestconfStrategy {
51 private final NetconfDataTreeService netconfService;
53 public NetconfRestconfStrategy(final DatabindContext databind, final NetconfDataTreeService netconfService,
54 final @Nullable DOMRpcService rpcService, final @Nullable DOMActionService actionService,
55 final @Nullable DOMYangTextSourceProvider sourceProvider,
56 final @Nullable DOMMountPointService mountPointService) {
57 super(databind, ImmutableMap.of(), rpcService, actionService, sourceProvider, mountPointService);
58 this.netconfService = requireNonNull(netconfService);
62 RestconfTransaction prepareWriteExecution() {
63 return new NetconfRestconfTransaction(modelContext(), netconfService);
67 void delete(final SettableRestconfFuture<Empty> future, final YangInstanceIdentifier path) {
68 final var tx = prepareWriteExecution();
70 Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
72 public void onSuccess(final CommitInfo result) {
73 future.set(Empty.value());
77 public void onFailure(final Throwable cause) {
78 future.setFailure(TransactionUtil.decodeException(cause, "DELETE", path, modelContext()));
80 }, MoreExecutors.directExecutor());
84 RestconfFuture<NormalizedNodePayload> dataGET(final DataPath path, final DataGetParams params) {
85 final var inference = path.inference();
86 final var fields = params.fields();
87 final List<YangInstanceIdentifier> fieldPaths;
89 final var tmp = NetconfFieldsTranslator.translate(inference.getEffectiveModelContext(), path.schema(),
91 fieldPaths = tmp == null || tmp.isEmpty() ? null : tmp;
96 final NormalizedNode node;
97 if (fieldPaths != null) {
98 node = readData(params.content(), path.instance(), params.withDefaults(), fieldPaths);
100 node = readData(params.content(), path.instance(), params.withDefaults());
102 return completeDataGET(inference, QueryParameters.of(params), node);
106 ListenableFuture<Optional<NormalizedNode>> read(final LogicalDatastoreType store,
107 final YangInstanceIdentifier path) {
108 return switch (store) {
109 case CONFIGURATION -> netconfService.getConfig(path);
110 case OPERATIONAL -> netconfService.get(path);
114 private ListenableFuture<Optional<NormalizedNode>> read(final LogicalDatastoreType store,
115 final YangInstanceIdentifier path, final List<YangInstanceIdentifier> fields) {
116 return switch (store) {
117 case CONFIGURATION -> netconfService.getConfig(path, fields);
118 case OPERATIONAL -> netconfService.get(path, fields);
123 * Read specific type of data from data store via transaction with specified subtrees that should only be read.
124 * Close {@link DOMTransactionChain} inside of object {@link RestconfStrategy} provided as a parameter.
126 * @param content type of data to read (config, state, all)
127 * @param path the parent path to read
128 * @param withDefa value of with-defaults parameter
129 * @param fields paths to selected subtrees which should be read, relative to to the parent path
130 * @return {@link NormalizedNode}
132 // FIXME: NETCONF-1155: this method should asynchronous
133 public @Nullable NormalizedNode readData(final @NonNull ContentParam content,
134 final @NonNull YangInstanceIdentifier path, final @Nullable WithDefaultsParam withDefa,
135 final @NonNull List<YangInstanceIdentifier> fields) {
136 return switch (content) {
138 // PREPARE STATE DATA NODE
139 final var stateDataNode = readDataViaTransaction(LogicalDatastoreType.OPERATIONAL, path, fields);
140 // PREPARE CONFIG DATA NODE
141 final var configDataNode = readDataViaTransaction(LogicalDatastoreType.CONFIGURATION, path, fields);
143 yield mergeConfigAndSTateDataIfNeeded(stateDataNode, withDefa == null ? configDataNode
144 : prepareDataByParamWithDef(configDataNode, path, withDefa.mode()));
147 final var read = readDataViaTransaction(LogicalDatastoreType.CONFIGURATION, path, fields);
148 yield withDefa == null ? read : prepareDataByParamWithDef(read, path, withDefa.mode());
150 case NONCONFIG -> readDataViaTransaction(LogicalDatastoreType.OPERATIONAL, path, fields);
155 * Read specific type of data {@link LogicalDatastoreType} via transaction in {@link RestconfStrategy} with
156 * specified subtrees that should only be read.
158 * @param store datastore type
159 * @param path parent path to selected fields
160 * @param closeTransactionChain if it is set to {@code true}, after transaction it will close transactionChain
161 * in {@link RestconfStrategy} if any
162 * @param fields paths to selected subtrees which should be read, relative to to the parent path
163 * @return {@link NormalizedNode}
165 private @Nullable NormalizedNode readDataViaTransaction(final @NonNull LogicalDatastoreType store,
166 final @NonNull YangInstanceIdentifier path, final @NonNull List<YangInstanceIdentifier> fields) {
167 return TransactionUtil.syncAccess(read(store, path, fields), path).orElse(null);
171 ListenableFuture<Boolean> exists(final YangInstanceIdentifier path) {
172 return Futures.transform(remapException(netconfService.getConfig(path)),
173 optionalNode -> optionalNode != null && optionalNode.isPresent(),
174 MoreExecutors.directExecutor());
177 private static <T> ListenableFuture<T> remapException(final ListenableFuture<T> input) {
178 final var ret = SettableFuture.<T>create();
179 Futures.addCallback(input, new FutureCallback<T>() {
181 public void onSuccess(final T result) {
186 public void onFailure(final Throwable cause) {
187 ret.setException(cause instanceof ReadFailedException ? cause
188 : new ReadFailedException("NETCONF operation failed", cause));
190 }, MoreExecutors.directExecutor());