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 static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
11 import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
12 import com.google.common.base.Optional;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import com.google.common.util.concurrent.ListenableFuture;
15 import java.util.ArrayList;
16 import java.util.Iterator;
17 import java.util.List;
18 import java.util.concurrent.ExecutionException;
19 import javax.ws.rs.core.Response.Status;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncDataBroker.DataChangeScope;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
24 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationException;
25 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizationOperation;
26 import org.opendaylight.controller.md.sal.common.impl.util.compat.DataNormalizer;
27 import org.opendaylight.controller.md.sal.dom.api.DOMDataBroker;
28 import org.opendaylight.controller.md.sal.dom.api.DOMDataChangeListener;
29 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
30 import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
31 import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
32 import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
33 import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
34 import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
35 import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
36 import org.opendaylight.controller.sal.core.api.Broker.ConsumerSession;
37 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorTag;
38 import org.opendaylight.controller.sal.restconf.impl.RestconfError.ErrorType;
39 import org.opendaylight.controller.sal.streams.listeners.ListenerAdapter;
40 import org.opendaylight.yangtools.concepts.ListenerRegistration;
41 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
42 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
43 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
44 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 public class BrokerFacade {
49 private final static Logger LOG = LoggerFactory.getLogger(BrokerFacade.class);
51 private final static BrokerFacade INSTANCE = new BrokerFacade();
52 private volatile DOMRpcService rpcService;
53 private volatile ConsumerSession context;
54 private DOMDataBroker domDataBroker;
56 private BrokerFacade() {
59 public void setRpcService(final DOMRpcService router) {
63 public void setContext(final ConsumerSession context) {
64 this.context = context;
67 public static BrokerFacade getInstance() {
68 return BrokerFacade.INSTANCE;
71 private void checkPreconditions() {
72 if (context == null || domDataBroker == null) {
73 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
78 public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
80 return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
83 public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
84 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
85 if (domDataBrokerService.isPresent()) {
86 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), CONFIGURATION, path);
88 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
92 public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
94 return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
97 public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
98 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
99 if (domDataBrokerService.isPresent()) {
100 return readDataViaTransaction(domDataBrokerService.get().newReadOnlyTransaction(), OPERATIONAL, path);
102 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
106 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
107 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
108 checkPreconditions();
109 final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
110 return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
113 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
114 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
115 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
116 if (domDataBrokerService.isPresent()) {
117 final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
118 return putDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
121 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
124 // POST configuration
125 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
126 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
127 checkPreconditions();
128 final DataNormalizationOperation<?> rootOp = ControllerContext.getInstance().getRootOperation();
129 return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, rootOp);
132 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
133 final DOMMountPoint mountPoint, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
134 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
135 if (domDataBrokerService.isPresent()) {
136 final DataNormalizationOperation<?> rootOp = new DataNormalizer(mountPoint.getSchemaContext()).getRootOperation();
137 return postDataViaTransaction(domDataBrokerService.get().newReadWriteTransaction(), CONFIGURATION, path,
140 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
143 // DELETE configuration
144 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
145 final YangInstanceIdentifier path) {
146 checkPreconditions();
147 return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
150 public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
151 final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
152 final Optional<DOMDataBroker> domDataBrokerService = mountPoint.getService(DOMDataBroker.class);
153 if (domDataBrokerService.isPresent()) {
154 return deleteDataViaTransaction(domDataBrokerService.get().newWriteOnlyTransaction(), CONFIGURATION, path);
156 throw new RestconfDocumentedException("DOM data broker service isn't available for mount point.");
160 public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
161 checkPreconditions();
162 if (rpcService == null) {
163 throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
165 return rpcService.invokeRpc(type, input);
168 public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
169 final ListenerAdapter listener) {
170 checkPreconditions();
172 if (listener.isListening()) {
176 final YangInstanceIdentifier path = listener.getPath();
177 final ListenerRegistration<DOMDataChangeListener> registration = domDataBroker.registerDataChangeListener(
178 datastore, path, listener, scope);
180 listener.setRegistration(registration);
183 private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
184 final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
185 LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
186 final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
187 if (listenableFuture != null) {
188 Optional<NormalizedNode<?, ?>> optional;
190 LOG.debug("Reading result data from transaction.");
191 optional = listenableFuture.get();
192 } catch (InterruptedException | ExecutionException e) {
193 throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
196 if (optional != null) {
197 if (optional.isPresent()) {
198 return optional.get();
205 private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
206 final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
207 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
208 final ListenableFuture<Optional<NormalizedNode<?, ?>>> futureDatastoreData = rWTransaction.read(datastore, path);
210 final Optional<NormalizedNode<?, ?>> optionalDatastoreData = futureDatastoreData.get();
211 if (optionalDatastoreData.isPresent() && payload.equals(optionalDatastoreData.get())) {
212 final String errMsg = "Post Configuration via Restconf was not executed because data already exists";
213 LOG.trace(errMsg + ":{}", path);
214 rWTransaction.cancel();
215 throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
216 ErrorTag.DATA_EXISTS);
218 } catch (InterruptedException | ExecutionException e) {
219 LOG.trace("It wasn't possible to get data loaded from datastore at path " + path);
222 ensureParentsByMerge(datastore, path, rWTransaction, root);
223 rWTransaction.merge(datastore, path, payload);
224 LOG.trace("Post " + datastore.name() + " via Restconf: {}", path);
225 return rWTransaction.submit();
228 private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
229 final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
230 final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final DataNormalizationOperation<?> root) {
231 LOG.trace("Put " + datastore.name() + " via Restconf: {}", path);
232 ensureParentsByMerge(datastore, path, writeTransaction, root);
233 writeTransaction.put(datastore, path, payload);
234 return writeTransaction.submit();
237 private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
238 final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
239 final YangInstanceIdentifier path) {
240 LOG.trace("Delete " + datastore.name() + " via Restconf: {}", path);
241 writeTransaction.delete(datastore, path);
242 return writeTransaction.submit();
245 public void setDomDataBroker(final DOMDataBroker domDataBroker) {
246 this.domDataBroker = domDataBroker;
249 private final void ensureParentsByMerge(final LogicalDatastoreType store,
250 final YangInstanceIdentifier normalizedPath, final DOMDataReadWriteTransaction rwTx,
251 final DataNormalizationOperation<?> root) {
252 final List<PathArgument> currentArguments = new ArrayList<>();
253 final Iterator<PathArgument> iterator = normalizedPath.getPathArguments().iterator();
254 DataNormalizationOperation<?> currentOp = root;
255 while (iterator.hasNext()) {
256 final PathArgument currentArg = iterator.next();
258 currentOp = currentOp.getChild(currentArg);
259 } catch (final DataNormalizationException e) {
261 throw new IllegalArgumentException(
262 String.format("Invalid child encountered in path %s", normalizedPath), e);
264 currentArguments.add(currentArg);
265 final YangInstanceIdentifier currentPath = YangInstanceIdentifier.create(currentArguments);
267 final Boolean exists;
271 final CheckedFuture<Boolean, ReadFailedException> future = rwTx.exists(store, currentPath);
272 exists = future.checkedGet();
273 } catch (final ReadFailedException e) {
274 LOG.error("Failed to read pre-existing data from store {} path {}", store, currentPath, e);
276 throw new IllegalStateException("Failed to read pre-existing data", e);
279 if (!exists && iterator.hasNext()) {
280 rwTx.merge(store, currentPath, currentOp.createDefault(currentArg));