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