February 6, 2018

1316 words 7 mins read

Developing ODL Apps - Getting Started

Developing ODL Apps - Getting Started

If you're new to OpenDaylight environment and try to follow the getting started guide at the official documentation page, you'll spend hours. In the worst case, it might take your entire weekend. :-)

This short tutorial is a 'working' version of the official development getting started guide. Note that versions do matter. I've spent hours trying to get everything work for Nitrogen before I read this thread. It's still broken for Nitrogen. This guide is based on OpenDaylight Carbon.

Install & Check Maven

$ apt-cache policy maven
maven:
  Installed: (none)
  Candidate: 3.3.9-3
  Version table:
     3.3.9-3 500
        500 http://kambing.ui.ac.id/ubuntu xenial/universe amd64 Packages
        500 http://kambing.ui.ac.id/ubuntu xenial/universe i386 Packages

$ sudo apt install maven

$ mvn -v
Apache Maven 3.3.9
Maven home: /usr/share/maven
Java version: 1.8.0_161, vendor: Oracle Corporation
Java home: /usr/lib/jvm/java-8-oracle/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "4.4.0-112-generic", arch: "amd64", family: "unix"

$ mkdir .m2
$ wget -q -O - https://raw.githubusercontent.com/opendaylight/odlparent/stable/boron/settings.xml > ~/.m2/settings.xml
$ mkdir devfolder && cd devfolder

Maven & ODL Archetype

Pay attention to the -DarchetypeRepository and -DarchetypeVersion flags.

$ mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller -DarchetypeArtifactId=opendaylight-startup-archetype \
-DarchetypeRepository=http://nexus.opendaylight.org/content/repositories/opendaylight.release/ \
-DarchetypeCatalog=remote -DarchetypeVersion=1.3.0-Carbon

Define value for property 'groupId': org.opendaylight.hallo
Define value for property 'artifactId': hallo
[INFO] Using property: version = 0.1.0-SNAPSHOT
Define value for property 'package' org.opendaylight.hallo: : 
Define value for property 'classPrefix' Hallo: : 
Define value for property 'copyright': hallo inc.

Build & Execute

~/devfolder/hallo$ mvn clean install -DskipTests
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ODL :: org.opendaylight.hallo :: hallo-api ......... SUCCESS [03:04 min]
[INFO] ODL :: org.opendaylight.hallo :: hallo-impl ........ SUCCESS [ 52.270 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-cli ......... SUCCESS [ 18.734 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-features .... SUCCESS [12:12 min]
[INFO] ODL :: org.opendaylight.hallo :: hallo-karaf ....... SUCCESS [11:21 min]
[INFO] ODL :: org.opendaylight.hallo :: hallo-artifacts ... SUCCESS [  1.753 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-it .......... SUCCESS [ 48.589 s]
[INFO] hallo .............................................. SUCCESS [ 32.835 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 31:49 min
[INFO] Finished at: 2018-02-05T03:36:20+07:00
[INFO] Final Memory: 223M/718M
[INFO] ------------------------------------------------------------------------
~/devfolder/hallo$ 

$ karaf/target/assembly/bin/karaf
~/devfolder/hallo$ karaf/target/assembly/bin/karaf
Apache Karaf starting up. Press Enter to open the shell now...
100% [========================================================================]
Karaf started in 37s. Bundle stats: 294 active, 294 total
    ________                       ________                .__  .__       .__     __       
    \_____  \ ______   ____   ____ \______ \ _____  ___.__.|  | |__| ____ |  |___/  |_     
     /   |   \\____ \_/ __ \ /    \ |    |  \\__  \<   |  ||  | |  |/ ___\|  |  \   __\    
    /    |    \  |_> >  ___/|   |  \|    `   \/ __ \\___  ||  |_|  / /_/  >   Y  \  |      
    \_______  /   __/ \___  >___|  /_______  (____  / ____||____/__\___  /|___|  /__|      
            \/|__|        \/     \/        \/     \/\/            /_____/      \/          
Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown OpenDaylight.
opendaylight-user@root>log:display | grep Hallo
2018-02-05 05:12:04,249 | INFO  | rint Extender: 1 | HalloProvider                    | 208 - org.opendaylight.hallo.impl - 0.1.0.SNAPSHOT | HalloProvider Session Initiated
opendaylight-user@root>log:display | grep hallo
2018-02-05 05:12:02,773 | INFO  | Event Dispatcher | BlueprintBundleTracker           | 173 - org.opendaylight.controller.blueprint - 0.6.0.Carbon | Creating blueprint container for bundle org.opendaylight.hallo.impl_0.1.0.SNAPSHOT [208] with paths [bundleentry://208.fwk1800890735/org/opendaylight/blueprint/impl-blueprint.xml]
2018-02-05 05:12:02,780 | INFO  | Event Dispatcher | BlueprintContainerImpl           | 15 - org.apache.aries.blueprint.core - 1.6.1 | Bundle org.opendaylight.hallo.impl/0.1.0.SNAPSHOT is waiting for dependencies [(objectClass=org.opendaylight.controller.md.sal.binding.api.DataBroker)]
2018-02-05 05:12:03,826 | INFO  | rint Extender: 1 | BlueprintContainerImpl           | 15 - org.apache.aries.blueprint.core - 1.6.1 | Bundle org.opendaylight.hallo.impl/0.1.0.SNAPSHOT is waiting for dependencies [(&(type=default)(objectClass=org.opendaylight.controller.md.sal.binding.api.DataBroker))]
2018-02-05 05:12:04,249 | INFO  | rint Extender: 1 | HalloProvider                    | 208 - org.opendaylight.hallo.impl - 0.1.0.SNAPSHOT | HalloProvider Session Initiated
2018-02-05 05:12:04,291 | INFO  | ntAdminThread #9 | BlueprintBundleTracker           | 173 - org.opendaylight.controller.blueprint - 0.6.0.Carbon | Blueprint container for bundle org.opendaylight.hallo.impl_0.1.0.SNAPSHOT [208] was successfully created

A Simple RPC API

$ cat api/src/main/yang/hallo.yang

module hallo {
    yang-version 1;
    namespace "urn:opendaylight:params:xml:ns:yang:hallo";
    prefix "hallo";
    revision "2015-01-05" {
        description "Initial revision of hallo model";
    }
}

$ rm api/src/main/yang/hallo.yang && nano api/src/main/yang/hallo.yang

# new hallo.yang
module hallo {
    yang-version 1;
    namespace "urn:opendaylight:params:xml:ns:yang:hallo";
    prefix "hallo";
    revision "2015-01-05" {
        description "Initial revision of hallo model";
    }
    rpc hallo-world {
        input {
            leaf name {
                type string;
            }
        }
        output {
            leaf greeting {
                type string;
            }
        }
    }
}

$ cd api && mvn clean install -DskipTests
...
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 16.155 s
[INFO] Finished at: 2018-02-05T05:10:55+07:00
[INFO] Final Memory: 55M/580M
[INFO] ------------------------------------------------------------------------

Service

$ nano impl/src/main/java/org/opendaylight/hallo/impl/HalloWorldImpl.java

/*
 * Copyright (c) 2016 ODL and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.hallo.impl;
import java.util.concurrent.Future;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hallo.rev150105.HalloService;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hallo.rev150105.HalloWorldInput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hallo.rev150105.HalloWorldOutput;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hallo.rev150105.HalloWorldOutputBuilder;
import org.opendaylight.yangtools.yang.common.RpcResult;
import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
public class HalloWorldImpl implements HalloService {
    @Override
    public Future<RpcResult<HalloWorldOutput>> halloWorld(HalloWorldInput input) {
        HalloWorldOutputBuilder halloBuilder = new HalloWorldOutputBuilder();
        halloBuilder.setGreeting("Hallo " + input.getName());
        return RpcResultBuilder.success(halloBuilder.build()).buildFuture();
    }
}

RPC Registry Reference

$ cat impl/src/main/resources/org/opendaylight/blueprint/impl-blueprint.xml

<?xml version="1.0" encoding="UTF-8"?>
<!-- vi: set et smarttab sw=4 tabstop=4: -->
<!--
Copyright (c) 2017 hallo inc. and others. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Public License v1.0 which accompanies this distribution,
and is available at http://www.eclipse.org/legal/epl-v10.html
-->
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
  xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"
  odl:use-default-for-reference-types="true">
  <reference id="dataBroker"
    interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"
    odl:type="default" />

   <reference id="rpcRegistry"
     interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"/>

  <bean id="provider"
    class="org.opendaylight.hallo.impl.HalloProvider"
    init-method="init" destroy-method="close">
    <argument ref="dataBroker" />

    <argument ref="rpcRegistry" />

  </bean>
</blueprint>

Provider

$ cat impl/src/main/java/org/opendaylight/hallo/impl/HalloProvider.java

/*
 * Copyright (c) 2017 hallo inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.hallo.impl;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RpcRegistration;
import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hallo.rev150105.HalloService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HalloProvider {
    private static final Logger LOG = LoggerFactory.getLogger(HalloProvider.class);
    private final DataBroker dataBroker;
     private final RpcProviderRegistry rpcProviderRegistry;
     private RpcRegistration<HalloService> serviceRegistration;
     public HalloProvider(final DataBroker dataBroker,RpcProviderRegistry rpcProviderRegistry) {
        this.dataBroker = dataBroker;
        this.rpcProviderRegistry = rpcProviderRegistry;
    }
    /**
     * Method called when the blueprint container is created.
     */
    public void init() {
        serviceRegistration = rpcProviderRegistry.addRpcImplementation(HalloService.class, new HalloWorldImpl());
        LOG.info("HalloProvider Session Initiated");
    }
    /**
     * Method called when the blueprint container is destroyed.
     */
    public void close() {
        serviceRegistration.close();
        LOG.info("HalloProvider Closed");
    }
}

Build & Execute

$ cd impl && mvn clean install -DskipTests
...
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 13.266 s
[INFO] Finished at: 2018-02-05T05:31:13+07:00
[INFO] Final Memory: 56M/500M
[INFO] ------------------------------------------------------------------------

$ cd .. && mvn clean install -DskipTests
...
[INFO] ------------------------------------------------------------------------
[INFO] Reactor Summary:
[INFO] 
[INFO] ODL :: org.opendaylight.hallo :: hallo-api ......... SUCCESS [ 28.781 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-impl ........ SUCCESS [ 14.900 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-cli ......... SUCCESS [ 13.989 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-features .... SUCCESS [ 12.178 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-karaf ....... SUCCESS [ 50.341 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-artifacts ... SUCCESS [  1.773 s]
[INFO] ODL :: org.opendaylight.hallo :: hallo-it .......... SUCCESS [ 27.518 s]
[INFO] hallo .............................................. SUCCESS [ 28.513 s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 03:02 min
[INFO] Finished at: 2018-02-05T05:34:43+07:00
[INFO] Final Memory: 221M/673M
[INFO] ------------------------------------------------------------------------

$ karaf/target/assembly/bin/karaf
...
opendaylight-user@root>bundle:list | grep hallo
207 | Active   |  80 | 0.1.0.SNAPSHOT                     | ODL :: org.opendaylight.hallo :: hallo-api                         
208 | Active   |  80 | 0.1.0.SNAPSHOT                     | ODL :: org.opendaylight.hallo :: hallo-impl                        
opendaylight-user@root>feature:list | grep hallo
odl-hallo-api                   | 0.1.0-SNAPSHOT   | x         | odl-hallo-0.1.0-SNAPSHOT          | OpenDaylight :: hallo :: api                      
odl-hallo                       | 0.1.0-SNAPSHOT   | x         | odl-hallo-0.1.0-SNAPSHOT          | OpenDaylight :: hallo                             
odl-hallo-rest                  | 0.1.0-SNAPSHOT   | x         | odl-hallo-0.1.0-SNAPSHOT          | OpenDaylight :: hallo :: REST                     
odl-hallo-ui                    | 0.1.0-SNAPSHOT   | x         | odl-hallo-0.1.0-SNAPSHOT          | OpenDaylight :: hallo :: UI                       
odl-hallo-cli                   | 0.1.0-SNAPSHOT   |           | odl-hallo-0.1.0-SNAPSHOT          | OpenDaylight :: hallo :: CLI                      
opendaylight-user@root>

At this stage you can test the service via Restconf/Swagger UI or using external application (e.g. Postman). Visual guide & some screenshots are available here.

comments powered by Disqus