SDN in Action: OpenDaylight MD-SAL Programming

                                                                           

薛国锋     xueguofeng2011@gmail.com

 

Model-driven Service Abstraction Layer (MD-SAL) presents an opportunity to unify both northbound and southbound APIs and the data structures used in various services and components of an SDN Controller. Currently MD-SAL can provide the following infrastructure services, such as Data Store, RPC / Service routing, Notification subscription and publish services, and allow developers of applications and plugins to develop against one set of APIs that are derived from a single model: Java generated APIs, DOM APIs, and REST APIs.

 

Today we are going to develop two mini MD-SAL instances – hello and hi on top of OpenDaylight to simulate the southbound protocol plugin, such as Openflow/OVSDB, BGP-LS/PCEP or NETCONF etc, as well as the northbound service application like IP+Optical Multilayer Optimization, inter-domian ××× or traffic enginnering. With these two applications and their interactions, we can better understand OpenDaylight MD-SAL programming: YANG modeling, RESTCONF, RPC, notification and datestore, etc.


SDN in Action: OpenDaylight MD-SAL Programming_SAL


-         Setup the development Environment

-         Creat the OpenDaylight southbound provider – ‘hello’

-         Creat the OpenDaylight northbound consumer – ‘hi’

-         Test the application

-         Debug OpenDaylight with Eclipse

-         Understand OpenDaylgiht software architecture


Setup the development environment

 

OpenDaylight maintains its own repositories outside of Maven Central, which means Maven cannot resolve OpenDaylight artifacts by default. Since OpenDaylight is organized as multiple inter-dependent projects, building a particular project usually means pulling in some artifacts. In order to make this work, Maven needs to know the location of OpenDaylight repositories and has to be taught to use them.This is achieved by making sure ~/.m2/settings.xml looks something like the copy kept in odlparent. You can do that quickly with the following command:

 

gset@ubuntu:~$ cp -n ~/.m2/settings.xml{,.orig} ; \wget -q -O - https://raw.githubusercontent.com/opendaylight/odlparent/stable/boron/settings.xml > ~/.m2/settings.xml

 

gset@ubuntu:~$ gedit ~/.m2/settings.xml

 

<?xml version="1.0" encoding="UTF-8"?>

<!-- vi: set et smarttab sw=2 tabstop=2: -->

<!--

 Copyright (c) 2014, 2015 Cisco Systems, 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

-->

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"

  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

  xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

 

  <profiles>

 

    <profile>

      <id>opendaylight-release</id>

      <repositories>  // Remote repositories for the dependent JARs

        <repository>

          <id>opendaylight-mirror</id>

          <name>opendaylight-mirror</name>

          <url>https://nexus.opendaylight.org/content/repositories/public/</url>

          <releases>

            <enabled>true</enabled>

            <updatePolicy>never</updatePolicy>

          </releases>

          <snapshots>

            <enabled>false</enabled>

          </snapshots>

        </repository>

      </repositories>

      <pluginRepositories>  // Remote repositories for the Maven plugins

        <pluginRepository>

          <id>opendaylight-mirror</id>

          <name>opendaylight-mirror</name>

          <url>https://nexus.opendaylight.org/content/repositories/public/</url>

          <releases>

            <enabled>true</enabled>

            <updatePolicy>never</updatePolicy>

          </releases>

          <snapshots>

            <enabled>false</enabled>

          </snapshots>

        </pluginRepository>

      </pluginRepositories>

    </profile>

 

    <profile>

      <id>opendaylight-snapshots</id>

      <repositories>  // Remote repositories for the dependent JARs

        <repository>

          <id>opendaylight-snapshot</id>

          <name>opendaylight-snapshot</name>

          <url>https://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/</url>

          <releases>

            <enabled>false</enabled>

          </releases>

          <snapshots>

            <enabled>true</enabled>

          </snapshots>

        </repository>

      </repositories>

      <pluginRepositories>  // Remote repositories for the Maven plugins

        <pluginRepository>

          <id>opendaylight-snapshot</id>

          <name>opendaylight-snapshot</name>

          <url>https://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/</url>

          <releases>

            <enabled>false</enabled>

          </releases>

          <snapshots>

            <enabled>true</enabled>

          </snapshots>

        </pluginRepository>

      </pluginRepositories>

</profile>

 

  </profiles>

 

  <activeProfiles>

    <activeProfile>opendaylight-release</activeProfile>

    <activeProfile>opendaylight-snapshots</activeProfile>

  </activeProfiles>

</settings>

 

An archetype is defined as an original pattern or model from which all other things of the same kind are made, and can help authors create Maven project templates for users, and provides users with the means to generate parameterized versions of those project templates. Archetypes are packaged up in a JAR and they consist of the archetype metadata which describes the contents of archetype, and a set of Velocity templates which make up the prototype project.

 

The opendaylight-startup-archetype is developed for Opendaylight projects. If you use it for the first time, it will take sometime to pull all the code from the remote repository. 

 

gset@ubuntu:~/.m2/repository/org/opendaylight/controller/opendaylight-startup-archetype$ tree

SDN in Action: OpenDaylight MD-SAL Programming_MD_02


Creat the OpenDaylight southbound provider – ‘hello’

 

gset@ubuntu:~$ 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

 

Follow the prompts to set the project parameters:

groupId: org.opendaylight.hello

artifactId: hello

version: 0.1.0-SNAPSHOT

package: org.opendaylight.hello

classPrefix: Hello

copyright: xgf, Inc.

copyrightYear: 2017

 

The top level directory created by opendaylight-startup-archetype:

gset@ubuntu:~/hello$ tree -L 1

SDN in Action: OpenDaylight MD-SAL Programming_MD_03

Create or update the following files:

/home/gset/hello/api/src/main/yang/hello.yang

/home/gset/hello/impl/target/classes/org/opendaylight/blueprint/impl-blueprint.xml

/home/gset/hello/impl/src/main/java/org/opendaylight/hello/impl/HelloProvider.java

/home/gset/hello/impl/src/main/java/org/opendaylight/hello/impl/HelloWorldImpl.java


gset@ubuntu:~/hello$ gedit /home/gset/hello/api/src/main/yang/hello.yang

module hello {

    yang-version 1;

    namespace "urn:opendaylight:params:xml:ns:yang:hello";

    prefix "hello";

    revision "2015-01-05" {

        description "Initial revision of hello model";

    }

    container hello {

      presence

        "Indicates the hello service is available";

      description

        "Top-level container for all hello database objects.";

      leaf number1 {

        type uint32;

        config true;            // Config data, not operational data

        default 1;

        description

         "number1";

      }

    }

    rpc hello-world {

        input {

            leaf name {

                type string;

            }

        }

        output {

            leaf greeting {

                type string;

            }

        }

    }

   notification helloDone {

     description

       "Done";

   }

}


gset@ubuntu:~/hello$ gedit /home/gset/hello/impl/target/classes/org/opendaylight/blueprint/impl-blueprint.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- vi: set et smarttab sw=4 tabstop=4: -->

<!--

Copyright © 2017 xgf, 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"   // Import the singleton OSGi service -  DataBroker

    interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"

    odl:type="default" />

 

  <reference id="rpcRegistry// Import the singleton OSGi service -  RpcProviderRegistry

     interface="org.opendaylight.controller.sal.binding.api.RpcProviderRegistry"/>

 

  <reference id="notificationService"   // Import the singleton OSGi service -  NotificationPublishService

    interface="org.opendaylight.controller.md.sal.binding.api.NotificationPublishService"/>

 

  <bean id="helloprovider" class="org.opendaylight.hello.impl.HelloProvider"

    init-method="init" destroy-method="close">

    <argument ref="dataBroker" />

    <argument ref="rpcRegistry" />

    <argument ref="notificationService" />    

  </bean>

// Inject the dependencies via constructor args when instantiate the Java object - helloprovider

 

</blueprint>


gset@ubuntu:~/hello$ gedit /home/gset/hello/impl/src/main/java/org/opendaylight/hello/impl/HelloProvider.java

package org.opendaylight.hello.impl;

 

import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;

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.hello.rev150105.HelloService;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

import static org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType.CONFIGURATION;

import java.util.Collection;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.Hello;

import org.opendaylight.controller.md.sal.binding.api.DataObjectModification;

import org.opendaylight.controller.md.sal.binding.api.DataTreeChangeListener;

import org.opendaylight.controller.md.sal.binding.api.DataTreeModification;

import org.opendaylight.controller.md.sal.binding.api.DataTreeIdentifier;

import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;

import org.opendaylight.yangtools.concepts.ListenerRegistration;

 

public class HelloProvider implements DataTreeChangeListener<Hello> {

 

    private static final Logger LOG = LoggerFactory.getLogger(HelloProvider.class);

 

    private DataBroker dataBroker;

    private RpcProviderRegistry rpcProviderRegistry;

    private NotificationPublishService notificationProvider;

 

    private RpcRegistration<HelloService> serviceRegistration;

 

    private ListenerRegistration<HelloProvider> dataTreeChangeListenerRegistration;

    private static final InstanceIdentifier<Hello> HELLO_IID = InstanceIdentifier.builder(Hello.class).build();

 

    public HelloProvider( DataBroker dataBroker, RpcProviderRegistry rpcProviderRegistry,

                                                  NotificationPublishService notificationPublishService)

    {

        this.dataBroker = dataBroker;

        this.rpcProviderRegistry = rpcProviderRegistry;

        this.notificationProvider = notificationPublishService;

    }

 

    public void init() {

        serviceRegistration = rpcProviderRegistry.addRpcImplementation(HelloService.class,

 new HelloWorldImpl(this.notificationProvider));

                 // Register the RPC implementation via the RPC Broker

 

        dataTreeChangeListenerRegistration = dataBroker.registerDataTreeChangeListener(

                               new DataTreeIdentifier<>(CONFIGURATION, HELLO_IID), this);

                 // Register via MD-SAL to listen on the change of config datastore

 

        LOG.info("HelloProvider Session Initiated");

    }

 

    public void close() {

        serviceRegistration.close();

        LOG.info("HelloProvider Closed");

    }

 

    @Override

    public void onDataTreeChanged(Collection<DataTreeModification<Hello>> changes) {

 

                   for(DataTreeModification<Hello> change: changes) {

                           DataObjectModification<Hello> rootNode = change.getRootNode();

 

                           if(rootNode.getModificationType() == DataObjectModification.ModificationType.WRITE) {

                               Hello oldHello = rootNode.getDataBefore();

                               Hello newHello = rootNode.getDataAfter();

                              

                                       System.out.print("\nThe datastore changed - the hello package");

 

                               Long number11 = oldHello.getNumber1();             

                               if(number11 != null) {

                                   System.out.print("\nThe old number is " +number11);

                               }

 

                               Long number12 = newHello.getNumber1();

                               if(number12 != null) {

                                   System.out.print("\nThe new number is " +number12);

                               }

 

                           }

                         else if(rootNode.getModificationType() == DataObjectModification.ModificationType.DELETE) {

                           }

                       }

    }

}

 

gset@ubuntu:~/hello$ gedit /home/gset/hello/impl/src/main/java/org/opendaylight/hello/impl/HelloWorldImpl.java

import java.util.concurrent.Future;

 

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloDone;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloDoneBuilder;

 

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloService;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldInput;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutput;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutputBuilder;

import org.opendaylight.yangtools.yang.common.RpcResult;

import org.opendaylight.yangtools.yang.common.RpcResultBuilder;

import org.opendaylight.controller.md.sal.binding.api.NotificationPublishService;

 

public class HelloWorldImpl implements HelloService {

 

    private NotificationPublishService notificationProvider;

 

    public HelloWorldImpl( NotificationPublishService notificationPublishService)

    {

        this.notificationProvider = notificationPublishService;

    }

 

    @Override

public Future<RpcResult<HelloWorldOutput>> helloWorld(HelloWorldInput input) {

   // The RPC implementation of helloworld

        HelloWorldOutputBuilder helloBuilder = new HelloWorldOutputBuilder();

        helloBuilder.setGreeting("\nHello " + input.getName() + ", done by the hello package");

 

   // Send the notification

        notificationProvider.offerNotification(new HelloDoneBuilder().build());

        System.out.print("\nSent the notifiation, done by the hello package");

 

        return RpcResultBuilder.success(helloBuilder.build()).buildFuture();

    }

}

 

gset@ubuntu:~/hello$ mvn clean install

gset@ubuntu:~/hello$ mvn clean install –DskipTests

[INFO] ODL :: org.opendaylight.hello :: hello-api ......... SUCCESS [ 13.812 s]

[INFO] ODL :: org.opendaylight.hello :: hello-impl ........ SUCCESS [  5.375 s]

[INFO] ODL :: org.opendaylight.hello :: hello-cli ......... SUCCESS [  5.205 s]

[INFO] ODL :: org.opendaylight.hello :: hello-features .... SUCCESS [01:59 min]

[INFO] ODL :: org.opendaylight.hello :: hello-karaf ....... SUCCESS [ 18.865 s]

[INFO] ODL :: org.opendaylight.hello :: hello-artifacts ... SUCCESS [  0.815 s]

[INFO] ODL :: org.opendaylight.hello :: hello-it .......... SUCCESS [ 36.549 s]

[INFO] hello .............................................. SUCCESS [ 13.035 s]

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 03:35 min

[INFO] Finished at: 2017-12-10T14:31:10-08:00

[INFO] Final Memory: 223M/861M

[INFO] ------------------------------------------------------------------------


Creat the OpenDaylight northbound consumer – ‘hi’

 

gset@ubuntu:~$ 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

 

Follow the prompts to set the project parameters:

groupId: org.opendaylight.hi

artifactId: hi

version: 0.1.0-SNAPSHOT

package: org.opendaylight.hi

classPrefix: Hi

copyright: xgf, Inc.

copyrightYear: 2017

 

The top level directory created by opendaylight-startup-archetype:

gset@ubuntu:~/hi$ tree -L 1

SDN in Action: OpenDaylight MD-SAL Programming_MD_04

Create or update the following files:

/home/gset/hi/impl/pom.xml      // add the dependency on hell-api

/home/gset/hi/api/src/main/yang/hi.yang

/home/gset/hi/impl/target/classes/org/opendaylight/blueprint/impl-blueprint.xml

/home/gset/hi/impl/src/main/java/org/opendaylight/hi/impl/HiServiceRuntimeMXBean.java

/home/gset/hi/impl/src/main/java/org/opendaylight/hi/impl/HiProvider.java

 

gset@ubuntu:~/hi$ gedit /home/gset/hi/impl/pom.xml

…..

<dependencies>

    …..

    <dependency>

      <groupId>org.opendaylight.hello</groupId>

      <artifactId>hello-api</artifactId>         // Add the dependency on the hello-api package

      <version>0.1.0-SNAPSHOT</version>

    </dependency>

    …..

  </dependencies>

…..

 

gset@ubuntu:~/hi$ gedit /home/gset/hi/api/src/main/yang/hi.yang

module hi {

    yang-version 1;

    namespace "urn:opendaylight:params:xml:ns:yang:hi";

    prefix "hi";

    revision "2015-01-05" {

        description "Initial revision of hi model";

    }

    rpc hi-world {

        input {

            leaf name {

                type string;

            }

        }

        output {

            leaf greeting {

                type string;

            }

        }

    }

}

 

gset@ubuntu:~$ gedit /home/gset/hi/impl/target/classes/org/opendaylight/blueprint/impl-blueprint.xml

<?xml version="1.0" encoding="UTF-8"?>

<!-- vi: set et smarttab sw=4 tabstop=4: -->

<!--

Copyright © 2017 xgf, 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" // Import the singleton OSGi service -  DataBroker

    interface="org.opendaylight.controller.md.sal.binding.api.DataBroker"

    odl:type="default" />

 

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

// Import the singleton OSGi service -  RpcProviderRegistry

 

  <bean id="hiprovider" class="org.opendaylight.hi.impl.HiProvider"

    init-method="init" destroy-method="close">

    <argument ref="dataBroker" />

    <argument ref="rpcRegistry" />

  </bean>

// Inject the dependencies via constructor args when instantiate the Java object - hiprovider

 

  <odl:rpc-implementation ref="hiprovider"/>

// Automatically finds the implemented RpcService interface – hiprovider, and registers the implementation with the MD-SAL RpcProviderRegistry.

 

  <odl:notification-listener ref="hiprovider"/>

// Register the NotificationListener implemenentation – hiprovider with the MD-SAL NotificationService to receive yang notifications.

 

</blueprint>

 

gset@ubuntu:~/hi$

gedit /home/gset/hi/impl/src/main/java/org/opendaylight/hi/impl/HiServiceRuntimeMXBean.java

package org.opendaylight.hi.impl;

 

public interface HiServiceRuntimeMXBean {

    Boolean Test1();

    void    Test2();

    int     Test3();

}

 

gset@ubuntu:~/hi$ gedit /home/gset/hi/impl/src/main/java/org/opendaylight/hi/impl/HiProvider.java

package org.opendaylight.hi.impl;

 

import java.util.concurrent.Future;

 

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.hi.rev150105.HiService;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

import org.opendaylight.controller.md.sal.common.util.jmx.AbstractMXBean;

 

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hi.rev150105.HiService;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hi.rev150105.HiWorldInput;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hi.rev150105.HiWorldOutput;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hi.rev150105.HiWorldInputBuilder;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hi.rev150105.HiWorldOutputBuilder;

 

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloService;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldInput;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutput;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldInputBuilder;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloWorldOutputBuilder;

 

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloListener;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloDone;

import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.hello.rev150105.HelloDoneBuilder;

 

import org.opendaylight.yangtools.yang.common.RpcResult;

import org.opendaylight.yangtools.yang.common.RpcResultBuilder;

 

public class HiProvider extends AbstractMXBean implements HiServiceRuntimeMXBean, HiService, HelloListener{

 

    private static final Logger LOG = LoggerFactory.getLogger(HiProvider.class);

 

    private final DataBroker dataBroker;

    private final RpcProviderRegistry rpcProviderRegistry;

 

    public HiProvider(final DataBroker dataBroker,RpcProviderRegistry rpcProviderRegistry) {

        super("HiProvider", "HiProvider-test", null);  // Register via MxBean

        this.dataBroker = dataBroker;

        this.rpcProviderRegistry = rpcProviderRegistry;

}

 

    public void init() {

        register();

        LOG.info("HiProvider Session Initiated");

    }

 

    public void close() {

       unregister();

       LOG.info("HiProvider Closed");

    }

 

    @Override

public Future<RpcResult<HiWorldOutput>> hiWorld(HiWorldInput input) {

   // The RPC implementation of hiworld

        HiWorldOutputBuilder hiBuilder = new HiWorldOutputBuilder();

        hiBuilder.setGreeting("\nHi " + input.getName() + ", done by the hi package");

        return RpcResultBuilder.success(hiBuilder.build()).buildFuture();

    }

   

    @Override

    public void onHelloDone(HelloDone notification)

{          // Receive the notification

                System.out.print("\nReceived messages from the Hello's notification - done by the hi package..........");

    }

       

    @Override

    public Boolean Test1() {

                System.out.print("\n--------------------Test1 !");

                String name = "Richard Xue";

               

                try {

                                HelloService service = rpcProviderRegistry.getRpcService(HelloService.class);

                                HelloWorldInput input = new HelloWorldInputBuilder().setName(name).build();

                                Future<RpcResult<HelloWorldOutput>> outputFuture = service.helloWorld(input);

                                RpcResult<HelloWorldOutput> outputResult = outputFuture.get();

       

                                System.out.print("\nCalled the Hello's RPC, done by the hi package ..........");

                                if(outputResult.isSuccessful())

                                                System.out.print(outputResult.getResult().getGreeting());

                                else

                                                System.out.print("\nSomething went wrong !");

                }

                catch (Exception e) {

                                e.printStackTrace();

               }

return Boolean.TRUE;

    }

   

    @Override

    public void    Test2()

    {

                System.out.print("\n--------------------Test2 !");

                String name = "Richard Xue";

               

                try {

                                HiService service = rpcProviderRegistry.getRpcService(HiService.class);

                                HiWorldInput input = new HiWorldInputBuilder().setName(name).build();

                                Future<RpcResult<HiWorldOutput>> outputFuture = service.hiWorld(input);

                                RpcResult<HiWorldOutput> outputResult = outputFuture.get();

       

                                System.out.print("\nCalled the Hi's RPC, done by the hi package ..........");

                                if(outputResult.isSuccessful())

                                                System.out.print(outputResult.getResult().getGreeting());

                                else

                                                System.out.print("\nSomething went wrong !");

                }

                catch (Exception e) {

                e.printStackTrace();

}

                return;

    }

   

    @Override public int Test3()

    {

                System.out.print("\n--------------------Test3 !");

                return 1;

    }

}

 

// Because its dependence - ‘hello’ is not avaliable during the test , ‘hi-it’ will fail.

gset@ubuntu:~/hi$ mvn clean install –DskipTests

[INFO] ODL :: org.opendaylight.hi :: hi-api ............... SUCCESS [ 10.777 s]

[INFO] ODL :: org.opendaylight.hi :: hi-impl .............. SUCCESS [  6.243 s]

[INFO] ODL :: org.opendaylight.hi :: hi-cli ............... SUCCESS [  4.444 s]

[INFO] ODL :: org.opendaylight.hi :: hi-features .......... SUCCESS [  8.012 s]

[INFO] ODL :: org.opendaylight.hi :: hi-karaf ............. SUCCESS [ 25.598 s]

[INFO] ODL :: org.opendaylight.hi :: hi-artifacts ......... SUCCESS [  1.305 s]

[INFO] ODL :: org.opendaylight.hi :: hi-it ................ SUCCESS [ 14.660 s]

[INFO] hi ................................................. SUCCESS [ 17.340 s]

[INFO] ------------------------------------------------------------------------

[INFO] BUILD SUCCESS

[INFO] ------------------------------------------------------------------------

[INFO] Total time: 01:30 min

[INFO] Finished at: 2017-12-10T14:28:56-08:00

[INFO] Final Memory: 225M/873M

[INFO] ------------------------------------------------------------------------


Test the application

 

gset@ubuntu:~/hello$ mvn clean install –DskipTests

// Upload the hi package to hello, and run hello

gset@ubuntu:~/hello$ cp -r /home/gset/hi/karaf/target/assembly/system/org/opendaylight/hi  /home/gset/hello/karaf/target/assembly/system/org/opendaylight/hi

gset@ubuntu:~/hello$  ./karaf/target/assembly/bin/karaf clean

SDN in Action: OpenDaylight MD-SAL Programming_SAL _05

opendaylight-user@root>feature:repo-add mvn:org.opendaylight.hi/hi-features/0.1.0-SNAPSHOT/xml/features

Adding feature url mvn:org.opendaylight.hi/hi-features/0.1.0-SNAPSHOT/xml/features

// Install the hi package

opendaylight-user@root>feature:install odl-hi-api odl-hi odl-hi-rest odl-hi-ui odl-hi-cli

opendaylight-user@root>feature:list | grep hello

opendaylight-user@root>feature:list | grep hi

SDN in Action: OpenDaylight MD-SAL Programming_SAL _06

Other frequently-used commands:

opendaylight-user@root>log:display | grep hello

opendaylight-user@root>feature:list | grep hello

opendaylight-user@root>log:tail | grep hello

opendaylight-user@root>feature:info odl-hi-api

opendaylight-user@root>system:shutdown –f

 

Test RPC, Notification and Datastore via Yangman:

http://localhost:8181/index.html

admin/admin

SDN in Action: OpenDaylight MD-SAL Programming_SAL _07

SDN in Action: OpenDaylight MD-SAL Programming_SAL _08

SDN in Action: OpenDaylight MD-SAL Programming_SAL _09


Test via Simple REST Client:

URL: http://10.0.0.150:8181/restconf/operations/hi:hi-world

Method: POST

Headers: Content-Type: application/json

Data:

{

    "input": {

    "name": "Mike Wang"

  }

}

SDN in Action: OpenDaylight MD-SAL Programming_SAL _10

Test  via JConsole:

gset@ubuntu:~$ $JAVA_HOME/bin/jconsole

SDN in Action: OpenDaylight MD-SAL Programming_SAL _11

SDN in Action: OpenDaylight MD-SAL Programming_SAL _12


Debug OpenDaylight with Eclipse

 

Backup the current eclipse-workspace. Run Eclispe: File/Import/Maven/Existing Maven Projects/Hello/Finish: for “Install - Check the items that you wish to install”, cancel it.

 

Run and debug the whole application:

gset@ubuntu:~/hello$ ./karaf/target/assembly/bin/karaf debug

SDN in Action: OpenDaylight MD-SAL Programming_SAL _13

Run and debug hello-it:

gset@ubuntu:~/ODL/hello/it$ mvn clean install  -Dkaraf.debug

Running org.opendaylight.hello.it.HelloIT

Listening for transport dt_socket at address: 5005

 

In Eclipse, select a project and change its Debug Configurations – Remote Java Application: ‘5005’ for port:

SDN in Action: OpenDaylight MD-SAL Programming_SAL _14


Understand OpenDaylight software architecture

 

OpenDaylight Controller is a modular open platform for customizing and automating networks of any size and scale, and as a JVM it can be run on any OS and Metal as long as it supports Java. The basic platform of OpenDaylight is Karaf, which is powered by OSGi, and supports hot deployment, dyamic loading bundles, SSH and dynamic configuration.

SDN in Action: OpenDaylight MD-SAL Programming_MD_15

Dependency injection frameworks support writing modular, flexible and clean code. OpenDaylight has the home-grown config subsystem as dependency injection framework, using yang modeling language for modeling the configuration, dependencies and state data for modules, while Blueprint is more user-friendly and now is the alternate to config subsystem. Blueprint is an OSGi compendium spec for a dependency injection framework designed specifically for use in an OSGi container. It was derived from Spring DM and is very similar. Karaf includes the Apache Aries blueprint implementation with its base features. To use blueprint a bundle provides XML resource(s) that describe what OSGi service dependencies are needed, what Java objects to instantiate for the bundle's business logic and how to wire them together. In addition, a bundle can export/advertise its own OSGi services.

 

YANG is a data modeling language used to model configuration and state data manipulated by the Network Configuration Protocol (NETCONF). YANG models the hierarchical organization of data as a tree in which each node has a name, and either a value or a set of child nodes. In order to describe the structure of data provided by controller components, YANG is proposed as the modeling language for service and data abstractions, and  YangTools can generate Java code parsing yang models and RESTCONF. Developer of a module that provides some functionality (a service, data, functions/procedure) can define a schema and thus create simpler APIs for the provided functionality, and thereby lower the risk of incorrect interpretation of data structures exposed through the MD-SAL.

SDN in Action: OpenDaylight MD-SAL Programming_SAL _16


Model-Driven SAL (MD-SAL) is the kernel of OpenDaylight Controller, and it provides a variety of functions required for adaptation between providers and consumers. First, it routes RPC calls between consumers and providers (RPC Broker). Second, it provides a subscription-based mechanism for delivery of notifications from publishers to subscribers (Notification Broker). Third, it routes data reads from consumers to a particular datastore and coordinates data changes between providers (Data Broker). Finally, it creates and manages Mounts (Mount Manager).

 

An RPC is a one-to-one call triggered by a consumer, which may be processed by a provider either local or remote.

A notification is an event, which a consumer may be interested in to receive, and which is triggered / originated in a Provider.

The datastore is a conceptual data tree, which is described by YANG schemas.  

A path is a unique locator of a leaf or sub-tree in the conceptual data tree.

A mount is a logically-nested MD-SAL instance, which may be using a separate set of YANG models; it supports its own RPCs and notifications and it allows for reusing device models and a context in network-wide contexts without having to redefine the device models in the controller.


SDN in Action: OpenDaylight MD-SAL Programming_SAL _17


The implementation of the above SAL functions requires the use of two data representations and two sets of SAL Plugin APIs.

The Binding-Independent data format/APIs is a Data Object Model (DOM) representation of YANG trees. This format is suitable for generic components, such as the data store, the NETCONF Connector, RESTCONF, which can derive behavior from a YANG model itself. The Binding- Aware data format/APIs is a specific YANG to Java language binding, which specifies how Java Data Transfer Objects (DTOs) and APIs are generated from YANG model. The API definition for these DTOs, interfaces for invoking / implementing RPCs, interfaces containing Notification callbacks are generated at compile time. Codecs to translate between the Java DTOs and DOM representation are generated on demand at run time. Note that the functionality and performance requirements for both data representations are the same.

 


Prepare for OpenDaylight Code:

http://blog.51cto.com/8493144/2049106

GettingStarted:Development Environment Setup:

https://wiki.opendaylight.org/view/GettingStarted:Development_Environment_Setup

OpenDaylight Controller:MD-SAL:Startup Project Archetype:

https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Startup_Project_Archetype

OpenDaylight Controller:MD-SAL:MD-SAL Document Review:Architecture

https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:MD-SAL_Document_Review:Architecture

Using Blueprint:

https://wiki.opendaylight.org/view/Using_Blueprint

Controller Core Functionality Tutorials:Application Development Tutorial:

https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Application_Development_Tutorial

OpenDaylight Controller:MD-SAL:Toaster Step-By-Step:

https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Toaster_Step-By-Step