My Wiki!

ODL Development Old

ODL

Learning Path

ODL Developer Guide:

OVSDB Integration:

General

Other wiki resources.

Dev Environment

Karaf Java Heap

(At least) in karaf 2.2.10: If running karaf through bin/start

Memory values could be configured in the bin/setenv file:

export JAVA_MIN_MEM=256M # Minimum memory for the JVM
export JAVA_MAX_MEM=1024M # Maximum memory for the JVM
export JAVA_PERM_MEM=128M # Minimum perm memory for the JVM
export JAVA_MAX_PERM_MEM=256M # Maximum memory for the JVM

If running karaf as a service (karaf-service)

In this case any exported variable seems to be ignored.

The maximum java heap size could be defined in the etc/karaf-wrapper.conf:

Maximum Java Heap Size (in MB)
wrapper.java.maxmemory=1024

Dev env

Maven Java Dev

  mvn help:effective-pom  
  # skip IT (integration test) nsu (no snapshot update)
  mvn clean install -DskipIT -DskipTests -nsu
dependency:analyze
mvn dependency:tree

Archetype

Project Structure

MD-SAL Concepts

SAL Plugin Every bundle in MD-SAL is plugin (openflow plugin, ovsdb plugin, Package handler, Topology manager, Flow Programmer…). The plugin may write data to data store (provider) or read data from data store (consumer). Southbound / Northbound plugins are programmers' view.

Registration Subsystem When loaded Providers register RPCs they implement with SAL, Consumers register with SAL for configuration data notification. The SAL components responsible for this is call Configuration Subsystem, which consists of RPC-Registry, Notification-Service, Data Broker (https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Toaster_Tutorial#Config-subsystem_Context).

Sample Interaction https://wiki.opendaylight.org/view/File:Add_Flow_use_case.png

  • OF Plugin register its RPC, Flow Programmer register for Flow Model Data notification.
  • AddFlow request comes from rest. And new Flow added to Flow Service Model Datastore.
  • Flow Programmer Service gets notification from Datastore. It want to tell OF Plugin to add flow to the appropriate switch.
  • Flow Programmer Service tell SAL to give it a reference to the Addflow RPC.
  • The AddFlow RPC request is routed to the OF Plugin, and the implementation method of the “AddFlow” RPC is invoked.

There is the sequence diagram for that usecase (learning switch) here.

Plugin Lifecycle

Starting Project

This document the step to start a project.

Project Structure

  mkdir dmm

Scaffolding Project

Use odl toolkit to generate basic project structure. Install it:

  git clone https://git.opendaylight.org/gerrit/p/toolkit.git && cd toolkit && mvn clean install -D skipTests

Alternative
Generate project:

  mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller \
  -DarchetypeRepository=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/ \
  -DarchetypeCatalog=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml

choose archetype: archetype-md-sal-app-simple

Generate some code

cd into the /generate project.
Run mvn clean install -Dgen
This will copy a number of additional files to the remaining four projects and finishes the initialization.
cd up one directory (i.e. cd ..). Now run a complete build (i.e. mvn clean install). You should see that the parent project (named after your artifact id), and four child projects (consumer, model, provider, and web) all compile successfully.
Delete the generate folder as it is no longer needed.

You now have a fully functional MD-SAL application template which provides a number of capabilities. Read on for more information about what each project provides a template for, and which you can discard.

At this point you can import your projects into eclipse or other IDE’s as you desire. See Getting Started: Eclipse.

Top pom.xml

cat pom.xml 
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <parent>
    <groupId>de.dailab.nemo.dmm</groupId>
    <artifactId>commons</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <relativePath>commons/parent</relativePath>
  </parent>

  <packaging>pom</packaging>
  <groupId>de.dailab.nemo.dmm</groupId>
  <artifactId>dmm</artifactId>
  <name>${project.artifactId}</name> <!-- Used by Sonar to set project name -->
  <version>1.0-SNAPSHOT</version>

    <properties>
        <sitedeploy>dav:http://nexus.opendaylight.org/content/sites/site</sitedeploy>
        <equinox.osgi.version>3.8.1.v20120830-144521</equinox.osgi.version>
        <ietf-inet-types.version>2010.09.24.4-SNAPSHOT</ietf-inet-types.version>
        <ietf-yang-types.version>2010.09.24.4-SNAPSHOT</ietf-yang-types.version>
        <jmxGeneratorPath>src/main/yang-gen-config</jmxGeneratorPath>
        <salGeneratorPath>src/main/yang-gen-sal</salGeneratorPath>
        <config.version>0.3.0-SNAPSHOT</config.version>
        <config.configfile.directory>etc/opendaylight/karaf</config.configfile.directory>
    </properties>

  <modules>
    <!-- Parent POM files -->
    <module>commons/parent</module>
		<!-- Parent POM files 
    <module>commons/integrationtest</module>
				 -->
    <module>distribution/opendaylight-karaf</module>
    <module>features/dmm</module>
    <!-- DMM Components -->
    <module>model</module>
    <module>provider</module>
    <module>consumer</module>
    <module>web</module>
    <!-- Integration Tests -->
		<!-- Integration Tests 
    <module>integrationtest</module>
				 -->
  </modules>

  <scm>
      <connection>scm:git:ssh://git.opendaylight.org:29418/controller.git</connection>
      <developerConnection>scm:git:ssh://git.opendaylight.org:29418/controller.git</developerConnection>
      <url>https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL</url>
      <tag>HEAD</tag>
  </scm>

  <distributionManagement>
    <!-- OpenDayLight Released artifact -->
    <repository>
      <id>opendaylight-release</id>
      <url>${nexusproxy}/repositories/opendaylight.release/</url>
    </repository>
    <!-- OpenDayLight Snapshot artifact -->
    <snapshotRepository>
      <id>opendaylight-snapshot</id>
      <url>${nexusproxy}/repositories/opendaylight.snapshot/</url>
    </snapshotRepository>
    <!-- Site deployment -->
    <site>
      <id>website</id>
      <url>${sitedeploy}</url>
    </site>
  </distributionManagement>

  <build>
		<pluginManagement>
			<plugins>
				<plugin>
					<artifactId>maven-deploy-plugin</artifactId>
					<configuration>
						<skip>true</skip>
					</configuration>
				</plugin>
				<!--  tells eclipse to import these folders into the package explorer as "source" folders
              which allows eclipse to resolve the classes correctly during an eclipse build -->
        <plugin>
          <groupId>org.codehaus.mojo</groupId>
          <artifactId>build-helper-maven-plugin</artifactId>
          <version>1.8</version>
          <executions>
            <execution>
              <id>add-source</id>
              <goals>
                <goal>add-source</goal>
              </goals>
              <phase>generate-sources</phase>
              <configuration>
                <sources>
                  <source>src/main/yang</source>
                  <source>${jmxGeneratorPath}</source>
                  <source>${salGeneratorPath}</source>
                </sources>
              </configuration>
            </execution>
          </executions>
        </plugin>
        <!--  cleans up auto generated code  -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <configuration>
            <filesets>
              <fileset>
                <directory>${jmxGeneratorPath}</directory>
                <includes>
                  <include>**</include>
                </includes>
              </fileset>
              <fileset>
                <directory>${salGeneratorPath}</directory>
                <includes>
                  <include>**</include>
                </includes>
              </fileset>
            </filesets>
          </configuration>
        </plugin>

        <!-- Ignore/Execute plugin execution -->
        <plugin>
          <groupId>org.eclipse.m2e</groupId>
          <artifactId>lifecycle-mapping</artifactId>
          <version>1.0.0</version>
          <configuration>
            <lifecycleMappingMetadata>
              <pluginExecutions>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>properties-maven-plugin</artifactId>
                    <versionRange>[0.0,)</versionRange>
                    <goals>
                      <goal>set-system-properties</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore/>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.codehaus.enunciate</groupId>
                    <artifactId>maven-enunciate-plugin</artifactId>
                    <versionRange>[0.0,)</versionRange>
                    <goals>
                      <goal>docs</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore/>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.jacoco</groupId>
                    <artifactId>jacoco-maven-plugin</artifactId>
                    <versionRange>[0.0,)</versionRange>
                    <goals>
                      <goal>prepare-agent</goal>
                      <goal>pre-test</goal>
                      <goal>post-test</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore/>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.ops4j.pax.exam</groupId>
                    <artifactId>maven-paxexam-plugin</artifactId>
                    <versionRange>[1.2.4,)</versionRange>
                    <goals>
                      <goal>generate-depends-file</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <execute>
                      <runOnIncremental>false</runOnIncremental>
                    </execute>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-checkstyle-plugin</artifactId>
                    <versionRange>[2.0,)</versionRange>
                    <goals>
                      <goal>check</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore/>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.opendaylight.yangtools</groupId>
                    <artifactId>yang-maven-plugin</artifactId>
                    <versionRange>[0.5,)</versionRange>
                    <goals>
                      <goal>generate-sources</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <execute/>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.codehaus.groovy.maven</groupId>
                    <artifactId>gmaven-plugin</artifactId>
                    <versionRange>1.0</versionRange>
                    <goals>
                      <goal>execute</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore/>
                  </action>
                </pluginExecution>
                <pluginExecution>
                  <pluginExecutionFilter>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-enforcer-plugin</artifactId>
                    <versionRange>${enforcer.version}</versionRange>
                    <goals>
                      <goal>enforce</goal>
                    </goals>
                  </pluginExecutionFilter>
                  <action>
                    <ignore/>
                  </action>
                </pluginExecution>
              </pluginExecutions>
            </lifecycleMappingMetadata>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>build-helper-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

Parent Pom: commons/parent/pom.xml

Common parent for all child project, with specification of dependencies.

Pom defines

  • options: version definition
  • dependencies: jars that need to be downloaded

Providers, consumers pom.xml specifies what they are dependent on.

Features pom.xml specifies the dependent features.

Move common dependency up level!

Feature Bundle: features

Include The Parent pom

specify commons pom artifact as parent:

    <parent>
	<groupId>de.dailab.nemo.dmm</groupId>
	<artifactId>commons</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<relativePath>../../commons/parent</relativePath>
    </parent>
 
    <artifactId>features-dmm</artifactId>
	<version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

features/dmm/pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
Necessary TODO: Put your copyright here.
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
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.opendaylight.toaster</groupId>
<artifactId>toaster-parent</artifactId>
<relativePath>../parent</relativePath>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>features-toaster</artifactId>
<name>${project.artifactId}</name>
<!-- Optional TODO: Uncomment version if you are not using a parent pom.xml
<version>0.0.1</version>
-->
<packaging>jar</packaging>
<properties>
<features.file>features.xml</features.file>
</properties>
<dependencies>
<!-- Necessary TODO: Put dependencies on any feature repos you use in your features.xml file.-->
<!-- Note: they will need to be <type>xml</xml>
and <classifier>features</classifier>.
One other thing to watch for is to make sure they are
<scope>compile</compile>, which they should be by default,
but be cautious lest they be at a different scope in a parent pom.
Examples:
<dependency>
<groupId>org.opendaylight.openflowplugin</groupId>
<artifactId>features-openflowplugin</artifactId>
<version>0.0.3-SNAPSHOT</version>
<classifier>features</classifier>
<type>xml</type>
</dependency>
-->
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>features-yangtools</artifactId>
<classifier>features</classifier>
<type>xml</type>
</dependency>
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>features-mdsal</artifactId>
<classifier>features</classifier>
<type>xml</type>
</dependency>
<!--
Necessary TODO: Put dependencies for bundles directly referenced
in your features.xml file. For every <bundle> reference in your
features.xml file, you need a corresponding dependency here.
Examples:
<dependency>
<groupId>org.opendaylight.toaster</groupId>
<artifactId>toaster-provider</artifactId>
</dependency>
<dependency>
<groupId>org.opendaylight.toaster</groupId>
<artifactId>toaster-model</artifactId>
</dependency>
-->
<dependency>
<groupId>org.opendaylight.toaster</groupId>
<artifactId>toaster-impl</artifactId>
</dependency>
<dependency>
<groupId>org.opendaylight.toaster</groupId>
<artifactId>toaster-consumer</artifactId>
</dependency>
<!--
Necessary TODO: Put dependencies for configfiles directly referenced
in your features.xml file. For every <configfile> reference in your
features.xml file, you need a corresponding dependency here.
Example (presuming here version is coming from the parent pom):
<dependency>
<groupId>org.opendaylight.toaster</groupId>
<artifactId>toaster-config</artifactId>
<type>xml</type>
<classifier>config</classifier>
</dependency>
-->
<!--
Optional TODO: Remove TODO comments.
-->
<!-- test to validate features.xml -->
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>features-test</artifactId>
<scope>test</scope>
</dependency>
<!-- dependency for opendaylight-karaf-empty for use by testing -->
<dependency>
<groupId>org.opendaylight.controller</groupId>
<artifactId>opendaylight-karaf-empty</artifactId>
<version>${karaf.empty.version}</version>
<type>zip</type>
</dependency>
<!-- Uncomment this if you get an error : java.lang.NoSuchMethodError: org.slf4j.helpers.MessageFormatter.format(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Lorg/slf4j/helpers/FormattingTuple;
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.2</version>
</dependency>
-->
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>filter</id>
<phase>generate-resources</phase>
<goals>
<goal>resources</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>attach-artifacts</id>
<phase>package</phase>
<goals>
<goal>attach-artifact</goal>
</goals>
<configuration>
<artifacts>
<artifact>
<file>${project.build.directory}/classes/${features.file}</file>
<type>xml</type>
<classifier>features</classifier>
</artifact>
</artifacts>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${surefire.version}</version>
<configuration>
<systemPropertyVariables>
<karaf.distro.groupId>org.opendaylight.controller</karaf.distro.groupId>
<karaf.distro.artifactId>opendaylight-karaf-empty</karaf.distro.artifactId>
<karaf.distro.version>${karaf.empty.version}</karaf.distro.version>
</systemPropertyVariables>
<dependenciesToScan>
<dependency>org.opendaylight.yangtools:features-test</dependency>
</dependenciesToScan>
</configuration>
</plugin>
</plugins>
</build>
</project>
 

Meta-feature

An example meta-feature.

The version paramerters are defined in commons/parent/pom.xml

<!-- TODO: for any repo dependencies, add an entry in the features/pom.xml dependency section -->
<repository>mvn:org.opendaylight.yangtools/features-yangtools/${yangtools.version}/xml/features</repository>
<repository>mvn:org.opendaylight.controller/features-mdsal/${controller.mdsal.version}/xml/features</repository>
<!--<repository>mvn:org.opendaylight.controller/features-restconf/${controller.restconf.version}/xml/features</repository>-->
<!-- TODO: for any feature dependencies, add an entry in the features/pom.xml dependency section -->
 
 
  <feature name="odl-adsal-all" description="OpenDaylight AD-SAL All Features" version="${sal.version}">
      <feature version="${sal.version}">odl-adsal-core</feature>
      <feature version="${sal.networkconfiguration.version}">odl-adsal-networkconfiguration</feature>
      <feature version="${sal.connection.version}">odl-adsal-connection</feature>
      <feature version="${clustering.services.version}">odl-adsal-clustering</feature>
      <feature version="${configuration.version}">odl-adsal-configuration</feature>
   </feature>
 
   <feature name='odl-toaster-api' version='${project.version}' description='OpenDaylight :: toaster :: API'>
<feature version='${yangtools.version}'>odl-yangtools-common</feature>
<feature version='${yangtools.version}'>odl-yangtools-binding</feature>
<!--<feature version='${controller.mdsal.version}'>odl-mdsal-broker</feature>-->
<!--<feature version='${restconf.version}'>odl-restconf</feature>-->
<bundle>mvn:org.opendaylight.toaster/toaster-api/${project.version}</bundle>
</feature>
<feature name='odl-toaster-impl' version='${project.version}' description='OpenDaylight :: toaster :: Impl'>
<!--<feature version='${yangtools.version}'>odl-yangtools-common</feature>-->
<!--<feature version='${yangtools.version}'>odl-yangtools-binding</feature>-->
<feature version='${controller.mdsal.version}'>odl-mdsal-broker</feature>
<feature version='${project.version}'>odl-toaster-api</feature>
<!--<feature version='${restconf.version}'>odl-restconf</feature>-->
<bundle>mvn:org.opendaylight.toaster/toaster-impl/${project.version}</bundle>
<configfile finalname="toaster-impl-config.xml">mvn:org.opendaylight.toaster/toaster-impl/${project.version}/xml/config</configfile>
</feature>
<feature name='odl-toaster-consumer' version='${project.version}' description='OpenDaylight :: toaster :: Consumer'>
<!--<feature version='${yangtools.version}'>odl-yangtools-common</feature>-->
<!--<feature version='${yangtools.version}'>odl-yangtools-binding</feature>-->
<feature version='${controller.mdsal.version}'>odl-mdsal-broker</feature>
<feature version='${project.version}'>odl-toaster-api</feature>
<!--<feature version='${restconf.version}'>odl-restconf</feature>-->
<bundle>mvn:org.opendaylight.toaster/toaster-consumer/${project.version}</bundle>
</feature>

Configfile Tag

Provider, consumer config.xml must match the ones generated (attach resource) with provider/consumer pom.xml.

<configfile finalname="toaster-impl-config.xml">mvn:de.dailab.nemo.dmm/dmm-provider/${project.version}/xml/config</configfile>
# generated config.xml is here:
~/.m2/repository/de/dailab/nemo/dmm/dmm-provider/1.0.0-SNAPSHOT/dmm-provider-1.0.0-SNAPSHOT-config.xml

Update Dependencies in Feature pom

All feature, bundle in dependency list of a local feature must be add in dependencies of feature-dmm/pom.xml:

 <dependencies>
    <!-- Necessary TODO: Put dependencies on any feature repos you use in your features.xml file.-->
    <dependency>
      <groupId>org.opendaylight.yangtools</groupId>
      <artifactId>features-yangtools</artifactId>
      <classifier>features</classifier>
      <type>xml</type>
      <version>${yangtools.version}</version>
    </dependency>
    <dependency>
      <groupId>org.opendaylight.controller</groupId>
      <artifactId>features-mdsal</artifactId>
      <classifier>features</classifier>
      <type>xml</type>
      <version>${controller.mdsal.version}</version>
    </dependency>
    <dependency>
      <groupId>org.opendaylight.controller</groupId>
      <artifactId>features-restconf</artifactId>
      <classifier>features</classifier>
      <type>xml</type>
      <version>${controller.restconf.version}</version>
    </dependency>

Install Features in Local Distribution

distribution-karaf/pom.xml

<dependencies>
...
<!-- Project local feautures -->
    <!--
      Necessary TODO put your features here.
 
      Note: they will need to be <type>xml</xml>
      and <classifier>features</classifier>.
 
      Note: they must be <scope>runtime</scope>
 
      Note: usually you would only need to depend
      on your own feature file here for your local distro,
      and possible the features-mdsal for odl-restconf
      (although, strange situations do exist :) )
 
      Example:
      <dependency>
        <groupId>org.opendaylight.controller</groupId>
        <artifactId>features-mdsal</artifactId>
        <classifier>features</classifier>
        <type>xml</type>
        <scope>runtime</scope>
      </dependency>
      <dependency>
        <groupId>org.opendaylight.openflowplugin</groupId>
        <artifactId>features-openflowplugin</artifactId>
        <version>0.1.0-SNAPSHOT</version>
        <classifier>features</classifier>
        <type>xml</type>
        <scope>runtime</scope>
      </dependency>
    -->
  </dependencies>
...
<build>
...
 
</pluginManagement>
<plugins>
      <plugin>
        <groupId>org.apache.karaf.tooling</groupId>
        <artifactId>karaf-maven-plugin</artifactId>
        <version>${karaf.version}</version>
        <extensions>true</extensions>
        <configuration>
          <bootFeatures>
            <feature>standard</feature>
            <!--
              Optional TODO: Add entries here for the features you want in your local distro
              Note: odl-restconf is a separate feature from odl-mdsal-broker.  If you want
              restconf, you need to list it here explicitely.
              Examples:
              <feature>odl-openflowplugin-flow-services</feature>
              <feature>odl-restconf</feature>
            -->
            <!-- Final TODO: Remove TODO Comments ;) -->
          </bootFeatures>
...

Install Features From Maven Repo

When feature bundle is installed in maven repo, it can be loaded to opendaylight:

  
  cd distribution-karaf/target/assembly/bin
  repo-add mvn:com.cisco.controller.samples/6-coffee-maker-features/1.0.0-SNAPSHOT/xml/features
  # real path is: /home/fedora/.m2/repository/de/dailab/nemo/dmm/features-dmm/1.0.0-SNAPSHOT/features-dmm-1.0.0-SNAPSHOT-features.xml
  repo-add mvn:de.dailab.nemo.dmm/features-dmm/1.0.0-SNAPSHOT/xml/features
  feature:install dmm-task-all
  
  

Conclusion: Features Workflow

  • In src/fetures.xml, declare local bundle, its config file, and dependant features in the feature definition.
14   <feature name='ima-controller' description="IMA :: Controller :: Main" version='${project.version}'>
 15     <feature version='${yangtools.version}'>odl-yangtools-common</feature>
 16     <feature version='${yangtools.version}'>odl-yangtools-binding</feature>
 17     <feature version='${mdsal.version}'>odl-mdsal-broker</feature>
 18     <bundle>mvn:de.dailab.nemo.ima.controller/ima-controller-main/${project.version}</bundle>
 19     <configfile finalname="${config.configfile.directory}/${config.controller-main.configfile}">mvn:de.dailab.nemo.ima.controller/ima-controller-main/${project.version}/xml    /config</configfile>
 20   </feature>
  • Add repository on top where the dependant features are located.
   
    <repository>mvn:org.opendaylight.controller/features-restconf/${mdsal.version}/xml/features</repository>
  • In features.pom, add the dependant repository to the dependency list:
<dependency>
 30       <groupId>org.opendaylight.controller</groupId>
 31       <artifactId>features-mdsal</artifactId>
 32       <classifier>features</classifier>
 33       <type>xml</type>
 34     </dependency>

Local controller: distribution/distribution-karaf

  mkdir -p distribution
  cd distribution
  mvn archetype:generate -DarchetypeGroupId=org.opendaylight.controller \
  -DarchetypeRepository=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/ \
  -DarchetypeCatalog=http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml

Select karaf-distro…

Parent pom

Specify commons pom artifact as parent

<parent>
	<groupId>de.dailab.nemo.dmm</groupId>
	<artifactId>commons</artifactId>
	<version>1.0.0-SNAPSHOT</version>
	<relativePath>../../commons/parent</relativePath>
</parent>
 
<groupId>de.dailab.nemo.dmm</groupId>
    <artifactId>distribution-karaf</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>jar</packaging>

bootFeature

Currently now feature is loaded at boot:

</pluginManagement>
     <plugins>
       <plugin>
         <groupId>org.apache.karaf.tooling</groupId>
         <artifactId>karaf-maven-plugin</artifactId>
         <version>${karaf.version}</version>
         <extensions>true</extensions>
         <configuration>
           <bootFeatures>
             <feature>standard</feature>
             <!--
               Optional TODO: Add entries here for the features you want in your local distro
               Note: odl-restconf is a separate feature from odl-mdsal-broker.  If you want
               restconf, you need to list it here explicitely.
               Examples:
               <feature>odl-openflowplugin-flow-services</feature>
               <feature>odl-restconf</feature>
             -->
             <!-- Final TODO: Remove TODO Comments ;) -->
           </bootFeatures>

Model

The Java DTO generated from yang model.

Provider

Config Subsystem Service

To wire the provider or consumer with SAL framework. The service dependencies are specified in a yang file inside provider source code, “provider/src/main/yang/task-provider-impl.yang”. This YANG file is specifically used to generate bundle initialization code that will help provide Java dependencies. We will provide details to wire a module into the config subsystem in the next section. This configuration file is read by the YANG Code Generator, which autogenerates Java files to help with wiring into the config subsystem. The plugin needed to perform code generation:

Yang Based API & Configuration

In provider/pom.xml, yang-to-java is done by yang-maven-plugin. The generated plugin configuration:

 19     <build>
 33             <plugin>
 ..........................
 34                 <groupId>org.opendaylight.yangtools</groupId>
 35                 <artifactId>yang-maven-plugin</artifactId>
 36                 <version>${yangtools.version}</version>
 37                 <executions>
 38                     <execution>
 39                         <id>config</id>  <-------- ?????
 40                         <goals>
 41                             <goal>generate-sources</goal>
 42                         </goals>
 43                         <configuration>
 44                             <codeGenerators>
 45                                 <generator>
 46                                     <codeGeneratorClass>org.opendaylight.controller.config.yangjmxgenerator.plugin.JMXGenerator</codeGeneratorClass>
 47                                     <outputBaseDir>${jmxGeneratorPath}</outputBaseDir>
 48                                     <additionalConfiguration>
 49                                         <namespaceToPackage1>urn:opendaylight:params:xml:ns:yang:controller==org.opendaylight.controller.config.yang</namespaceToPackage1>
 50                                     </additionalConfiguration>
 51                                 </generator>
 52                                 <generator>
 53                                     <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
 54                                     <outputBaseDir>${salGeneratorPath}</outputBaseDir>
 55                                 </generator>
 56                             </codeGenerators>
 57                             <inspectDependencies>true</inspectDependencies>
 58                         </configuration>
 59                     </execution>
 60                 </executions>
 61                 <dependencies>
 62                     <dependency>
 63                         <groupId>org.opendaylight.yangtools</groupId>
 64                         <artifactId>maven-sal-api-gen-plugin</artifactId>
 65                         <version>${yangtools.version}</version>
 66                         <type>jar</type>
 67                     </dependency>
 68                     <dependency>
 69                         <groupId>org.opendaylight.controller</groupId>
 70                         <artifactId>yang-jmx-generator-plugin</artifactId>
 71                         <version>0.2.5-SNAPSHOT</version>
 72                     </dependency>
 73                 </dependencies>
 74             </plugin>

ERROR The above code run code generation twice, one to the specified <outputBaseDir>${salGeneratorPath}</outputBaseDir>, under execution id “config”. The other as default to target/generated-sources. –

> Duplicate class error.

Do a mvn help:effective-pom to see details. If this is from the md-sal archetype –

> TODO bug report!!!!

How to fix?

remove “id” from “execution”

Eclipse m2e-build-helper plugin

Source not detected by eclipse. No problem with mvn console!!!

The org.eclipse.m2e:lifecycle-mapping plugin doesn't exist actually. It should be used from the <build><pluginManagement> section of your pom. That way, it's not resolved by Maven but can be read by m2e.

But a more practical solution to your problem would be to install the m2e build-helper connector in eclipse. You can install it from the Window > Preferences > Maven > Discovery > open catalog. That way build-helper-maven-plugin:add-sources would be called in eclipse without having you to change your pom.xml.

Config Subsystem Dependency

Besides the plugin contained in pom.xml, we add code in the coffee-maker-provider-impl.yang file to wire RPC, notification and MD-SAL data broker dependencies .

28     // Augments the 'configuration' choice node under modules/module.
 29     // We consume the three main services, RPCs, DataStore, and Notifications
 30     augment "/config:modules/config:module/config:configuration" {
 31         case task-provider-impl {
 32             when "/config:modules/config:module/config:type = 'task-provider-impl'";
 33 
 34             container rpc-registry {
 35                 uses config:service-ref {
 36                     refine type {
 37                         mandatory true;
 38                         config:required-identity mdsal:binding-rpc-registry;
 39                     }
 40                 }
 41             }
 42 
 43             container notification-service {
 44                 uses config:service-ref {
 45                     refine type {
 46                         mandatory true;
 47                         config:required-identity mdsal:binding-notification-service;
 48                     }
 49                 }
 50             }
 51 
 52             container data-broker {
 53                 uses config:service-ref {
 54                         refine type {
 55                         mandatory false;
 56                         config:required-identity mdsal:binding-async-data-broker;
 57                     }
 58                 }
 59             }
 60         }
 61     }

When you run mvn clean install, you will see a list of files generated under src/main/yang-gen-config and src/main/yang-gen-sal directories. Do not make any changes in the java files generated in those directories, and do commit them to your git/svn repository.

There are two more files generated under src/main/java: ModelProviderModule.java and ModelProviderModuleFactory.java. Update these files to wire MD-SAL dependencies.

provider/src/main/java/org/opendaylight/controller/config/yang/config/task_provider/impl/
├── TaskProviderModuleFactory.java
└── TaskProviderModule.java

Wiring a module into the Config Subsystem

The ModuleFactory.java is used when MD-SAL create an instance of the producer service.

To initialize a module in the config subsystem and make it available for use in a controller environment, you must specify the module information within an XML-formatted configuration file. This file is also auto generated from yang file? or from archetype?, provider/src/main/resources/configuration/initial/05-provider-config.xml.

In this example the module name and type use the namespace specified in task-provider-impl.yang. We add dependencies on rpc-registry, data-broker, and notification-service, which are required at runtime.

Registrate ServiceProvider Implementation With Global RPC

In TaskProviderModule:

 14 public class TaskProviderModule extends org.opendaylight.controller.config.yang.config.task_provider.impl.AbstractTaskProviderModule {
...
30     @Override
 31     public AutoCloseable createInstance() {
 32         final TaskProvider appProvider = new TaskProvider();
 33
            // Need data brocker to read/write to MD-SAL Config? and Operational Datastore
 34         DataBroker dataBrokerService = getDataBrokerDependency();
 35         appProvider.setDataService(dataBrokerService);
 36         // Registration with global RPC
 37         RpcProviderRegistry rpcRegistryDependency = getRpcRegistryDependency();
 38         final BindingAwareBroker.RpcRegistration<TaskService> rpcRegistration =
 39                                 rpcRegistryDependency
 40                                     .addRpcImplementation(TaskService.class, appProvider);
 41 
 42         //retrieves the notification service for publishing notifications
 43         NotificationProviderService notificationService = getNotificationServiceDependency();
 44 
 45 
 46         // Wrap toaster as AutoCloseable and close registrations to md-sal at
 47         // close()
 48         final class CloseResources implements AutoCloseable {
 49 
 50             @Override
 51             public void close() throws Exception {
 52                 rpcRegistration.close();
 53                 appProvider.close();
 54                 log.info("TaskProvider (instance {}) torn down.", this);
 55             }
 56         }
 57 
 58         AutoCloseable ret = new CloseResources();
 59         log.info("TaskProvider (instance {}) initialized.", ret);
 60         return ret;
 61     }

Implement The Service

The RPC registration above tells SAL to create an instance fo the service implementation when invoked:

  
   31     public AutoCloseable createInstance() {
   32         final TaskProvider appProvider = new TaskProvider();

The service implemenation is here:

  12 import de.dailab.nemo.dmm.provider.TaskProvider;
  

So all custom Provider implementation should be done with in this namespace.

Consumer

Consumer Plugin is also specified by yang file.

Initialize Consumer Instance with a Provider Instance

13 public class TaskConsumerModule extends org.opendaylight.controller.config.yang.config.task_consumer.impl.AbstractTaskConsumerModule {
 14     private static final Logger log = LoggerFactory.getLogger(TaskConsumerModule.class);
 15 
...
 28 
 29     @Override
 30     public AutoCloseable createInstance() {
 31         TaskService service = getRpcRegistryDependency().getRpcService(TaskService.class);  <--------- Here it is
 32 
 33         final TaskConsumerImpl consumerImpl = new TaskConsumerImpl(service);
 34 
 35 
 36         final class AutoCloseableService implements TaskConsumerService, AutoCloseable {
 37 
 38             @Override
 39             public void close() throws Exception {
 40                 log.info("TaskConsumerService (instance {}) torn down.", this);
 41             }
 42 
 43             @Override
 44             public void createEntry(Map<String, String> data) {
 45                 consumerImpl.createEntry(data);
 46             }
 47         }
 48 
 49         AutoCloseable ret = new AutoCloseableService();
 50         log.info("TaskConsumerService (instance {}) initialized.", ret );
 51         return ret;
 52     }

Data Store

Binding Independent Context

Binding Independent are the API that is not generated or provided by the producer or consumer. They a derived directly from yang model by Restconf service.

Load dependent Bundles

  feature:install dmm-task-all odl-mdsal-apidocs odl-netconf-mdsal

RESTConf and the DataStore

Background on Restconf, Mapping

Ref:

Listing supported Yang module by restconf (karaf web):

  http://192.168.201.43:8181/restconf/modules

Restconf operations overview

Restconf allows access to datastores in the controller. There are two datastores:

Config: Contains data inserted via controller
Operational: Contains other data

Each request must start with the URI /restconf. Restconf listens on port 8080 for HTTP requests.

Restconf supports OPTIONS, GET, PUT, POST, and DELETE operations. Request and response data can either be in the XML or JSON format. XML structures according to yang are defined at: XML-YANG. JSON structures are defined at: JSON-YANG. Data in the request must have a correctly set Content-Type field in the http header with the allowed value of the media type. The media type of the requested data has to be set in the Accept field. Get the media types for each resource by calling the OPTIONS operation. Most of the paths of the pathsRestconf endpoints use Instance Identifier. <identifier> is used in the explanation of the operations.

<identifier>

It must start with <moduleName>:<nodeName> where <moduleName> is a name of the module and <nodeName> is the name of a node in the module. It is sufficient to just use <nodeName> after <moduleName>:<nodeName>. Each <nodeName> has to be separated by /.

<nodeName> can represent a data node which is a list or container yang built-in type. If the data node is a list, there must be defined keys of the list behind the data node name for example, <nodeName>/<valueOfKey1>/<valueOfKey2>.

The format <moduleName>:<nodeName> has to be used in this case as well: Module A has node A1. Module B augments node A1 by adding node X. Module C augments node A1 by adding node X. For clarity, it has to be known which node is X (for example: C:X). For more details about encoding, see: Restconf 02 - Encoding YANG Instance Identifiers in the Request URI.

Configuration Subsystem: Hybrid SAL, Activators

How To Load Bundles The Hybrid Way?

Load Bundle with Activator:

  • Bundle Activator defined in pom.xml
  • Activator class either inherits from AbstractBindingAwareConsumer or ComponentActivatorAbstractBase

Using Config Subsystem:

  • Use yang modeling language as a language for modelling the configuration, dependencies and state data for Modules
  • In l2switch repo there are xml files that describe the different modules, dependencies and sequence of loading.

Sample

Instance Identifier

The Instance Identifier is a unique identifier of an element (location) in the yang data tree; basically it is the path to the node that uniquely identifies all the node's parent nodes. For unique identification of list elements it is required to specify key values as well.

MD-SAL currently provides three different APIs to access data in the common data store:

Binding APIs (Java generated DTOs)
DOM APIs
HTTP Restconf APIs

Example

Consider the following simple YANG model for inventory:

module inventory {
    namespace "urn:opendaylight:inventory";
    prefix inv;
    revision "2013-06-07";
    container nodes {
        list node {
            key "id";
            leaf "id" {
                type "string";
            }
        }
    }
}

And having one instance of node with name foo;

Lets asume we want to create instance identifier for node foo, in following bindings/ formats:

    YANG / XML / XPath version

    /inv:nodes/inv:node[id="foo"]

    Binding-Aware version (generated APIs)

    import org.opendaylight.yang.gen.urn.opendaylight.inventory.rev130607.Nodes;
    import org.opendaylight.yang.gen.urn.opendaylight.inventory.rev130607.nodes.Node;
    import org.opendaylight.yang.gen.urn.opendaylight.inventory.rev130607.nodes.NodeKey;

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

    InstanceIdentifier<Node> identifier = InstanceIdentifier.builder(Nodes.class).child(Node.class,new NodeKey("foo")).toInstance();

    Note: Last call toInstance() does not return an instance of node, but Java version of Instance identifier of Instance identifier which uniquely identifies node "foo";

    Binding Independent version (yang-data-api)

    import org.opendaylight.yang.common.QName;
    import org.opendaylight.yang.data.api.InstanceIdentifier;

    QName nodes = QName.create("urn:opendaylight:inventory","2013-06-07","nodes");
    QName node = QName.create("urn:opendaylight:inventory","2013-06-07","node");
    QName idName = QName.create("urn:opendaylight:inventory","2013-06-07","id");
    InstanceIdentifier = InstanceIdentifier.builder()
        .node(nodes)
        .nodeWithKey(node,idName,"foo")
        .toInstance();

Note: Last call toInstance() does not return an instance of node, but Java version of Instance identifier which uniquely identifies node “foo”;

HTTP Restconf APIs

http://localhost:8080/restconf/config/inventory:nodes/node/foo

Note: We assume that HTTP APIs are exposed on localhost, port 8080

.

Create a task via Restconf

To create the controller you will do a REST post (you will need a rest client such as PostMan for google chrome).

HTTP Method => POST 
URL => http://localhost:8080/restconf/config 
Header =>   Content-Type: application/yang.data+json  
Body =>  
{
   "toaster:toaster" :
   {
     "toaster:toasterManufacturer" : "General Electric",
     "toaster:toasterModelNumber" : "123",
     "toaster:toasterStatus" : "up"
    }
}

# my body
Body =>  
{
  "task:task":
  {"entry":
   	[
      {
        "entry-id":"12",
        "title":"cool title",
        "desc":"this is it"
      }
    ]
  }
}

Source Code & Tutorials

Programming tools

ODL Network Visualization

Mininet

Install Mininet

Install OVS packages first:

openstack-neutron-openvswitch.noarch : Neutron openvswitch plugin
openvswitch.x86_64 : Open vSwitch daemon/database/utilities
openvswitch-controller.x86_64 : Open vSwitch OpenFlow controller
openvswitch-devel.i686 : Open vSwitch OpenFlow development package (library, headers)
openvswitch-devel.x86_64 : Open vSwitch OpenFlow development package (library, headers)
openvswitch-test.noarch : Open vSwitch testing utilities
python-openvswitch.noarch : Open vSwitch python bindings

openvswitch-controller the default controller.

Install mininet from GIT as instructed:

Start mininet

  sudo mn --controller=remote,ip=172.16.102.161 --topo tree,3
  sudo mn --topo single,3 --mac --switch ovsk,protocols=OpenFlow13 --controller remote
  

OVSDB Mininet


Navigation
Toolbox