Refactor leaf names in odl-netconf-device.yang
[netconf.git] / docs / user-guide.rst
1 .. _netconf-user-guide:
2
3 .. |ss| raw:: html
4
5    <strike>
6
7 .. |se| raw:: html
8
9    </strike>
10
11 NETCONF User Guide
12 ==================
13
14 Overview
15 --------
16
17 NETCONF is an XML-based protocol used for configuration and monitoring
18 devices in the network. The base NETCONF protocol is described in
19 `RFC-6241 <https://www.rfc-editor.org/rfc/rfc6241>`__.
20
21 **NETCONF in OpenDaylight:.**
22
23 OpenDaylight supports the NETCONF protocol as a northbound server as
24 well as a southbound plugin. It also includes a set of test tools for
25 simulating NETCONF devices and clients.
26
27 Southbound (netconf-connector)
28 ------------------------------
29
30 The NETCONF southbound plugin is capable of connecting to remote NETCONF
31 devices and exposing their configuration/operational datastores, RPCs
32 and notifications as MD-SAL mount points. These mount points allow
33 applications and remote users (over RESTCONF) to interact with the
34 mounted devices.
35
36 In terms of RFCs, the connector supports:
37
38 -  `RFC-6241 <https://www.rfc-editor.org/rfc/rfc6241>`__
39
40 -  `RFC-5277 <https://www.rfc-editor.org/rfc/rfc5277>`__
41
42 -  `RFC-6022 <https://www.rfc-editor.org/rfc/rfc6022>`__
43
44 -  `RFC-7895 <https://www.rfc-editor.org/rfc/rfc7895>`__
45
46 **Netconf-connector is fully model-driven (utilizing the YANG modeling
47 language) so in addition to the above RFCs, it supports any
48 data/RPC/notifications described by a YANG model that is implemented by
49 the device.**
50
51 .. tip::
52
53     NETCONF southbound can be activated by installing
54     ``odl-netconf-connector-all`` Karaf feature.
55
56 .. _netconf-connector:
57
58 Netconf-connector configuration
59 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
60
61 NETCONF connectors are configured directly through the usage of the
62 network-topology model. You can configure new NETCONF connectors both
63 through the NETCONF server for MD-SAL (port 2830) or RESTCONF. This guide
64 focuses on RESTCONF.
65
66 .. important::
67
68     Since 2022.09 Chlorine there is only one RESTCONF endpoint:
69
70     - | ``http://localhost:8181/rests`` is related to `RFC-8040 <https://www.rfc-editor.org/rfc/rfc8040>`__,
71       | can be activated by installing ``odl-restconf-nb``
72        Karaf feature.
73
74     | Resources for configuration and operational datastores start
75      ``/rests/data/``,
76     | e. g. GET
77      http://localhost:8181/rests/data/network-topology:network-topology
78      with response of both datastores. It's allowed to use query
79      parameters to distinguish between them.
80     | e. g. GET
81      http://localhost:8181/rests/data/network-topology:network-topology?content=config
82      for configuration datastore
83     | and GET
84      http://localhost:8181/rests/data/network-topology:network-topology?content=nonconfig
85      for operational datastore.
86
87     | Also if a data node in the path expression is a YANG leaf-list or list
88      node, the path segment has to be constructed by having leaf-list or
89      list node name, followed by an "=" character, then followed by the
90      leaf-list or list value. Any reserved characters must be
91      percent-encoded.
92     | e. g. GET
93      http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf?content=config
94      for retrieving data from configuration datastore for
95      topology-netconf value of topology list.
96
97 Preconditions
98 ^^^^^^^^^^^^^
99
100 1. OpenDaylight is running
101
102 2. In Karaf, you must have the ``odl-netconf-topology`` or
103    ``odl-netconf-clustered-topology`` feature installed.
104
105 3. Feature ``odl-restconf-nb`` must be installed
106
107 Spawning new NETCONF connectors
108 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
109
110 To create a new NETCONF connector you need to send the following PUT request
111 to RESTCONF:
112
113 .. list-table::
114    :widths: 1 5
115
116    * - rfc8040
117      - http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=new-netconf-device
118
119 You could use the same body to create the new  NETCONF connector with a POST
120 without specifying the node in the URL:
121
122 .. list-table::
123    :widths: 1 5
124
125    * - rfc8040
126      - http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf
127
128 Payload:
129
130 .. tabs::
131
132    .. tab:: XML
133
134       **Content-type:** ``application/xml``
135
136       **Accept:** ``application/xml``
137
138       **Authentication:** ``admin:admin``
139
140       .. code-block:: xml
141
142          <node xmlns="urn:TBD:params:xml:ns:yang:network-topology">
143            <node-id>new-netconf-device</node-id>
144            <host xmlns="urn:opendaylight:netconf-node-topology">127.0.0.1</host>
145            <port xmlns="urn:opendaylight:netconf-node-topology">17830</port>
146            <login-password-unencrypted xmlns="urn:opendaylight:netconf-node-topology">
147              <username xmlns="urn:opendaylight:netconf-node-topology">admin</username>
148              <password xmlns="urn:opendaylight:netconf-node-topology">admin</password>
149            </login-password-unencrypted>
150            <tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>
151            <!-- non-mandatory fields with default values, you can safely remove these if you do not wish to override any of these values-->
152            <reconnect-on-changed-schema xmlns="urn:opendaylight:netconf-node-topology">false</reconnect-on-changed-schema>
153            <connection-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">20000</connection-timeout-millis>
154            <max-connection-attempts xmlns="urn:opendaylight:netconf-node-topology">0</max-connection-attempts>
155            <min-backoff-millis xmlns="urn:opendaylight:netconf-node-topology">2000</min-backoff-millis>
156            <backoff-multiplier xmlns="urn:opendaylight:netconf-node-topology">1.5</backoff-multiplier>
157            <!-- keepalive-delay set to 0 turns off keepalives-->
158            <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">120</keepalive-delay>
159          </node>
160
161    .. tab:: JSON
162
163       **Content-type:** ``application/json``
164
165       **Accept:** ``application/json``
166
167       **Authentication:** ``admin:admin``
168
169       .. code-block:: json
170
171          {
172              "node": [
173                  {
174                      "node-id": "new-netconf-device",
175                      "netconf-node-topology:port": 17830,
176                      "netconf-node-topology:reconnect-on-changed-schema": false,
177                      "netconf-node-topology:connection-timeout-millis": 20000,
178                      "netconf-node-topology:tcp-only": false,
179                      "netconf-node-topology:max-connection-attempts": 0,
180                      "netconf-node-topology:login-password-unencrypted": {
181                         "netconf-node-topology:username": "admin",
182                         "netconf-node-topology:password": "admin"
183                      },
184                      "netconf-node-topology:backoff-multiplier": 1.5,
185                      "netconf-node-topology:host": "127.0.0.1",
186                      "netconf-node-topology:min-backoff-millis": 2000,
187                      "netconf-node-topology:keepalive-delay": 120
188                  }
189              ]
190          }
191
192 Note that the device name in <node-id> element must match the last
193 element of the restconf URL.
194
195 Reconfiguring an existing connector
196 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
197
198 The steps to reconfigure an existing connector are exactly the same as
199 when spawning a new connector. The old connection will be disconnected
200 and a new connector with the new configuration will be created. This needs
201 to be done with a PUT request because the node already exists. A POST
202 request will fail for that reason.
203
204 Additionally, a PATCH request can be used to modify an existing
205 configuration. Currently, only yang-patch (`RFC-8072 <https://www.rfc-editor.org/rfc/rfc8072>`__)
206 is supported. The URL would be the same as the above PUT examples.
207 Using JSON for the body, the headers needed for the request would
208 be:
209
210 Headers:
211
212 -  Accept: application/yang-data+json
213
214 -  Content-Type: application/yang-patch+json
215
216 Example JSON payload to modify the password entry:
217
218 ::
219
220     {
221       "ietf-restconf:yang-patch" : {
222         "patch-id" : "0",
223         "edit" : [
224           {
225             "edit-id" : "edit1",
226             "operation" : "merge",
227             "target" : "",
228             "value" : {
229              "node": [
230                 {
231                  "node-id": "new-netconf-device",
232                  "netconf-node-topology:password" : "newpassword"
233                 }
234              ]
235             }
236          }
237         ]
238       }
239     }
240
241 Deleting an existing connector
242 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
243
244 To remove an already configured NETCONF connector you need to send a
245 DELETE request to the same PUT request URL that was used to create the
246 device:
247
248 .. list-table::
249    :widths: 1 5
250
251    * - rfc8040
252      - http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=new-netconf-device
253
254 .. note::
255
256     No body is needed to delete the node/device
257
258 Connecting to a device not supporting NETCONF monitoring
259 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
260
261 The netconf-connector in OpenDaylight relies on ietf-netconf-monitoring
262 support when connecting to remote NETCONF device. The
263 ietf-netconf-monitoring support allows netconf-connector to list and
264 download all YANG schemas that are used by the device. NETCONF connector
265 can only communicate with a device if it knows the set of used schemas
266 (or at least a subset). However, some devices use YANG models internally
267 but do not support NETCONF monitoring. Netconf-connector can also
268 communicate with these devices, but you have to side load the necessary
269 yang models into OpenDaylight’s YANG model cache for netconf-connector.
270 In general there are 2 situations you might encounter:
271
272 **1. NETCONF device does not support ietf-netconf-monitoring but it does
273 list all its YANG models as capabilities in HELLO message**
274
275 This could be a device that internally uses only ietf-inet-types YANG
276 model with revision 2010-09-24. In the HELLO message that is sent from
277 this device there is this capability reported:
278
279 ::
280
281     urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2010-09-24
282
283 **For such devices you only need to put the schema into folder
284 cache/schema inside your Karaf distribution.**
285
286 .. important::
287
288     The file with YANG schema for ietf-inet-types has to be called
289     ietf-inet-types@2010-09-24.yang. It is the required naming format of
290     the cache.
291
292 **2. NETCONF device does not support ietf-netconf-monitoring and it does
293 NOT list its YANG models as capabilities in HELLO message**
294
295 Compared to device that lists its YANG models in HELLO message, in this
296 case there would be no capability with ietf-inet-types in the HELLO
297 message. This type of device basically provides no information about the
298 YANG schemas it uses so its up to the user of OpenDaylight to properly
299 configure netconf-connector for this device.
300
301 Netconf-connector has an optional configuration attribute called
302 yang-module-capabilities and this attribute can contain a list of "YANG
303 module based" capabilities. So by setting this configuration attribute,
304 it is possible to override the "yang-module-based" capabilities reported
305 in HELLO message of the device. To do this, we need to modify the
306 configuration of netconf-connector like in the example below:
307
308 .. tabs::
309
310    .. tab:: XML
311
312       **Content-type:** ``application/xml``
313
314       **Accept:** ``application/xml``
315
316       **Authentication:** ``admin:admin``
317
318       .. code-block:: xml
319
320          <node xmlns="urn:TBD:params:xml:ns:yang:network-topology">
321            <node-id>r5</node-id>
322            <host xmlns="urn:opendaylight:netconf-node-topology">127.0.0.1</host>
323            <port xmlns="urn:opendaylight:netconf-node-topology">8305</port>
324            <login-password-unencrypted xmlns="urn:opendaylight:netconf-node-topology">
325              <username xmlns="urn:opendaylight:netconf-node-topology">root</username>
326              <password xmlns="urn:opendaylight:netconf-node-topology">root</password>
327            </login-password-unencrypted>
328            <tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>
329            <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">30</keepalive-delay>
330            <yang-module-capabilities xmlns="urn:opendaylight:netconf-node-topology">
331              <override>true</override>
332              <capability xmlns="urn:opendaylight:netconf-node-topology">
333                urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&amp;revision=2013-07-15
334              </capability>
335            </yang-module-capabilities>
336          </node>
337
338    .. tab:: JSON
339
340       **Content-type:** ``application/json``
341
342       **Accept:** ``application/json``
343
344       **Authentication:** ``admin:admin``
345
346       .. code-block:: json
347
348          {
349              "node": [
350                  {
351                      "node-id": "device",
352                      "netconf-node-topology:host": "127.0.0.1",
353                      "netconf-node-topology:login-password-unencrypted": {
354                         "netconf-node-topology:password": "root",
355                         "netconf-node-topology:username": "root"
356                      },
357                      "netconf-node-topology:yang-module-capabilities": {
358                          "override": true,
359                          "capability": [
360                              "urn:ietf:params:xml:ns:yang:ietf-inet-types?module=ietf-inet-types&revision=2013-07-15"
361                          ]
362                      },
363                      "netconf-node-topology:port": 8305,
364                      "netconf-node-topology:tcp-only": false,
365                      "netconf-node-topology:keepalive-delay": 30
366                  }
367              ]
368          }
369
370 **Remember to also put the YANG schemas into the cache folder.**
371
372 .. note::
373
374     For putting multiple capabilities, you just need to replicate the
375     capability element inside yang-module-capability element.
376     Capability element is modeled as a leaf-list. With this
377     configuration, we would make the remote device report usage of
378     ietf-inet-types in the eyes of netconf-connector.
379
380 Connecting to a device supporting only NETCONF 1.0
381 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
382
383 OpenDaylight is schema-based distribution and heavily depends on YANG
384 models. However some legacy NETCONF devices are not schema-based and
385 implement just RFC 4741. This type of device does not utilize YANG
386 models internally and OpenDaylight does not know how to communicate
387 with such devices, how to validate data, or what the semantics of data
388 are.
389
390 NETCONF connector can communicate also with these devices, but the
391 trade-offs are worsened possibilities in utilization of NETCONF
392 mountpoints. Using RESTCONF with such devices is not supported. Also
393 communicating with schemaless devices from application code is slightly
394 different.
395
396 To connect to schemaless device, there is a optional configuration option
397 in netconf-node-topology model called schemaless. You have to set this
398 option to true.
399
400 Clustered NETCONF connector
401 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
402
403 To spawn NETCONF connectors that are cluster-aware you need to install
404 the ``odl-netconf-clustered-topology`` karaf feature.
405
406 .. warning::
407
408     The ``odl-netconf-topology`` and ``odl-netconf-clustered-topology``
409     features are considered **INCOMPATIBLE**. They both manage the same
410     space in the datastore and would issue conflicting writes if
411     installed together.
412
413 Configuration of clustered NETCONF connectors works the same as the
414 configuration through the topology model in the previous section.
415
416 When a new clustered connector is configured the configuration gets
417 distributed among the member nodes and a NETCONF connector is spawned on
418 each node. From these nodes a master is chosen which handles the schema
419 download from the device and all the communication with the device. You
420 will be able to read/write to/from the device from all slave nodes due
421 to the proxy data brokers implemented.
422
423 You can use the ``odl-netconf-clustered-topology`` feature in a single
424 node scenario as well but the code that uses akka will be used, so for a
425 scenario where only a single node is used, ``odl-netconf-topology``
426 might be preferred.
427
428 Netconf-connector utilization
429 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
430
431 Once the connector is up and running, users can utilize the new mount
432 point instance. By using RESTCONF or from their application code. This
433 chapter deals with using RESTCONF and more information for app
434 developers can be found in the developers guide or in the official
435 tutorial application **ncmount** that can be found in the coretutorials
436 project:
437
438 -  https://github.com/opendaylight/coretutorials/tree/master/ncmount
439
440 Reading data from the device
441 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
442
443 Just invoke (no body needed):
444
445 GET
446 http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=new-netconf-device/yang-ext:mount?content=nonconfig
447
448 This will return the entire content of operation datastore from the
449 device. To view just the configuration datastore, change **nonconfig**
450 in this URL to **config**.
451
452 Writing configuration data to the device
453 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
454
455 In general, you cannot simply write any data you want to the device. The
456 data have to conform to the YANG models implemented by the device. In
457 this example we are adding a new interface-configuration to the mounted
458 device (assuming the device supports Cisco-IOS-XR-ifmgr-cfg YANG model).
459 In fact this request comes from the tutorial dedicated to the
460 **ncmount** tutorial app.
461
462 POST
463 http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=new-netconf-device/yang-ext:mount/Cisco-IOS-XR-ifmgr-cfg:interface-configurations
464
465 ::
466
467     <interface-configuration xmlns="http://cisco.com/ns/yang/Cisco-IOS-XR-ifmgr-cfg">
468         <active>act</active>
469         <interface-name>mpls</interface-name>
470         <description>Interface description</description>
471         <bandwidth>32</bandwidth>
472         <link-status></link-status>
473     </interface-configuration>
474
475 Should return 200 response code with no body.
476
477 .. tip::
478
479     This call is transformed into a couple of NETCONF RPCs. Resulting
480     NETCONF RPCs that go directly to the device can be found in the
481     OpenDaylight logs after invoking ``log:set TRACE
482     org.opendaylight.controller.sal.connect.netconf`` in the Karaf
483     shell. Seeing the NETCONF RPCs might help with debugging.
484
485 This request is very similar to the one where we spawned a new netconf
486 device. That’s because we used the loopback netconf-connector to write
487 configuration data into config-subsystem datastore and config-subsystem
488 picked it up from there.
489
490 Invoking custom RPC
491 ^^^^^^^^^^^^^^^^^^^
492
493 Devices can implement any additional RPC and as long as it provides YANG
494 models for it, it can be invoked from OpenDaylight. Following example
495 shows how to invoke the get-schema RPC (get-schema is quite common among
496 netconf devices). Invoke:
497
498 POST
499 http://localhost:8181/rests/operations/network-topology:network-topology/topology=topology-netconf/node=new-netconf-device/yang-ext:mount/ietf-netconf-monitoring:get-schema
500
501 ::
502
503     <input xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring">
504       <identifier>ietf-yang-types</identifier>
505       <version>2013-07-15</version>
506     </input>
507
508 This call should fetch the source for ietf-yang-types YANG model from
509 the mounted device.
510
511 Receiving Netconf Device Notifications on a http client
512 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
513
514 Devices emit netconf alarms and notifications in certain situations, which can demand
515 attention from Device Administration. The notifications are received as Netconf messages on an
516 active Netconf session.
517
518 Opendaylight provides the way to stream the device notifications over a http session.
519
520 - Step 1: Mount the device (assume node name is test_device)
521
522 - Step 2: Wait for the device to be connected.
523
524 - Step 3: Create the Subscription for notification on the active session.
525
526  .. code-block::
527
528     POST
529     http://localhost:8181/rests/operations/network-topology:network-topology/topology=topology-netconf/node=test_device/yang-ext:mount/notifications:create-subscription
530     Content-Type: application/json
531     Accept: application/json
532
533  .. code-block:: json
534
535     {
536       "input": {
537         "stream": "NETCONF"
538        }
539     }
540
541 - Step 4: Create the http Stream for the events.
542
543 .. code-block::
544
545     POST
546     http://localhost:8181/rests/operations/odl-device-notification:subscribe-device-notification
547     Content-Type: application/json
548     Accept: application/json
549
550 .. code-block:: json
551
552     {
553       "input": {
554          "path":"/network-topology:network-topology/topology[topology-id='topology-netconf']/node[node-id='test_device']"
555       }
556     }
557
558 The response suggests the http url for reading the notifications.
559
560 .. code-block:: json
561
562     {
563        "odl-device-notification:output": {
564             "stream-path": "http://localhost:8181/rests/notif/test_device?notificationType=test_device"
565         }
566     }
567
568 - Step 5: User can access the url in the response and the notifications will be as follows.
569
570 .. code-block::
571
572     GET
573     http://localhost:8181/rests/notif/test_device?notificationType=test_device
574     Content-Type: application/xml
575     Accept: application/xml
576
577
578 .. code-block:: xml
579
580     : ping
581
582     : ping
583
584     : ping
585
586     : ping
587
588     : ping
589
590     data: <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2022-06-17T07:01:08.60228Z</eventTime><netconf-session-start xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications"><username>root</username><source-host>127.0.0.1</source-host><session-id>2</session-id></netconf-session-start></notification>
591
592     data: <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2022-06-17T07:01:12.458258Z</eventTime><netconf-session-end xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-notifications"><username>root</username><source-host>127.0.0.1</source-host><termination-reason>closed</termination-reason><session-id>2</session-id></netconf-session-end></notification>
593
594 Change event notification subscription tutorial
595 -----------------------------------------------
596
597 Subscribing to data change notifications makes it possible to obtain
598 notifications about data manipulation (insert, change, delete) which are
599 done on any specified **path** of any specified **datastore** with
600 specific **scope**. In following examples *{odlAddress}* is address of
601 server where ODL is running and *{odlPort}* is port on which
602 OpenDaylight is running. OpenDaylight offers two methods for receiving notifications:
603 Server-Sent Events (SSE) and WebSocket. SSE is the default notification mechanism used in OpenDaylight.
604
605 SSE notifications subscription process
606 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
607
608 In this section we will learn what steps need to be taken in order to
609 successfully subscribe to data change event notifications.
610
611 Create stream
612 ^^^^^^^^^^^^^
613
614 In order to use event notifications you first need to call RPC that
615 creates notification stream that you can later listen to. You need to
616 provide three parameters to this RPC:
617
618 -  **path**: data store path that you plan to listen to. You can
619    register listener on containers, lists and leaves.
620
621 -  **datastore**: data store type. *OPERATIONAL* or *CONFIGURATION*.
622
623 -  **scope**: Represents scope of data change. Possible options are:
624
625    -  BASE: only changes directly to the data tree node specified in the
626       path will be reported
627
628    -  ONE: changes to the node and to direct child nodes will be
629       reported
630
631    -  SUBTREE: changes anywhere in the subtree starting at the node will
632       be reported
633
634 The RPC to create the stream can be invoked via RESTCONF like this:
635
636 ::
637
638     OPERATION: POST
639     URI:  http://{odlAddress}:{odlPort}/rests/operations/sal-remote:create-data-change-event-subscription
640     HEADER: Content-Type=application/json
641             Accept=application/json
642
643 .. code-block:: json
644
645        {
646            "input": {
647                "path": "/toaster:toaster/toaster:toasterStatus",
648                "sal-remote-augment:datastore": "OPERATIONAL",
649                "sal-remote-augment:scope": "ONE"
650            }
651        }
652
653 The response should look something like this:
654
655 .. code-block:: json
656
657     {
658         "sal-remote:output": {
659             "stream-name": "data-change-event-subscription/toaster:toaster/toaster:toasterStatus/datastore=CONFIGURATION/scope=SUBTREE"
660         }
661     }
662
663 **stream-name** is important because you will need to use it when you
664 subscribe to the stream in the next step.
665
666 .. note::
667
668     Internally, this will create a new listener for *stream-name* if it
669     did not already exist.
670
671 Subscribe to stream
672 ^^^^^^^^^^^^^^^^^^^
673
674 In order to subscribe to stream and obtain SSE location you need
675 to call *GET* on your stream path. The URI should generally be
676 `http://{odlAddress}:{odlPort}/rests/data/ietf-restconf-monitoring:restconf-state/streams/stream/{streamName}`,
677 where *{streamName}* is the *stream-name* parameter contained in
678 response from *create-data-change-event-subscription* RPC from the
679 previous step.
680
681 ::
682
683    OPERATION: GET
684    URI: http://{odlAddress}:{odlPort}/rests/data/ietf-restconf-monitoring:restconf-state/streams/stream/data-change-event-subscription/toaster:toaster/datastore=CONFIGURATION/scope=SUBTREE
685
686 The subscription call may be modified with the following query parameters defined in the RESTCONF RFC:
687
688 -  `filter <https://www.rfc-editor.org/rfc/rfc8040#section-4.8.4>`__
689
690 -  `start-time <https://www.rfc-editor.org/rfc/rfc8040#section-4.8.7>`__
691
692 -  `end-time <https://www.rfc-editor.org/rfc/rfc8040#section-4.8.8>`__
693
694 In addition, the following ODL extension query parameter is supported:
695
696 :odl-leaf-nodes-only:
697   If this parameter is set to "true", create and update notifications will only
698   contain the leaf nodes modified instead of the entire subscription subtree.
699   This can help in reducing the size of the notifications.
700
701 :odl-skip-notification-data:
702   If this parameter is set to "true", create and update notifications will only
703   contain modified leaf nodes without data.
704   This can help in reducing the size of the notifications.
705
706 The response should look something like this:
707
708 .. code-block:: json
709
710     {
711         "subscribe-to-notification:location": "http://localhost:8181/rests/notif/data-change-event-subscription/network-topology:network-topology/datastore=CONFIGURATION/scope=SUBTREE"
712     }
713
714 .. note::
715
716     During this phase there is an internal check for to see if a
717     listener for the *stream-name* from the URI exists. If not, new a
718     new listener is registered with the DOM data broker.
719
720 Receive notifications
721 ^^^^^^^^^^^^^^^^^^^^^
722
723 Once you got SSE location you can now connect to it and
724 start receiving data change events. The request should look something like this:
725
726 ::
727
728     curl -v -X GET  http://localhost:8181/rests/notif/data-change-event-subscription/toaster:toaster/toasterStatus/datastore=OPERATIONAL/scope=ONE  -H "Content-Type: text/event-stream" -H "Authorization: Basic YWRtaW46YWRtaW4="
729
730
731 WebSocket notifications subscription process
732 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
733
734 Enabling WebSocket notifications in OpenDaylight requires a manual setup before starting the application.
735 The following steps can be followed to enable WebSocket notifications in OpenDaylight:
736
737 1. Open the file `org.opendaylight.restconf.nb.rfc8040.cfg`, at `etc/` folder inside your Karaf distribution. Or create in case it does not exist.
738 2. Locate the `use-sse` configuration parameter and change its value from `true` to `false`. Or add ``use-sse=false`` as new line in case this parameter is not present.
739 3. Save the changes made to the `org.opendaylight.restconf.nb.rfc8040.cfg` file.
740 4. Restart OpenDaylight if it is already running.
741
742 Once these steps are completed, WebSocket notifications will be enabled in OpenDaylight,
743 and they can be used for receiving notifications instead of SSE.
744
745 WebSocket Notifications subscription process is the same as SSE until you receive a location of WebSocket.
746 You can follow steps given above and after subscribing to a notification stream over WebSocket,
747 you will receive a response indicating that the subscription was successful:
748
749 .. code-block:: json
750
751     {
752         "subscribe-to-notification:location": "ws://localhost:8181/rests/notif/data-change-event-subscription/network-topology:network-topology/datastore=CONFIGURATION/scope=SUBTREE"
753     }
754
755 You can use this WebSocket to listen to data
756 change notifications. To listen to notifications you can use a
757 JavaScript client or if you are using chrome browser you can use the
758 `Simple WebSocket
759 Client <https://chrome.google.com/webstore/detail/simple-websocket-client/pfdhoblngboilpfeibdedpjgfnlcodoo>`__.
760
761 Also, for testing purposes, there is simple Java application named
762 WebSocketClient. The application is placed in the
763 */restconf/websocket-client* project. It accepts a WebSocket URI
764 as an input parameter. After starting the utility (WebSocketClient
765 class directly in Eclipse/InteliJ Idea) received notifications should be
766 displayed in console.
767
768 Notifications are always in XML format and look like this:
769
770 .. code-block:: xml
771
772     <notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
773         <eventTime>2014-09-11T09:58:23+02:00</eventTime>
774         <data-changed-notification xmlns="urn:opendaylight:params:xml:ns:yang:controller:md:sal:remote">
775             <data-change-event>
776                 <path xmlns:meae="http://netconfcentral.org/ns/toaster">/meae:toaster</path>
777                 <operation>updated</operation>
778                 <data>
779                    <!-- updated data -->
780                 </data>
781             </data-change-event>
782         </data-changed-notification>
783     </notification>
784
785 Example use case
786 ~~~~~~~~~~~~~~~~
787
788 The typical use case is listening to data change events to update web
789 page data in real time. In this tutorial we will be using toaster as the
790 base.
791
792 When you call *make-toast* RPC, it sets *toasterStatus* to "down" to
793 reflect that the toaster is busy making toast. When it finishes,
794 *toasterStatus* is set to "up" again. We will listen to these toaster
795 status changes in data store and will reflect it on our web page in
796 real-time thanks to WebSocket data change notification.
797
798 Simple javascript client implementation
799 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
800
801 We will create a simple JavaScript web application that will listen for
802 updates on *toasterStatus* leaf and update some elements of our web page
803 according to the new toaster status state.
804
805 Create stream
806 ^^^^^^^^^^^^^
807
808 First you need to create stream that you are planning to subscribe to.
809 This can be achieved by invoking "create-data-change-event-subscription"
810 RPC on RESTCONF via AJAX request. You need to provide data store
811 **path** that you plan to listen on, **data store type** and **scope**.
812 If the request is successful you can extract the **stream-name** from
813 the response and use that to subscribe to the newly created stream. The
814 *{username}* and *{password}* fields represent the credentials that you
815 use to connect to OpenDaylight via RESTCONF:
816
817 .. note::
818
819     The default user name and password are "admin".
820
821 .. code-block:: javascript
822
823     function createStream() {
824         $.ajax(
825             {
826                 url: 'http://{odlAddress}:{odlPort}/rests/operations/sal-remote:create-data-change-event-subscription',
827                 type: 'POST',
828                 headers: {
829                   'Authorization': 'Basic ' + btoa('{username}:{password}'),
830                   'Content-Type': 'application/json'
831                 },
832                 data: JSON.stringify(
833                     {
834                         'input': {
835                             'path': '/toaster:toaster/toaster:toasterStatus',
836                             'sal-remote-augment:datastore': 'OPERATIONAL',
837                             'sal-remote-augment:scope': 'ONE'
838                         }
839                     }
840                 )
841             }).done(function (data) {
842                 // this function will be called when ajax call is executed successfully
843                 subscribeToStream(data.output['stream-name']);
844             }).fail(function (data) {
845                 // this function will be called when ajax call fails
846                 console.log("Create stream call unsuccessful");
847             })
848     }
849
850 Subscribe to stream
851 ^^^^^^^^^^^^^^^^^^^
852
853 The Next step is to subscribe to the stream. To subscribe to the stream
854 you need to call *GET* on
855 *http://{odlAddress}:{odlPort}/rests/data/ietf-restconf-monitoring:restconf-state/streams/stream/{stream-name}*.
856 If the call is successful, you get WebSocket address for this stream in
857 **Location** parameter inside response header. You can get response
858 header by calling *getResponseHeader(\ *Location*)* on HttpRequest
859 object inside *done()* function call:
860
861 .. code-block:: javascript
862
863     function subscribeToStream(streamName) {
864         $.ajax(
865             {
866                 url: 'http://{odlAddress}:{odlPort}/rests/data/ietf-restconf-monitoring:restconf-state/streams/stream/' + streamName;
867                 type: 'GET',
868                 headers: {
869                   'Authorization': 'Basic ' + btoa('{username}:{password}'),
870                 }
871             }
872         ).done(function (data, textStatus, httpReq) {
873             // we need function that has http request object parameter in order to access response headers.
874             listenToNotifications(httpReq.getResponseHeader('Location'));
875         }).fail(function (data) {
876             console.log("Subscribe to stream call unsuccessful");
877         });
878     }
879
880 Receive notifications
881 ^^^^^^^^^^^^^^^^^^^^^
882
883 Once you have WebSocket server location you can now connect to it and
884 start receiving data change events. You need to define functions that
885 will handle events on WebSocket. In order to process incoming events
886 from OpenDaylight you need to provide a function that will handle
887 *onmessage* events. The function must have one parameter that represents
888 the received event object. The event data will be stored in
889 *event.data*. The data will be in an XML format that you can then easily
890 parse using jQuery.
891
892 .. code-block:: javascript
893
894     function listenToNotifications(socketLocation) {
895         try {
896             var notificatinSocket = new WebSocket(socketLocation);
897
898             notificatinSocket.onmessage = function (event) {
899                 // we process our received event here
900                 console.log('Received toaster data change event.');
901                 $($.parseXML(event.data)).find('data-change-event').each(
902                     function (index) {
903                         var operation = $(this).find('operation').text();
904                         if (operation == 'updated') {
905                             // toaster status was updated so we call function that gets the value of toasterStatus leaf
906                             updateToasterStatus();
907                             return false;
908                         }
909                     }
910                 );
911             }
912             notificatinSocket.onerror = function (error) {
913                 console.log("Socket error: " + error);
914             }
915             notificatinSocket.onopen = function (event) {
916                 console.log("Socket connection opened.");
917             }
918             notificatinSocket.onclose = function (event) {
919                 console.log("Socket connection closed.");
920             }
921             // if there is a problem on socket creation we get exception (i.e. when socket address is incorrect)
922         } catch(e) {
923             alert("Error when creating WebSocket" + e );
924         }
925     }
926
927 The *updateToasterStatus()* function represents function that calls
928 *GET* on the path that was modified and sets toaster status in some web
929 page element according to received data. After the WebSocket connection
930 has been established you can test events by calling make-toast RPC via
931 RESTCONF.
932
933 .. note::
934
935     for more information about WebSockets in JavaScript visit `Writing
936     WebSocket client
937     applications <https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_client_applications>`__
938
939 Netconf-connector + Netopeer
940 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
941
942 `Netopeer <https://github.com/cesnet/netopeer>`__ (an open-source
943 NETCONF server) can be used for testing/exploring NETCONF southbound in
944 OpenDaylight.
945
946 Netopeer installation
947 ^^^^^^^^^^^^^^^^^^^^^
948
949 A `Docker <https://www.docker.com/>`__ container with netopeer will be
950 used in this guide. To install Docker and start the `netopeer
951 image <https://hub.docker.com/r/sysrepo/sysrepo-netopeer2>`__ perform
952 following steps:
953
954 1. Install docker https://docs.docker.com/get-started/
955
956 2. Start the netopeer image:
957
958    ::
959
960        docker run -it --name sysrepo -p 830:830 --rm sysrepo/sysrepo-netopeer2:latest
961
962 3. Verify netopeer is running by invoking (netopeer should send its
963    HELLO message right away:
964
965    ::
966
967        ssh root@localhost -p 830 -s netconf
968        (password root)
969
970 Mounting netopeer NETCONF server
971 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
972
973 Preconditions:
974
975 -  OpenDaylight is started with features ``odl-restconf-all`` and
976    ``odl-netconf-connector-all``.
977
978 -  Netopeer is up and running in docker
979
980 Now just follow the section: `Spawning new NETCONF connectors`_.
981 In the payload change the:
982
983 -  name, e.g., to netopeer
984
985 -  username/password to your system credentials
986
987 -  ip to localhost
988
989 -  port to 1831.
990
991 After netopeer is mounted successfully, its configuration can be read
992 using RESTCONF by invoking:
993
994 GET
995 http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=netopeer/yang-ext:mount?content:config
996
997 Northbound (NETCONF servers)
998 ----------------------------
999
1000 OpenDaylight provides 2 types of NETCONF servers:
1001
1002 -  **NETCONF server for config-subsystem (listening by default on port
1003    1830)**
1004
1005    -  Serves as a default interface for config-subsystem and allows
1006       users to spawn/reconfigure/destroy modules (or applications) in
1007       OpenDaylight
1008
1009 -  **NETCONF server for MD-SAL (listening by default on port 2830)**
1010
1011    -  Serves as an alternative interface for MD-SAL (besides RESTCONF)
1012       and allows users to read/write data from MD-SAL’s datastore and to
1013       invoke its rpcs (NETCONF notifications are not available in the
1014       Boron release of OpenDaylight)
1015
1016 .. note::
1017
1018     The reason for having 2 NETCONF servers is that config-subsystem and
1019     MD-SAL are 2 different components of OpenDaylight and require
1020     different approaches for NETCONF message handling and data
1021     translation. These 2 components will probably merge in the future.
1022
1023 .. note::
1024
1025     Since Nitrogen release, there has been performance regression in NETCONF
1026     servers accepting SSH connections. While opening a connection takes
1027     less than 10 seconds on Carbon, on Nitrogen time can increase up to
1028     60 seconds. Please see https://jira.opendaylight.org/browse/ODLPARENT-112
1029
1030 NETCONF server for config-subsystem
1031 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1032
1033 This NETCONF server is the primary interface for config-subsystem. It
1034 allows the users to interact with config-subsystem in a standardized
1035 NETCONF manner.
1036
1037 In terms of RFCs, these are supported:
1038
1039 -  `RFC-6241 <https://www.rfc-editor.org/rfc/rfc6241>`__
1040
1041 -  `RFC-5277 <https://www.rfc-editor.org/rfc/rfc5277>`__
1042
1043 -  `RFC-6470 <https://www.rfc-editor.org/rfc/rfc6470>`__
1044
1045    -  (partially, only the schema-change notification is available in
1046       Boron release)
1047
1048 -  `RFC-6022 <https://www.rfc-editor.org/rfc/rfc6022>`__
1049
1050 For regular users it is recommended to use RESTCONF + the
1051 controller-config loopback mountpoint instead of using pure NETCONF. How
1052 to do that is specific for each component/module/application in
1053 OpenDaylight and can be found in their dedicated user guides.
1054
1055 NETCONF server for MD-SAL
1056 ~~~~~~~~~~~~~~~~~~~~~~~~~
1057
1058 This NETCONF server is just a generic interface to MD-SAL in
1059 OpenDaylight. It uses the standard MD-SAL APIs and serves as an
1060 alternative to RESTCONF. It is fully model-driven and supports any data
1061 and rpcs that are supported by MD-SAL.
1062
1063 In terms of RFCs, these are supported:
1064
1065 -  `RFC-6241 <https://www.rfc-editor.org/rfc/rfc6241>`__
1066
1067 -  `RFC-6022 <https://www.rfc-editor.org/rfc/rfc6022>`__
1068
1069 -  `RFC-7895 <https://www.rfc-editor.org/rfc/rfc7895>`__
1070
1071 Notifications over NETCONF are not supported in the Boron release.
1072
1073 .. tip::
1074
1075     Install NETCONF northbound for MD-SAL by installing feature:
1076     ``odl-netconf-mdsal`` in karaf. Default binding port is **2830**.
1077
1078 Configuration
1079 ^^^^^^^^^^^^^
1080
1081 The default configuration can be found in file: *08-netconf-mdsal.xml*.
1082 The file contains the configuration for all necessary dependencies and a
1083 single SSH endpoint starting on port 2830. There is also a (by default
1084 disabled) TCP endpoint. It is possible to start multiple endpoints at
1085 the same time either in the initial configuration file or while
1086 OpenDaylight is running.
1087
1088 The credentials for SSH endpoint can also be configured here, the
1089 defaults are admin/admin. Credentials in the SSH endpoint are not yet
1090 managed by the centralized AAA component and have to be configured
1091 separately.
1092
1093 Verifying MD-SAL’s NETCONF server
1094 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1095
1096 After the NETCONF server is available it can be examined by a command
1097 line ssh tool:
1098
1099 ::
1100
1101     ssh admin@localhost -p 2830 -s netconf
1102
1103 The server will respond by sending its HELLO message and can be used as
1104 a regular NETCONF server from then on.
1105
1106 Mounting the MD-SAL’s NETCONF server
1107 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1108
1109 To perform this operation, just spawn a new netconf-connector as
1110 described in `Spawning new NETCONF connectors`_. Just change the ip to
1111 "127.0.0.1" port to "2830" and its name to "controller-mdsal".
1112
1113 Now the MD-SAL’s datastore can be read over RESTCONF via NETCONF by
1114 invoking:
1115
1116 GET
1117 http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=controller-mdsal/yang-ext:mount?content:nonconfig
1118
1119 .. note::
1120
1121     This might not seem very useful, since MD-SAL can be accessed
1122     directly from RESTCONF or from Application code, but the same method
1123     can be used to mount and control other OpenDaylight instances by the
1124     "master OpenDaylight".
1125
1126 NETCONF stress/performance measuring tool
1127 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1128
1129 This is basically a NETCONF client that puts NETCONF servers under heavy
1130 load of NETCONF RPCs and measures the time until a configurable amount
1131 of them is processed.
1132
1133 RESTCONF stress-performance measuring tool
1134 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1135
1136 Very similar to NETCONF stress tool with the difference of using
1137 RESTCONF protocol instead of NETCONF.
1138
1139 YANGLIB remote repository
1140 -------------------------
1141
1142 There are scenarios in NETCONF deployment, that require for a centralized
1143 YANG models repository. YANGLIB plugin provides such remote repository.
1144
1145 To start this plugin, you have to install odl-yanglib feature. Then you
1146 have to configure YANGLIB either through RESTCONF or NETCONF. We will
1147 show how to configure YANGLIB through RESTCONF.
1148
1149 YANGLIB configuration
1150 ~~~~~~~~~~~~~~~~~~~~~
1151 YANGLIB configuration works through OSGi Configuration Admin interface, in the
1152 ``org.opendaylight.netconf.yanglib`` configuration PID. There are three tuneables you can
1153 set:
1154
1155 * ``cache-folder``, which defaults to ``cache/schema``
1156 * ``binding-address``, which defaults to ``localhost``
1157 * ``binding-port``, which defaults to ``8181``
1158
1159 In order to change these settings, you can either modify the corresponding configuration
1160 file, ``etc/org.opendaylight.netconf.yanglib.cfg``, for example:
1161
1162 ::
1163     cache-folder = cache/newSchema
1164     binding-address = localhost
1165     binding-port = 8181
1166
1167 Or use Karaf CLI:
1168
1169 ::
1170     opendaylight-user@root>config:edit org.opendaylight.netconf.yanglib
1171     opendaylight-user@root>config:property-set cache-folder cache/newSchema
1172     opendaylight-user@root>config:property-set binding-address localhost
1173     opendaylight-user@root>config:property-set binding-port 8181
1174     opendaylight-user@root>config:update
1175
1176 This YANGLIB takes all YANG sources from the configured sources folder and
1177 for each generates URL in form:
1178
1179 ::
1180
1181     http://localhost:8181/yanglib/schemas/{modelName}/{revision}
1182
1183 On this URL will be hosted YANG source for particular module.
1184
1185 YANGLIB instance also writes this URL along with source identifier to
1186 ietf-netconf-yang-library/modules-state/module list.
1187
1188 Netconf-connector with YANG library as fallback
1189 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1190
1191 There is an optional configuration in netconf-connector called
1192 yang-library. You can specify YANG library to be plugged as additional
1193 source provider into the mount's schema repository. Since YANGLIB
1194 plugin is advertising provided modules through yang-library model, we
1195 can use it in mount point's configuration as YANG library.  To do this,
1196 we need to modify the configuration of netconf-connector by adding this
1197 XML
1198
1199 ::
1200
1201     <yang-library xmlns="urn:opendaylight:netconf-node-topology">
1202       <yang-library-url xmlns="urn:opendaylight:netconf-node-topology">http://localhost:8181/rests/data/ietf-yang-library:modules-state</yang-library-url>
1203       <username xmlns="urn:opendaylight:netconf-node-topology">admin</username>
1204       <password xmlns="urn:opendaylight:netconf-node-topology">admin</password>
1205     </yang-library>
1206
1207 This will register YANGLIB provided sources as a fallback schemas for
1208 particular mount point.
1209
1210 Restconf northbound configuration
1211 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1212 Restconf-nb configuration works through OSGi Configuration Admin interface, in the
1213 ``org.opendaylight.restconf.nb.rfc8040`` configuration PID. There are six tuneables you can
1214 set:
1215
1216 * ``maximum-fragment-length``, which defaults to ``0``
1217 * ``heartbeat-interval``, which defaults to ``10000``
1218 * ``idle-timeout``, which defaults to ``30000``
1219 * ``ping-executor-name-prefix``, which defaults to ``ping-executor``
1220 * ``max-thread-count``, which defaults to ``1``
1221 * ``use-sse``, which defaults to ``true``
1222
1223 *maximum-fragment-length* â€” Maximum web-socket fragment length in number of Unicode code units (characters)
1224 (exceeded message length leads to fragmentation of messages)
1225
1226 *heartbeat-interval* â€” Interval in milliseconds between sending of ping control frames.
1227
1228 *idle-timeout* â€” Maximum idle time of web-socket session before the session is closed (milliseconds).
1229
1230 *ping-executor-name-prefix* â€” Name of thread group Ping Executor will be run with.
1231
1232 *max-thread-count* â€” Number of threads Ping Executor will be run with.
1233
1234 *use-sse* â€” In case of ``true`` access to notification streams will be via Server-Sent Events.
1235 Otherwise web-socket servlet will be initialized.
1236
1237 In order to change these settings, you can either modify the corresponding configuration
1238 file, ``org.opendaylight.restconf.nb.rfc8040.cfg``, for example:
1239
1240 ::
1241
1242     maximum-fragment-length=0
1243     heartbeat-interval=10000
1244     idle-timeout=30000
1245     ping-executor-name-prefix="ping-executor"
1246     max-thread-count=1
1247     use-sse=true
1248
1249 Or use Karaf CLI:
1250
1251 ::
1252
1253     opendaylight-user@root>config:edit org.opendaylight.restconf.nb.rfc8040
1254     opendaylight-user@root>config:property-set maximum-fragment_length 0
1255     opendaylight-user@root>config:property-set heartbeat-interval 10000
1256     opendaylight-user@root>config:property-set idle-timeout 30000
1257     opendaylight-user@root>config:property-set ping-executor-name-prefix "ping-executor"
1258     opendaylight-user@root>config:property-set max-thread-count 1
1259     opendaylight-user@root>config:property-set use-sse true
1260     opendaylight-user@root>config:update
1261
1262 NETCONF Call Home
1263 -----------------
1264
1265 Call Home Installation
1266 ~~~~~~~~~~~~~~~~~~~~~~
1267
1268 ODL Call-Home server is installed in Karaf by installing karaf feature
1269 ``odl-netconf-callhome-ssh``. RESTCONF feature is recommended for
1270 configuring Call Home & testing its functionality.
1271
1272 ::
1273
1274   feature:install odl-netconf-callhome-ssh
1275
1276
1277 .. note::
1278
1279     In order to test Call Home functionality we recommend Netopeer or
1280     Netopeer2. See `Netopeer Call Home <https://github.com/CESNET/netopeer/wiki/CallHome>`__
1281     or `Netopeer2 <https://github.com/CESNET/netopeer2>`__ to learn how to
1282     enable call-home on Netopeer.
1283
1284 Northbound Call-Home API
1285 ~~~~~~~~~~~~~~~~~~~~~~~~
1286
1287 The northbound Call Home API is used for administering the Call-Home Server. The
1288 following describes this configuration.
1289
1290 Global Configuration
1291 ^^^^^^^^^^^^^^^^^^^^
1292
1293 .. important::
1294   The global configuration is not a part of the `RFC 8071
1295   <https://www.rfc-editor.org/rfc/rfc8071>`__ and, therefore, subject to change.
1296
1297 Configuring global credentials
1298 ''''''''''''''''''''''''''''''
1299
1300 The ODL Call-Home server allows user to configure global credentials, which will be
1301 used for devices connecting over SSH transport protocol that do not have
1302 device-specific credentials configured.
1303
1304 This is done by creating
1305 ``/odl-netconf-callhome-server:netconf-callhome-server/global/credentials``
1306 with username and passwords specified.
1307
1308 *Configuring global username & passwords to try*
1309
1310 .. code-block::
1311
1312     PUT HTTP/1.1
1313     /rests/data/odl-netconf-callhome-server:netconf-callhome-server/global/credentials
1314     Content-Type: application/json
1315     Accept: application/json
1316
1317 .. code-block:: json
1318
1319     {
1320       "credentials":
1321       {
1322         "username": "example",
1323         "passwords": [ "first-password-to-try", "second-password-to-try" ]
1324       }
1325     }
1326
1327 Configuring to accept any ssh server key using global credentials
1328 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
1329
1330 By default Netconf Call-Home Server accepts only incoming connections
1331 from allowed devices
1332 ``/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices``,
1333 if user desires to allow all incoming connections, it is possible to set
1334 ``accept-all-ssh-keys`` to ``true`` in
1335 ``/odl-netconf-callhome-server:netconf-callhome-server/global``.
1336
1337 The name of these devices in ``netconf-topology`` will be in format
1338 ``ip-address:port``. For naming devices see Device-Specific
1339 Configuration.
1340
1341 *Allowing unknown devices to connect*
1342
1343 This is a debug feature and should not be used in production. Besides being an obvious
1344 security issue, this also causes the Call-Home Server to drastically increase its output
1345 to the log.
1346
1347 .. code-block::
1348
1349     PUT HTTP/1.1
1350     /rests/data/odl-netconf-callhome-server:netconf-callhome-server/global/accept-all-ssh-keys
1351     Content-Type: application/json
1352     Accept: application/json
1353
1354 .. code-block:: json
1355
1356     {
1357         "accept-all-ssh-keys": "true"
1358     }
1359
1360 Device-Specific Configuration
1361 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1362
1363 Netconf Call Home server supports both of the secure transports used
1364 by the Network Configuration Protocol (NETCONF) - Secure Shell (SSH),
1365 and Transport Layer Security (TLS).
1366
1367 Configure device to connect over SSH protocol
1368 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1369
1370 Netconf Call Home Server uses device provided SSH server key (host key)
1371 to identify device. The pairing of name and server key is configured in
1372 ``/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices``.
1373 This list is colloquially called a allowlist.
1374
1375 If the Call-Home Server finds the SSH host key in the allowlist, it continues
1376 to negotiate a NETCONF connection over an SSH session. If the SSH host key is
1377 not found, the connection between the Call Home server and the device is dropped
1378 immediately. In either case, the device that connects to the Call home server
1379 leaves a record of its presence in the operational store.
1380
1381 Configuring Device with Device-specific Credentials
1382 '''''''''''''''''''''''''''''''''''''''''''''''''''
1383
1384 Adding specific device to the allowed list is done by creating
1385 ``/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device={device}``
1386 with device-id and connection parameters inside the ssh-client-params container.
1387
1388 *Configuring Device with Credentials*
1389
1390 .. code-block::
1391
1392     PUT HTTP/1.1
1393     /rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=example
1394     Content-Type: application/json
1395     Accept: application/json
1396
1397 .. code-block:: json
1398
1399     {
1400       "device": {
1401         "unique-id": "example",
1402         "ssh-client-params": {
1403           "credentials": {
1404             "username": "example",
1405             "passwords": [ "password" ]
1406           },
1407           "host-key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDHoH1jMjltOJnCt999uaSfc48ySutaD3ISJ9fSECe1Spdq9o9mxj0kBTTTq+2V8hPspuW75DNgN+V/rgJeoUewWwCAasRx9X4eTcRrJrwOQKzb5Fk+UKgQmenZ5uhLAefi2qXX/agFCtZi99vw+jHXZStfHm9TZCAf2zi+HIBzoVksSNJD0VvPo66EAvLn5qKWQD4AdpQQbKqXRf5/W8diPySbYdvOP2/7HFhDukW8yV/7ZtcywFUIu3gdXsrzwMnTqnATSLPPuckoi0V2jd8dQvEcu1DY+rRqmqu0tEkFBurlRZDf1yhNzq5xWY3OXcjgDGN+RxwuWQK3cRimcosH"
1408         }
1409       }
1410     }
1411
1412 Configuring Device with Global Credentials
1413 '''''''''''''''''''''''''''''''''''''''''''''''''''
1414
1415 It is possible to omit ``username`` and ``password`` for ssh-client-params,
1416 in such case values from global credentials will be used.
1417
1418 *Example of configuring device*
1419
1420 .. code-block::
1421
1422     PUT HTTP/1.1
1423     /rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=example
1424     Content-Type: application/json
1425     Accept: application/json
1426
1427 .. code-block:: json
1428
1429     {
1430       "device": {
1431         "unique-id": "example",
1432         "ssh-client-params": {
1433           "host-key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDHoH1jMjltOJnCt999uaSfc48ySutaD3ISJ9fSECe1Spdq9o9mxj0kBTTTq+2V8hPspuW75DNgN+V/rgJeoUewWwCAasRx9X4eTcRrJrwOQKzb5Fk+UKgQmenZ5uhLAefi2qXX/agFCtZi99vw+jHXZStfHm9TZCAf2zi+HIBzoVksSNJD0VvPo66EAvLn5qKWQD4AdpQQbKqXRf5/W8diPySbYdvOP2/7HFhDukW8yV/7ZtcywFUIu3gdXsrzwMnTqnATSLPPuckoi0V2jd8dQvEcu1DY+rRqmqu0tEkFBurlRZDf1yhNzq5xWY3OXcjgDGN+RxwuWQK3cRimcosH"
1434         }
1435       }
1436     }
1437
1438 Deprecated configuration models for devices accessed with SSH protocol
1439 ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
1440
1441 With `RFC 8071 <https://www.rfc-editor.org/rfc/rfc8071>`__ alignment and adding
1442 support for TLS transport following configuration models have been marked
1443 deprecated.
1444
1445 Configuring Device with Global Credentials
1446 '''''''''''''''''''''''''''''''''''''''''''''''''''
1447
1448 *Example of configuring device*
1449
1450 .. code-block::
1451
1452     PUT HTTP/1.1
1453     /rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=example
1454     Content-Type: application/json
1455     Accept: application/json
1456
1457 .. code-block:: json
1458
1459     {
1460       "device": {
1461         "unique-id": "example",
1462         "ssh-host-key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDHoH1jMjltOJnCt999uaSfc48ySutaD3ISJ9fSECe1Spdq9o9mxj0kBTTTq+2V8hPspuW75DNgN+V/rgJeoUewWwCAasRx9X4eTcRrJrwOQKzb5Fk+UKgQmenZ5uhLAefi2qXX/agFCtZi99vw+jHXZStfHm9TZCAf2zi+HIBzoVksSNJD0VvPo66EAvLn5qKWQD4AdpQQbKqXRf5/W8diPySbYdvOP2/7HFhDukW8yV/7ZtcywFUIu3gdXsrzwMnTqnATSLPPuckoi0V2jd8dQvEcu1DY+rRqmqu0tEkFBurlRZDf1yhNzq5xWY3OXcjgDGN+RxwuWQK3cRimcosH"
1463       }
1464     }
1465
1466 Configuring Device with Device-specific Credentials
1467 '''''''''''''''''''''''''''''''''''''''''''''''''''
1468
1469 Call Home Server also allows the configuration of credentials per device basis.
1470 This is done by introducing ``credentials`` container into the
1471 device-specific configuration. Format is same as in global credentials.
1472
1473 *Configuring Device with Credentials*
1474
1475 .. code-block::
1476
1477     PUT HTTP/1.1
1478     /rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=example
1479     Content-Type: application/json
1480     Accept: application/json
1481
1482 .. code-block:: json
1483
1484     {
1485       "device": {
1486         "unique-id": "example",
1487         "credentials": {
1488           "username": "example",
1489           "passwords": [ "password" ]
1490         },
1491         "ssh-host-key": "AAAAB3NzaC1yc2EAAAADAQABAAABAQDHoH1jMjltOJnCt999uaSfc48ySutaD3ISJ9fSECe1Spdq9o9mxj0kBTTTq+2V8hPspuW75DNgN+V/rgJeoUewWwCAasRx9X4eTcRrJrwOQKzb5Fk+UKgQmenZ5uhLAefi2qXX/agFCtZi99vw+jHXZStfHm9TZCAf2zi+HIBzoVksSNJD0VvPo66EAvLn5qKWQD4AdpQQbKqXRf5/W8diPySbYdvOP2/7HFhDukW8yV/7ZtcywFUIu3gdXsrzwMnTqnATSLPPuckoi0V2jd8dQvEcu1DY+rRqmqu0tEkFBurlRZDf1yhNzq5xWY3OXcjgDGN+RxwuWQK3cRimcosH"
1492       }
1493     }
1494
1495 Configure device to connect over TLS protocol
1496 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1497
1498 Netconf Call Home Server allows devices to use TLS transport protocol to
1499 establish a connection towards the NETCONF device. This communication
1500 requires proper setup to make two-way TLS authentication possible for client
1501 and server.
1502
1503 The initial step is to configure certificates and keys for two-way TLS by
1504 storing them within the netconf-keystore.
1505
1506 *Adding a client private key credential to the netconf-keystore*
1507
1508 .. code-block::
1509
1510     POST HTTP/1.1
1511     /rests/operations/netconf-keystore:add-keystore-entry
1512     Content-Type: application/json
1513     Accept: application/json
1514
1515 .. code-block:: json
1516
1517   {
1518     "input": {
1519       "key-credential": [
1520         {
1521           "key-id": "example-client-key-id",
1522           "private-key": "base64encoded-private-key",
1523           "passphrase": "passphrase"
1524         }
1525       ]
1526     }
1527   }
1528
1529 *Associate a private key with a client and CA certificates chain*
1530
1531 .. code-block::
1532
1533     POST HTTP/1.1
1534     /rests/operations/netconf-keystore:add-private-key
1535     Content-Type: application/json
1536     Accept: application/json
1537
1538 .. code-block:: json
1539
1540   {
1541     "input": {
1542       "private-key": [
1543         {
1544           "name": "example-client-key-id",
1545           "data": "key-data",
1546           "certificate-chain": [
1547             "certificate-data"
1548           ]
1549         }
1550       ]
1551     }
1552   }
1553
1554 *Add a list of trusted CA and server certificates*
1555
1556 .. code-block::
1557
1558     POST HTTP/1.1
1559     /rests/operations/netconf-keystore:add-trusted-certificate
1560     Content-Type: application/json
1561     Accept: application/json
1562
1563 .. code-block:: json
1564
1565   {
1566     "input": {
1567       "trusted-certificate": [
1568         {
1569           "name": "example-ca-certificate",
1570           "certificate": "ca-certificate-data"
1571         },
1572         {
1573           "name": "example-server-certificate",
1574           "certificate": "server-certificate-data"
1575         }
1576       ]
1577     }
1578   }
1579
1580 In a second step, it is required to create an allowed device associated with
1581 a server certificate and client key. The server certificate will be used to
1582 identify and pin the NETCONF device during SSL handshake and should be unique
1583 among the allowed devices.
1584
1585 *Add device configuration for TLS protocol to allowed devices list*
1586
1587 .. code-block::
1588
1589     PUT HTTP/1.1
1590     /rests/data/odl-netconf-callhome-server:netconf-callhome-server/allowed-devices/device=example-device
1591     Content-Type: application/json
1592     Accept: application/json
1593
1594 .. code-block:: json
1595
1596   {
1597     "device": {
1598       "unique-id": "example-device",
1599       "tls-client-params": {
1600         "key-id": "example-client-key-id",
1601         "certificate-id": "example-server-certificate"
1602       }
1603     }
1604   }
1605
1606 Operational Status
1607 ^^^^^^^^^^^^^^^^^^
1608
1609 Once an entry is made on the config side of "allowed-devices", the Call-Home Server will
1610 populate a corresponding operational device that is the same as the config device but
1611 has an additional status. By default, this status is *DISCONNECTED*. Once a device calls
1612 home, this status will change to one of:
1613
1614 *CONNECTED* â€” The device is currently connected and the NETCONF mount is available for network
1615 management.
1616
1617 *FAILED_AUTH_FAILURE* â€” The last attempted connection was unsuccessful because the Call-Home
1618 Server was unable to provide the acceptable credentials of the device. The device is also
1619 disconnected and not available for network management.
1620
1621 *FAILED_NOT_ALLOWED* â€” The last attempted connection was unsuccessful because the device was
1622 not recognized as an acceptable device. The device is also disconnected and not available for
1623 network management.
1624
1625 *FAILED* â€” The last attempted connection was unsuccessful for a reason other than not
1626 allowed to connect or incorrect client credentials. The device is also disconnected and not
1627 available for network management.
1628
1629 *DISCONNECTED* â€” The device is currently disconnected.
1630
1631 Rogue Devices
1632 '''''''''''''
1633
1634 Devices that are not on the allowlist might try to connect to the Call-Home Server. In
1635 these cases, the server will keep a record by instantiating an operational device. There
1636 will be no corresponding config device for these rogues. They can be identified readily
1637 because their device id, rather than being user-supplied, will be of the form
1638 "address:port". Note that if a device calls back multiple times, there will only be
1639 a single operatinal entry (even if the port changes); these devices are recognized by
1640 their unique host key.
1641
1642 Southbound Call-Home API
1643 ~~~~~~~~~~~~~~~~~~~~~~~~
1644
1645 The Call-Home Server listens for incoming TCP connections and assumes that the other side of
1646 the connection is a device calling home via a NETCONF connection with SSH for
1647 management. The server uses port 4334 by default and this can be configured via a
1648 blueprint configuration file.
1649
1650 The device **must** initiate the connection and the server will not try to re-establish the
1651 connection in case of a drop. By requirement, the server cannot assume it has connectivity
1652 to the device due to NAT or firewalls among others.
1653
1654 Reading data with selected fields
1655 ---------------------------------
1656
1657 Overview
1658 ~~~~~~~~
1659
1660 If user would like to read only selected fields from a NETCONF device, it is possible to use
1661 the fields query parameter that is described by RFC-8040. RESTCONF parses content of query
1662 parameter into format that is accepted by NETCONF subtree filtering - filtering of data is done
1663 on NETCONF server, not on NETCONF client side. This approach optimizes network traffic load,
1664 because data in which user doesn't have interest, is not transferred over network.
1665
1666 Next advantages:
1667
1668 * using single RESTCONF request and single NETCONF RPC for reading multiple subtrees
1669 * possibility to read only selected fields under list node across multiple hierarchies
1670   (it cannot be done without proper selection API)
1671
1672 .. note::
1673
1674   More information about fields query parameter: `RFC 8071 <https://www.rfc-editor.org/rfc/rfc8040#section-4.8.3>`__
1675
1676 Preparation of data
1677 ~~~~~~~~~~~~~~~~~~~
1678
1679 For demonstration, we will define next YANG model:
1680
1681 ::
1682
1683     module test-module {
1684         yang-version 1.1;
1685         namespace "urn:opendaylight:test-module";
1686         prefix "tm";
1687         revision "2023-02-16";
1688
1689         container root {
1690             container simple-root {
1691                 leaf leaf-a {
1692                     type string;
1693                 }
1694                 leaf leaf-b {
1695                     type string;
1696                 }
1697                 leaf-list ll {
1698                     type string;
1699                 }
1700                 container nested {
1701                     leaf sample-x {
1702                         type boolean;
1703                     }
1704                     leaf sample-y {
1705                         type boolean;
1706                     }
1707                 }
1708             }
1709
1710             container list-root {
1711                 leaf branch-ab {
1712                     type int32;
1713                 }
1714                 list top-list {
1715                     key "key-1 key-2";
1716                     ordered-by user;
1717                     leaf key-1 {
1718                         type string;
1719                     }
1720                     leaf key-2 {
1721                         type string;
1722                     }
1723                     container next-data {
1724                         leaf switch-1 {
1725                             type empty;
1726                         }
1727                         leaf switch-2 {
1728                             type empty;
1729                         }
1730                     }
1731                     list nested-list {
1732                         key "identifier";
1733                         leaf identifier {
1734                             type string;
1735                         }
1736                         leaf foo {
1737                             type int32;
1738                         }
1739                     }
1740                 }
1741             }
1742         }
1743     }
1744
1745 Follow the :doc:`testtool` instructions to save this schema and run it with testtool.
1746
1747 Mounting NETCONF device that runs on NETCONF testtool:
1748
1749 .. code-block:: bash
1750
1751   curl --location --request PUT 'http://127.0.0.1:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=testtool' \
1752   --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
1753   --header 'Content-Type: application/json' \
1754   --data-raw '{
1755       "node": [
1756           {
1757               "node-id": "testtool",
1758               "netconf-node-topology:host": "127.0.0.1",
1759               "netconf-node-topology:port": 17830,
1760               "netconf-node-topology:keepalive-delay": 100,
1761               "netconf-node-topology:tcp-only": false,
1762               "netconf-node-topology:login-password-unencrypted": {
1763                   "netconf-node-topology:username": "admin",
1764                   "netconf-node-topology:password": "admin"
1765               },
1766           }
1767       ]
1768   }'
1769
1770 Setting initial configuration on NETCONF device:
1771
1772 .. code-block:: bash
1773
1774   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' \
1775   --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
1776   --header 'Content-Type: application/json' \
1777   --data-raw '{
1778       "root": {
1779           "simple-root": {
1780               "leaf-a": "asddhg",
1781               "leaf-b": "ffffff",
1782               "ll": [
1783                   "str1",
1784                   "str2",
1785                   "str3"
1786               ],
1787               "nested": {
1788                   "sample-x": true,
1789                   "sample-y": false
1790               }
1791           },
1792           "list-root": {
1793               "branch-ab": 5,
1794               "top-list": [
1795                   {
1796                       "key-1": "ka",
1797                       "key-2": "kb",
1798                       "next-data": {
1799                           "switch-1": [
1800                               null
1801                           ],
1802                           "switch-2": [
1803                               null
1804                           ]
1805                       },
1806                       "nested-list": [
1807                           {
1808                               "identifier": "f1",
1809                               "foo": 1
1810                           },
1811                           {
1812                               "identifier": "f2",
1813                               "foo": 10
1814                           },
1815                           {
1816                               "identifier": "f3",
1817                               "foo": 20
1818                           }
1819                       ]
1820                   },
1821                   {
1822                       "key-1": "kb",
1823                       "key-2": "ka",
1824                       "next-data": {
1825                           "switch-1": [
1826                               null
1827                           ]
1828                       },
1829                       "nested-list": [
1830                           {
1831                               "identifier": "e1",
1832                               "foo": 1
1833                           },
1834                           {
1835                               "identifier": "e2",
1836                               "foo": 2
1837                           },
1838                           {
1839                               "identifier": "e3",
1840                               "foo": 3
1841                           }
1842                       ]
1843                   },
1844                   {
1845                       "key-1": "kc",
1846                       "key-2": "ke",
1847                       "next-data": {
1848                           "switch-2": [
1849                               null
1850                           ]
1851                       },
1852                       "nested-list": [
1853                           {
1854                               "identifier": "q1",
1855                               "foo": 13
1856                           },
1857                           {
1858                               "identifier": "q2",
1859                               "foo": 14
1860                           },
1861                           {
1862                               "identifier": "q3",
1863                               "foo": 15
1864                           }
1865                       ]
1866                   }
1867               ]
1868           }
1869       }
1870   }'
1871
1872 Examples
1873 --------
1874
1875 1. Reading whole leaf-list 'll' and leaf 'nested/sample-x' under 'simple-root' container.
1876
1877 RESTCONF request:
1878
1879 .. code-block:: bash
1880
1881     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' \
1882     --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
1883     --header 'Cookie: JSESSIONID=node01h4w82eorc1k61866b71qjgj503.node0'
1884
1885 Generated NETCONF RPC request:
1886
1887 .. code-block:: xml
1888
1889     <rpc message-id="m-18" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
1890         <get-config>
1891             <source>
1892                 <running/>
1893             </source>
1894             <filter xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:type="subtree">
1895                 <root xmlns="urn:ietf:params:xml:ns:yang:test-model">
1896                     <simple-root>
1897                         <ll/>
1898                         <nested>
1899                             <sample-x/>
1900                         </nested>
1901                     </simple-root>
1902                 </root>
1903             </filter>
1904         </get-config>
1905     </rpc>
1906
1907 .. note::
1908
1909     Using fields query parameter it is also possible to read whole leaf-list or list without
1910     necessity to specify value / key predicate (without reading parent entity). Such scenario
1911     is not permitted in RFC-8040 paths alone - fields query parameter can be used as
1912     workaround for this case.
1913
1914 RESTCONF response:
1915
1916 .. code-block:: json
1917
1918     {
1919         "test-module:simple-root": {
1920             "ll": [
1921                 "str3",
1922                 "str1",
1923                 "str2"
1924             ],
1925             "nested": {
1926                 "sample-x": true
1927             }
1928         }
1929     }
1930
1931 2. Reading all identifiers of 'nested-list' under all elements of 'top-list'.
1932
1933 RESTCONF request:
1934
1935 .. code-block:: bash
1936
1937     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)' \
1938     --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
1939     --header 'Cookie: JSESSIONID=node01h4w82eorc1k61866b71qjgj503.node0'
1940
1941 Generated NETCONF RPC request:
1942
1943 .. code-block:: xml
1944
1945     <rpc message-id="m-27" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
1946         <get-config>
1947             <source>
1948                 <running/>
1949             </source>
1950             <filter xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:type="subtree">
1951                 <root xmlns="urn:ietf:params:xml:ns:yang:test-model">
1952                     <list-root>
1953                         <top-list>
1954                             <nested-list>
1955                                 <identifier/>
1956                             </nested-list>
1957                             <key-1/>
1958                             <key-2/>
1959                         </top-list>
1960                     </list-root>
1961                 </root>
1962             </filter>
1963         </get-config>
1964     </rpc>
1965
1966 .. note::
1967
1968     NETCONF client automatically fetches values of list keys since they are required for correct
1969     deserialization of NETCONF response and at the end serialization of response to RESTCONF
1970     response (JSON/XML).
1971
1972 RESTCONF response:
1973
1974 .. code-block:: json
1975
1976     {
1977         "test-module:list-root": {
1978             "top-list": [
1979                 {
1980                     "key-1": "ka",
1981                     "key-2": "kb",
1982                     "nested-list": [
1983                         {
1984                             "identifier": "f3"
1985                         },
1986                         {
1987                             "identifier": "f2"
1988                         },
1989                         {
1990                             "identifier": "f1"
1991                         }
1992                     ]
1993                 },
1994                 {
1995                     "key-1": "kb",
1996                     "key-2": "ka",
1997                     "nested-list": [
1998                         {
1999                             "identifier": "e3"
2000                         },
2001                         {
2002                             "identifier": "e2"
2003                         },
2004                         {
2005                             "identifier": "e1"
2006                         }
2007                     ]
2008                 },
2009                 {
2010                     "key-1": "kc",
2011                     "key-2": "ke",
2012                     "nested-list": [
2013                         {
2014                             "identifier": "q3"
2015                         },
2016                         {
2017                             "identifier": "q2"
2018                         },
2019                         {
2020                             "identifier": "q1"
2021                         }
2022                     ]
2023                 }
2024             ]
2025         }
2026     }
2027
2028 3. Reading value of leaf 'branch-ab' and all values of leaves 'switch-1' that are placed
2029    under 'top-list' list elements.
2030
2031 RESTCONF request:
2032
2033 .. code-block:: bash
2034
2035     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' \
2036     --header 'Authorization: Basic YWRtaW46YWRtaW4=' \
2037     --header 'Cookie: JSESSIONID=node01jx6o5thwae9t1ft7c2zau5zbz4.node0'
2038
2039 Generated NETCONF RPC request:
2040
2041 .. code-block:: xml
2042
2043     <rpc message-id="m-42" xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
2044         <get-config>
2045             <source>
2046                 <running/>
2047             </source>
2048             <filter xmlns:ns0="urn:ietf:params:xml:ns:netconf:base:1.0" ns0:type="subtree">
2049                 <root xmlns="urn:ietf:params:xml:ns:yang:test-model">
2050                     <list-root>
2051                         <branch-ab/>
2052                         <top-list>
2053                             <next-data>
2054                                 <switch-1/>
2055                             </next-data>
2056                             <key-1/>
2057                             <key-2/>
2058                         </top-list>
2059                     </list-root>
2060                 </root>
2061             </filter>
2062         </get-config>
2063     </rpc>
2064
2065 RESTCONF response:
2066
2067 .. code-block:: json
2068
2069     {
2070         "test-module:list-root": {
2071             "branch-ab": 5,
2072             "top-list": [
2073                 {
2074                     "key-1": "ka",
2075                     "key-2": "kb",
2076                     "next-data": {
2077                         "switch-1": [
2078                             null
2079                         ]
2080                     }
2081                 },
2082                 {
2083                     "key-1": "kb",
2084                     "key-2": "ka",
2085                     "next-data": {
2086                         "switch-1": [
2087                             null
2088                         ]
2089                     }
2090                 },
2091                 {
2092                     "key-1": "kc",
2093                     "key-2": "ke"
2094                 }
2095             ]
2096         }
2097     }
2098
2099 RESTCONF OpenAPI
2100 ----------------
2101
2102 Overview
2103 ~~~~~~~~
2104
2105 The OpenAPI provides full API for configurational data which can be edited (by POST, PUT, PATCH and DELETE).
2106 For operational data we only provide GET API. For the majority of requests you can see only config data in examples.
2107 That’s because we can show only one example per request. The exception when you can see operational data in an
2108 example is when data are representing an operational (config false) container with no config data in it.
2109
2110
2111 Using the OpenAPI Explorer through HTTP
2112 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2113
2114 1. Install OpenApi into Karaf by installing karaf feature:
2115
2116 ::
2117
2118     $ feature:install odl-restconf-openapi
2119
2120 2.  Navigate to OpenAPI in your web browser which is available at URLs:
2121
2122 -  http://localhost:8181/openapi/explorer/index.html for general overview
2123
2124 -  http://localhost:8181/openapi/api/v3/single for JSON data
2125
2126 .. note::
2127
2128     In the URL links for OpenAPI, change *localhost* to the IP/Host name of your actual server.
2129
2130 3.  Enter the username and password.
2131     By default the credentials are  *admin/admin*.
2132
2133 4.  Select any model to try out.
2134
2135 5.  Select any available request to try out.
2136
2137 6.  Click on the **Try it out** button.
2138
2139 7.  Provide any required parameters or edit request body.
2140
2141 8.  Click the **Execute** button.
2142
2143 9.  You can see responses to the given request.
2144
2145
2146 OpenAPI Explorer can also be used for connected device. How to connect a device can be found :ref:`here <netconf-connector>`.
2147
2148 OpenAPI URLs in that case would look like this:
2149
2150 -  `http://localhost:8181/openapi/explorer/index.html?urls.primaryName=17830-sim-device resources - RestConf RFC 8040 <http://localhost:8181/openapi/explorer/index.html?urls.primaryName=17830-sim-device%20resources%20-%20RestConf%20RFC%208040>`_ for device overview
2151
2152 -  http://localhost:8181/openapi/api/v3/mounts/1 for JSON data
2153
2154 -  `http://localhost:8181/openapi/api/v3/mounts/1/toaster(2009-11-20) <http://localhost:8181/openapi/api/v3/mounts/1/toaster(2009-11-20)>`__ JSON data for given model
2155
2156 .. note::
2157
2158     The URL links for OpenAPI are made for device with name *17830-sim-device* and model toaster
2159     with *2009-11-20* revision and need to be changed accordingly to connected device.