From 16479a22c24293b33dc1c6b8d89e49ae94839fb2 Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 15 Apr 2014 16:00:27 +0200 Subject: [PATCH] BUG-732 Add rollback-on-error error-option to edit-config rpcs from netconf-connector Just in case rollback-on-error capability is supported + Add error response warning to netconf-connector Change-Id: I16251a12b270bd0ed89b39bfccc5251b4b3fe934 Signed-off-by: Maros Marsalek --- .../sal/connect/netconf/NetconfDevice.java | 8 +++-- .../netconf/NetconfDeviceListener.java | 30 ++++++++++++++++--- ...etconfDeviceTwoPhaseCommitTransaction.java | 14 ++++++++- .../sal/connect/netconf/NetconfMapping.java | 18 ++++++++++- 4 files changed, 62 insertions(+), 8 deletions(-) diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java index 4ea0fa5645..8d52950a29 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDevice.java @@ -119,6 +119,9 @@ public class NetconfDevice implements Provider, // NetconfDeviceListener listener; + private boolean rollbackSupported; + + public NetconfDevice(String name) { this.name = name; this.logger = LoggerFactory.getLogger(NetconfDevice.class + "#" + name); @@ -172,7 +175,7 @@ public class NetconfDevice implements Provider, // } } - void bringUp(final SchemaSourceProvider delegate, final Set capabilities) { + void bringUp(final SchemaSourceProvider delegate, final Set capabilities, final boolean rollbackSupported) { // This has to be called from separate thread, not from netty thread calling onSessionUp in DeviceListener. // Reason: delegate.getSchema blocks thread when waiting for response // however, if the netty thread is blocked, no incoming message can be processed @@ -181,6 +184,7 @@ public class NetconfDevice implements Provider, // processingExecutor.submit(new Runnable() { @Override public void run() { + NetconfDevice.this.rollbackSupported = rollbackSupported; remoteSourceProvider = schemaSourceProvider.createInstanceFor(delegate); deviceContextProvider = new NetconfDeviceSchemaContextProvider(NetconfDevice.this, remoteSourceProvider); deviceContextProvider.createContextFromCapabilities(capabilities); @@ -363,7 +367,7 @@ public class NetconfDevice implements Provider, // public DataCommitTransaction requestCommit( DataModification modification) { NetconfDeviceTwoPhaseCommitTransaction twoPhaseCommit = new NetconfDeviceTwoPhaseCommitTransaction(this, - modification, true); + modification, true, rollbackSupported); try { twoPhaseCommit.prepare(); } catch (InterruptedException e) { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java index 7ef4569600..d6bf868b75 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceListener.java @@ -7,6 +7,7 @@ */ package org.opendaylight.controller.sal.connect.netconf; +import com.google.common.collect.Sets; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; @@ -39,6 +40,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; class NetconfDeviceListener implements NetconfClientSessionListener { + private static final class Request { final UncancellableFuture> future; final NetconfMessage request; @@ -80,7 +82,14 @@ class NetconfDeviceListener implements NetconfClientSessionListener { delegate = SchemaSourceProviders.noopProvider(); } - device.bringUp(delegate, caps); + device.bringUp(delegate, caps, isRollbackSupported(session.getServerCapabilities())); + + } + + private static boolean isRollbackSupported(final Collection serverCapabilities) { + // TODO rollback capability cannot be searched for in Set caps + // since this set does not contain module-less capabilities + return Sets.newHashSet(serverCapabilities).contains(NetconfMapping.NETCONF_ROLLBACK_ON_ERROR_URI.toString()); } private synchronized void tearDown(final Exception e) { @@ -137,9 +146,22 @@ class NetconfDeviceListener implements NetconfClientSessionListener { requests.poll(); LOG.debug("Matched {} to {}", r.request, message); - // FIXME: this can throw exceptions, which should result - // in the future failing - NetconfMapping.checkValidReply(r.request, message); + try { + NetconfMapping.checkValidReply(r.request, message); + } catch (IllegalStateException e) { + LOG.warn("Invalid request-reply match, reply message contains different message-id", e); + r.future.setException(e); + return; + } + + try { + NetconfMapping.checkSuccessReply(message); + } catch (IllegalStateException e) { + LOG.warn("Error reply from remote device", e); + r.future.setException(e); + return; + } + r.future.set(Rpcs.getRpcResult(true, NetconfMapping.toNotificationNode(message, device.getSchemaContext()), Collections.emptyList())); } else { diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java index 5f14c264ed..8a74b17ac4 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfDeviceTwoPhaseCommitTransaction.java @@ -11,9 +11,11 @@ import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NET import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_COMMIT_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_CONFIG_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_EDIT_CONFIG_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_ERROR_OPTION_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_OPERATION_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_RUNNING_QNAME; import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.NETCONF_TARGET_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.NetconfMapping.ROLLBACK_ON_ERROR_OPTION; import java.util.Collection; import java.util.Collections; @@ -33,6 +35,7 @@ import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifie import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode; +import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -47,13 +50,15 @@ class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction modification; private final NetconfDevice device; private final boolean candidateSupported; + private final boolean rollbackSupported; public NetconfDeviceTwoPhaseCommitTransaction(NetconfDevice device, DataModification modification, - boolean candidateSupported) { + boolean candidateSupported, boolean rollbackOnErrorSupported) { this.device = Preconditions.checkNotNull(device); this.modification = Preconditions.checkNotNull(modification); this.candidateSupported = candidateSupported; + this.rollbackSupported = rollbackOnErrorSupported; } void prepare() throws InterruptedException, ExecutionException { @@ -91,7 +96,14 @@ class NetconfDeviceTwoPhaseCommitTransaction implements DataCommitTransaction>of()); } + Node targetWrapperNode = ImmutableCompositeNode.create(NETCONF_TARGET_QNAME, ImmutableList.>of(targetNode)); + + if(rollbackSupported) { + LOG.debug("Rollback-on-error supported, setting {} to {}", NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); + ret.addLeaf(NETCONF_ERROR_OPTION_QNAME, ROLLBACK_ON_ERROR_OPTION); + } + ret.add(targetWrapperNode); return ret; } diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java index a8ef4dd2fb..3e729149eb 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/NetconfMapping.java @@ -19,6 +19,8 @@ import javax.activation.UnsupportedDataTypeException; import javax.annotation.Nullable; import org.opendaylight.controller.netconf.api.NetconfMessage; +import org.opendaylight.controller.netconf.util.messages.NetconfMessageUtil; +import org.opendaylight.controller.netconf.util.xml.XmlUtil; import org.opendaylight.controller.sal.common.util.Rpcs; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.RpcError; @@ -52,6 +54,7 @@ public class NetconfMapping { public static URI NETCONF_URI = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"); public static String NETCONF_MONITORING_URI = "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"; public static URI NETCONF_NOTIFICATION_URI = URI.create("urn:ietf:params:xml:ns:netconf:notification:1.0"); + public static URI NETCONF_ROLLBACK_ON_ERROR_URI = URI.create("urn:ietf:params:netconf:capability:rollback-on-error:1.0"); public static QName NETCONF_QNAME = QName.create(NETCONF_URI, null, "netconf"); public static QName NETCONF_RPC_QNAME = QName.create(NETCONF_QNAME, "rpc"); @@ -71,6 +74,9 @@ public class NetconfMapping { public static QName NETCONF_CANDIDATE_QNAME = QName.create(NETCONF_QNAME, "candidate"); public static QName NETCONF_RUNNING_QNAME = QName.create(NETCONF_QNAME, "running"); + public static QName NETCONF_ERROR_OPTION_QNAME = QName.create(NETCONF_QNAME, "error-option"); + public static String ROLLBACK_ON_ERROR_OPTION = "rollback-on-error"; + public static QName NETCONF_RPC_REPLY_QNAME = QName.create(NETCONF_QNAME, "rpc-reply"); public static QName NETCONF_OK_QNAME = QName.create(NETCONF_QNAME, "ok"); public static QName NETCONF_DATA_QNAME = QName.create(NETCONF_QNAME, "data"); @@ -248,7 +254,17 @@ public class NetconfMapping { public static void checkValidReply(NetconfMessage input, NetconfMessage output) { String inputMsgId = input.getDocument().getDocumentElement().getAttribute("message-id"); String outputMsgId = output.getDocument().getDocumentElement().getAttribute("message-id"); - Preconditions.checkState(inputMsgId.equals(outputMsgId), "Rpc request and reply message IDs must be same."); + + if(inputMsgId.equals(outputMsgId) == false) { + String requestXml = XmlUtil.toString(input.getDocument()); + String responseXml = XmlUtil.toString(output.getDocument()); + throw new IllegalStateException(String.format("Rpc request and reply message IDs must be same. Request: %s, response: %s", requestXml, responseXml)); + } } + public static void checkSuccessReply(NetconfMessage output) { + if(NetconfMessageUtil.isErrorMessage(output)) { + throw new IllegalStateException(String.format("Response contains error: %s", XmlUtil.toString(output.getDocument()))); + } + } } -- 2.36.6