import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;
import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.OPERATIONAL;
+
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import org.opendaylight.controller.md.sal.dom.api.DOMDataReadWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMDataWriteTransaction;
import org.opendaylight.controller.md.sal.dom.api.DOMMountPoint;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationListener;
+import org.opendaylight.controller.md.sal.dom.api.DOMNotificationService;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcException;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcResult;
import org.opendaylight.controller.md.sal.dom.api.DOMRpcService;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorTag;
import org.opendaylight.netconf.sal.restconf.impl.RestconfError.ErrorType;
import org.opendaylight.netconf.sal.streams.listeners.ListenerAdapter;
+import org.opendaylight.netconf.sal.streams.listeners.NotificationListenerAdapter;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
private volatile DOMRpcService rpcService;
private volatile ConsumerSession context;
private DOMDataBroker domDataBroker;
+ private DOMNotificationService domNotification;
private BrokerFacade() {
}
public void setRpcService(final DOMRpcService router) {
- rpcService = router;
+ this.rpcService = router;
+ }
+
+ public void setDomNotificationService(final DOMNotificationService domNotification) {
+ this.domNotification = domNotification;
}
public void setContext(final ConsumerSession context) {
}
private void checkPreconditions() {
- if (context == null || domDataBroker == null) {
+ if ((this.context == null) || (this.domDataBroker == null)) {
throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
}
}
// READ configuration
public NormalizedNode<?, ?> readConfigurationData(final YangInstanceIdentifier path) {
checkPreconditions();
- return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
+ return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), CONFIGURATION, path);
}
public NormalizedNode<?, ?> readConfigurationData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
// READ operational
public NormalizedNode<?, ?> readOperationalData(final YangInstanceIdentifier path) {
checkPreconditions();
- return readDataViaTransaction(domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
+ return readDataViaTransaction(this.domDataBroker.newReadOnlyTransaction(), OPERATIONAL, path);
}
public NormalizedNode<?, ?> readOperationalData(final DOMMountPoint mountPoint, final YangInstanceIdentifier path) {
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
checkPreconditions();
- return putDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
+ return putDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
}
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPut(
throw new RestconfDocumentedException(errMsg);
}
+ public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext context,
+ final SchemaContext globalSchema) {
+ final DOMDataReadWriteTransaction patchTransaction = this.domDataBroker.newReadWriteTransaction();
+ final List<PATCHStatusEntity> editCollection = new ArrayList<>();
+ List<RestconfError> editErrors;
+ final List<RestconfError> globalErrors = null;
+ int errorCounter = 0;
+
+ for (final PATCHEntity patchEntity : context.getData()) {
+ final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
+
+ switch (operation) {
+ case CREATE:
+ if (errorCounter == 0) {
+ try {
+ postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
+ patchEntity.getNode(), globalSchema);
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+ } catch (final RestconfDocumentedException e) {
+ editErrors = new ArrayList<>();
+ editErrors.addAll(e.getErrors());
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+ errorCounter++;
+ }
+ }
+ break;
+ case REPLACE:
+ if (errorCounter == 0) {
+ try {
+ putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
+ .getTargetNode(), patchEntity.getNode(), globalSchema);
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+ } catch (final RestconfDocumentedException e) {
+ editErrors = new ArrayList<>();
+ editErrors.addAll(e.getErrors());
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+ errorCounter++;
+ }
+ }
+ break;
+ case DELETE:
+ if (errorCounter == 0) {
+ try {
+ deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
+ .getTargetNode());
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+ } catch (final RestconfDocumentedException e) {
+ editErrors = new ArrayList<>();
+ editErrors.addAll(e.getErrors());
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+ errorCounter++;
+ }
+ }
+ break;
+ case REMOVE:
+ if (errorCounter == 0) {
+ try {
+ deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
+ .getTargetNode());
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
+ } catch (final RestconfDocumentedException e) {
+ LOG.error("Error removing {} by {} operation", patchEntity.getTargetNode().toString(),
+ patchEntity.getEditId(), e);
+ }
+ }
+ break;
+ }
+ }
+
+ //TODO: make sure possible global errors are filled up correctly and decide transaction submission based on that
+ //globalErrors = new ArrayList<>();
+ if (errorCounter == 0) {
+ final CheckedFuture<Void, TransactionCommitFailedException> submit = patchTransaction.submit();
+ return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), true,
+ globalErrors);
+ } else {
+ patchTransaction.cancel();
+ return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false,
+ globalErrors);
+ }
+ }
+
// POST configuration
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
final SchemaContext globalSchema, final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload) {
checkPreconditions();
- return postDataViaTransaction(domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
+ return postDataViaTransaction(this.domDataBroker.newReadWriteTransaction(), CONFIGURATION, path, payload, globalSchema);
}
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataPost(
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
final YangInstanceIdentifier path) {
checkPreconditions();
- return deleteDataViaTransaction(domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
+ return deleteDataViaTransaction(this.domDataBroker.newWriteOnlyTransaction(), CONFIGURATION, path);
}
public CheckedFuture<Void, TransactionCommitFailedException> commitConfigurationDataDelete(
// RPC
public CheckedFuture<DOMRpcResult, DOMRpcException> invokeRpc(final SchemaPath type, final NormalizedNode<?, ?> input) {
checkPreconditions();
- if (rpcService == null) {
+ if (this.rpcService == null) {
throw new RestconfDocumentedException(Status.SERVICE_UNAVAILABLE);
}
LOG.trace("Invoke RPC {} with input: {}", type, input);
- return rpcService.invokeRpc(type, input);
+ return this.rpcService.invokeRpc(type, input);
}
public void registerToListenDataChanges(final LogicalDatastoreType datastore, final DataChangeScope scope,
}
final YangInstanceIdentifier path = listener.getPath();
- final ListenerRegistration<DOMDataChangeListener> registration = domDataBroker.registerDataChangeListener(
+ final ListenerRegistration<DOMDataChangeListener> registration = this.domDataBroker.registerDataChangeListener(
datastore, path, listener, scope);
listener.setRegistration(registration);
private NormalizedNode<?, ?> readDataViaTransaction(final DOMDataReadTransaction transaction,
final LogicalDatastoreType datastore, final YangInstanceIdentifier path) {
- LOG.trace("Read " + datastore.name() + " via Restconf: {}", path);
+ LOG.trace("Read {} via Restconf: {}", datastore.name(), path);
final ListenableFuture<Optional<NormalizedNode<?, ?>>> listenableFuture = transaction.read(datastore, path);
if (listenableFuture != null) {
Optional<NormalizedNode<?, ?>> optional;
LOG.debug("Reading result data from transaction.");
optional = listenableFuture.get();
} catch (InterruptedException | ExecutionException e) {
- LOG.warn("Exception by reading " + datastore.name() + " via Restconf: {}", path, e);
+ LOG.warn("Exception by reading {} via Restconf: {}", datastore.name(), path, e);
throw new RestconfDocumentedException("Problem to get data from transaction.", e.getCause());
}
// FIXME: This is doing correct post for container and list children
// not sure if this will work for choice case
if(payload instanceof MapNode) {
- LOG.trace("POST " + datastore.name() + " via Restconf: {} with payload {}", path, payload);
+ LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
return rWTransaction.submit();
}
+ private void postDataWithinTransaction(
+ final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+ // FIXME: This is doing correct post for container and list children
+ // not sure if this will work for choice case
+ if(payload instanceof MapNode) {
+ LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
+ final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+ rWTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+ for(final MapEntryNode child : ((MapNode) payload).getValue()) {
+ final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+ checkItemDoesNotExists(rWTransaction, datastore, childPath);
+ rWTransaction.put(datastore, childPath, child);
+ }
+ } else {
+ checkItemDoesNotExists(rWTransaction,datastore, path);
+ ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
+ rWTransaction.put(datastore, path, payload);
+ }
+ }
+
private void checkItemDoesNotExists(final DOMDataReadWriteTransaction rWTransaction,final LogicalDatastoreType store, final YangInstanceIdentifier path) {
final ListenableFuture<Boolean> futureDatastoreData = rWTransaction.exists(store, path);
try {
if (futureDatastoreData.get()) {
final String errMsg = "Post Configuration via Restconf was not executed because data already exists";
- LOG.trace(errMsg + ":{}", path);
+ LOG.trace("{}:{}", errMsg, path);
rWTransaction.cancel();
throw new RestconfDocumentedException("Data already exists for path: " + path, ErrorType.PROTOCOL,
ErrorTag.DATA_EXISTS);
}
} catch (InterruptedException | ExecutionException e) {
- LOG.warn("It wasn't possible to get data loaded from datastore at path " + path, e);
+ LOG.warn("It wasn't possible to get data loaded from datastore at path {}", path, e);
}
}
private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
- LOG.trace("Put " + datastore.name() + " via Restconf: {} with payload {}", path, payload);
+ LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
writeTransaction.put(datastore, path, payload);
return writeTransaction.submit();
}
+ private void putDataWithinTransaction(
+ final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+ LOG.trace("Put {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
+ ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
+ writeTransaction.put(datastore, path, payload);
+ }
+
private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(
final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path) {
- LOG.trace("Delete " + datastore.name() + " via Restconf: {}", path);
+ LOG.trace("Delete {} via Restconf: {}", datastore.name(), path);
writeTransaction.delete(datastore, path);
return writeTransaction.submit();
}
+ private void deleteDataWithinTransaction(
+ final DOMDataWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path) {
+ LOG.trace("Delete {} within Restconf PATCH: {}", datastore.name(), path);
+ writeTransaction.delete(datastore, path);
+ }
+
public void setDomDataBroker(final DOMDataBroker domDataBroker) {
this.domDataBroker = domDataBroker;
}
ImmutableNodes.fromInstanceId(schemaContext, YangInstanceIdentifier.create(normalizedPathWithoutChildArgs));
rwTx.merge(store, rootNormalizedPath, parentStructure);
}
+
+ public void registerToListenNotification(final NotificationListenerAdapter listener) {
+ checkPreconditions();
+
+ if (listener.isListening()) {
+ return;
+ }
+
+ final SchemaPath path = listener.getSchemaPath();
+ final ListenerRegistration<DOMNotificationListener> registration = this.domNotification
+ .registerNotificationListener(listener, path);
+
+ listener.setRegistration(registration);
+ }
}