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.RestconfDocumentedException;
34 import org.opendaylight.restconf.common.errors.RestconfFuture;
35 import org.opendaylight.restconf.common.errors.SettableRestconfFuture;
36 import org.opendaylight.restconf.nb.rfc8040.legacy.NormalizedNodePayload;
37 import org.opendaylight.restconf.nb.rfc8040.legacy.QueryParameters;
38 import org.opendaylight.restconf.nb.rfc8040.utils.parser.NetconfFieldsTranslator;
39 import org.opendaylight.restconf.server.api.DataGetParams;
40 import org.opendaylight.restconf.server.api.DatabindContext;
41 import org.opendaylight.restconf.server.spi.ApiPathNormalizer.DataPath;
42 import org.opendaylight.yangtools.yang.common.Empty;
43 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
44 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
47 * Implementation of RESTCONF operations on top of a raw NETCONF backend.
49 * @see NetconfDataTreeService
51 public final class NetconfRestconfStrategy extends RestconfStrategy {
52 private final NetconfDataTreeService netconfService;
54 public NetconfRestconfStrategy(final DatabindContext databind, final NetconfDataTreeService netconfService,
55 final @Nullable DOMRpcService rpcService, final @Nullable DOMActionService actionService,
56 final @Nullable DOMYangTextSourceProvider sourceProvider,
57 final @Nullable DOMMountPointService mountPointService) {
58 super(databind, ImmutableMap.of(), rpcService, actionService, sourceProvider, mountPointService);
59 this.netconfService = requireNonNull(netconfService);
63 RestconfTransaction prepareWriteExecution() {
64 return new NetconfRestconfTransaction(modelContext(), netconfService);
68 void delete(final SettableRestconfFuture<Empty> future, final YangInstanceIdentifier path) {
69 final var tx = prepareWriteExecution();
71 Futures.addCallback(tx.commit(), new FutureCallback<CommitInfo>() {
73 public void onSuccess(final CommitInfo result) {
74 future.set(Empty.value());
78 public void onFailure(final Throwable cause) {
79 future.setFailure(TransactionUtil.decodeException(cause, "DELETE", path, modelContext()));
81 }, MoreExecutors.directExecutor());
85 RestconfFuture<NormalizedNodePayload> dataGET(final DataPath path, final DataGetParams params) {
86 final var inference = path.inference();
87 final var fields = params.fields();
88 final List<YangInstanceIdentifier> fieldPaths;
90 final List<YangInstanceIdentifier> tmp;
92 tmp = NetconfFieldsTranslator.translate(inference.getEffectiveModelContext(), path.schema(), fields);
93 } catch (RestconfDocumentedException e) {
94 return RestconfFuture.failed(e);
96 fieldPaths = tmp == null || tmp.isEmpty() ? null : tmp;
101 final NormalizedNode node;
102 if (fieldPaths != null) {
103 node = readData(params.content(), path.instance(), params.withDefaults(), fieldPaths);
105 node = readData(params.content(), path.instance(), params.withDefaults());
107 return completeDataGET(inference, QueryParameters.of(params), node);
111 ListenableFuture<Optional<NormalizedNode>> read(final LogicalDatastoreType store,
112 final YangInstanceIdentifier path) {
113 return switch (store) {
114 case CONFIGURATION -> netconfService.getConfig(path);
115 case OPERATIONAL -> netconfService.get(path);
119 private ListenableFuture<Optional<NormalizedNode>> read(final LogicalDatastoreType store,
120 final YangInstanceIdentifier path, final List<YangInstanceIdentifier> fields) {
121 return switch (store) {
122 case CONFIGURATION -> netconfService.getConfig(path, fields);
123 case OPERATIONAL -> netconfService.get(path, fields);
128 * Read specific type of data from data store via transaction with specified subtrees that should only be read.
129 * Close {@link DOMTransactionChain} inside of object {@link RestconfStrategy} provided as a parameter.
131 * @param content type of data to read (config, state, all)
132 * @param path the parent path to read
133 * @param withDefa value of with-defaults parameter
134 * @param fields paths to selected subtrees which should be read, relative to to the parent path
135 * @return {@link NormalizedNode}
137 // FIXME: NETCONF-1155: this method should asynchronous
138 public @Nullable NormalizedNode readData(final @NonNull ContentParam content,
139 final @NonNull YangInstanceIdentifier path, final @Nullable WithDefaultsParam withDefa,
140 final @NonNull List<YangInstanceIdentifier> fields) {
141 return switch (content) {
143 // PREPARE STATE DATA NODE
144 final var stateDataNode = readDataViaTransaction(LogicalDatastoreType.OPERATIONAL, path, fields);
145 // PREPARE CONFIG DATA NODE
146 final var configDataNode = readDataViaTransaction(LogicalDatastoreType.CONFIGURATION, path, fields);
148 yield mergeConfigAndSTateDataIfNeeded(stateDataNode, withDefa == null ? configDataNode
149 : prepareDataByParamWithDef(configDataNode, path, withDefa.mode()));
152 final var read = readDataViaTransaction(LogicalDatastoreType.CONFIGURATION, path, fields);
153 yield withDefa == null ? read : prepareDataByParamWithDef(read, path, withDefa.mode());
155 case NONCONFIG -> readDataViaTransaction(LogicalDatastoreType.OPERATIONAL, path, fields);
160 * Read specific type of data {@link LogicalDatastoreType} via transaction in {@link RestconfStrategy} with
161 * specified subtrees that should only be read.
163 * @param store datastore type
164 * @param path parent path to selected fields
165 * @param closeTransactionChain if it is set to {@code true}, after transaction it will close transactionChain
166 * in {@link RestconfStrategy} if any
167 * @param fields paths to selected subtrees which should be read, relative to to the parent path
168 * @return {@link NormalizedNode}
170 private @Nullable NormalizedNode readDataViaTransaction(final @NonNull LogicalDatastoreType store,
171 final @NonNull YangInstanceIdentifier path, final @NonNull List<YangInstanceIdentifier> fields) {
172 return TransactionUtil.syncAccess(read(store, path, fields), path).orElse(null);
176 ListenableFuture<Boolean> exists(final YangInstanceIdentifier path) {
177 return Futures.transform(remapException(netconfService.getConfig(path)),
178 optionalNode -> optionalNode != null && optionalNode.isPresent(),
179 MoreExecutors.directExecutor());
182 private static <T> ListenableFuture<T> remapException(final ListenableFuture<T> input) {
183 final var ret = SettableFuture.<T>create();
184 Futures.addCallback(input, new FutureCallback<T>() {
186 public void onSuccess(final T result) {
191 public void onFailure(final Throwable cause) {
192 ret.setException(cause instanceof ReadFailedException ? cause
193 : new ReadFailedException("NETCONF operation failed", cause));
195 }, MoreExecutors.directExecutor());