Replace supported admonitions with rst directives
[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.1.1 or later
34
35    -  Java 7- or Java 8-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/stable/boron/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.controller -DarchetypeArtifactId=opendaylight-startup-archetype \
63        -DarchetypeRepository=https://nexus.opendaylight.org/content/repositories/public/ \
64        -DarchetypeCatalog=https://nexus.opendaylight.org/content/repositories/public/archetype-catalog.xml
65
66 2. Update the properties values as follows. Ensure that the groupid and
67    the artifactid is lower case.
68
69    .. code:: shell
70
71        Define value for property 'groupId': : org.opendaylight.example
72        Define value for property 'artifactId': : example
73        Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0-SNAPSHOT
74        Define value for property 'package':  org.opendaylight.example: :
75        Define value for property 'classPrefix':  ${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)}
76        Define value for property 'copyright': : Copyright (c) 2015 Yoyodyne, Inc.
77
78 3. Accept the default value of classPrefix that is,
79    ``(${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)})``.
80    The classPrefix creates a Java Class Prefix by capitalizing the first
81    character of the artifactId.
82
83    .. note::
84
85        In this scenario, the classPrefix used is "Example". Create a
86        top-level directory for the archetype.
87
88    .. code:: shell
89
90        ${artifactId}/
91        example/
92        cd example/
93        api/
94        artifacts/
95        features/
96        impl/
97        karaf/
98        pom.xml
99
100 4. Build the *example* project.
101
102    .. note::
103
104        Depending on your development machine’s specification this might
105        take a little while. Ensure that you are in the project’s root
106        directory, example/, and then issue the build command, shown
107        below.
108
109    .. code:: shell
110
111        mvn clean install
112
113 5. Start the *example* project for the first time.
114
115    .. code:: shell
116
117        cd karaf/target/assembly/bin
118        ls
119        ./karaf
120
121 6. Wait for the karaf cli that appears as follows. Wait for OpenDaylight
122    to fully load all the components. This can take a minute or two after
123    the prompt appears. Check the CPU on your dev machine, specifically
124    the Java process to see when it calms down.
125
126    .. code:: shell
127
128        opendaylight-user@root>
129
130 7. Verify if the “example” module is built and search for the log entry
131    which includes the entry *ExampleProvider Session Initiated*.
132
133    .. code:: shell
134
135        log:display | grep Example
136
137 8. Shutdown the OpenDaylight through the console by using the following
138    command.
139
140    .. code:: shell
141
142        shutdown -f
143
144 Defining a Simple Hello World RPC
145 ---------------------------------
146
147 1.  | Run the maven archetype *opendaylight-startup-archetype*, and
148       create the *hello* project.
149
150     .. code:: shell
151
152         mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller -DarchetypeArtifactId=opendaylight-startup-archetype \
153         -DarchetypeRepository=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/ \
154         -DarchetypeCatalog=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml
155
156 2.  Update the Properties values as follows.
157
158     .. code:: shell
159
160         Define value for property 'groupId': : org.opendaylight.hello
161         Define value for property 'artifactId': : hello
162         Define value for property 'version':  1.0-SNAPSHOT: : 1.0.0-SNAPSHOT
163         Define value for property 'package':  org.opendaylight.hello: :
164         Define value for property 'classPrefix':  ${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)}
165         Define value for property 'copyright': : Copyright(c) Yoyodyne, Inc.
166
167 3.  View the *hello* project.
168
169     .. code:: shell
170
171         cd hello/
172         ls -1
173         api
174         artifacts
175         features
176         impl
177         karaf
178         pom.xml
179
180 4.  Build *hello* project by using the following command.
181
182     .. code:: shell
183
184         mvn clean install
185
186 5.  Verify that the project is functioning by executing karaf.
187
188     .. code:: shell
189
190         cd karaf/target/assembly/bin
191         ./karaf
192
193 6.  | The karaf cli appears as follows.
194     | NOTE: Remember to wait for OpenDaylight to load completely. Verify
195       that the Java process CPU has stabilized.+
196
197     .. code:: shell
198
199         opendaylight-user@root>
200
201 7.  Verify that the *hello* module is loaded by checking the log.
202
203     .. code:: shell
204
205         log:display | grep Hello
206
207 8.  Shutdown karaf.
208
209     .. code:: shell
210
211         shutdown -f
212
213 9.  Return to the top of the directory structure:
214
215     .. code:: shell
216
217         cd ../../../../
218
219 10. View the entry point to understand where the log line came from. The
220     entry point is in the impl project:
221
222     .. code:: shell
223
224         impl/src/main/java/org/opendaylight/hello/impl/HelloProvider.java
225
226 11. Add any new things that you are doing in your implementation by
227     using the HelloProvider.onSessionInitiate method. Its analogous to
228     an Activator.
229
230     .. code:: java
231
232         @Override
233             public void onSessionInitiated(ProviderContext session) {
234                 LOG.info("HelloProvider Session Initiated");
235             }
236
237 Add a simple HelloWorld RPC API
238 -------------------------------
239
240 1. Navigate to the file.
241
242    ::
243
244        Edit
245        api/src/main/yang/hello.yang
246
247 2. Edit this file as follows. In the following example, we are adding
248    the code in a YANG module to define the *hello-world* RPC:
249
250    .. code:: yang
251
252        module hello {
253            yang-version 1;
254            namespace "urn:opendaylight:params:xml:ns:yang:hello";
255            prefix "hello";
256            revision "2015-01-05" {
257                description "Initial revision of hello model";
258            }
259            rpc hello-world {
260                input {
261                    leaf name {
262                        type string;
263                    }
264                }
265                output {
266                    leaf greating {
267                        type string;
268                    }
269                }
270            }
271        }
272
273 3. Return to the hello/api directory and build your API as follows.
274
275    .. code:: shell
276
277        cd ../../../
278        mvn clean install
279
280 Implement the HelloWorld RPC API
281 --------------------------------
282
283 1. Define the HelloService, which is invoked through the *hello-world*
284    API.
285
286    .. code:: shell
287
288        cd ../impl/src/main/java/org/opendaylight/hello/impl/
289
290 2. Create a new file called HelloWorldImpl.java and add in the code
291    below.
292
293    .. code:: java
294
295        package org.opendaylight.hello.impl;
296        import java.util.concurrent.Future;
297        import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloService;
298        import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldInput;
299        import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutput;
300        import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutputBuilder;
301        import org.opendaylight.yangtools.yang.common.RpcResult;
302        import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
303        public class HelloWorldImpl implements HelloService {
304            @Override
305            public Future<RpcResult<HelloWorldOutput>> helloWorld(HelloWorldInput input) {
306                HelloWorldOutputBuilder helloBuilder = new HelloWorldOutputBuilder();
307                helloBuilder.setGreating("Hello " + input.getName());
308                return RpcResultBuilder.success(helloBuilder.build()).buildFuture();
309            }
310        }
311
312 3. The HelloProvider.java file is in the current directory. Register the
313    RPC that you created in the *hello.yang* file in the
314    HelloProvider.java file. You can either edit the HelloProvider.java
315    to match what is below or you can simple replace it with the code
316    below.
317
318    .. code:: java
319
320        /*
321         * Copyright(c) Yoyodyne, Inc. and others.  All rights reserved.
322         *
323         * This program and the accompanying materials are made available under the
324         * terms of the Eclipse Public License v1.0 which accompanies this distribution,
325         * and is available at http://www.eclipse.org/legal/epl-v10.html
326         */
327        package org.opendaylight.hello.impl;
328
329        import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
330        import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
331        import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
332        import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloService;
333        import org.slf4j.Logger;
334        import org.slf4j.LoggerFactory;
335
336        public class HelloProvider implements BindingAwareProvider, AutoCloseable {
337            private static final Logger LOG = LoggerFactory.getLogger(HelloProvider.class);
338            private RpcRegistration<HelloService> helloService;
339            @Override
340            public void onSessionInitiated(ProviderContext session) {
341                LOG.info("HelloProvider Session Initiated");
342                helloService = session.addRpcImplementation(HelloService.class, new HelloWorldImpl());
343            }
344            @Override
345            public void close() throws Exception {
346                LOG.info("HelloProvider Closed");
347                if (helloService != null) {
348                    helloService.close();
349                }
350            }
351        }
352
353 4. Optionally, you can also build the Java classes which will register
354    the new RPC. This is useful to test the edits you have made to
355    HelloProvider.java and HelloWorldImpl.java.
356
357    .. code:: shell
358
359        cd ../../../../../../../
360        mvn clean install
361
362 5. Return to the top level directory
363
364    .. code:: shell
365
366        cd ../
367
368 6. Build the entire *hello* again, which will pickup the changes you
369    have made and build them into your project:
370
371    .. code:: shell
372
373        mvn clean install
374
375 Execute the *hello* project for the first time
376 ----------------------------------------------
377
378 1. Run karaf
379
380    .. code:: shell
381
382        cd ../karaf/target/assembly/bin
383        ./karaf
384
385 2. Wait for the project to load completely. Then view the log to see the
386    loaded *Hello* Module:
387
388    .. code:: shell
389
390        log:display | grep Hello
391
392 Test the *hello-world* RPC via REST
393 -----------------------------------
394
395 There are a lot of ways to test your RPC. Following are some examples.
396
397 1. Using the API Explorer through HTTP
398
399 2. Using a browser REST client
400
401 Using the API Explorer through HTTP
402 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
403
404 1. | Navigate to `apidoc
405      UI <http://localhost:8181/apidoc/explorer/index.html>`__ with your
406      web browser.
407    | NOTE: In the URL mentioned above, Change *localhost* to the IP/Host
408      name to reflect your development machine’s network address.
409
410 2. Select
411
412    .. code:: shell
413
414        hello(2015-01-05)
415
416 3. Select
417
418    ::
419
420        POST /operations/hello:hello-world
421
422 4. Provide the required value.
423
424    .. code:: json
425
426        {"hello:input": { "name":"Your Name"}}
427
428 5. Click the button.
429
430 6. Enter the username and password, by default the credentials are
431    admin/admin.
432
433 7. In the response body you should see.
434
435    .. code:: json
436
437        {
438          "output": {
439            "greating": "Hello Your Name"
440          }
441        }
442
443 Using a browser REST client
444 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
445
446 | For example, use the following information in the Firefox plugin
447   *RESTClient*
448   [`https://github.com/chao/RESTClient} <https://github.com/chao/RESTClient}>`__
449
450 ::
451
452     POST: http://192.168.1.43:8181/restconf/operations/hello:hello-world
453
454 Header:
455
456 ::
457
458     application/json
459
460 Body:
461
462 .. code:: json
463
464     {"input": {
465         "name": "Andrew"
466       }
467     }
468
469 Troubleshooting
470 ---------------
471
472 If you get a response code 501 while attempting to POST
473 /operations/hello:hello-world, check the file: HelloProvider.java and
474 make sure the helloService member is being set. By not invoking
475 "session.addRpcImplementation()" the REST API will be unable to map
476 /operations/hello:hello-world url to HelloWorldImpl.
477