X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=docs%2Fuser-guide.rst;h=d0b8b62a4685de602f6849483e3b8b39428cd988;hb=5bf8a5c5d766160769b84469babb98785c42f8e5;hp=c2988f849c2fae97c022b3a51b4bdc41346264a4;hpb=5bc455ab7471a308b77e1455da43f1d8202aa89c;p=netconf.git diff --git a/docs/user-guide.rst b/docs/user-guide.rst index c2988f849c..d0b8b62a46 100644 --- a/docs/user-guide.rst +++ b/docs/user-guide.rst @@ -1,5 +1,13 @@ .. _netconf-user-guide: +.. |ss| raw:: html + + + +.. |se| raw:: html + + + NETCONF User Guide ================== @@ -48,8 +56,10 @@ the device.** Netconf-connector configuration ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -There are 2 ways for configuring netconf-connector: NETCONF or RESTCONF. -This guide focuses on using RESTCONF. +NETCONF connectors are configured directly through the usage of the +network-topology model. You can configure new NETCONF connectors both +through the NETCONF server for MD-SAL (port 2830) or RESTCONF. This guide +focuses on RESTCONF. .. important:: @@ -78,105 +88,186 @@ This guide focuses on using RESTCONF. http://localhost:8181/rests/data/network-topology:network-topology?content=nonconfig for operational datastore. + | Also in case of `RFC-8040 `__, + if a data node in the path expression is a YANG leaf-list or list + node, the path segment has to be constructed by having leaf-list or + list node name, followed by an "=" character, then followed by the + leaf-list or list value. Any reserved characters must be + percent-encoded. + | e. g. GET + http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf?content=config + for retrieving data from configuration datastore for + topology-netconf value of topology list is equivalent to the deprecated request + | |ss| GET |se| + http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf, + which is related to `draft-bierman-netconf-restconf-02 + `__. + Examples in the `Spawning new NETCONF connectors`_ section include both bierman02 and rfc8040 + formats +Preconditions +^^^^^^^^^^^^^ +1. OpenDaylight is running -Default configuration -^^^^^^^^^^^^^^^^^^^^^ +2. In Karaf, you must have the ``odl-netconf-topology`` or + ``odl-netconf-clustered-topology`` feature installed. -The default configuration contains all the necessary dependencies (file: -01-netconf.xml) and a single instance of netconf-connector (file: -99-netconf-connector.xml) called **controller-config** which connects -itself to the NETCONF northbound in OpenDaylight in a loopback fashion. -The connector mounts the NETCONF server for config-subsystem in order to -enable RESTCONF protocol for config-subsystem. This RESTCONF still goes -via NETCONF, but using RESTCONF is much more user friendly than using -NETCONF. +3. Feature ``odl-restconf`` must be installed -Spawning additional netconf-connectors while the controller is running -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Spawning new NETCONF connectors +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Preconditions: +To create a new NETCONF connector you need to send the following PUT request +to RESTCONF: -1. OpenDaylight is running +.. list-table:: + :widths: 1 5 -2. In Karaf, you must have the netconf-connector installed (at the Karaf - prompt, type: ``feature:install odl-netconf-connector-all``); the - loopback NETCONF mountpoint will be automatically configured and - activated + * - bierman02 + - http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/new-netconf-device + * - rfc8040 + - http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=new-netconf-device -3. Wait until log displays following entry: - RemoteDevice{controller-config}: NETCONF connector initialized - successfully +You could use the same body to create the new NETCONF connector with a POST +without specifying the node in the URL: -To configure a new netconf-connector you need to send following request -to RESTCONF: +.. list-table:: + :widths: 1 5 -POST -http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules + * - bierman02 + - http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf + * - rfc8040 + - http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf + +Payload: + +.. tabs:: + + .. tab:: XML + + **Content-type:** ``application/xml`` + + **Accept:** ``application/xml`` + + **Authentication:** ``admin:admin`` + + .. code-block:: xml + + + new-netconf-device + 127.0.0.1 + 17830 + admin + admin + false + + false + 20000 + 0 + 2000 + 1.5 + + 120 + + + .. tab:: JSON + + **Content-type:** ``application/json`` + + **Accept:** ``application/json`` + + **Authentication:** ``admin:admin`` + + .. code-block:: json + + { + "node": [ + { + "node-id": "new-netconf-device", + "netconf-node-topology:port": 17830, + "netconf-node-topology:reconnect-on-changed-schema": false, + "netconf-node-topology:connection-timeout-millis": 20000, + "netconf-node-topology:tcp-only": false, + "netconf-node-topology:max-connection-attempts": 0, + "netconf-node-topology:username": "admin", + "netconf-node-topology:password": "admin", + "netconf-node-topology:sleep-factor": 1.5, + "netconf-node-topology:host": "127.0.0.1", + "netconf-node-topology:between-attempts-timeout-millis": 2000, + "netconf-node-topology:keepalive-delay": 120 + } + ] + } + +Note that the device name in element must match the last +element of the restconf URL. + +Reconfiguring an existing connector +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The steps to reconfigure an existing connector are exactly the same as +when spawning a new connector. The old connection will be disconnected +and a new connector with the new configuration will be created. This needs +to be done with a PUT request because the node already exists. A POST +request will fail for that reason. + +Additionally, a PATCH request can be used to modify an existing +configuration. Currently, only yang-patch (`RFC-8072 `__) +is supported. The URL would be the same as the above PUT examples. +Using JSON for the body, the headers needed for the request would +be: Headers: -- Accept application/xml +- Accept: application/yang.patch-status+json + +- Content-Type: application/yang.patch+json -- Content-Type application/xml +Example JSON payload to modify the password entry: :: - - prefix:sal-netconf-connector - new-netconf-device -
127.0.0.1
- 830 - admin - admin - false - - prefix:netty-event-executor - global-event-executor - - - prefix:binding-broker-osgi-registry - binding-osgi-broker - - - prefix:dom-broker-osgi-registry - dom-broker - - - prefix:netconf-client-dispatcher - global-netconf-dispatcher - - - prefix:threadpool - global-netconf-processing-executor - - - prefix:scheduled-threadpool - global-netconf-ssh-scheduled-executor - -
- -This spawns a new netconf-connector which tries to connect to (or mount) -a NETCONF device at 127.0.0.1 and port 830. You can check the -configuration of config-subsystem’s configuration datastore. The new -netconf-connector will now be present there. Just invoke: + { + "ietf-restconf:yang-patch" : { + "patch-id" : "0", + "edit" : [ + { + "edit-id" : "edit1", + "operation" : "merge", + "target" : "", + "value" : { + "node": [ + { + "node-id": "new-netconf-device", + "netconf-node-topology:password" : "newpassword" + } + ] + } + } + ] + } + } -GET -http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules +Deleting an existing connector +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The response will contain the module for new-netconf-device. +To remove an already configured NETCONF connector you need to send a +DELETE request to the same PUT request URL that was used to create the +device: -Right after the new netconf-connector is created, it writes some useful -metadata into the datastore of MD-SAL under the network-topology -subtree. This metadata can be found at: +.. list-table:: + :widths: 1 5 -GET -http://localhost:8181/restconf/operational/network-topology:network-topology/ + * - bierman02 + - http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/new-netconf-device + * - rfc8040 + - http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=new-netconf-device + +.. note:: -Information about connection status, device capabilities, etc. can be -found there. + No body is needed to delete the node/device Connecting to a device not supporting NETCONF monitoring ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -226,206 +317,76 @@ yang-module-capabilities and this attribute can contain a list of "YANG module based" capabilities. So by setting this configuration attribute, it is possible to override the "yang-module-based" capabilities reported in HELLO message of the device. To do this, we need to modify the -configuration of netconf-connector by adding this XML (It needs to be -added next to the address, port, username etc. configuration elements): - -:: - - - - urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24 - - +configuration of netconf-connector like in the example below: + +.. tabs:: + + .. tab:: XML + + **Content-type:** ``application/xml`` + + **Accept:** ``application/xml`` + + **Authentication:** ``admin:admin`` + + .. code-block:: xml + + + r5 + 127.0.0.1 + 8305 + root + root + false + 30 + + true + + urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2013-07-15 + + + + + .. tab:: JSON + + **Content-type:** ``application/json`` + + **Accept:** ``application/json`` + + **Authentication:** ``admin:admin`` + + .. code-block:: json + + { + "node": [ + { + "node-id": "device", + "netconf-node-topology:host": "127.0.0.1", + "netconf-node-topology:password": "root", + "netconf-node-topology:username": "root", + "netconf-node-topology:yang-module-capabilities": { + "override": true, + "capability": [ + "urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2013-07-15" + ] + }, + "netconf-node-topology:port": 8305, + "netconf-node-topology:tcp-only": false, + "netconf-node-topology:keepalive-delay": 30 + } + ] + } **Remember to also put the YANG schemas into the cache folder.** .. note:: For putting multiple capabilities, you just need to replicate the - capability xml element inside yang-module-capability element. + capability element inside yang-module-capability element. Capability element is modeled as a leaf-list. With this configuration, we would make the remote device report usage of ietf-inet-types in the eyes of netconf-connector. -Reconfiguring Netconf-Connector While the Controller is Running -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -It is possible to change the configuration of a running module while the -whole controller is running. This example will continue where the last -left off and will change the configuration for the brand new -netconf-connector after it was spawned. Using one RESTCONF request, we -will change both username and password for the netconf-connector. - -To update an existing netconf-connector you need to send following -request to RESTCONF: - -PUT -http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules/module/odl-sal-netconf-connector-cfg:sal-netconf-connector/new-netconf-device - -:: - - - prefix:sal-netconf-connector - new-netconf-device - bob - passwd - false - - prefix:netty-event-executor - global-event-executor - - - prefix:binding-broker-osgi-registry - binding-osgi-broker - - - prefix:dom-broker-osgi-registry - dom-broker - - - prefix:netconf-client-dispatcher - global-netconf-dispatcher - - - prefix:threadpool - global-netconf-processing-executor - - - prefix:scheduled-threadpool - global-netconf-ssh-scheduled-executor - - - -Since a PUT is a replace operation, the whole configuration must be -specified along with the new values for username and password. This -should result in a 2xx response and the instance of netconf-connector -called new-netconf-device will be reconfigured to use username bob and -password passwd. New configuration can be verified by executing: - -GET -http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules/module/odl-sal-netconf-connector-cfg:sal-netconf-connector/new-netconf-device - -With new configuration, the old connection will be closed and a new one -established. - -Destroying Netconf-Connector While the Controller is Running -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Using RESTCONF one can also destroy an instance of a module. In case of -netconf-connector, the module will be destroyed, NETCONF connection -dropped and all resources will be cleaned. To do this, simply issue a -request to following URL: - -DELETE -http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/controller-config/yang-ext:mount/config:modules/module/odl-sal-netconf-connector-cfg:sal-netconf-connector/new-netconf-device - -The last element of the URL is the name of the instance and its -predecessor is the type of that module (In our case the type is -**sal-netconf-connector** and name **new-netconf-device**). The type and -name are actually the keys of the module list. - -Netconf-connector configuration with MD-SAL -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -It is also possible to configure new NETCONF connectors directly through -MD-SAL with the usage of the network-topology model. You can configure -new NETCONF connectors both through the NETCONF server for MD-SAL (port -2830) or RESTCONF. This guide focuses on RESTCONF. - -.. tip:: - - To enable NETCONF connector configuration through MD-SAL install - either the ``odl-netconf-topology`` or - ``odl-netconf-clustered-topology`` feature. We will explain the - difference between these features later. - -Preconditions -^^^^^^^^^^^^^ - -1. OpenDaylight is running - -2. In Karaf, you must have the ``odl-netconf-topology`` or - ``odl-netconf-clustered-topology`` feature installed. - -3. Feature ``odl-restconf`` must be installed - -4. Wait until log displays following entry: - - :: - - Successfully pushed configuration snapshot 02-netconf-topology.xml(odl-netconf-topology,odl-netconf-topology) - - or until - - :: - - GET http://localhost:8181/restconf/operational/network-topology:network-topology/topology/topology-netconf/ - - returns a non-empty response, for example: - - :: - - - topology-netconf - - -Spawning new NETCONF connectors -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To create a new NETCONF connector you need to send the following request -to RESTCONF: - -:: - - PUT http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/new-netconf-device - -Headers: - -- Accept: application/xml - -- Content-Type: application/xml - -Payload: - -:: - - - new-netconf-device - 127.0.0.1 - 17830 - admin - admin - false - - false - 20000 - 0 - 2000 - 1.5 - - 120 - - -Note that the device name in element must match the last -element of the restconf URL. - -Reconfiguring an existing connector -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The steps to reconfigure an existing connector are exactly the same as -when spawning a new connector. The old connection will be disconnected -and a new connector with the new configuration will be created. - -Deleting an existing connector -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -To remove an already configured NETCONF connector you need to send the -following: - -:: - - DELETE http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/new-netconf-device - Connecting to a device supporting only NETCONF 1.0 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -484,7 +445,7 @@ developers can be found in the developers guide or in the official tutorial application **ncmount** that can be found in the coretutorials project: -- https://github.com/opendaylight/coretutorials/tree/stable/beryllum/ncmount +- https://github.com/opendaylight/coretutorials/tree/master/ncmount Reading data from the device ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -578,7 +539,7 @@ following steps: :: - docker run -rm -t -p 1831:830 dockeruser/netopeer + docker run --rm -t -p 1831:830 dockeruser/netopeer 3. Verify netopeer is running by invoking (netopeer should send its HELLO message right away: @@ -598,8 +559,7 @@ Preconditions: - Netopeer is up and running in docker -Now just follow the chapter: `Spawning -netconf-connector <#_spawning_additional_netconf_connectors_while_the_controller_is_running>`__. +Now just follow the section: `Spawning new NETCONF connectors`_. In the payload change the: - name, e.g., to netopeer @@ -729,10 +689,8 @@ Mounting the MD-SAL’s NETCONF server ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ To perform this operation, just spawn a new netconf-connector as -described in `Spawning -netconf-connector <#_spawning_additional_netconf_connectors_while_the_controller_is_running>`__. -Just change the ip to "127.0.0.1" port to "2830" and its name to -"controller-mdsal". +described in `Spawning new NETCONF connectors`_. Just change the ip to +"127.0.0.1" port to "2830" and its name to "controller-mdsal". Now the MD-SAL’s datastore can be read over RESTCONF via NETCONF by invoking: @@ -747,590 +705,6 @@ http://localhost:8181/restconf/operational/network-topology:network-topology/top can be used to mount and control other OpenDaylight instances by the "master OpenDaylight". -NETCONF testtool ----------------- - -**NETCONF testtool is a set of standalone runnable jars that can:** - -- Simulate NETCONF devices (suitable for scale testing) - -- Stress/Performance test NETCONF devices - -- Stress/Performance test RESTCONF devices - -These jars are part of OpenDaylight’s controller project and are built -from the NETCONF codebase in OpenDaylight. - -.. tip:: - - Download testtool from OpenDaylight Nexus at: - https://nexus.opendaylight.org/content/repositories/public/org/opendaylight/netconf/netconf-testtool/1.1.0-Boron/ - -**Nexus contains 3 executable tools:** - -- executable.jar - device simulator - -- stress.client.tar.gz - NETCONF stress/performance measuring tool - -- perf-client.jar - RESTCONF stress/performance measuring tool - -.. tip:: - - Each executable tool provides help. Just invoke ``java -jar - --help`` - -NETCONF device simulator -~~~~~~~~~~~~~~~~~~~~~~~~ - -NETCONF testtool (or NETCONF device simulator) is a tool that - -- Simulates 1 or more NETCONF devices - -- Is suitable for scale, performance or crud testing - -- Uses core implementation of NETCONF server from OpenDaylight - -- Generates configuration files for controller so that the OpenDaylight - distribution (Karaf) can easily connect to all simulated devices - -- Provides broad configuration options - -- Can start a fully fledged MD-SAL datastore - -- Supports notifications - -Building testtool -^^^^^^^^^^^^^^^^^ - -1. Check out latest NETCONF repository from - `git `__ - -2. Move into the ``opendaylight/netconf/tools/netconf-testtool/`` folder - -3. Build testtool using the ``mvn clean install`` command - -Downloading testtool -^^^^^^^^^^^^^^^^^^^^ - -Netconf-testtool is now part of default maven build profile for -controller and can be also downloaded from nexus. The executable jar for -testtool can be found at: -`nexus-artifacts `__ - -Running testtool -^^^^^^^^^^^^^^^^ - -1. After successfully building or downloading, move into the - ``opendaylight/netconf/tools/netconf-testtool/target/`` folder and - there is file ``netconf-testtool-1.1.0-SNAPSHOT-executable.jar`` (or - if downloaded from nexus just take that jar file) - -2. Execute this file using, e.g.: - - :: - - java -jar netconf-testtool-1.1.0-SNAPSHOT-executable.jar - - This execution runs the testtool with default for all parameters and - you should see this log output from the testtool : - - :: - - 10:31:08.206 [main] INFO o.o.c.n.t.t.NetconfDeviceSimulator - Starting 1, SSH simulated devices starting on port 17830 - 10:31:08.675 [main] INFO o.o.c.n.t.t.NetconfDeviceSimulator - All simulated devices started successfully from port 17830 to 17830 - -Default Parameters -'''''''''''''''''' - -The default parameters for testtool are: - -- Use SSH - -- Run 1 simulated device - -- Device port is 17830 - -- YANG modules used by device are only: ietf-netconf-monitoring, - ietf-yang-types, ietf-inet-types (these modules are required for - device in order to support NETCONF monitoring and are included in the - netconf-testtool) - -- Connection timeout is set to 30 minutes (quite high, but when testing - with 10000 devices it might take some time for all of them to fully - establish a connection) - -- Debug level is set to false - -- No distribution is modified to connect automatically to the NETCONF - testtool - -Verifying testtool -^^^^^^^^^^^^^^^^^^ - -To verify that the simulated device is up and running, we can try to -connect to it using command line ssh tool. Execute this command to -connect to the device: - -:: - - ssh admin@localhost -p 17830 -s netconf - -Just accept the server with yes (if required) and provide any password -(testtool accepts all users with all passwords). You should see the -hello message sent by simulated device. - -Testtool help -^^^^^^^^^^^^^ - -:: - - usage: netconf testtool [-h] [--edit-content EDIT-CONTENT] [--async-requests {true,false}] [--thread-amount THREAD-AMOUNT] [--throttle THROTTLE] - [--auth AUTH AUTH] [--controller-destination CONTROLLER-DESTINATION] [--device-count DEVICES-COUNT] - [--devices-per-port DEVICES-PER-PORT] [--schemas-dir SCHEMAS-DIR] [--notification-file NOTIFICATION-FILE] - [--initial-config-xml-file INITIAL-CONFIG-XML-FILE] [--starting-port STARTING-PORT] - [--generate-config-connection-timeout GENERATE-CONFIG-CONNECTION-TIMEOUT] - [--generate-config-address GENERATE-CONFIG-ADDRESS] [--generate-configs-batch-size GENERATE-CONFIGS-BATCH-SIZE] - [--distribution-folder DISTRO-FOLDER] [--ssh {true,false}] [--exi {true,false}] [--debug {true,false}] - [--md-sal {true,false}] [--time-out TIME-OUT] [-ip IP] [--thread-pool-size THREAD-POOL-SIZE] [--rpc-config RPC-CONFIG] - - netconf testtool - - named arguments: - -h, --help show this help message and exit - --edit-content EDIT-CONTENT - --async-requests {true,false} - --thread-amount THREAD-AMOUNT - The number of threads to use for configuring devices. - --throttle THROTTLE Maximum amount of async requests that can be open at a time, with mutltiple threads this gets divided among all threads - --auth AUTH AUTH Username and password for HTTP basic authentication in order username password. - --controller-destination CONTROLLER-DESTINATION - Ip address and port of controller. Must be in following format : if available it will be used for spawning - netconf connectors via topology configuration as a part of URI. Example (http:///restconf/config/network-topology:network-topology/topology/topology-netconf/node/)otherwise it will - just start simulated devices and skip the execution of PUT requests - --device-count DEVICES-COUNT - Number of simulated netconf devices to spin. This is the number of actual ports open for the devices. - --devices-per-port DEVICES-PER-PORT - Amount of config files generated per port to spoof more devices than are actually running - --schemas-dir SCHEMAS-DIR - Directory containing yang schemas to describe simulated devices. Some schemas e.g. netconf monitoring and inet types are - included by default - --notification-file NOTIFICATION-FILE - Xml file containing notifications that should be sent to clients after create subscription is called - --initial-config-xml-file INITIAL-CONFIG-XML-FILE - Xml file containing initial simulatted configuration to be returned via get-config rpc - --starting-port STARTING-PORT - First port for simulated device. Each other device will have previous+1 port number - --generate-config-connection-timeout GENERATE-CONFIG-CONNECTION-TIMEOUT - Timeout to be generated in initial config files - --generate-config-address GENERATE-CONFIG-ADDRESS - Address to be placed in generated configs - --generate-configs-batch-size GENERATE-CONFIGS-BATCH-SIZE - Number of connector configs per generated file - --distribution-folder DISTRO-FOLDER - Directory where the karaf distribution for controller is located - --ssh {true,false} Whether to use ssh for transport or just pure tcp - --exi {true,false} Whether to use exi to transport xml content - --debug {true,false} Whether to use debug log level instead of INFO - --md-sal {true,false} Whether to use md-sal datastore instead of default simulated datastore. - --time-out TIME-OUT the maximum time in seconds for executing each PUT request - -ip IP Ip address which will be used for creating a socket address.It can either be a machine name, such as java.sun.com, or a - textual representation of its IP address. - --thread-pool-size THREAD-POOL-SIZE - The number of threads to keep in the pool, when creating a device simulator. Even if they are idle. - --rpc-config RPC-CONFIG - Rpc config file. It can be used to define custom rpc behavior, or override the default one.Usable for testing buggy device - behavior. - - -Supported operations -^^^^^^^^^^^^^^^^^^^^ - -Testtool default simple datastore supported operations: - -get-schema - returns YANG schemas loaded from user specified directory, - -edit-config - always returns OK and stores the XML from the input in a local - variable available for get-config and get RPC. Every edit-config - replaces the previous data, - -commit - always returns OK, but does not actually commit the data, - -get-config - returns local XML stored by edit-config, - -get - returns local XML stored by edit-config with netconf-state subtree, - but also supports filtering. - -(un)lock - returns always OK with no lock guarantee - -create-subscription - returns always OK and after the operation is triggered, provided - NETCONF notifications (if any) are fed to the client. No filtering - or stream recognition is supported. - -Note: when operation="delete" is present in the payload for edit-config, -it will wipe its local store to simulate the removal of data. - -When using the MD-SAL datastore testtool behaves more like normal -NETCONF server and is suitable for crud testing. create-subscription is -not supported when testtool is running with the MD-SAL datastore. - -Notification support -^^^^^^^^^^^^^^^^^^^^ - -Testtool supports notifications via the --notification-file switch. To -trigger the notification feed, create-subscription operation has to be -invoked. The XML file provided should look like this example file: - -:: - - - - - - - - - - - 2011-01-04T12:30:46 - - single no delay - - - ]]> - - - - - - 2 - - 5 - - XXXX - - scheduled 5 times 10 seconds each - - - ]]> - - - - - 2 - - XXXX - - single with delay - - - ]]> - - - - -Connecting testtool with controller Karaf distribution -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Auto connect to OpenDaylight -'''''''''''''''''''''''''''' - -It is possible to make OpenDaylight auto connect to the simulated -devices spawned by testtool (so user does not have to post a -configuration for every NETCONF connector via RESTCONF). The testtool is -able to modify the OpenDaylight distribution to auto connect to the -simulated devices after feature ``odl-netconf-connector-all`` is -installed. When running testtool, issue this command (just point the -testool to the distribution: - -:: - - java -jar netconf-testtool-1.1.0-SNAPSHOT-executable.jar --device-count 10 --distribution-folder ~/distribution-karaf-0.4.0-SNAPSHOT/ --debug true - -With the distribution-folder parameter, the testtool will modify the -distribution to include configuration for netconf-connector to connect -to all simulated devices. So there is no need to spawn -netconf-connectors via RESTCONF. - -Running testtool and OpenDaylight on different machines -''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -The testtool binds by default to 0.0.0.0 so it should be accessible from -remote machines. However you need to set the parameter -"generate-config-address" (when using autoconnect) to the address of -machine where testtool will be run so OpenDaylight can connect. The -default value is localhost. - -Executing operations via RESTCONF on a mounted simulated device -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Simulated devices support basic RPCs for editing their config. This part -shows how to edit data for simulated device via RESTCONF. - -Test YANG schema -'''''''''''''''' - -The controller and RESTCONF assume that the data that can be manipulated -for mounted device is described by a YANG schema. For demonstration, we -will define a simple YANG model: - -:: - - module test { - yang-version 1; - namespace "urn:opendaylight:test"; - prefix "tt"; - - revision "2014-10-17"; - - - container cont { - - leaf l { - type string; - } - } - } - -Save this schema in file called test@2014-10-17.yang and store it a -directory called test-schemas/, e.g., your home folder. - -Editing data for simulated device -''''''''''''''''''''''''''''''''' - -- Start the device with following command: - - :: - - java -jar netconf-testtool-1.1.0-SNAPSHOT-executable.jar --device-count 10 --distribution-folder ~/distribution-karaf-0.4.0-SNAPSHOT/ --debug true --schemas-dir ~/test-schemas/ - -- Start OpenDaylight - -- Install odl-netconf-connector-all feature - -- Install odl-restconf feature - -- Check that you can see config data for simulated device by executing - GET request to - - :: - - http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/17830-sim-device/yang-ext:mount/ - -- The data should be just and empty data container - -- Now execute edit-config request by executing a POST request to: - - :: - - http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/17830-sim-device/yang-ext:mount - - with headers: - - :: - - Accept application/xml - Content-Type application/xml - - and payload: - - :: - - - Content - - -- Check that you can see modified config data for simulated device by - executing GET request to - - :: - - http://localhost:8181/restconf/config/network-topology:network-topology/topology/topology-netconf/node/17830-sim-device/yang-ext:mount/ - -- Check that you can see the same modified data in operational for - simulated device by executing GET request to - - :: - - http://localhost:8181/restconf/operational/network-topology:network-topology/topology/topology-netconf/node/17830-sim-device/yang-ext:mount/ - -.. warning:: - - Data will be mirrored in operational datastore only when using the - default simple datastore. - - -Testing User defined RPC -^^^^^^^^^^^^^^^^^^^^^^^^ - -The NETCONF test-tool allows using custom RPC. Custom RPC needs to be defined in yang model provide to test-tool along -with parameter ``--schemas-dir``. - -The input and output of the custom RPC should be provided with ``--rpc-config`` parameter as a path to the file containing -definition of input and output. The format of the custom RPC file is xml as shown below. - -Start the device with following command: - -:: - - java -jar netconf/tools/netconf-testtool/target/netconf-testtool-1.7.0-SNAPSHOT-executable.jar --schemas-dir ~/test-schemas/ --rpc-config ~/tmp/customrpc.xml --debug=true - -Example YANG model file: - -:: - - module example-ops { - namespace "urn:example-ops:reboot"; - prefix "ops"; - - import ietf-yang-types { - prefix "yang"; - } - - - revision "2016-07-07" { - description "Initial version."; - reference "example document."; - } - - - rpc reboot { - description "Reboot operation."; - input { - leaf delay { - type uint32; - units "seconds"; - default 0; - description - "Delay in seconds."; - } - leaf message { - type string; - description - "Log message."; - } - } - } - } - - -Example payload (RPC config file customrpc.xml): - -:: - - - - - - 300 - message - - - - - - - - - - - - -Example of use: - -:: - - POST http://localhost:8181/restconf/operations/network-topology:network-topology/topology/topology-netconf/node/new-netconf-device/yang-ext:mount/example-ops:get-reboot-info - -If successful the command will return code 200. - - - -.. note:: - - A working example of user defined RPC can be found in TestToolTest.java class of the tools[netconf-testtool] project. - - -Known problems -^^^^^^^^^^^^^^ - -Slow creation of devices on virtual machines -'''''''''''''''''''''''''''''''''''''''''''' - -When testtool seems to take unusually long time to create the devices -use this flag when running it: - -:: - - -Dorg.apache.sshd.registerBouncyCastle=false - -Too many files open -''''''''''''''''''' - -When testtool or OpenDaylight starts to fail with TooManyFilesOpen -exception, you need to increase the limit of open files in your OS. To -find out the limit in linux execute: - -:: - - ulimit -a - -Example sufficient configuration in linux: - -:: - - core file size (blocks, -c) 0 - data seg size (kbytes, -d) unlimited - scheduling priority (-e) 0 - file size (blocks, -f) unlimited - pending signals (-i) 63338 - max locked memory (kbytes, -l) 64 - max memory size (kbytes, -m) unlimited - open files (-n) 500000 - pipe size (512 bytes, -p) 8 - POSIX message queues (bytes, -q) 819200 - real-time priority (-r) 0 - stack size (kbytes, -s) 8192 - cpu time (seconds, -t) unlimited - max user processes (-u) 63338 - virtual memory (kbytes, -v) unlimited - file locks (-x) unlimited - -To set these limits edit file: /etc/security/limits.conf, for example: - -:: - - * hard nofile 500000 - * soft nofile 500000 - root hard nofile 500000 - root soft nofile 500000 - -"Killed" -'''''''' - -The testtool might end unexpectedly with a simple message: "Killed". -This means that the OS killed the tool due to too much memory consumed -or too many threads spawned. To find out the reason on linux you can use -following command: - -:: - - dmesg | egrep -i -B100 'killed process' - -Also take a look at this file: /proc/sys/kernel/threads-max. It limits -the number of threads spawned by a process. Sufficient (but probably -much more than enough) value is, e.g., 126676 - NETCONF stress/performance measuring tool ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1427,12 +801,6 @@ particular mount point. NETCONF Call Home ----------------- -.. important:: - - The call home feature is experimental and will change in a future - release. In particular, the Yang models will change to those specified - in the `RFC 8071 `__ - Call Home Installation ~~~~~~~~~~~~~~~~~~~~~~ @@ -1447,8 +815,10 @@ configuring Call Home & testing its functionality. .. note:: - In order to test Call Home functionality we recommend Netopeer. - See `Netopeer Call Home `__ to learn how to enable call-home on Netopeer. + In order to test Call Home functionality we recommend Netopeer or + Netopeer2. See `Netopeer Call Home `__ + or `Netopeer2 `__ to learn how to + enable call-home on Netopeer. Northbound Call-Home API ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -1459,12 +829,16 @@ following describes this configuration. Global Configuration ^^^^^^^^^^^^^^^^^^^^ +.. important:: + The global configuration is not a part of the `RFC 8071 + `__ and, therefore, subject to change. + Configuring global credentials '''''''''''''''''''''''''''''' -ODL Call-Home server allows user to configure global credentials, which -will be used for devices which does not have device-specific credentials -configured. +ODL Call-Home server allows user to configure global credentials, which will be +used for connected over SSH transport protocol devices which does not have +device-specific credentials configured. This is done by creating ``/odl-netconf-callhome-server:netconf-callhome-server/global/credentials`` @@ -1472,7 +846,7 @@ with username and passwords specified. *Configuring global username & passwords to try* -.. code-block:: none +.. code-block:: PUT /restconf/config/odl-netconf-callhome-server:netconf-callhome-server/global/credentials HTTP/1.1 @@ -1509,7 +883,7 @@ This is a debug feature and should not be used in production. Besides being an o security issue, this also causes the Call-Home Server to drastically increase its output to the log. -.. code-block:: none +.. code-block:: POST /restconf/config/odl-netconf-callhome-server:netconf-callhome-server/global HTTP/1.1 @@ -1527,8 +901,12 @@ to the log. Device-Specific Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Allowing Device & Configuring Name -'''''''''''''''''''''''''''''''''' +Netconf Call Home server supports both of the secure transports used +by the Network Configuration Protocol (NETCONF) - Secure Shell (SSH), +and Transport Layer Security (TLS). + +Configure device to connect over SSH protocol +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Netconf Call Home Server uses device provided SSH server key (host key) to identify device. The pairing of name and server key is configured in @@ -1541,9 +919,76 @@ not found, the connection between the Call Home server and the device is dropped immediately. In either case, the device that connects to the Call home server leaves a record of its presence in the operational store. +Configuring Device with Device-specific Credentials +''''''''''''''''''''''''''''''''''''''''''''''''''' + +Adding specific device to the allowed list is done by creating +``/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device/{device}`` +with device-id and connection parameters inside the ssh-client-params container. + +*Configuring Device with Credentials* + +.. code-block:: + + PUT + /restconf/config/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device/example HTTP/1.1 + Content-Type: application/json + Accept: application/json + +.. code-block:: json + + { + "device": { + "unique-id": "example", + "ssh-client-params": { + "credentials": { + "username": "example", + "passwords": [ "password" ] + }, + "ssh-host-key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDHoH1jMjltOJnCt999uaSfc48ySutaD3ISJ9fSECe1Spdq9o9mxj0kBTTTq+2V8hPspuW75DNgN+V/rgJeoUewWwCAasRx9X4eTcRrJrwOQKzb5Fk+UKgQmenZ5uhLAefi2qXX/agFCtZi99vw+jHXZStfHm9TZCAf2zi+HIBzoVksSNJD0VvPo66EAvLn5qKWQD4AdpQQbKqXRf5/W8diPySbYdvOP2/7HFhDukW8yV/7ZtcywFUIu3gdXsrzwMnTqnATSLPPuckoi0V2jd8dQvEcu1DY+rRqmqu0tEkFBurlRZDf1yhNzq5xWY3OXcjgDGN+RxwuWQK3cRimcosH" + } + } + } + +Configuring Device with Global Credentials +''''''''''''''''''''''''''''''''''''''''''''''''''' + +It is possible to omit 'username' and 'password' for ssh-client-params, +in such case values from global credentials will be used. + *Example of configuring device* -.. code-block:: none +.. code-block:: + + PUT + /restconf/config/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device/example HTTP/1.1 + Content-Type: application/json + Accept: application/json + +.. code-block:: json + + { + "device": { + "unique-id": "example", + "ssh-client-params": { + "host-key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDHoH1jMjltOJnCt999uaSfc48ySutaD3ISJ9fSECe1Spdq9o9mxj0kBTTTq+2V8hPspuW75DNgN+V/rgJeoUewWwCAasRx9X4eTcRrJrwOQKzb5Fk+UKgQmenZ5uhLAefi2qXX/agFCtZi99vw+jHXZStfHm9TZCAf2zi+HIBzoVksSNJD0VvPo66EAvLn5qKWQD4AdpQQbKqXRf5/W8diPySbYdvOP2/7HFhDukW8yV/7ZtcywFUIu3gdXsrzwMnTqnATSLPPuckoi0V2jd8dQvEcu1DY+rRqmqu0tEkFBurlRZDf1yhNzq5xWY3OXcjgDGN+RxwuWQK3cRimcosH" + } + } + } + +Deprecated configuration models for devices accessed with SSH protocol +'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +With `RFC 8071 `__ alignment and adding +support for TLS transport following configuration models has been marked +deprecated. + +Configuring Device with Global Credentials +''''''''''''''''''''''''''''''''''''''''''''''''''' + +*Example of configuring device* + +.. code-block:: PUT /restconf/config/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device/example HTTP/1.1 @@ -1568,7 +1013,7 @@ device-specific configuration. Format is same as in global credentials. *Configuring Device with Credentials* -.. code-block:: none +.. code-block:: PUT /restconf/config/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device/example HTTP/1.1 @@ -1588,6 +1033,117 @@ device-specific configuration. Format is same as in global credentials. } } +Configure device to connect over TLS protocol +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Netconf Call Home Server allows devices to use TLS transport protocol to +establish a connection towards the NETCONF device. This communication +requires proper setup to make two-way TLS authentication possible for client +and server. + +The initial step is to configure certificates and keys for two-way TLS by +storing them within the netconf-keystore. + +*Adding a client private key credential to the netconf-keystore* + +.. code-block:: + + POST + /rests/operations/netconf-keystore:add-keystore-entry HTTP/1.1 + Content-Type: application/json + Accept: application/json + +.. code-block:: json + + { + "input": { + "key-credential": [ + { + "key-id": "example-client-key-id", + "private-key": "base64encoded-private-key", + "passphrase": "passphrase" + } + ] + } + } + +*Associate a private key with a client and CA certificates chain* + +.. code-block:: + + POST + /rests/operations/netconf-keystore:add-private-key HTTP/1.1 + Content-Type: application/json + Accept: application/json + +.. code-block:: json + + { + "input": { + "private-key": [ + { + "name": "example-client-key-id", + "data": "key-data", + "certificate-chain": [ + "certificate-data" + ] + } + ] + } + } + +*Add a list of trusted CA and server certificates* + +.. code-block:: + + POST + /rests/operations/netconf-keystore:add-trusted-certificate HTTP/1.1 + Content-Type: application/json + Accept: application/json + +.. code-block:: json + + { + "input": { + "trusted-certificate": [ + { + "name": "example-ca-certificate", + "certificate": "ca-certificate-data" + }, + { + "name": "example-server-certificate", + "certificate": "server-certificate-data" + } + ] + } + } + +In a second step, it is required to create an allowed device associated with +a server certificate and client key. The server certificate will be used to +identify and pin the NETCONF device during SSL handshake and should be unique +among the allowed devices. + +*Add device configuration for TLS protocol to allowed devices list* + +.. code-block:: + + PUT + /restconf/config/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device/example-device HTTP/1.1 + Content-Type: application/json + Accept: application/json + +.. code-block:: json + + { + "device": { + "unique-id": "example-device", + "tls-client-params": { + "key-id": "example-client-key-id", + "certificate-id": "example-server-certificate" + } + } + } + Operational Status ^^^^^^^^^^^^^^^^^^ @@ -1635,3 +1191,378 @@ blueprint configuration file. The device **must** initiate the connection and the server will not try to re-establish the connection in case of a drop. By requirement, the server cannot assume it has connectivity to the device due to NAT or firewalls among others. + +Reading data with selected fields +--------------------------------- + +Overview +~~~~~~~~ + +If user would like to read only selected fields from NETCONF device, it is possible to use +fields query parameter that is described by RFC-8040. RESTCONF parses content of query +parameter into format that is accepted by NETCONF subtree filtering - filtering of data is done +on NETCONF server, not on NETCONF client side. This approach optimizes network traffic load, +because data in which user doesn't have interest, is not transferred over network. + +Next advantages: + +* using single RESTCONF request and single NETCONF RPC for reading multiple subtrees +* possibility to read only selected fields under list node across multiple hierarchies + (it cannot be done without proper selection API) + +.. note:: + + More information about fields query parameter: `RFC 8071 `__ + +Preparation of data +~~~~~~~~~~~~~~~~~~~ + +Mounting NETCONF device that runs on NETCONF testtool: + +.. code-block:: bash + + curl --location --request PUT 'http://127.0.0.1:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=testtool' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "node": [ + { + "node-id": "testtool", + "netconf-node-topology:host": "127.0.0.1", + "netconf-node-topology:port": 36000, + "netconf-node-topology:keepalive-delay": 100, + "netconf-node-topology:tcp-only": false, + "netconf-node-topology:username": "admin", + "netconf-node-topology:password": "admin" + } + ] + }' + +Setting initial configuration on NETCONF device: + +.. code-block:: bash + + curl --location --request PUT 'http://127.0.0.1:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=testtool/yang-ext:mount/test-module:root' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Content-Type: application/json' \ + --data-raw '{ + "root": { + "simple-root": { + "leaf-a": "asddhg", + "leaf-b": "ffffff", + "ll": [ + "str1", + "str2", + "str3" + ], + "nested": { + "sample-x": true, + "sample-y": false + } + }, + "list-root": { + "branch-ab": 5, + "top-list": [ + { + "key-1": "ka", + "key-2": "kb", + "next-data": { + "switch-1": [ + null + ], + "switch-2": [ + null + ] + }, + "nested-list": [ + { + "identifier": "f1", + "foo": 1 + }, + { + "identifier": "f2", + "foo": 10 + }, + { + "identifier": "f3", + "foo": 20 + } + ] + }, + { + "key-1": "kb", + "key-2": "ka", + "next-data": { + "switch-1": [ + null + ] + }, + "nested-list": [ + { + "identifier": "e1", + "foo": 1 + }, + { + "identifier": "e2", + "foo": 2 + }, + { + "identifier": "e3", + "foo": 3 + } + ] + }, + { + "key-1": "kc", + "key-2": "ke", + "next-data": { + "switch-2": [ + null + ] + }, + "nested-list": [ + { + "identifier": "q1", + "foo": 13 + }, + { + "identifier": "q2", + "foo": 14 + }, + { + "identifier": "q3", + "foo": 15 + } + ] + } + ] + } + } + }' + +Examples +-------- + +1. Reading whole leaf-list 'll' and leaf 'nested/sample-x' under 'simple-root' container. + +RESTCONF request: + +.. code-block:: bash + + curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=testtool/yang-ext:mount/test-module:root/simple-root?content=config&fields=ll;nested/sample-x' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Cookie: JSESSIONID=node01h4w82eorc1k61866b71qjgj503.node0' + +Generated NETCONF RPC request: + +.. code-block:: xml + + + + + + + + + + + + + + + + + + + +.. note:: + + Using fields query parameter it is also possible to read whole leaf-list or list without + necessity to specify value / key predicate (without reading parent entity). Such scenario + is not permitted in RFC-8040 paths alone - fields query parameter can be used as + workaround for this case. + +RESTCONF response: + +.. code-block:: json + + { + "test-module:simple-root": { + "ll": [ + "str3", + "str1", + "str2" + ], + "nested": { + "sample-x": true + } + } + } + +2. Reading all identifiers of 'nested-list' under all elements of 'top-list'. + +RESTCONF request: + +.. code-block:: bash + + curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=testtool/yang-ext:mount/test-module:root/list-root?content=config&fields=top-list(nested-list/identifier)' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Cookie: JSESSIONID=node01h4w82eorc1k61866b71qjgj503.node0' + +Generated NETCONF RPC request: + +.. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + +.. note:: + + NETCONF client automatically fetches values of list keys since they are required for correct + deserialization of NETCONF response and at the end serialization of response to RESTCONF + response (JSON/XML). + +RESTCONF response: + +.. code-block:: json + + { + "test-module:list-root": { + "top-list": [ + { + "key-1": "ka", + "key-2": "kb", + "nested-list": [ + { + "identifier": "f3" + }, + { + "identifier": "f2" + }, + { + "identifier": "f1" + } + ] + }, + { + "key-1": "kb", + "key-2": "ka", + "nested-list": [ + { + "identifier": "e3" + }, + { + "identifier": "e2" + }, + { + "identifier": "e1" + } + ] + }, + { + "key-1": "kc", + "key-2": "ke", + "nested-list": [ + { + "identifier": "q3" + }, + { + "identifier": "q2" + }, + { + "identifier": "q1" + } + ] + } + ] + } + } + +3. Reading value of leaf 'branch-ab' and all values of leaves 'switch-1' that are placed + under 'top-list' list elements. + +RESTCONF request: + +.. code-block:: bash + + curl --location --request GET 'http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=testtool/yang-ext:mount/test-module:root/list-root?content=config&fields=branch-ab;top-list/next-data/switch-1' \ + --header 'Authorization: Basic YWRtaW46YWRtaW4=' \ + --header 'Cookie: JSESSIONID=node01jx6o5thwae9t1ft7c2zau5zbz4.node0' + +Generated NETCONF RPC request: + +.. code-block:: xml + + + + + + + + + + + + + + + + + + + + + + + +RESTCONF response: + +.. code-block:: json + + { + "test-module:list-root": { + "branch-ab": 5, + "top-list": [ + { + "key-1": "ka", + "key-2": "kb", + "next-data": { + "switch-1": [ + null + ] + } + }, + { + "key-1": "kb", + "key-2": "ka", + "next-data": { + "switch-1": [ + null + ] + } + }, + { + "key-1": "kc", + "key-2": "ke" + } + ] + } + }