Clear the initial content-type for DELETE requests
[netconf.git] / docs / developer-guide.rst
1 .. _netconf-dev-guide:
2
3 NETCONF Developer Guide
4 =======================
5
6 .. note::
7
8     Reading the NETCONF section in the User Guide is likely useful as it
9     contains an overview of NETCONF in OpenDaylight and a how-to for
10     spawning and configuring NETCONF connectors.
11
12 This chapter is recommended for application developers who want to
13 interact with mounted NETCONF devices from their application code. It
14 tries to demonstrate all the use cases from user guide with RESTCONF but
15 now from the code level. One important difference would be the
16 demonstration of NETCONF notifications and notification listeners. The
17 notifications were not shown using RESTCONF because **RESTCONF does not
18 support notifications from mounted NETCONF devices.**
19
20 .. note::
21
22     It may also be useful to read the generic `OpenDaylight MD-SAL app
23     development
24     tutorial <https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:MD-SAL_App_Tutorial>`__
25     before diving into this chapter. This guide assumes awareness of
26     basic OpenDaylight application development.
27
28 Sample app overview
29 -------------------
30
31 All the examples presented here are implemented by a sample OpenDaylight
32 application called **ncmount** in the ``coretutorials`` OpenDaylight
33 project. It can be found on the github mirror of OpenDaylight’s
34 repositories:
35
36 -  https://github.com/opendaylight/coretutorials/tree/stable/boron/ncmount
37
38 or checked out from the official OpenDaylight repository:
39
40 -  https://git.opendaylight.org/gerrit/#/admin/projects/coretutorials
41
42 **The application was built using the** `project startup maven
43 archetype <https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Startup_Project_Archetype>`__
44 **and demonstrates how to:**
45
46 -  preconfigure connectors to NETCONF devices
47
48 -  retrieve MountPointService (registry of available mount points)
49
50 -  listen and react to changing connection state of netconf-connector
51
52 -  add custom device YANG models to the app and work with them
53
54 -  read data from device in binding aware format (generated java APIs
55    from provided YANG models)
56
57 -  write data into device in binding aware format
58
59 -  trigger and listen to NETCONF notifications in binding aware format
60
61 Detailed information about the structure of the application can be found
62 at:
63 https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Tutorials:Netconf_Mount
64
65 .. note::
66
67     The code in ncmount is fully **binding aware** (works with generated
68     java APIs from provided YANG models). However it is also possible to
69     perform the same operations in **binding independent** manner.
70
71 NcmountProvider
72 ~~~~~~~~~~~~~~~
73
74 The NcmountProvider class (found in NcmountProvider.java) is the central
75 point of the ncmount application and all the application logic is
76 contained there. The following sections will detail its most interesting
77 pieces.
78
79 Retrieve MountPointService
80 ^^^^^^^^^^^^^^^^^^^^^^^^^^
81
82 The MountPointService is a central registry of all available mount
83 points in OpenDaylight. It is just another MD-SAL service and is
84 available from the ``session`` attribute passed by
85 ``onSessionInitiated`` callback:
86
87 ::
88
89     @Override
90     public void onSessionInitiated(ProviderContext session) {
91         LOG.info("NcmountProvider Session Initiated");
92
93         // Get references to the data broker and mount service
94         this.mountService = session.getSALService(MountPointService.class);
95
96         ...
97
98         }
99     }
100
101 Listen for connection state changes
102 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
103
104 It is important to know when a mount point appears, when it is fully
105 connected and when it is disconnected or removed. The exact states of a
106 mount point are:
107
108 -  Connected
109
110 -  Connecting
111
112 -  Unable to connect
113
114 To receive this kind of information, an application has to register
115 itself as a notification listener for the preconfigured netconf-topology
116 subtree in MD-SAL’s datastore. This can be performed in the
117 ``onSessionInitiated`` callback as well:
118
119 ::
120
121     @Override
122     public void onSessionInitiated(ProviderContext session) {
123
124         ...
125
126         this.dataBroker = session.getSALService(DataBroker.class);
127
128         // Register ourselves as the REST API RPC implementation
129         this.rpcReg = session.addRpcImplementation(NcmountService.class, this);
130
131         // Register ourselves as data change listener for changes on Netconf
132         // nodes. Netconf nodes are accessed via "Netconf Topology" - a special
133         // topology that is created by the system infrastructure. It contains
134         // all Netconf nodes the Netconf connector knows about. NETCONF_TOPO_IID
135         // is equivalent to the following URL:
136         // .../restconf/operational/network-topology:network-topology/topology/topology-netconf
137         if (dataBroker != null) {
138             this.dclReg = dataBroker.registerDataChangeListener(LogicalDatastoreType.OPERATIONAL,
139                     NETCONF_TOPO_IID.child(Node.class),
140                     this,
141                     DataChangeScope.SUBTREE);
142         }
143     }
144
145 The implementation of the callback from MD-SAL when the data change can
146 be found in the
147 ``onDataChanged(AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject>
148 change)`` callback of `NcmountProvider
149 class <https://github.com/opendaylight/coretutorials/blob/stable/boron/ncmount/impl/src/main/java/ncmount/impl/NcmountProvider.java>`__.
150
151 Reading data from the device
152 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
153
154 The first step when trying to interact with the device is to get the
155 exact mount point instance (identified by an instance identifier) from
156 the MountPointService:
157
158 ::
159
160     @Override
161     public Future<RpcResult<ShowNodeOutput>> showNode(ShowNodeInput input) {
162         LOG.info("showNode called, input {}", input);
163
164         // Get the mount point for the specified node
165         // Equivalent to '.../restconf/<config | operational>/opendaylight-inventory:nodes/node/<node-name>/yang-ext:mount/'
166         // Note that we can read both config and operational data from the same
167         // mount point
168         final Optional<MountPoint> xrNodeOptional = mountService.getMountPoint(NETCONF_TOPO_IID
169                 .child(Node.class, new NodeKey(new NodeId(input.getNodeName()))));
170
171         Preconditions.checkArgument(xrNodeOptional.isPresent(),
172                 "Unable to locate mountpoint: %s, not mounted yet or not configured",
173                 input.getNodeName());
174         final MountPoint xrNode = xrNodeOptional.get();
175
176         ....
177     }
178
179 .. note::
180
181     The triggering method in this case is called ``showNode``. It is a
182     YANG-defined RPC and NcmountProvider serves as an MD-SAL RPC
183     implementation among other things. This means that ``showNode`` an
184     be triggered using RESTCONF.
185
186 The next step is to retrieve an instance of the ``DataBroker`` API from
187 the mount point and start a read transaction:
188
189 ::
190
191     @Override
192     public Future<RpcResult<ShowNodeOutput>> showNode(ShowNodeInput input) {
193
194         ...
195
196         // Get the DataBroker for the mounted node
197         final DataBroker xrNodeBroker = xrNode.getService(DataBroker.class).get();
198         // Start a new read only transaction that we will use to read data
199         // from the device
200         final ReadOnlyTransaction xrNodeReadTx = xrNodeBroker.newReadOnlyTransaction();
201
202         ...
203     }
204
205 Finally, it is possible to perform the read operation:
206
207 ::
208
209     @Override
210     public Future<RpcResult<ShowNodeOutput>> showNode(ShowNodeInput input) {
211
212         ...
213
214         InstanceIdentifier<InterfaceConfigurations> iid =
215                 InstanceIdentifier.create(InterfaceConfigurations.class);
216
217         Optional<InterfaceConfigurations> ifConfig;
218         try {
219             // Read from a transaction is asynchronous, but a simple
220             // get/checkedGet makes the call synchronous
221             ifConfig = xrNodeReadTx.read(LogicalDatastoreType.CONFIGURATION, iid).checkedGet();
222         } catch (ReadFailedException e) {
223             throw new IllegalStateException("Unexpected error reading data from " + input.getNodeName(), e);
224         }
225
226         ...
227     }
228
229 The instance identifier is used here again to specify a subtree to read
230 from the device. At this point application can process the data as it
231 sees fit. The ncmount app transforms the data into its own format and
232 returns it from ``showNode``.
233
234 .. note::
235
236     More information can be found in the source code of ncmount sample
237     app + on wiki:
238     https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Tutorials:Netconf_Mount