Table of Contents
ODL Development Old
ODL
Learning Path
ODL Developer Guide:
-
- Look at other links on this site.
-
- yang model reference: https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Model_Reference
- MD-SAL Architecture : https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture
- search wiki for MD-SAL:Architecture
- Tutorial:
- l2switch, md-sal implementation of l2 functionalities: https://wiki.opendaylight.org/view/L2_Switch https://wiki.opendaylight.org/view/L2_Switch:Main
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
mkdir features/dmm mv features/* features/dmm
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?
- See the Transition plan here: https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Architecture#Transition_Plan
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
- At the end, modify miniet with ovsdb: https://wiki.opendaylight.org/view/OVSDB_Integration:Mininet_OVSDB_Tutorial