X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=opendaylight%2Fmd-sal%2Fsal-netconf-connector%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fcontroller%2Fsal%2Fconnect%2Fnetconf%2Fsal%2Ftx%2FNetconfDeviceWriteOnlyTx.java;h=4b53dd7c44dc74a67eedf75f923fe220befb3e1c;hb=b30a2df0721908317c9b4770972b5647dfafd3c7;hp=6f6e8141193fe4910032d91768c047f31a14e4de;hpb=287dd97fc2375eb32186515ecfdac32ee1a36d83;p=controller.git diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java index 6f6e814119..4b53dd7c44 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTx.java @@ -8,31 +8,32 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.DISCARD_CHANGES_RPC_CONTENT; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME; -import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CONFIG_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DEFAULT_OPERATION_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_DISCARD_CHANGES_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_EDIT_CONFIG_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_ERROR_OPTION_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_OPERATION_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_TARGET_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.ROLLBACK_ON_ERROR_OPTION; - import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; -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; import com.google.common.util.concurrent.ListenableFuture; import java.util.Collections; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; -import javax.annotation.Nullable; +import java.util.concurrent.atomic.AtomicBoolean; import org.opendaylight.controller.md.sal.common.api.TransactionStatus; import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType; import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException; @@ -46,10 +47,11 @@ import org.opendaylight.yangtools.yang.common.RpcError; import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.opendaylight.yangtools.yang.data.api.CompositeNode; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.SimpleNode; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; import org.opendaylight.yangtools.yang.data.impl.NodeFactory; @@ -57,65 +59,81 @@ import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { +public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction, FutureCallback> { private static final Logger LOG = LoggerFactory.getLogger(NetconfDeviceWriteOnlyTx.class); private final RemoteDeviceId id; private final RpcImplementation rpc; private final DataNormalizer normalizer; + private final boolean rollbackSupported; + private final boolean candidateSupported; private final CompositeNode targetNode; + // Allow commit to be called only once + private final AtomicBoolean finished = new AtomicBoolean(false); + public NetconfDeviceWriteOnlyTx(final RemoteDeviceId id, final RpcImplementation rpc, final DataNormalizer normalizer, final boolean candidateSupported, final boolean rollbackOnErrorSupported) { this.id = id; this.rpc = rpc; this.normalizer = normalizer; - this.targetNode = getTargetNode(candidateSupported); + + this.candidateSupported = candidateSupported; + this.targetNode = getTargetNode(this.candidateSupported); this.rollbackSupported = rollbackOnErrorSupported; } - // FIXME add logging - @Override public boolean cancel() { - if(isCommitted()) { + if(isFinished()) { return false; } return discardChanges(); } - private boolean isCommitted() { - // TODO 732 - return true; + private boolean isFinished() { + return finished.get(); } private boolean discardChanges() { - // TODO 732 + finished.set(true); + + if(candidateSupported) { + sendDiscardChanges(); + } return true; } // TODO should the edit operations be blocking ? + // TODO should the discard-changes operations be blocking ? @Override public void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { - Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can replaceModuleCaps only configuration, not %s", store); + checkNotFinished(); + Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can merge only configuration, not %s", store); try { final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id); final CompositeNode legacyData = normalizer.toLegacy(path, data); - sendEditRpc(createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE)); + sendEditRpc( + createEditConfigStructure(legacyPath, Optional.of(ModifyAction.REPLACE), Optional.fromNullable(legacyData)), Optional.of(ModifyAction.NONE)); } catch (final ExecutionException e) { - LOG.warn("Error putting data to {}, data: {}, discarding changes", path, data, e); + LOG.warn("{}: Error putting data to {}, data: {}, discarding changes", id, path, data, e); discardChanges(); - throw new RuntimeException("Error while replacing " + path, e); + throw new RuntimeException(id + ": Error while replacing " + path, e); } } + private void checkNotFinished() { + Preconditions.checkState(isFinished() == false, "%s: Transaction %s already finished", id, getIdentifier()); + } + @Override public void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { - Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can replaceModuleCaps only configuration, not %s", store); + checkNotFinished(); + Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store); try { final YangInstanceIdentifier legacyPath = NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id); @@ -123,31 +141,32 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { sendEditRpc( createEditConfigStructure(legacyPath, Optional. absent(), Optional.fromNullable(legacyData)), Optional. absent()); } catch (final ExecutionException e) { - LOG.warn("Error merging data to {}, data: {}, discarding changes", path, data, e); + LOG.warn("{}: Error merging data to {}, data: {}, discarding changes", id, path, data, e); discardChanges(); - throw new RuntimeException("Error while merging " + path, e); + throw new RuntimeException(id + ": Error while merging " + path, e); } } @Override public void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { - Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "Can replaceModuleCaps only configuration, not %s", store); + checkNotFinished(); + Preconditions.checkArgument(store == LogicalDatastoreType.CONFIGURATION, "%s: Can merge only configuration, not %s", id, store); try { - sendEditRpc(createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE), Optional.absent()), Optional.of(ModifyAction.NONE)); + sendEditRpc( + createEditConfigStructure(NetconfDeviceReadOnlyTx.toLegacyPath(normalizer, path, id), Optional.of(ModifyAction.DELETE), Optional.absent()), Optional.of(ModifyAction.NONE)); } catch (final ExecutionException e) { - LOG.warn("Error deleting data {}, discarding changes", path, e); + LOG.warn("{}: Error deleting data {}, discarding changes", id, path, e); discardChanges(); - throw new RuntimeException("Error while deleting " + path, e); + throw new RuntimeException(id + ": Error while deleting " + path, e); } } @Override public CheckedFuture submit() { final ListenableFuture commmitFutureAsVoid = Futures.transform(commit(), new Function, Void>() { - @Nullable @Override - public Void apply(@Nullable final RpcResult input) { + public Void apply(final RpcResult input) { return null; } }); @@ -162,25 +181,46 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { @Override public ListenableFuture> commit() { - // FIXME do not allow commit if closed or failed + checkNotFinished(); + finished.set(true); - final ListenableFuture> rpcResult = rpc.invokeRpc(NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, getCommitRequest()); - return Futures.transform(rpcResult, new Function, RpcResult>() { - @Override - public RpcResult apply(@Nullable final RpcResult input) { - if(input.isSuccessful()) { - return RpcResultBuilder.success(TransactionStatus.COMMITED).build(); - } else { - final RpcResultBuilder failed = RpcResultBuilder.failed(); - for (final RpcError rpcError : input.getErrors()) { - failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(), rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause()); + if(candidateSupported == false) { + return Futures.immediateFuture(RpcResultBuilder.success(TransactionStatus.COMMITED).build()); + } + + final ListenableFuture> rpcResult = rpc.invokeRpc( + NetconfMessageTransformUtil.NETCONF_COMMIT_QNAME, NetconfMessageTransformUtil.COMMIT_RPC_CONTENT); + + final ListenableFuture> transformed = Futures.transform(rpcResult, + new Function, RpcResult>() { + @Override + public RpcResult apply(final RpcResult input) { + if (input.isSuccessful()) { + return RpcResultBuilder.success(TransactionStatus.COMMITED).build(); + } else { + final RpcResultBuilder failed = RpcResultBuilder.failed(); + for (final RpcError rpcError : input.getErrors()) { + failed.withError(rpcError.getErrorType(), rpcError.getTag(), rpcError.getMessage(), + rpcError.getApplicationTag(), rpcError.getInfo(), rpcError.getCause()); + } + return failed.build(); + } } - return failed.build(); - } - } - }); + }); + + Futures.addCallback(transformed, this); + return transformed; + } + + @Override + public void onSuccess(final RpcResult result) { + LOG.debug("{}: Write successful, transaction: {}", id, getIdentifier()); + } - // FIXME 732 detect commit failure + @Override + public void onFailure(final Throwable t) { + LOG.warn("{}: Write failed, transaction {}, discarding changes", id, getIdentifier(), t); + discardChanges(); } private void sendEditRpc(final CompositeNode editStructure, final Optional defaultOperation) throws ExecutionException { @@ -200,21 +240,36 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { } } + private void sendDiscardChanges() { + final ListenableFuture> discardFuture = rpc.invokeRpc(NETCONF_DISCARD_CHANGES_QNAME, DISCARD_CHANGES_RPC_CONTENT); + Futures.addCallback(discardFuture, new FutureCallback>() { + @Override + public void onSuccess(final RpcResult result) { + LOG.debug("{}: Discarding transaction: {}", id, getIdentifier()); + } + + @Override + public void onFailure(final Throwable t) { + LOG.error("{}: Discarding changes failed, transaction: {}. Device configuration might be corrupted", id, getIdentifier(), t); + throw new RuntimeException(id + ": Discarding changes failed, transaction " + getIdentifier(), t); + } + }); + } + private CompositeNode createEditConfigStructure(final YangInstanceIdentifier dataPath, final Optional operation, final Optional lastChildOverride) { Preconditions.checkArgument(Iterables.isEmpty(dataPath.getPathArguments()) == false, "Instance identifier with empty path %s", dataPath); - List reversedPath = Lists.reverse(dataPath.getPath()); - // Create deepest edit element with expected edit operation - CompositeNode previous = getDeepestEditElement(reversedPath.get(0), operation, lastChildOverride); + CompositeNode previous = getDeepestEditElement(dataPath.getLastPathArgument(), operation, lastChildOverride); + Iterator it = dataPath.getReversePathArguments().iterator(); // Remove already processed deepest child - reversedPath = Lists.newArrayList(reversedPath); - reversedPath.remove(0); + it.next(); // Create edit structure in reversed order - for (final YangInstanceIdentifier.PathArgument arg : reversedPath) { + while (it.hasNext()) { + final YangInstanceIdentifier.PathArgument arg = it.next(); final CompositeNodeBuilder builder = ImmutableCompositeNode.builder(); builder.setQName(arg.getNodeType()); @@ -298,13 +353,6 @@ public class NetconfDeviceWriteOnlyTx implements DOMDataWriteTransaction { } } - private ImmutableCompositeNode getCommitRequest() { - final CompositeNodeBuilder commitInput = ImmutableCompositeNode.builder(); - commitInput.setQName(NETCONF_COMMIT_QNAME); - return commitInput.toInstance(); - } - - @Override public Object getIdentifier() { return this;