OpenApi: Document feature installation and usage
[docs.git] / docs / developer-guides / developing-apps-on-the-opendaylight-controller.rst
1 Developing apps on the OpenDaylight controller
2 ==============================================
3
4 This section provides the information required to develop apps on
5 an OpenDaylight controller. Apps can be developed either within the
6 controller using a Model-Driven SAL (MD-SAL) archetype or via an
7 external app using the RESTCONF API to communicate with the controller.
8
9 Overview
10 --------
11
12 This section starts app development within an OpenDaylight controller.
13
14 Perform the following steps to develop an app:
15
16 1.  Create a local repository for the code using a simple build process.
17
18 2.  Start the OpenDaylight controller.
19
20 3.  Test a simple remote procedure call (RPC) that was created based on
21     the principle of *hello world*.
22
23 Prerequisites
24 -------------
25
26 The following are the prerequisites for app creation:
27
28 -   A development environment with the following setup and working correctly
29     from the shell:
30
31     -   Maven 3.8.3 or later
32
33     -   Java 11-compliant JDK
34
35     -   An appropriate Maven settings.xml file. One way to get the
36         default OpenDaylight settings.xml file is:
37
38         .. code:: shell
39
40             cp -n ~/.m2/settings.xml{,.orig}
41             wget -q -O - https://raw.githubusercontent.com/opendaylight/odlparent/master/settings.xml > ~/.m2/settings.xml
42
43 .. note::
44
45     For Linux or Mac OS X development operating systems, the default local
46     repository is ~/.m2/repository. For other platforms, the default local
47     repository location varies.
48
49 Building an Example module
50 --------------------------
51
52 Perform the following steps to develop an app:
53
54 1.  Create an *Example* project using Maven and an archetype called the
55     *opendaylight-startup-archetype*. For first time downloads, this project
56     will take some time to pull all the code from the remote repository.
57
58     .. code:: shell
59
60         mvn archetype:generate -DarchetypeGroupId=org.opendaylight.archetypes \
61             -DarchetypeArtifactId=opendaylight-startup-archetype \
62             -DarchetypeCatalog=remote -DarchetypeVersion=<VERSION>
63
64     The correct VERSION depends on desired Simultaneous Release:
65
66     .. list-table:: Archetype versions
67        :widths: auto
68        :header-rows: 1
69
70       * - OpenDaylight Simultaneous Release
71         - ``opendaylight-startup-archetype`` version
72
73       * - Magnesium Development
74         - 1.3.0-SNAPSHOT
75
76       * - Aluminium Development
77         - 1.4.0-SNAPSHOT
78
79 2.  Update the properties values. Ensure that the values for the ``groupId`` and
80     the ``artifactId`` are in lower case.
81
82     .. code:: text
83
84         Define value for property 'groupId': org.opendaylight.example
85         Define value for property 'artifactId': example
86         [INFO] Using property: version = 0.1.0-SNAPSHOT
87         Define value for property 'package' org.opendaylight.example: :
88         Define value for property 'classPrefix' Example: :
89         Define value for property 'copyright': Copyright (c) 2021 Yoyodyne, Inc.
90         [INFO] Using property: copyrightYear = 2021
91
92 3.  Accept the default value of ``classPrefix``, that is:
93     ``(${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)})``.
94     The ``classPrefix`` creates a Java Class Prefix by capitalizing the first
95     character of the ``artifactId``.
96
97     .. note::
98
99         This will create a directory with the name given to ``artifactId``
100         in the above dialog, with the following contents.
101
102     .. code:: shell
103
104         .gitreview
105         api/
106         artifacts/
107         cli/
108         features/
109         impl/
110         it/
111         karaf/
112         pom.xml
113         src/
114
115 4.  Build the *example* project.
116
117     .. note::
118
119         Build time varies depending on the development machine specification.
120         Ensure that you are in the project’s root directory (example/,) and then
121         issue the the following build command.
122
123     .. code:: shell
124
125         mvn clean install
126
127 5.  Initialize the *example* project.
128
129     .. code:: shell
130
131         cd karaf/target/assembly/bin
132         ls
133         ./karaf
134
135 6.  Wait for the Karaf CLI to appear. Wait for OpenDaylight to fully load
136     all components. This can take a minute or two after the prompt appears.
137     Check the CPU on the dev machine, specifically the Java process to see
138     when it slows down.
139
140     .. code:: shell
141
142         opendaylight-user@root>
143
144 7.  Verify if the “example” module is built and search for the log entry
145     that includes the entry *ExampleProvider Session Initiated*.
146
147     .. code:: shell
148
149         log:display | grep Example
150
151 8.  Enter the following command to shutdown OpenDaylight through the console:
152
153     .. code:: shell
154
155         shutdown -f
156
157 Defining a simple HelloWorld RPC
158 --------------------------------
159
160 1.  Build a *hello* example from the Maven archetype *opendaylight-startup-archetype*,
161     same as what was done in the previous steps.
162
163 2.  View the entry point to understand the origins of the log line. The
164     entry point starts in the ``./impl`` project:
165
166     .. code:: shell
167
168         impl/src/main/java/org/opendaylight/hello/impl/HelloProvider.java
169
170 3.  Add any new content that you are doing in your implementation by
171     using the ``HelloProvider.init`` method. It is analogous to an Activator.
172
173     .. code:: java
174
175         /**
176          * Method called when the blueprint container is created.
177          */
178         public void init() {
179             LOG.info("HelloProvider Session Initiated");
180         }
181
182 Add a simple HelloWorld RPC API
183 -------------------------------
184
185 1.  Navigate to ``api/src/main/yang``.
186
187     .. code:: shell
188
189         cd api/src/main/yang/
190
191 2.  Edit the ``hello.yang`` file. In the following example, we are adding
192     the code in a YANG module to define the *hello-world* RPC:
193
194     .. code::
195
196         module hello {
197             yang-version 1.1;
198             namespace "urn:opendaylight:params:xml:ns:yang:hello";
199             prefix "hello";
200
201             revision "2021-03-21" {
202                 description "Initial revision of hello model";
203             }
204
205             rpc hello-world {
206                 input {
207                     leaf name {
208                         type string;
209                     }
210                 }
211                 output {
212                     leaf greeting {
213                         type string;
214                     }
215                 }
216             }
217         }
218
219 3.  Return to the ``hello/api`` directory. Do the following to build the API:
220
221     .. code:: shell
222
223         cd ../../../
224         mvn clean install
225
226 Implement the HelloWorld RPC API
227 --------------------------------
228
229 1.  Define the HelloService that was invoked through the *hello-world* API.
230
231     .. code:: shell
232
233         cd ../impl/src/main/java/org/opendaylight/hello/impl/
234
235     The ``HelloProvider.java`` file is in the current directory. Register the
236     RPC that you created in the *hello.yang* file in the
237     ``HelloProvider.java`` file. You can either edit the ``HelloProvider.java``
238     to match what is below or simply replace it with the code below.
239
240     .. code:: java
241
242         /*
243          * Copyright © 2021 Copyright (c) 2021 Yoyodyne, Inc. and others.  All rights reserved.
244          *
245          * This program and the accompanying materials are made available under the
246          * terms of the Eclipse Public License v1.0 which accompanies this distribution,
247          * and is available at http://www.eclipse.org/legal/epl-v10.html
248          */
249         package org.opendaylight.hello.impl;
250
251         import com.google.common.util.concurrent.ListenableFuture;
252         import org.opendaylight.mdsal.binding.api.DataBroker;
253         import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev210321.HelloService;
254         import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev210321.HelloWorldInput;
255         import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev210321.HelloWorldOutput;
256         import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev210321.HelloWorldOutputBuilder;
257         import org.opendaylight.yangtools.yang.common.RpcResult;
258         import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
259         import org.slf4j.Logger;
260         import org.slf4j.LoggerFactory;
261
262         public class HelloProvider implements HelloService {
263
264             private static final Logger LOG = LoggerFactory.getLogger(HelloProvider.class);
265
266             private final DataBroker dataBroker;
267
268             public HelloProvider(final DataBroker dataBroker) {
269                 this.dataBroker = dataBroker;
270             }
271
272             @Override
273             public ListenableFuture<RpcResult<HelloWorldOutput>> helloWorld(HelloWorldInput input) {
274                 HelloWorldOutputBuilder helloBuilder = new HelloWorldOutputBuilder();
275                 helloBuilder.setGreeting("Hello " + input.getName());
276                 return RpcResultBuilder.success(helloBuilder.build()).buildFuture();
277             }
278
279             /**
280              * Method called when the blueprint container is created.
281              */
282             public void init() {
283                 LOG.info("HelloProvider Session Initiated");
284             }
285
286             /**
287              * Method called when the blueprint container is destroyed.
288              */
289             public void close() {
290                 LOG.info("HelloProvider Closed");
291             }
292         }
293
294 2.  Update Blueprint XML file.
295
296     .. code:: shell
297
298         cd ../../../../../resources/OSGI-INF/blueprint/
299
300     You can either edit the ``impl-blueprint.xml`` to match what is below
301     or simply replace it with the XML below.
302
303     .. code:: xml
304
305         <?xml version="1.0" encoding="UTF-8"?>
306         <!-- vi: set et smarttab sw=4 tabstop=4: -->
307         <!--
308         Copyright © 2021 Copyright (c) 2021 Yoyodyne, Inc. and others. All rights reserved.
309
310         This program and the accompanying materials are made available under the
311         terms of the Eclipse Public License v1.0 which accompanies this distribution,
312         and is available at http://www.eclipse.org/legal/epl-v10.html
313         -->
314         <blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
315           xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
316           odl:use-default-for-reference-types="true">
317
318           <reference id="dataBroker"
319             interface="org.opendaylight.mdsal.binding.api.DataBroker"
320             odl:type="default" />
321
322           <bean id="provider"
323             class="org.opendaylight.hello.impl.HelloProvider"
324             init-method="init" destroy-method="close">
325             <argument ref="dataBroker" />
326           </bean>
327
328           <odl:rpc-implementation ref="provider"/>
329
330         </blueprint>
331
332 3.  Optionally, users can build the Java classes that will register
333     the new RPC. This is useful to test the edits that were made to
334     ``HelloProvider.java``.
335
336     .. code:: shell
337
338         cd ../../../../../
339         mvn clean install
340
341 4.  Return to the top level directory
342
343     .. code:: shell
344
345         cd ../
346
347 5.  Build the entire *hello* again. This will pickup the new changes,
348     and then build them into the project:
349
350     .. code:: shell
351
352         mvn clean install
353
354 Execute the *hello* project for the first time
355 ----------------------------------------------
356
357 1.  Run karaf
358
359     .. code:: shell
360
361         cd karaf/target/assembly/bin
362         ./karaf
363
364 2.  Wait for the project to load completely. Then view the log to see the
365     loaded *Hello* Module:
366
367     .. code:: shell
368
369         log:display | grep Hello
370
371 Test the *hello-world* RPC via REST
372 -----------------------------------
373
374 There are a lot of ways to test a RPC. The following are a few examples.
375
376 1.  Using the API Explorer through HTTP
377
378 2.  Using a browser REST client
379
380 Using the API Explorer through HTTP
381 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
382
383 1.  Navigate to `openapi
384     UI <http://localhost:8181/openapi/explorer/index.html>`__ with your
385     web browser.
386
387     .. note::
388
389         In the URL link for *openapi UI*, change *localhost* to the IP/Host
390         name to reflect your development machine’s network address.
391
392 2.  Enter the username and password.
393     By default the credentials are  *admin/admin*.
394
395 3.  Select
396
397     .. code:: shell
398
399         hello
400
401 4.  Select
402
403     ::
404
405         POST /rests/operations/hello:hello-world
406
407 5.  Click on the **Try it out** button.
408
409 6.  Provide the required request input.
410
411     .. code:: json
412
413         {
414           "input": {
415             "name": "Your Name"
416           }
417         }
418
419
420 7.  Select **application/json** for *Media type* in the *Responses* section.
421
422 8.  Click the **Execute** button.
423
424 9.  In the response body you should see
425
426     .. code:: json
427
428         {
429           "hello:output": {
430             "greeting": "Hello Your Name"
431           }
432         }
433
434
435 Using a browser REST client
436 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
437
438 Next, use a browser to POST a REST client request.
439 For example, use the following information in the Firefox plugin:
440
441 *RESTClient* `https://github.com/chao/RESTClient <https://github.com/chao/RESTClient>`_
442
443 ::
444
445     POST: http://localhost:8181/rests/operations/hello:hello-world
446
447 Header:
448
449 ::
450
451     Accept: application/json
452     Content-Type: application/json
453     Authorization: Basic admin admin
454
455 Body:
456
457 .. code:: json
458
459     {
460       "input": {
461         "name": "Your Name"
462       }
463     }
464
465 In the response body you should see:
466
467 .. code:: json
468
469     {
470       "hello:output": {
471         "greeting": "Hello Your Name"
472       }
473     }
474
475 Troubleshooting
476 ---------------
477
478 If you get a response code 500 while attempting to
479 ``POST /rests/operations/hello:hello-world``, check the file:
480 ``impl/src/main/resources/OSGI-INF/blueprint/impl-blueprint.xml``
481 and make sure the following element is specified for ``<blueprint>``.
482
483 .. code:: xml
484
485     <odl:rpc-implementation ref="provider"/>