X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=docs%2Fdeveloper-guide.rst;h=68154f1782c69520de910651eed70478878ccea2;hb=bfbd8c6613b68174d0b2474b44f05eddbca3b2bf;hp=6cc2017e8d6ae6bb8af30eeee77438b7638ab6d4;hpb=7f1eb54634cb53cb3b11bfdd3bc2237f752969c1;p=netconf.git diff --git a/docs/developer-guide.rst b/docs/developer-guide.rst index 6cc2017e8d..68154f1782 100644 --- a/docs/developer-guide.rst +++ b/docs/developer-guide.rst @@ -21,7 +21,7 @@ support notifications from mounted NETCONF devices.** It may also be useful to read the generic `OpenDaylight MD-SAL app development - tutorial `__ + tutorial `__ before diving into this chapter. This guide assumes awareness of basic OpenDaylight application development. @@ -33,14 +33,14 @@ application called **ncmount** in the ``coretutorials`` OpenDaylight project. It can be found on the github mirror of OpenDaylight’s repositories: -- https://github.com/opendaylight/coretutorials/tree/stable/boron/ncmount +- https://github.com/opendaylight/coretutorials/tree/master/ncmount or checked out from the official OpenDaylight repository: -- https://git.opendaylight.org/gerrit/#/admin/projects/coretutorials +- https://git.opendaylight.org/gerrit/admin/repos/coretutorials **The application was built using the** `project startup maven -archetype `__ +archetype `__ **and demonstrates how to:** - preconfigure connectors to NETCONF devices @@ -60,7 +60,7 @@ archetype , DataObject> change)`` callback of `NcmountProvider -class `__. +class `__. Reading data from the device ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -235,4 +235,184 @@ returns it from ``showNode``. More information can be found in the source code of ncmount sample app + on wiki: - https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Tutorials:Netconf_Mount + https://wiki-archive.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Tutorials:Netconf_Mount + +Reading selected fields from device +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Using NETCONF DOM API it is also possible to read only selected fields +from NETCONF device. NETCONF mountpoint exposes 2 services that provides +this functionality: + +a. **NetconfDataTreeService** - It provides overloaded methods 'get' + (operational data) and 'getConfig' (configuration data) + with 'List fields' parameters. This service + should be used if transaction properties are not required. +b. **NetconfDOMDataBrokerFieldsExtension** - It implements DOMDataBroker + interface - provides the same transaction functionality plus method + for reading of data with selected fields: read(LogicalDatastoreType, + YangInstanceIdentifier, List). + Instance of NetconfDOMDataBrokerFieldsExtension can be obtained + using 'getExtensions()' method on the DOMDataBroker instance. + +'List fields' parameter specifies list of paths +that are read from device using subtree filtering. These paths must be +relative to parent path - returned data follows schema of the last path +argument of parent path. + +**Mechanics** + +* parent path: /a/b/c +* fields: [/x/y; /w/z] + +NETCONF server will read data on following paths: +* /a/b/c/x/y +* /a/b/c/w/z + +And place read data under /a/b/c path - thus returned data will have +following structure: + +.. code-block:: + + c: { + x: { + y: { + // data ... + } + } + w: { + z: { + // data ... + } + } + } + +From the view of DOM API, YangInstanceIdentifier (first parameter) represents +the parent path, and List represents list of selected +fields. + +**Example: using NetconfDataTreeService** + +The following method demonstrates reading of 2 fields under parent list 'l1': + +* parent path: /c1/l1 +* fields (leaves 'x1' and 'x2'): [/c2/x1, /c2/l2/x2] + +Result will contain whole MapNode with possibly multiple MapEntryNode-s that +contain only keys of list elements and selected fields. + +.. code-block:: java + + public void readData(final DOMMountPoint mountPoint) { + final NetconfDataTreeService dataTreeService = mountPoint.getService(NetconfDataTreeService.class).get(); + + final YangInstanceIdentifier parentPath = YangInstanceIdentifier.builder() + .node(CONTAINER_C1_NI) // container 'c1' (root element) + .node(LIST_L1_NI) // list 'l1' placed under container 'c1' + .build(); + final YangInstanceIdentifier leafX1Field = YangInstanceIdentifier.builder() + .node(CONTAINER_C2_NI) // container 'c2' placed under list 'l1' + .node(LEAF_X1_NI) // leaf 'x1' placed under container 'c2' + .build(); + final YangInstanceIdentifier leafX2Field = YangInstanceIdentifier.builder() + .node(CONTAINER_C2_NI) // container 'c2' placed under list 'l1' + .node(NESTED_LIST_L2_NI) // list 'l2' placed under container 'c2' + .node(LEAF_X2_NI) // leaf 'x2' placed under list 'l2' + .build(); + + final ListenableFuture>> config = dataTreeService.getConfig( + parentPath, Lists.newArrayList(leafX1Field, leafX2Field)); + + Futures.addCallback(config, new FutureCallback>>() { + @Override + public void onSuccess(@Nullable final Optional> normalizedNode) { + normalizedNode.ifPresent(node -> LOG.info("Read data: {}", NormalizedNodes.toStringTree(node))); + /* + We expect data with following structure: + l1: [ + { + key-l1: "k1", + c2: { + x1: 10, + l2: [ + { + key-l2: 1, + x2: "foo" + }, + ... + ] + } + }, + ... + ] + */ + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("Failed to read data: {}", parentPath, throwable); + } + }); + } + +**Example: using NetconfDOMDataBrokerFieldsExtension** + +The following method demonstrates reading of 2 fields under parent +container 'c1': + +* parent path: /c1 +* fields (leaf-list 'll1 'and container 'c2'): [/ll1, /l1=k1/c2] + +Result will contain ContainerNode with identifier 'c1' and children +nodes (if they are present) of type LeafSetNode and MapNode. + +.. code-block:: java + + public void readData(final DOMMountPoint mountPoint) { + final DOMDataBroker domDataBroker = mountPoint.getService(DOMDataBroker.class).get(); + final NetconfDOMDataBrokerFieldsExtension domFieldsDataBroker = domDataBroker.getExtensions().getInstance( + NetconfDOMDataBrokerFieldsExtension.class); + + final YangInstanceIdentifier parentPath = YangInstanceIdentifier.builder() + .node(CONTAINER_C1_NI) // container 'c1' (root element) + .build(); + final YangInstanceIdentifier ll1Field = YangInstanceIdentifier.builder() + .node(LEAF_LIST_LL1_NI) // leaf-list 'll1' placed under container 'c1' + .build(); + final YangInstanceIdentifier c2Field = YangInstanceIdentifier.builder() + .node(LIST_L1_NI) // list 'l1' placed under container 'c1' + .nodeWithKey(LIST_L1_QN, LIST_L1_KEY_QN, "k1") // specific list entry with key value 'k1' + .node(CONTAINER_C2_NI) // container 'c2' placed under container 'c1' + .build(); + + final FluentFuture>> data; + try (NetconfDOMFieldsReadTransaction roTx = domFieldsDataBroker.newReadOnlyTransaction()) { + data = roTx.read(LogicalDatastoreType.CONFIGURATION, parentPath, Lists.newArrayList(ll1Field, c2Field)); + } + + data.addCallback(new FutureCallback<>() { + @Override + public void onSuccess(@Nullable final Optional> normalizedNode) { + normalizedNode.ifPresent(node -> LOG.info("Read data: {}", NormalizedNodes.toStringTree(node))); + /* + We expect data with following structure: + c1: { + ll1: [...], + l1: [ + { + l1-key: "k1", + c2: { + // data ... + } + } + ] + } + */ + } + + @Override + public void onFailure(final Throwable throwable) { + LOG.error("Failed to read data: {}", parentPath, throwable); + } + }, MoreExecutors.directExecutor()); + }