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