SDN in Action: OpenDaylight MD-SAL Programming

                                                                           

薛国锋     [email protected]

 

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_第1张图片


-         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

 

  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">

 

 

 

   

      opendaylight-release

        // Remote repositories for the dependent JARs

       

          opendaylight-mirror

          opendaylight-mirror

          https://nexus.opendaylight.org/content/repositories/public/

         

            true

            never

         

         

            false

         

       

     

        // Remote repositories for the Maven plugins

       

          opendaylight-mirror

          opendaylight-mirror

          https://nexus.opendaylight.org/content/repositories/public/

         

            true

            never

         

         

            false

         

       

     

   

 

   

      opendaylight-snapshots

       // Remote repositories for the dependent JARs

       

          opendaylight-snapshot

          opendaylight-snapshot

          https://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/

         

            false

         

         

            true

         

       

     

       // Remote repositories for the Maven plugins

       

          opendaylight-snapshot

          opendaylight-snapshot

          https://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/

          

            false

         

         

            true

         

       

     

 

 

 

 

    opendaylight-release

    opendaylight-snapshots

 

 

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_第2张图片


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_第3张图片

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

  xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"

  odl:use-default-for-reference-types="true">

 

  // Import the singleton OSGi service -  DataBroker

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

    odl:type="default" />

 

  // Import the singleton OSGi service -  RpcProviderRegistry

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

 

  // Import the singleton OSGi service -  NotificationPublishService

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

 

 

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

   

   

        

 

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

 


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 {

 

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

 

    private DataBroker dataBroker;

    private RpcProviderRegistry rpcProviderRegistry;

    private NotificationPublishService notificationProvider;

 

    private RpcRegistration serviceRegistration;

 

    private ListenerRegistration dataTreeChangeListenerRegistration;

    private static final InstanceIdentifier 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> changes) {

 

                   for(DataTreeModification change: changes) {

                           DataObjectModification 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> 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_第4张图片

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

…..

    …..

   

      org.opendaylight.hello

      hello-api         // Add the dependency on the hello-api package

      0.1.0-SNAPSHOT

   

    …..

 

…..

 

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

  xmlns:odl="http://opendaylight.org/xmlns/blueprint/v1.0.0"

  odl:use-default-for-reference-types="true">

 

  // Import the singleton OSGi service -  DataBroker

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

    odl:type="default" />

 

 

// Import the singleton OSGi service -  RpcProviderRegistry

 

 

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

   

   

 

// 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.

 

 

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> 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> outputFuture = service.helloWorld(input);

                                RpcResult 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> outputFuture = service.hiWorld(input);

                                RpcResult 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_第5张图片

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_第6张图片

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_第7张图片

SDN in Action: OpenDaylight MD-SAL Programming_第8张图片

SDN in Action: OpenDaylight MD-SAL Programming_第9张图片


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_第10张图片

Test  via JConsole:

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

SDN in Action: OpenDaylight MD-SAL Programming_第11张图片

SDN in Action: OpenDaylight MD-SAL Programming_第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_第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_第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_第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_第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_第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:

https://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