SDN in Action: OpenDaylight MD-SAL Programming
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.
- 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
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
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
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
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
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
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"
}
}
Test via JConsole:
gset@ubuntu:~$ $JAVA_HOME/bin/jconsole
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
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:
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.
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.
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.
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
Using Blueprint:
https://wiki.opendaylight.org/view/Using_Blueprint
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
转载于:https://blog.51cto.com/8493144/2051433