From 373836d91e59e4d5cd783a9597deb6457f8f807e Mon Sep 17 00:00:00 2001 From: Maros Marsalek Date: Tue, 9 Jun 2015 10:15:35 +0200 Subject: [PATCH 1/1] BUG-3604 Ignore empty edits in netconf connector Ignore merge/put operation with data that result in an empty edit-config rpc. Some netconf devices ignore it, but some dont and return errors. This kind of merge is triggered e.g. by Restconf when trying to ensure parent structure for top level list. Change-Id: I8351be304f4e55e000eb7fe0da262549ec377459 Signed-off-by: Maros Marsalek (cherry picked from commit e931d7f83a470f9c5af6a0b69bc45382720e2ed7) --- .../netconf/sal/tx/AbstractWriteTx.java | 26 +++++++++++++++++++ .../sal/tx/NetconfDeviceWriteOnlyTxTest.java | 16 ++++++++++++ 2 files changed, 42 insertions(+) diff --git a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java index f896559c1c..93efc18b21 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java @@ -18,10 +18,15 @@ import org.opendaylight.yangtools.yang.common.RpcResult; import org.opendaylight.yangtools.yang.data.api.ModifyAction; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.MixinNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class AbstractWriteTx implements DOMDataWriteTransaction { + private static final Logger LOG = LoggerFactory.getLogger(AbstractWriteTx.class); + private final long defaultRequestTimeoutMillis; protected final RemoteDeviceId id; protected final NetconfBaseOps netOps; @@ -89,6 +94,12 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { public synchronized void put(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { checkEditable(store); + // trying to write only mixin nodes (not visible when serialized). Ignoring. Some devices cannot handle empty edit-config rpc + if(containsOnlyNonVisibleData(path, data)) { + LOG.debug("Ignoring put for {} and data {}. Resulting data structure is empty.", path, data); + return; + } + try { editConfig( netOps.createEditConfigStrcture(Optional.>fromNullable(data), Optional.of(ModifyAction.REPLACE), path), Optional.of(ModifyAction.NONE)); @@ -104,6 +115,12 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { public synchronized void merge(final LogicalDatastoreType store, final YangInstanceIdentifier path, final NormalizedNode data) { checkEditable(store); + // trying to write only mixin nodes (not visible when serialized). Ignoring. Some devices cannot handle empty edit-config rpc + if (containsOnlyNonVisibleData(path, data)) { + LOG.debug("Ignoring merge for {} and data {}. Resulting data structure is empty.", path, data); + return; + } + try { editConfig( netOps.createEditConfigStrcture(Optional.>fromNullable(data), Optional.absent(), path), Optional.absent()); @@ -112,6 +129,15 @@ public abstract class AbstractWriteTx implements DOMDataWriteTransaction { } } + /** + * Check whether the data to be written consists only from mixins + */ + private static boolean containsOnlyNonVisibleData(final YangInstanceIdentifier path, final NormalizedNode data) { + // There's only one such case:top level list (pathArguments == 1 && data is Mixin) + // any other mixin nodes are contained by a "regular" node thus visible when serialized + return path.getPathArguments().size() == 1 && data instanceof MixinNode; + } + @Override public synchronized void delete(final LogicalDatastoreType store, final YangInstanceIdentifier path) { checkEditable(store); diff --git a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java index 6ccd169572..3a378ed427 100644 --- a/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java +++ b/opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java @@ -3,10 +3,13 @@ package org.opendaylight.controller.sal.connect.netconf.sal.tx; import static org.junit.Assert.fail; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.atMost; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_CANDIDATE_QNAME; +import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_FILTER_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.NETCONF_RUNNING_QNAME; import static org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransformUtil.toPath; @@ -28,7 +31,9 @@ import org.opendaylight.controller.sal.connect.netconf.util.NetconfMessageTransf import org.opendaylight.controller.sal.connect.util.RemoteDeviceId; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.netconf.monitoring.rev101004.NetconfState; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaPath; @@ -55,6 +60,17 @@ public class NetconfDeviceWriteOnlyTxTest { yangIId = YangInstanceIdentifier.builder().node(NetconfState.QNAME).build(); } + @Test + public void testIgnoreNonVisibleData() { + final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, mock(SchemaContext.class)), + false, 60000L); + final MapNode emptyList = ImmutableNodes.mapNodeBuilder(NETCONF_FILTER_QNAME).build(); + tx.merge(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(NETCONF_FILTER_QNAME)), emptyList); + tx.put(LogicalDatastoreType.CONFIGURATION, YangInstanceIdentifier.create(new YangInstanceIdentifier.NodeIdentifier(NETCONF_FILTER_QNAME)), emptyList); + + verify(rpc, atMost(1)).invokeRpc(any(SchemaPath.class), any(NormalizedNode.class)); + } + @Test public void testDiscardChanges() { final WriteCandidateTx tx = new WriteCandidateTx(id, new NetconfBaseOps(rpc, mock(SchemaContext.class)), -- 2.36.6