BUG-3604 Ignore empty edits in netconf connector 23/22223/2
authorMaros Marsalek <mmarsale@cisco.com>
Tue, 9 Jun 2015 08:15:35 +0000 (10:15 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Tue, 9 Jun 2015 18:29:16 +0000 (18:29 +0000)
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 <mmarsale@cisco.com>
(cherry picked from commit e931d7f83a470f9c5af6a0b69bc45382720e2ed7)

opendaylight/md-sal/sal-netconf-connector/src/main/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/AbstractWriteTx.java
opendaylight/md-sal/sal-netconf-connector/src/test/java/org/opendaylight/controller/sal/connect/netconf/sal/tx/NetconfDeviceWriteOnlyTxTest.java

index f896559c1c8bc33ef46158c84c4ed8512bedb783..93efc18b21e077e81312017502396d9725477e96 100644 (file)
@@ -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.<NormalizedNode<?, ?>>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.<NormalizedNode<?, ?>>fromNullable(data), Optional.<ModifyAction>absent(), path), Optional.<ModifyAction>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);
index 6ccd169572347271b7314215346111dc200cd4b2..3a378ed427fb6def7846eebd84aefe5e529afd8e 100644 (file)
@@ -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)),