From: Maros Marsalek Date: Tue, 9 Jun 2015 08:15:35 +0000 (+0200) Subject: BUG-3604 Ignore empty edits in netconf connector X-Git-Tag: release/beryllium~474 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=373836d91e59e4d5cd783a9597deb6457f8f807e;hp=5b3c73132a0745f93be4d2f798a2802e62f7bd52 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) --- 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)),