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.collect.Lists;
import com.google.common.util.concurrent.CheckedFuture;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
throw new RestconfDocumentedException(errMsg);
}
- public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext context,
- final SchemaContext globalSchema)
- throws Exception {
- final DOMDataReadWriteTransaction patchTransaction = this.domDataBroker.newReadWriteTransaction();
- final List<PATCHStatusEntity> editCollection = new ArrayList<>();
+ public PATCHStatusContext patchConfigurationDataWithinTransaction(final PATCHContext patchContext) throws Exception {
+ final DOMMountPoint mountPoint = patchContext.getInstanceIdentifierContext().getMountPoint();
+
+ // get new transaction and schema context on server or on mounted device
+ final SchemaContext schemaContext;
+ final DOMDataReadWriteTransaction patchTransaction;
+ if (mountPoint == null) {
+ schemaContext = patchContext.getInstanceIdentifierContext().getSchemaContext();
+ patchTransaction = this.domDataBroker.newReadWriteTransaction();
+ } else {
+ schemaContext = mountPoint.getSchemaContext();
+
+ final Optional<DOMDataBroker> optional = mountPoint.getService(DOMDataBroker.class);
+
+ if (optional.isPresent()) {
+ patchTransaction = optional.get().newReadWriteTransaction();
+ } else {
+ // if mount point does not have broker it is not possible to continue and global error is reported
+ LOG.error("Http PATCH {} has failed - device {} does not support broker service",
+ patchContext.getPatchId(), mountPoint.getIdentifier());
+ return new PATCHStatusContext(
+ patchContext.getPatchId(),
+ null,
+ false,
+ ImmutableList.of(new RestconfError(
+ ErrorType.APPLICATION,
+ ErrorTag.OPERATION_FAILED,
+ "DOM data broker service isn't available for mount point "
+ + mountPoint.getIdentifier()))
+ );
+ }
+ }
+ final List<PATCHStatusEntity> editCollection = new ArrayList<>();
List<RestconfError> editErrors;
- int errorCounter = 0;
+ boolean withoutError = true;
- for (final PATCHEntity patchEntity : context.getData()) {
+ for (final PATCHEntity patchEntity : patchContext.getData()) {
final PATCHEditOperation operation = PATCHEditOperation.valueOf(patchEntity.getOperation().toUpperCase());
switch (operation) {
case CREATE:
- if (errorCounter == 0) {
+ if (withoutError) {
try {
postDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
- patchEntity.getNode(), globalSchema);
+ patchEntity.getNode(), schemaContext);
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
} catch (final RestconfDocumentedException e) {
+ LOG.error("Error call http PATCH operation {} on target {}",
+ operation,
+ patchEntity.getTargetNode().toString());
+
editErrors = new ArrayList<>();
editErrors.addAll(e.getErrors());
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
- errorCounter++;
+ withoutError = false;
}
}
break;
case REPLACE:
- if (errorCounter == 0) {
+ if (withoutError) {
try {
putDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
- .getTargetNode(), patchEntity.getNode(), globalSchema);
+ .getTargetNode(), patchEntity.getNode(), schemaContext);
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
} catch (final RestconfDocumentedException e) {
+ LOG.error("Error call http PATCH operation {} on target {}",
+ operation,
+ patchEntity.getTargetNode().toString());
+
editErrors = new ArrayList<>();
editErrors.addAll(e.getErrors());
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
- errorCounter++;
+ withoutError = false;
}
}
break;
case DELETE:
- if (errorCounter == 0) {
+ if (withoutError) {
try {
deleteDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity
.getTargetNode());
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
} catch (final RestconfDocumentedException e) {
+ LOG.error("Error call http PATCH operation {} on target {}",
+ operation,
+ patchEntity.getTargetNode().toString());
+
editErrors = new ArrayList<>();
editErrors.addAll(e.getErrors());
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
- errorCounter++;
+ withoutError = false;
}
}
break;
case REMOVE:
- if (errorCounter == 0) {
+ if (withoutError) {
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);
+ LOG.error("Error call http PATCH operation {} on target {}",
+ operation,
+ patchEntity.getTargetNode().toString());
+
+ editErrors = new ArrayList<>();
+ editErrors.addAll(e.getErrors());
+ editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
+ withoutError = false;
}
}
break;
case MERGE:
- if (errorCounter == 0) {
+ if (withoutError) {
try {
mergeDataWithinTransaction(patchTransaction, CONFIGURATION, patchEntity.getTargetNode(),
- patchEntity.getNode(), globalSchema);
+ patchEntity.getNode(), schemaContext);
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), true, null));
} catch (final RestconfDocumentedException e) {
+ LOG.error("Error call http PATCH operation {} on target {}",
+ operation,
+ patchEntity.getTargetNode().toString());
+
editErrors = new ArrayList<>();
editErrors.addAll(e.getErrors());
editCollection.add(new PATCHStatusEntity(patchEntity.getEditId(), false, editErrors));
- errorCounter++;
+ withoutError = false;
}
}
break;
+ default:
+ LOG.error("Unsupported http PATCH operation {} on target {}",
+ operation,
+ patchEntity.getTargetNode().toString());
+ break;
}
}
// if errors then cancel transaction and return error status
- if (errorCounter != 0) {
+ if (!withoutError) {
patchTransaction.cancel();
- return new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
+ return new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection), false, null);
}
// if no errors commit transaction
Futures.addCallback(future, new FutureCallback<Void>() {
@Override
public void onSuccess(@Nullable final Void result) {
- status.setStatus(new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
+ status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
true, null));
waiter.countDown();
}
@Override
public void onFailure(final Throwable t) {
// if commit failed it is global error
- status.setStatus(new PATCHStatusContext(context.getPatchId(), ImmutableList.copyOf(editCollection),
- false, Lists.newArrayList(
+ LOG.error("Http PATCH {} transaction commit has failed", patchContext.getPatchId());
+ status.setStatus(new PATCHStatusContext(patchContext.getPatchId(), ImmutableList.copyOf(editCollection),
+ false, ImmutableList.of(
new RestconfError(ErrorType.APPLICATION, ErrorTag.OPERATION_FAILED, t.getMessage()))));
waiter.countDown();
}
@Override
public void onSuccess(final Optional<NormalizedNode<?, ?>> result) {
- responseWaiter.countDown();
handlingCallback(null, datastore, path, result, readData);
+ responseWaiter.countDown();
}
@Override
return readData.getResult();
}
+ /**
+ * POST data and submit transaction {@link DOMDataReadWriteTransaction}
+ * @return
+ */
private CheckedFuture<Void, TransactionCommitFailedException> postDataViaTransaction(
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 {} 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);
- 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);
- }
+ LOG.trace("POST {} via Restconf: {} with payload {}", datastore.name(), path, payload);
+ postData(rWTransaction, datastore, path, payload, schemaContext);
return rWTransaction.submit();
}
+ /**
+ * POST data and do NOT submit transaction {@link DOMDataReadWriteTransaction}
+ */
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);
+ LOG.trace("POST {} within Restconf PATCH: {} with payload {}", datastore.name(), path, payload);
+ postData(rWTransaction, datastore, path, payload, schemaContext);
+ }
+
+ // FIXME: This is doing correct post for container and list children, not sure if this will work for choice case
+ private void postData(final DOMDataReadWriteTransaction rWTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload,
+ final SchemaContext schemaContext) {
+ if (payload instanceof MapNode) {
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()) {
+ 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);
+ checkItemDoesNotExists(rWTransaction, datastore, path);
ensureParentsByMerge(datastore, path, rWTransaction, schemaContext);
rWTransaction.put(datastore, path, payload);
}
Futures.addCallback(future, new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable final Boolean result) {
- responseWaiter.countDown();
handlingCallback(null, store, path, Optional.of(result), readData);
+ responseWaiter.countDown();
}
@Override
Futures.addCallback(future, new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable final Boolean result) {
- responseWaiter.countDown();
handlingCallback(null, store, path, Optional.of(result), readData);
+ responseWaiter.countDown();
}
@Override
}
}
+ /**
+ * PUT data and submit {@link DOMDataReadWriteTransaction}
+ */
private CheckedFuture<Void, TransactionCommitFailedException> putDataViaTransaction(
- final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ final DOMDataReadWriteTransaction readWriteTransaction, final LogicalDatastoreType datastore,
final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
LOG.trace("Put {} via Restconf: {} with payload {}", datastore.name(), path, payload);
- ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
- writeTransaction.put(datastore, path, payload);
- return writeTransaction.submit();
+ putData(readWriteTransaction, datastore, path, payload, schemaContext);
+ return readWriteTransaction.submit();
}
+ /**
+ * PUT data and do NOT submit {@link DOMDataReadWriteTransaction}
+ */
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);
+ putData(writeTransaction, datastore, path, payload, schemaContext);
+ }
+
+ // FIXME: This is doing correct put for container and list children, not sure if this will work for choice case
+ private void putData(final DOMDataReadWriteTransaction writeTransaction, final LogicalDatastoreType datastore,
+ final YangInstanceIdentifier path, final NormalizedNode<?, ?> payload, final SchemaContext schemaContext) {
+ if (payload instanceof MapNode) {
+ final NormalizedNode<?, ?> emptySubtree = ImmutableNodes.fromInstanceId(schemaContext, path);
+ writeTransaction.merge(datastore, YangInstanceIdentifier.create(emptySubtree.getIdentifier()), emptySubtree);
+ ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
+ for (final MapEntryNode child : ((MapNode) payload).getValue()) {
+ final YangInstanceIdentifier childPath = path.node(child.getIdentifier());
+ writeTransaction.put(datastore, childPath, child);
+ }
+ } else {
+ ensureParentsByMerge(datastore, path, writeTransaction, schemaContext);
+ writeTransaction.put(datastore, path, payload);
+ }
}
private CheckedFuture<Void, TransactionCommitFailedException> deleteDataViaTransaction(