Do not serialize the unchanged for child-nodes-only 74/107774/2
authorSangwook Ha <sangwook.ha@verizon.com>
Fri, 8 Sep 2023 15:52:54 +0000 (08:52 -0700)
committerRobert Varga <nite@hq.sk>
Sun, 10 Sep 2023 14:44:17 +0000 (14:44 +0000)
Subscribing data change notification with the child-nodes-only mode may
generate notifications with unnecessary data change events for the list
key nodes in the data path.

Serialize only data tree nodes with effective changes. This filters out
unnecessary data change events and prevents notification of data updates
without any real changes.

Also, add missing test cases for the child-nodes-only mode.

JIRA: NETCONF-1153
Change-Id: I2ed388ee2e5c364a96240d8b5a519651cf2f5b47
Signed-off-by: Sangwook Ha <sangwook.ha@verizon.com>
restconf/restconf-nb/src/main/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/AbstractWebsocketSerializer.java
restconf/restconf-nb/src/test/java/org/opendaylight/restconf/nb/rfc8040/streams/listeners/ListenerAdapterTest.java
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-create.json [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-create.xml [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-delete.json [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-delete.xml [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update1.json [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update1.xml [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update2.json [new file with mode: 0644]
restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update2.xml [new file with mode: 0644]

index 961edb0ed283f823384700a27f8b600dfe42a18b..aa911e49ed62317332fcf2fcb54d7f6a4e99fa11 100644 (file)
@@ -52,8 +52,7 @@ abstract class AbstractWebsocketSerializer<T extends Exception> {
                 changedLeafNodesOnly);
         }
         if (params.childNodesOnly()) {
-            serializeChildNodesOnly(mutableRootPath(candidate), candidate.getRootNode(), skipData);
-            return true;
+            return serializeChildNodesOnly(mutableRootPath(candidate), candidate.getRootNode(), skipData);
         }
 
         serializeData(candidate.getRootPath().getPathArguments(), candidate.getRootNode(), skipData);
@@ -122,20 +121,28 @@ abstract class AbstractWebsocketSerializer<T extends Exception> {
         return ret;
     }
 
-    private void serializeChildNodesOnly(final Deque<PathArgument> path, final DataTreeCandidateNode current,
+    private boolean serializeChildNodesOnly(final Deque<PathArgument> path, final DataTreeCandidateNode current,
             final boolean skipData) throws T {
-        switch (current.modificationType()) {
+        return switch (current.modificationType()) {
             // just a subtree modification, recurse
             case SUBTREE_MODIFIED -> {
+                var updated = false;
                 for (var child : current.childNodes()) {
                     path.add(child.name());
-                    serializeChildNodesOnly(path, child, skipData);
+                    updated |= serializeChildNodesOnly(path, child, skipData);
                     path.removeLast();
                 }
+                yield updated;
             }
-            // other modification, serialize it
-            default -> serializeData(path, current, skipData);
-        }
+            // other modification, serialize it if updated
+            default -> {
+                final var updated = !isNotUpdate(current);
+                if (updated) {
+                    serializeData(path, current, skipData);
+                }
+                yield updated;
+            }
+        };
     }
 
     private void serializeData(final Collection<PathArgument> dataPath, final DataTreeCandidateNode candidate,
index 24abcccef4828935c7daa6c9cba4d6cc5b3755fe..717e6473e007423d31d7e07efdc955af1b6bf790 100644 (file)
@@ -31,6 +31,7 @@ import org.opendaylight.mdsal.dom.api.DOMDataBroker;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeChangeService;
 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
 import org.opendaylight.restconf.api.query.ChangedLeafNodesOnlyParam;
+import org.opendaylight.restconf.api.query.ChildNodesOnlyParam;
 import org.opendaylight.restconf.api.query.LeafNodesOnlyParam;
 import org.opendaylight.restconf.api.query.SkipNotificationDataParam;
 import org.opendaylight.restconf.api.query.StartTimeParam;
@@ -71,6 +72,14 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
             "/listener-adapter-test/notif-changed-leaves-update.json";
     private static final String JSON_NOTIF_CHANGED_LEAVES_DELETE =
             "/listener-adapter-test/notif-changed-leaves-delete.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_CREATE =
+            "/listener-adapter-test/notif-child-nodes-only-create.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
+        "/listener-adapter-test/notif-child-nodes-only-update1.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
+        "/listener-adapter-test/notif-child-nodes-only-update2.json";
+    private static final String JSON_NOTIF_CHILD_NODES_ONLY_DELETE =
+            "/listener-adapter-test/notif-child-nodes-only-delete.json";
 
     private static final String XML_NOTIF_LEAVES_CREATE = "/listener-adapter-test/notif-leaves-create.xml";
     private static final String XML_NOTIF_LEAVES_UPDATE =  "/listener-adapter-test/notif-leaves-update.xml";
@@ -81,6 +90,14 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
             "/listener-adapter-test/notif-changed-leaves-update.xml";
     private static final String XML_NOTIF_CHANGED_LEAVES_DELETE =
             "/listener-adapter-test/notif-changed-leaves-delete.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_CREATE =
+        "/listener-adapter-test/notif-child-nodes-only-create.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE1 =
+        "/listener-adapter-test/notif-child-nodes-only-update1.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_UPDATE2 =
+        "/listener-adapter-test/notif-child-nodes-only-update2.xml";
+    private static final String XML_NOTIF_CHILD_NODES_ONLY_DELETE =
+        "/listener-adapter-test/notif-child-nodes-only-delete.xml";
 
     private static final String JSON_NOTIF_CONT_CREATE = "/listener-adapter-test/notif-cont-create.json";
     private static final String JSON_NOTIF_CONT_UPDATE = "/listener-adapter-test/notif-cont-update.json";
@@ -157,19 +174,14 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         private CountDownLatch notificationLatch = new CountDownLatch(1);
 
         ListenerAdapterTester(final YangInstanceIdentifier path, final String streamName,
-                              final NotificationOutputType outputType,
-                              final boolean leafNodesOnly, final boolean skipNotificationData) {
+                final NotificationOutputType outputType, final boolean leafNodesOnly,
+                final boolean skipNotificationData, final boolean changedLeafNodesOnly, final boolean childNodesOnly) {
             super(path, streamName, outputType);
             setQueryParams(NotificationQueryParams.of(StartTimeParam.forUriValue("1970-01-01T00:00:00Z"), null, null,
-                LeafNodesOnlyParam.of(leafNodesOnly), SkipNotificationDataParam.of(skipNotificationData), null, null));
-        }
-
-        ListenerAdapterTester(final YangInstanceIdentifier path, final String streamName,
-                              final NotificationOutputType outputType,
-                              final boolean changedLeafNodesOnly) {
-            super(path, streamName, outputType);
-            setQueryParams(NotificationQueryParams.of(StartTimeParam.forUriValue("1970-01-01T00:00:00Z"), null, null,
-                    null, null, ChangedLeafNodesOnlyParam.of(changedLeafNodesOnly), null));
+                leafNodesOnly ? LeafNodesOnlyParam.of(true) : null,
+                skipNotificationData ? SkipNotificationDataParam.of(true) : null,
+                changedLeafNodesOnly ? ChangedLeafNodesOnlyParam.of(true) : null,
+                childNodesOnly ? ChildNodesOnlyParam.of(true) : null));
         }
 
         @Override
@@ -221,7 +233,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
     @Test
     public void testJsonNotifsLeaves() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
-            true, false);
+            true, false, false, false);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         final DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
@@ -262,7 +274,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
     @Test
     public void testJsonNotifsChangedLeaves() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.JSON,
-                true);
+                false, false, true, false);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         final DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
@@ -308,10 +320,46 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         adapter.assertGot(getNotifJson(JSON_NOTIF_CHANGED_LEAVES_DELETE));
     }
 
+    @Test
+    public void testJsonChildNodesOnly() throws Exception {
+        final var adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
+            NotificationOutputType.JSON, false, false, false, true);
+        adapter.setCloseVars(domDataBroker, databindProvider);
+
+        final var changeService = domDataBroker.getExtensions()
+            .getInstance(DOMDataTreeChangeService.class);
+        final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+        changeService.registerDataTreeChangeListener(root, adapter);
+
+        final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_CREATE));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE1));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_UPDATE2));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
+        writeTransaction.commit();
+        adapter.assertGot(getNotifJson(JSON_NOTIF_CHILD_NODES_ONLY_DELETE));
+    }
+
     @Test
     public void testXmlLeavesOnly() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
-            true, false);
+            true, false, false, false);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
@@ -357,7 +405,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
     @Test
     public void testXmlChangedLeavesOnly() throws Exception {
         ListenerAdapterTester adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey", NotificationOutputType.XML,
-                true);
+                false, false, true, false);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         DOMDataTreeChangeService changeService = domDataBroker.getExtensions()
@@ -411,6 +459,42 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
         assertTrue(notification.contains("augment-instance-identifier-patch-module:leaf1"));
     }
 
+    @Test
+    public void testXmlChildNodesOnly() throws Exception {
+        final var adapter = new ListenerAdapterTester(PATCH_CONT_YIID, "Casey",
+            NotificationOutputType.XML, false, false, false, true);
+        adapter.setCloseVars(domDataBroker, databindProvider);
+
+        final var changeService = domDataBroker.getExtensions()
+            .getInstance(DOMDataTreeChangeService.class);
+        final var root = new DOMDataTreeIdentifier(LogicalDatastoreType.CONFIGURATION, PATCH_CONT_YIID);
+        changeService.registerDataTreeChangeListener(root, adapter);
+
+        final var iid = InstanceIdentifier.create(PatchCont.class).child(MyList1.class, new MyList1Key("Althea"));
+        WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_CREATE));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.put(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Bertha").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE1));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.merge(LogicalDatastoreType.CONFIGURATION, iid,
+            new MyList1Builder().setMyLeaf11("Jed").setName("Althea").build());
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_UPDATE2));
+
+        writeTransaction = dataBroker.newWriteOnlyTransaction();
+        writeTransaction.delete(LogicalDatastoreType.CONFIGURATION, iid);
+        writeTransaction.commit();
+        adapter.assertXmlSimilar(getResultXml(XML_NOTIF_CHILD_NODES_ONLY_DELETE));
+    }
+
     @Test
     public void testJsonContNotifications() throws Exception {
         jsonNotifications(PATCH_CONT_YIID, false, JSON_NOTIF_CONT_CREATE,
@@ -485,7 +569,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
     private void jsonNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
             final String jsonNotifCreate, final String jsonNotifUpdate, final String jsonNotifDelete) throws Exception {
         final var adapter = new ListenerAdapterTester(pathYiid, "Casey",
-                NotificationOutputType.JSON, false, skipData);
+                NotificationOutputType.JSON, false, skipData, false, false);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         final var changeService = domDataBroker.getExtensions()
@@ -516,7 +600,7 @@ public class ListenerAdapterTest extends AbstractConcurrentDataBrokerTest {
     private void xmlNotifications(final YangInstanceIdentifier pathYiid, final boolean skipData,
             final String xmlNotifCreate, final String xmlNotifUpdate, final String xmlNotifDelete) throws Exception {
         final var adapter = new ListenerAdapterTester(pathYiid, "Casey", NotificationOutputType.XML,
-                false, skipData);
+                false, skipData, false, false);
         adapter.setCloseVars(domDataBroker, databindProvider);
 
         final var changeService = domDataBroker.getExtensions()
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-create.json b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-create.json
new file mode 100644 (file)
index 0000000..e86e20b
--- /dev/null
@@ -0,0 +1,23 @@
+{
+    "urn-ietf-params-xml-ns-netconf-notification-1.0:notification":{
+        "urn-opendaylight-params-xml-ns-yang-controller-md-sal-remote:data-changed-notification":{
+            "data-change-event":[
+                {
+                    "path":"/instance-identifier-patch-module:patch-cont",
+                    "operation":"created",
+                    "data":{
+                        "instance-identifier-patch-module:patch-cont":{
+                            "my-list1":[
+                                {
+                                    "name":"Althea",
+                                    "my-leaf11":"Jed"
+                                }
+                            ]
+                        }
+                    }
+                }
+            ]
+        },
+        "event-time":"2020-10-14T11:16:51.111635+02:00"
+    }
+}
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-create.xml b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-create.xml
new file mode 100644 (file)
index 0000000..cb2d71e
--- /dev/null
@@ -0,0 +1,17 @@
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+    <eventTime>2020-10-19T12:21:02.876756+02:00</eventTime>
+    <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+        <data-change-event>
+            <path>/instance-identifier-patch-module:patch-cont</path>
+            <operation>created</operation>
+            <data>
+                <patch-cont xmlns="instance:identifier:patch:module">
+                    <my-list1>
+                        <name>Althea</name>
+                        <my-leaf11>Jed</my-leaf11>
+                    </my-list1>
+                </patch-cont>
+            </data>
+        </data-change-event>
+    </data-changed-notification>
+</notification>
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-delete.json b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-delete.json
new file mode 100644 (file)
index 0000000..c0c5f34
--- /dev/null
@@ -0,0 +1,13 @@
+{
+    "urn-ietf-params-xml-ns-netconf-notification-1.0:notification":{
+        "urn-opendaylight-params-xml-ns-yang-controller-md-sal-remote:data-changed-notification":{
+            "data-change-event":[
+                {
+                    "path":"/instance-identifier-patch-module:patch-cont",
+                    "operation":"deleted"
+                }
+            ]
+        },
+        "event-time":"2020-10-14T11:20:33.271836+02:00"
+    }
+}
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-delete.xml b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-delete.xml
new file mode 100644 (file)
index 0000000..9f4c544
--- /dev/null
@@ -0,0 +1,9 @@
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+    <eventTime>2020-10-19T12:21:02.876756+02:00</eventTime>
+    <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+        <data-change-event>
+            <path>/instance-identifier-patch-module:patch-cont</path>
+            <operation>deleted</operation>
+        </data-change-event>
+    </data-changed-notification>
+</notification>
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update1.json b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update1.json
new file mode 100644 (file)
index 0000000..0b67f48
--- /dev/null
@@ -0,0 +1,21 @@
+{
+    "urn-ietf-params-xml-ns-netconf-notification-1.0:notification":{
+        "urn-opendaylight-params-xml-ns-yang-controller-md-sal-remote:data-changed-notification":{
+            "data-change-event":[
+                {
+                    "path":"/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']",
+                    "operation":"updated",
+                    "data":{
+                        "instance-identifier-patch-module:my-list1":[
+                            {
+                                "name":"Althea",
+                                "my-leaf11":"Bertha"
+                            }
+                        ]
+                    }
+                }
+            ]
+        },
+        "event-time":"2020-10-14T11:16:51.111635+02:00"
+    }
+}
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update1.xml b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update1.xml
new file mode 100644 (file)
index 0000000..615bbbc
--- /dev/null
@@ -0,0 +1,15 @@
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+    <eventTime>2020-10-19T12:21:02.876756+02:00</eventTime>
+    <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+        <data-change-event>
+            <path>/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']</path>
+            <operation>updated</operation>
+            <data>
+                <my-list1 xmlns="instance:identifier:patch:module">
+                    <name>Althea</name>
+                    <my-leaf11>Bertha</my-leaf11>
+                </my-list1>
+            </data>
+        </data-change-event>
+    </data-changed-notification>
+</notification>
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update2.json b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update2.json
new file mode 100644 (file)
index 0000000..25c1331
--- /dev/null
@@ -0,0 +1,16 @@
+{
+    "urn-ietf-params-xml-ns-netconf-notification-1.0:notification":{
+        "urn-opendaylight-params-xml-ns-yang-controller-md-sal-remote:data-changed-notification":{
+            "data-change-event":[
+                {
+                    "path":"/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf11",
+                    "operation":"updated",
+                    "data":{
+                        "instance-identifier-patch-module:my-leaf11":"Jed"
+                    }
+                }
+            ]
+        },
+        "event-time":"2020-10-14T11:19:34.549843+02:00"
+    }
+}
diff --git a/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update2.xml b/restconf/restconf-nb/src/test/resources/listener-adapter-test/notif-child-nodes-only-update2.xml
new file mode 100644 (file)
index 0000000..c00f5f5
--- /dev/null
@@ -0,0 +1,12 @@
+<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
+    <eventTime>2020-10-19T12:21:02.876756+02:00</eventTime>
+    <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
+        <data-change-event>
+            <path>/instance-identifier-patch-module:patch-cont/instance-identifier-patch-module:my-list1/instance-identifier-patch-module:my-list1[instance-identifier-patch-module:name='Althea']/instance-identifier-patch-module:my-leaf11</path>
+            <operation>updated</operation>
+            <data>
+                <my-leaf11 xmlns="instance:identifier:patch:module">Jed</my-leaf11>
+            </data>
+        </data-change-event>
+    </data-changed-notification>
+</notification>