ODL + Maven + OSGi

Maven is a project/build management tool that is used in OpenDaylight to handle the builds and dependencies. I decided to try and build an OSGi bundle from scratch that interacts with ODL, that is similar to the python stats script I wrote earlier.

To get started you can run mvn from the command prompt to have Maven build your project directory structure. Maven dictates a general structure for where your source, test, and binary files will go. I used the following command for to setup my project which I’m calling mystats (with a group ID of example.com):

mvn archetype:generate -DgroupId=com.example -DartifactId=mystats -DarchetypeArtifactId=maven-archetype-quickstart -Dpackage=com.example.mystats -DinteractiveMode=false

This creates a directory mystats with the following structure:

   |-src
   |---main
   |-----java
   |-------com
   |---------example
   |-----------mystats

It also creates a pom.xml file which is key to the maven build process.

As you may recall from my previous post on OSGi, an OSGi bundle has a manifest file that declares the various packages that are imported and exported. By using a plugin, maven can generate this for you while doing the build and insert it into your bundle with the jar file. To make all this work I had to make the following changes to the pom file:

  • Added a parent that references the ODL project. Note the relativePath field which needs to point to your ODL pom.xml. (lines 4-9)
  • Changed the packaging from jar to osgi. (line 15)
  • Added the felix plugin for OSGi, and declared the packages that will be imported an what this bundle will export. (lines 18-48)

Here we also define an Activator class for the bundle

  • I am importing the following:
    • statisticsmanager – to get the stats
    • switchmanager – to find all the switches
    • sal – service abstraction layer
    • slf4j – logging
    • apache.felix.dm – dependency management
  • Declare our dependencies, in this case statisticamanager and sal.  (lines 49-66)
    • The dependencies are transitive and should grab what is needed, but I ended up putting the sal in there manually to get the correct version.
    • Remember when you did a mvn clean install when building the controller? That installs a bunch of the stuff you’ll be accessing in your ~/.m2/repositories directory. Go have a look there to see where some of this stuff is coming from.

Next we need to create the Activator.java and MyStats.java files.  The Activator helps manage the lifecycle of the bundle, allowing you to specify what gets run when the bundle gets initialized, starts, stops, etc.  I probably don’t even need one for this simple example, but I put one in anyway.  The MyStats file is the actual program that uses the IStatisticsManager interface to pull the stats for all the switches.

Once everything is coded up we can build the package with Maven by executing a mvn package which builds the bundle/package.

$ mvn package
[INFO] Scanning for projects...
[INFO]                                                                         
[INFO] ------------------------------------------------------------------------
[INFO] Building mystats 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO] 
[INFO] --- properties-maven-plugin:1.0-alpha-2:set-system-properties (default) @ mystats ---
[INFO] Set 1 system property
[INFO] 
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ mystats ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/fhsu/code/odl/mystats/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:compile (default-compile) @ mystats ---
[INFO] Compiling 2 source files to /home/fhsu/code/odl/mystats/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ mystats ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /home/fhsu/code/odl/mystats/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:2.3.2:testCompile (default-testCompile) @ mystats ---
[INFO] Nothing to compile - all classes are up to date
[INFO] 
[INFO] --- maven-surefire-plugin:2.14.1:test (default-test) @ mystats ---
[INFO] Surefire report directory: /home/fhsu/code/odl/mystats/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.example.mystats.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.006 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0

[INFO] 
[INFO] --- maven-bundle-plugin:2.3.6:bundle (default-bundle) @ mystats ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.872s
[INFO] Finished at: Mon May 13 16:27:20 PDT 2013
[INFO] Final Memory: 23M/340M
[INFO] ------------------------------------------------------------------------

If you’re running Eclipse, you may need to update the packages using the m2e tool. Now we can install it in the OSGi runtime that is running the controller:

osgi> install file:/Users/fhsu/dev/eclipse/odlworkspace/mystats/target/mystats-0.0.1-SNAPSHOT.jar
Bundle id is 117
RegisteredServices   null
ServicesInUse        null
Fragments            null
ClassLoader          null
LoaderProxy          com.example.mystats; bundle-version="0.0.1.SNAPSHOT"
Headers               Bnd-LastModified = 1368335490782
 Build-Jdk = 1.7.0_17
 Built-By = fhsu
 Bundle-Activator = com.example.mystats.Activator
 Bundle-ManifestVersion = 2
 Bundle-Name = mystats
 Bundle-SymbolicName = com.example.mystats
 Bundle-Version = 0.0.1.SNAPSHOT
 Created-By = Apache Maven Bundle Plugin
 Export-Package = com.example.mystats;uses:="org.opendaylight.controller.sal.core,org.apache.felix.dm,org.slf4j,org.opendaylight.controller.sal.flowprogrammer,org.opendaylight.controller.statisticsmanager,org.opendaylight.controller.switchmanager,org.opendaylight.controller.sal.reader,org.opendaylight.controller.sal.match,org.opendaylight.controller.sal.utils";version="0.0.1.SNAPSHOT"
 Import-Package = org.apache.felix.dm;version="[3.0,4)",org.opendaylight.controller.sal.core;version="[0.4,1)",org.opendaylight.controller.sal.flowprogrammer;version="[0.4,1)",org.opendaylight.controller.sal.match;version="[0.4,1)",org.opendaylight.controller.sal.reader;version="[0.4,1)",org.opendaylight.controller.sal.utils;version="[0.4,1)",org.opendaylight.controller.statisticsmanager;version="[0.4,1)",org.opendaylight.controller.switchmanager;version="[0.4,1)",org.slf4j;version="[1.7,2)"
 Manifest-Version = 1.0
 Tool = Bnd-1.50.0

BundleContext        null
BundleId             117
StartLevel           1
SymbolicName         com.example.mystats
Location             file:/Users/fhsu/dev/eclipse/odlworkspace/mystats/target/mystats-0.0.1-SNAPSHOT.jar
State                2
Bundle                 117|Installed  |    1|com.example.mystats(0.0.1.SNAPSHOT)
Version              0.0.1.SNAPSHOT
BundleData           com.example.mystats.0.1.SNAPSHOT
KeyHashCode          117
StateChanging        null
BundleDescription    com.example.mystats.0.1.SNAPSHOT
Framework            org.eclipse.osgi.framework.internal.core.Framework@10695754
ResolutionFailureException org.osgi.framework.BundleException: The bundle "com.example.mystats.0.1.SNAPSHOT [117]" could not be resolved
Revisions            [com.example.mystats.0.1.SNAPSHOT]
ProtectionDomain     null
Key                  117
LastModified         1368335508452

Now we can start our bundle and see the results:

osgi> start 117
Node: OF|00:00:00:00:00:00:00:07
 DST: NW_DST(10.0.0.3,null) Bytes: 784
 DST: NW_DST(10.0.0.6,null) Bytes: 784
 DST: NW_DST(10.0.0.8,null) Bytes: 2646
 DST: NW_DST(10.0.0.2,null) Bytes: 686
 DST: NW_DST(10.0.0.4,null) Bytes: 784
 DST: NW_DST(10.0.0.7,null) Bytes: 2548
 DST: NW_DST(10.0.0.5,null) Bytes: 784
 DST: NW_DST(10.0.0.1,null) Bytes: 588
Node: OF|00:00:00:00:00:00:00:06
 DST: NW_DST(10.0.0.3,null) Bytes: 784
 DST: NW_DST(10.0.0.6,null) Bytes: 2548
 DST: NW_DST(10.0.0.8,null) Bytes: 784
 DST: NW_DST(10.0.0.2,null) Bytes: 588
 DST: NW_DST(10.0.0.4,null) Bytes: 784
 DST: NW_DST(10.0.0.7,null) Bytes: 784
 DST: NW_DST(10.0.0.5,null) Bytes: 2548
 DST: NW_DST(10.0.0.1,null) Bytes: 588
Node: OF|00:00:00:00:00:00:00:05
 DST: NW_DST(10.0.0.3,null) Bytes: 1568
 DST: NW_DST(10.0.0.6,null) Bytes: 2156
 DST: NW_DST(10.0.0.8,null) Bytes: 2254
 DST: NW_DST(10.0.0.2,null) Bytes: 1274
 DST: NW_DST(10.0.0.4,null) Bytes: 1568
 DST: NW_DST(10.0.0.7,null) Bytes: 2156
 DST: NW_DST(10.0.0.5,null) Bytes: 2156
 DST: NW_DST(10.0.0.1,null) Bytes: 1176
Node: OF|00:00:00:00:00:00:00:04
 DST: NW_DST(10.0.0.3,null) Bytes: 2548
 DST: NW_DST(10.0.0.6,null) Bytes: 784
 DST: NW_DST(10.0.0.8,null) Bytes: 784
 DST: NW_DST(10.0.0.2,null) Bytes: 588
 DST: NW_DST(10.0.0.4,null) Bytes: 2548
 DST: NW_DST(10.0.0.7,null) Bytes: 784
 DST: NW_DST(10.0.0.5,null) Bytes: 784
 DST: NW_DST(10.0.0.1,null) Bytes: 588
Node: OF|00:00:00:00:00:00:00:03
 DST: NW_DST(10.0.0.3,null) Bytes: 588
 DST: NW_DST(10.0.0.6,null) Bytes: 588
 DST: NW_DST(10.0.0.8,null) Bytes: 686
 DST: NW_DST(10.0.0.2,null) Bytes: 2058
 DST: NW_DST(10.0.0.4,null) Bytes: 588
 DST: NW_DST(10.0.0.7,null) Bytes: 588
 DST: NW_DST(10.0.0.5,null) Bytes: 588
 DST: NW_DST(10.0.0.1,null) Bytes: 1960
Node: OF|00:00:00:00:00:00:00:02
 DST: NW_DST(10.0.0.3,null) Bytes: 2156
 DST: NW_DST(10.0.0.6,null) Bytes: 1372
 DST: NW_DST(10.0.0.8,null) Bytes: 1470
 DST: NW_DST(10.0.0.2,null) Bytes: 1862
 DST: NW_DST(10.0.0.4,null) Bytes: 2156
 DST: NW_DST(10.0.0.7,null) Bytes: 1372
 DST: NW_DST(10.0.0.5,null) Bytes: 1372
 DST: NW_DST(10.0.0.1,null) Bytes: 1764
Node: OF|00:00:00:00:00:00:00:01
 DST: NW_DST(10.0.0.3,null) Bytes: 1568
 DST: NW_DST(10.0.0.6,null) Bytes: 1372
 DST: NW_DST(10.0.0.8,null) Bytes: 1470
 DST: NW_DST(10.0.0.2,null) Bytes: 1274
 DST: NW_DST(10.0.0.4,null) Bytes: 1568
 DST: NW_DST(10.0.0.7,null) Bytes: 1372
 DST: NW_DST(10.0.0.5,null) Bytes: 1372
 DST: NW_DST(10.0.0.1,null) Bytes: 1176
osgi>

This is a pretty simple example, but I found it useful to start understanding how all these things work together. Let me know if I’ve misinterpreted anything! You can find all the code on github:
https://github.com/fredhsu/odl-apps

Advertisements

10 thoughts on “ODL + Maven + OSGi

  1. martin says:

    Hi, It is great to see you blog, i am also interest in ODL, but i am totally new to OSGi, So i don’t really understand how it works? So i have a couple of question need your help.
    1. How bundle get service from other bundle, Let’s take arphandler bundle as a example, obiviously, arphandler need service from sal DataPacketService, Seems that arphandler don’t initialited retrieve that service, but someone inject this service to it by callback function named setDataPacketService, when this callback will be called?
    2. How ODL contoller will get startup? what sequence of bundle’s startup, is there any configuration file there?

  2. Zuhran Khan Khattak says:

    Please can you tell me in which directory should I create the new Module?
    I am using the directory which is the workspace for eclipse. After modifying the pom.xml file, the mvn package command does not works. Can you help me in this regard please. I am posting here the result of “mvn package” command.

    [INFO] Scanning for projects…
    [ERROR] The build could not read 1 project -> [Help 1]
    [ERROR]
    [ERROR] The project org.controller:writingpackets:1.0-SNAPSHOT (/home/zuhran/Desktop/Workspace/writingpackets/pom.xml) has 1 error
    [ERROR] Non-resolvable parent POM: Could not find artifact org.opendaylight.controller:commons.opendaylight:pom:1.4.0-SNAPSHOT and ‘parent.relativePath’ points at wrong local POM @ line 4, column 10 -> [Help 2]
    [ERROR]
    [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
    [ERROR] Re-run Maven using the -X switch to enable full debug logging.
    [ERROR]

    • vikram says:

      ensure below three
      settings.xml is in the directory ~/.m2/
      Assigned enough memory to mvn
      jdk_home is properly set and ensure it points to jdk not jre

  3. Arun says:

    Hi Fred, Thanks for the pos

    I actually followed this post and wrote the code to obtain topology information using ITopologyManager interface.

    void getTopologyInfo()
    {
    topoNodes = topoManager.getNodeEdges();
    Iterator nodeItr = topoNodes.entrySet().iterator();
    while(nodeItr.hasNext())
    {
    Map.Entry mapEntry = (Map.Entry)nodeItr.next();
    System.out.println(“N: “+mapEntry.getKey());
    Set edges = (Set)mapEntry.getValue();
    Iterator edgeItr = edges.iterator();
    while(edgeItr.hasNext())
    {
    System.out.println(“E: “+edgeItr.next());
    }
    }
    }

    I, however, couldn’t find an appropriate way to test this code. As I understand, your example, flowstats, is event based i.e., the flow info gets printed when packets are exchanged between mininet hosts. However, I just attempt to print mininet topology info.

    When I start the bundle and run mininet (with a basic topology using “sudo mn” command), I don’t see any message being printed neither in the osgi console nor in the opendaylight.log file. Would appreciate any help to fix this issue.

    Thanks.

    • I haven’t touched this code in quite a while, but two things come to mind:
      1) Did the bundle start correct and register with OSGI?
      2) Are you seeing the topology in the web GUI?

      • Arun says:

        Hi Fred, thanks for getting back.

        Yes, the bundle got correctly registered and started.

        I also see the topology in the GUI.

        With print statements for debugging, I see that the getTopologyInfo() method gets called but it doesn’t enter the while loop in the above piece of code (apparently because there’s no nodes/edges fetched from the topology).

        It’s not clear to me what causes this issue.

        P.S.: I start the bundle after executing “sudo mn” command.

    • Arun says:

      Yeah, I’ve this:

      ITopologyManager topologyManager = (ITopologyManager)ServiceHelper.getInstance(ITopologyManager.class, containerName, this);

      And, my datatype declaration for topoNodes is:

      Map<Node, Set> topoNodes;

  4. Vinayak says:

    Hi, I’m trying to do similar code. I’m using org.opendaylight.controller.staststics.northbound. But when I run the bundle I’m getting error “BundleException: The bundle “org.opendaylight.controller.MyStatistics_0.1.0 [263]” could not be resolved. Reason: Missing Constraint: Import-Package: org.opendaylight.controller.sal.core; version=”0.9.0″” . Can You please help me in solving this error? I’m not able to get any answer for it. And many times when I tried to rectify it I get similar error for any other package. Please help.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s