2 * Copyright (c) 2014 Cisco Systems, Inc. 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.controller.sal.restconf.impl;
10 import com.google.common.base.Optional;
11 import com.google.common.util.concurrent.CheckedFuture;
12 import com.google.common.util.concurrent.ListenableFuture;
13 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
14 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
15 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
16 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
17 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
18 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
19 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
20 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
21 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
22 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
23 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
24 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
25 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
26 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
27 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
28 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
29 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
30 import org.opendaylight.yangtools.concepts.ListenerRegistration;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.common.RpcResult;
33 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
34 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
35 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
36 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import javax.ws.rs.core.Response.Status;
41 import java.util.ArrayList;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.concurrent.ExecutionException;
45 import java.util.concurrent.Future;
47 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
48 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
50 public class BrokerFacade {
51 private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
53 private final static BrokerFacade INSTANCE = new BrokerFacade();
54 private volatile ConsumerSession context;
55 private DOMDataBroker domDataBroker;
57 private BrokerFacade() {
60 public void setContext(final ConsumerSession context) {
61 this.context = context;
64 public static BrokerFacade getInstance() {
65 return BrokerFacade.INSTANCE;
68 private void checkPreconditions() {
69 if (context == null || domDataBroker == null) {
70 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
75 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
77 return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
80 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
81 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
82 if (domDataBrokerService.isPresent()) {
83 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
85 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
89 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
91 return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
94 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
95 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
96 if (domDataBrokerService.isPresent()) {
97 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
99 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
103 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
104 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
105 checkPreconditions();
106 DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
107 return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
110 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
111 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
112 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
113 if (domDataBrokerService.isPresent()) {
114 DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
115 return putDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
118 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
121 // POST configuration
122 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
123 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
124 checkPreconditions();
125 DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
126 return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
129 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
130 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
131 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
132 if (domDataBrokerService.isPresent()) {
133 DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
134 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
137 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
140 // DELETE configuration
141 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
142 final YangInstanceIdentifier path) {
143 checkPreconditions();
144 return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
147 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
148 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
149 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
150 if (domDataBrokerService.isPresent()) {
151 return deleteDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path);
153 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
157 public Future<RpcResult<CompositeNode>> invokeRpc(final QName type, final CompositeNode payload) {
158 this.checkPreconditions();
160 return context.rpc(type, payload);
163 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
164 final ListenerAdapter listener) {
165 this.checkPreconditions();
167 if (listener.isListening()) {
171 YangInstanceIdentifier path = listener.getPath();
172 final ListenerRegistration<DOMDataChangeListener> registration = domDataBroker.registerDataChangeListener(
173 datastore, path, listener, scope);
175 listener.setRegistration(registration);
178 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
179 LogicalDatastoreType datastore, YangInstanceIdentifier path) {
180 LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
181 final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
182 if (listenableFuture != null) {
183 Optional<NormalizedNode<?, ?>> optional;
185 LOG.debug("Reading result data from transaction.");
186 optional = listenableFuture.get();
187 } catch (InterruptedException | ExecutionException e) {
188 throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
191 if (optional != null) {
192 if (optional.isPresent()) {
193 return optional.get();
200 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
201 final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
202 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, DataNormalizationOperation<?> root) {
203 ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
205 final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
206 if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
207 String errMsg = "Post Configuration via Restconf was not executed because data already exists";
208 LOG.trace(errMsg + ":{}", path);
209 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
210 ErrorTag.DATA_EXISTS);
212 } catch (InterruptedException | ExecutionException e) {
213 LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
216 ensureParentsByMerge(datastore, path, rWTransaction, root);
217 rWTransaction.merge(datastore, path, payload);
218 LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
219 return rWTransaction.submit();
222 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
223 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
224 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, DataNormalizationOperation<?> root) {
225 LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
226 ensureParentsByMerge(datastore, path, writeTransaction, root);
227 writeTransaction.put(datastore, path, payload);
228 return writeTransaction.submit();
231 private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
232 final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
233 YangInstanceIdentifier path) {
234 LOG.info("Delete " + datastore.name() + " via Restconf: {}", path);
235 writeTransaction.delete(datastore, path);
236 return writeTransaction.submit();
239 public void setDomDataBroker(DOMDataBroker domDataBroker) {
240 this.domDataBroker = domDataBroker;
243 private final void ensureParentsByMerge(final LogicalDatastoreType store,
244 final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx,
245 final DataNormalizationOperation<?> root) {
246 List<PathArgument> currentArguments = new ArrayList<>();
247 Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
248 DataNormalizationOperation<?> currentOp = root;
249 while (iterator.hasNext()) {
250 PathArgument currentArg = iterator.next();
252 currentOp = currentOp.getChild(currentArg);
253 } catch (DataNormalizationException e) {
254 throw new IllegalArgumentException(
255 String.format("Invalid child encountered in path %s", normalizedPath), e);
257 currentArguments.add(currentArg);
258 YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments);
260 final Boolean exists;
264 CheckedFuture<Boolean, ReadFailedException> future =
265 rwTx.exists(store, currentPath);
266 exists = future.checkedGet();
267 } catch (ReadFailedException e) {
268 LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
269 throw new IllegalStateException("Failed to read pre-existing data", e);
273 if (!exists && iterator.hasNext()) {
274 rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));