====== - SDN Network Controller for ISCO ====== Newer version for ODL Carbon: http://localhost/~dang/wiki/doku.php?id=work_dai_labor:projects:isco:pj_dev:controller:sdn_controller_carbon#archetype ===== - Dev Workspace ===== mvn + eclipse ws: /home/dang/data/src/80_isco_ws -> /mnt/extdata/src/80_isco_ws GIT ===== - Resources ===== * https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Application_Development_Tutorial * https://www.opendaylight.org/tutorials * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Startup_Project_Archetype * https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Main * https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Tutorials:Starting_A_Project:ch0 * https://communities.cisco.com/community/developer/opendaylight/blog/2016/02/03/introduction-to-opendaylight-startup-archetype * Config subsystem: https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:MD-SAL_Document_Review:Config_SubSystem My old tutorials: * http://localhost/~dang/wiki/doku.php?id=work_dai_labor:projects:imovefan:imovefan_ws:openstack_odl:odl_tutorial:odl_operation * http://localhost/~dang/wiki/doku.php?id=work_dai_labor:projects:imovefan:imovefan_ws:deliverables:odl_tutorial&s[]=archetype ===== - Summary ===== - Creating workspace with archetype - Adding module - Wiring module with Config-subsystem ====== - Getting Started ====== ===== - Archetype ===== 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 \ -DarchetypeArtifactId=opendaylight-startup-archetype -DarchetypeVersion= ==== - DarchetypeArtifactId ==== -DarchetypeArtifactId=opendaylight-startup-archetype If this option is omitted, a list of artifacts will be prompted for selection. This is useful to selectively create project modules. The selections are: Choose archetype: 1: http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml -> org.opendaylight.controller:config-module-archetype (Archetype for new module managed by configuration subsystem) 2: http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml -> org.opendaylight.controller:opendaylight-karaf-features-archetype (-) 3: http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml -> org.opendaylight.controller:opendaylight-configfile-archetype (Configuration files for md-sal) 4: http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml -> org.opendaylight.controller:opendaylight-karaf-distro-archetype (-) 5: http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml -> org.opendaylight.controller:opendaylight-startup-archetype (-) 6: http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml -> org.opendaylight.controller.archetypes:odl-model-project (-) 7: http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/archetype-catalog.xml -> org.opendaylight.dlux:dlux-app (-) Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 1 ==== - DarchetypeVersion ==== -DarchetypeVersion= You need to enter the proper that depends on the ODL release you want to work in. for example: For the current Master (Carbon) use 1.3.0-SNAPSHOT For Boron "SR0" use 1.2.0-Boron For Boron SR1 use 1.2.1-Boron-SR1 Note each version of the archetype generates version numbers in pom.xml dependencies for its intended ODL revision. Check the availability of version in archetypeRepository: https://nexus.opendaylight.org/content/repositories/opendaylight.release/org/opendaylight/controller/opendaylight-startup-archetype/ ==== Headline ==== Respond to the prompts (Please note that groupid and artifactid need to be all lower case): Define value for property 'groupId': : org.opendaylight.example Define value for property 'artifactId': : example Define value for property 'package': org.opendaylight.example: : Define value for property 'classPrefix': ${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)} Define value for property 'copyright': : Yoyodyne, Inc. In particular, accept the default value of classPrefix (${artifactId.substring(0,1).toUpperCase()}${artifactId.substring(1)}) which creates a Java Class Prefix by capitalizing the first character of the artifactId ( so in this example the classPrefix will be Example). If you want to change any of the defaults, say 'N' at the last question; which will give you the opportunity to change them. Source: https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Startup_Project_Archetype Source: https://communities.cisco.com/community/developer/opendaylight/blog/2016/02/03/introduction-to-opendaylight-startup-archetype ===== - Generate Additional / Single Module ===== We want to generate main module for the controller. We use archetypeArtifactId=config-module-archetype with archetypeVersion=0.5.1-Boron-SR1 for Boron SR1, which can be looked up in the repo. 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 -DarchetypeArtifactId=config-module-archetype -DarchetypeVersion=0.5.1-Boron-SR1 Define value for property 'groupId': : com.gtarc.isco.network.controller Define value for property 'artifactId': : isco-controller-main Define value for property 'version': 1.0-SNAPSHOT: : 0.1.0 Define value for property 'package': com.gtarc.isco.network.controller: : [INFO] Using property: config-api-version = 0.2.5-SNAPSHOT [INFO] Using property: config-api-yang-revision = 2013-04-05 [INFO] Using property: maven-bundle-plugin-version = 2.4.0 [INFO] Using property: module-implementation-name = impl Define value for property 'module-name': : isco-controller-main Define value for property 'module-name-java-prefix': : ISCOController [INFO] Using property: revision = 2014-01-31 [INFO] Using property: service-java-class = java.lang.AutoCloseable [INFO] Using property: yang-maven-plugin-version = 0.6.2-SNAPSHOT [INFO] Using property: yang-namespace-mapping-from = urn:opendaylight:params:xml:ns:yang:controller [INFO] Using property: yang-namespace-mapping-to = org.opendaylight.controller.config.yang The module is set to use top pom as parent, but we want to use parent pom. Edit parent part in the pom: === Code: === Update feature module. What feature our new module requires? TODO: why? #vim src/main/features/features.xml 18 19 odl-mdsal-broker 20 odl-mdsal-models 21 mvn:com.gtarc.isco.network.controller/isco-controller-main/{{VERSION}} 22 ==== - Maven build Error ==== Problem: cannot resolve artefacts *-impl, *-config. Description: mvn:install does not resolve local build in reactor. Combine with compile or package. * https://stackoverflow.com/questions/29712865/maven-cannot-resolve-dependency-for-module-in-same-multi-module-project * https://stackoverflow.com/a/28850624/707704 **Solution (not NICE)**: compile child-module first so they are in local repo. mvn:install just looks for dependencies in repo, not compile locally. Solution (Maybe): * https://www.smartics.eu/confluence/display/BLOG/2013/07/22/Using+Aggregate+and+Parent+POMs * #5 of this http://blog.sonatype.com/2011/02/top-ten-reasons-to-move-to-maven-3/ Solution (not working): find ~/.m2/repository -name _remote.repositories -exec rm -v {} \; find ~/.m2/repository -name _maven.repositories -exec rm -v {} \; Solution (not working): Combine with compile or package e.g.: mvn compile dependency:tree ==== - First build TODO ==== * Checkstyle error with the config-module-archetype (isco-controller-main). Disable checkstyle for now: Source: http://stackoverflow.com/questions/13430161/preventing-checkstyle-from-running-in-a-specific-maven-submodule Checkstyle will still run, but will perform a no-op... vim pom.xml ... ... true ... ... * remove all m2/repo * mvn clean install -U / -nsu to force update, refresh local repo * ==== Questions ==== * Why feature parent is different from other module's parent? (XML not exported by karaf-maven-plugin)? **Commit checkpoint 0: restructure project folders ** **Working on new branch target_checkpoint_1** ==== - Eclipse Import ==== * workspace must be different from folder with parent pom.xml, e.g., folder containing checked out git repo. * Import existing maven project * root is the folder containing all modules * select all: root pom, modules * Next as far as you can ==== Eclipse Error ==== Add yang generated files to source ==== - Second Build target_checkpoint_1 ==== * Adding Rest interface / module. * Config controller main module. Study this: * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:MD-SAL_Document_Review:Config_SubSystem * https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Main#Configuration_outside_of_config-subsystem_domain **Terminology** * **Module**: A compact part of a system, whose configuration is managed by the configuration subsystem. * Module factory: Important for creating module instances. Module factories are uniquely identified by names. * **Service**: Public API, which is used to access module instances (similar to interface in Java). Any module can implement or provide multiple services. * Configuration: Application state represented by the definition of modules, properties, and the relations among them. There are 2 yang files in the generated config-module archetype: isco-controller-main-impl.yang & isco-controller-main.yang * toaster**.yang (Service, public API)**: This file defines the north bound data model. Specifically, it defines the abstraction of a toaster that is visible to north-bound clients (e.g. the restconf API). * toaster-provider-**impl.yang (ODL Module)**: This file defines an implementation of the toaster service and the services it needs from the MD-SAL framework, e.g. the data-broker, which is used to store the operational data of the toaster. The impl is to generate MODULE, which imports the other (service). The other yang is to genereate SERVICE, data model for OPERATIONAL data. === Code: Yang files === [dang@dai142 isco-controller]$ cat isco-controller-main/src/main/yang/isco-controller-main.yang module isco-controller-main { yang-version 1; namespace "urn:opendaylight:params:xml:ns:yang:controller:config:isco-controller-main"; prefix "isco-controller-main"; import config { prefix config; revision-date 2013-04-05; } description "This module contains the base YANG definitions for isco-controller-main services."; revision "2014-01-31" { description "Initial revision."; } // This is the definition of a service identity isco-controller-main { base "config:service-type"; // TODO modify the java class config:java-class " java.lang.AutoCloseable"; <------ Implement SERVICE class and put it here? see https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Sample_Project } } [dang@dai142 isco-controller]$ cat isco-controller-main/src/main/yang/isco-controller-main-impl.yang module isco-controller-main-impl { yang-version 1; namespace "urn:opendaylight:params:xml:ns:yang:controller:config:isco-controller-main:impl"; prefix "isco-controller-main-impl"; import config { prefix config; revision-date 2013-04-05; } import isco-controller-main { prefix isco-controller-main; revision-date 2014-01-31; } description "This module contains the base YANG definitions for isco-controller-main impl implementation."; revision "2014-01-31" { description "Initial revision."; } // This is the definition of a service implementation identity isco-controller-main-impl { base config:module-type; config:provided-service isco-controller-main:isco-controller-main; <---------- Service delaration???? config:java-name-prefix ISCOController; } augment "/config:modules/config:module/config:configuration" { case isco-controller-main-impl { when "/config:modules/config:module/config:type = 'isco-controller-main-impl'"; leaf simple-attribute { type uint32; } container dto-attribute { leaf inner-attribute { type string; } } // Dependency attribute demonstration, the config:required-identity points to a service type // In this case it is the same service type as this implementation provides: isco-controller-main container dependency-attribute { uses config:service-ref { refine type { mandatory false; config:required-identity isco-controller-main:isco-controller-main; } } } } } ==== - Generated files and their roles ==== # Step 1: Write YANG model describing the interface(in config subsystem called service) and implementation(in config subsystem called Module) of your application. # These yang models might be included inside src/main/yang folder of a maven-module for an application or inside a completely separate maven-module that will only deal with configuration for a particular application. # YANG model should contain the definition of one or more services and one or more Modules for those services. # YANG model needs to depend on config.yang module provided by config-api bundle inside config subsystem, therefore depend on the config-api bundle on maven dependencies level. # YANG model config.yang defines the basic structures necessary for the definition of new Modules and services. # # Step 2: Configure the yang-maven-plugin in pom.xml inside maven-module containing YANG models for config subsystem. # Step 3: Execute build to generate java from yang. # # Step 4: Yang-maven-plugin with code generator from config subsystem will generate 2 classes inside src/main/java folder for each module defined in the YANG schemas. # FooModuleFactory - Simple factory for Module classes # FooModule - JMX compatible wrapper class that wraps an instance of a Module(could be an application or its part). Users are expected to modify this class # Additional classes generated in target folder which are not meant for the developer(details in following sections) since these classes are regenerated with every build unlike the FooModuleFactory and FooModule # All generated classes are basically the binding between application and config subsystem # # Step 5: Implement the createInstance() method inside the FooModule class to instantiate your application. # Getters are used to retrieve the dependencies and configuration needed for application instantiation === - Code: Generated files and their roles === [dang@dai142 isco-controller-main]$ tree src/main/ src/main/ ├── java │   └── org │   └── opendaylight │   └── controller │   └── config │   └── yang │   └── config │   └── isco_controller_main │   └── impl │   ├── ISCOControllerModuleFactory.java │   └── ISCOControllerModule.java <---------- MODULE implements CreateInstance() to start SERVICE ├── yang │   ├── isco-controller-main-impl.yang <--------- yang def for MODULE │   └── isco-controller-main.yang <--------- yang def for SERVICE ├── yang-gen-code │   └── org │   └── opendaylight │   └── yang │   └── gen │   └── v1 │   └── urn │   └── opendaylight │   └── params │   └── xml │   └── ns │   └── yang │   └── controller │   └── config │   └── isco │   └── controller │   └── main │   ├── impl │   │   └── rev140131 │   │   ├── IscoControllerMainImpl.java │   │   ├── modules │   │   │   └── module │   │   │   └── configuration │   │   │   ├── isco │   │   │   │   └── controller │   │   │   │   └── main │   │   │   │   └── impl │   │   │   │   ├── DependencyAttributeBuilder.java │   │   │   │   ├── DependencyAttribute.java │   │   │   │   ├── DtoAttributeBuilder.java │   │   │   │   └── DtoAttribute.java │   │   │   ├── IscoControllerMainImplBuilder.java │   │   │   └── IscoControllerMainImpl.java <-------- Generated MODULE │   │   ├── $YangModelBindingProvider.java │   │   └── $YangModuleInfoImpl.java │   └── rev140131 │   ├── IscoControllerMain.java <-------- Generated SERVICE │   ├── $YangModelBindingProvider.java │   └── $YangModuleInfoImpl.java └── yang-gen-config └── org └── opendaylight └── controller └── config └── yang └── config └── isco_controller_main ├── AutoCloseableServiceInterface.java └── impl ├── AbstractISCOControllerModuleFactory.java ├── AbstractISCOControllerModule.java ├── DtoAttribute.java └── ISCOControllerModuleMXBean.java ==== Using Blueprint (Boron and after) ==== Search Boron and after: https://wiki.opendaylight.org/view/OpenDaylight_Controller:Main We continue with https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Toaster_Step-By-Step_-_Boron_and_after ==== Continue with Bindingaware provider (Following) ==== * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Toaster_Step-By-Step:Berllium-SR2#Define_the_KitchenService_interface ==== - Filling the generated files ==== * https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Sample_Project Now we need to understand MD-SAL: * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:MD-SAL_Document_Review:MD_SAL * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Toaster_Step-By-Step#Prepare_the_Project_Structure * Extra: http://docs.inocybe.com/dev-guide/content/_generated_config_source_files_examples.html (previous section also) ==== - Implementing Service and Data Model ==== **(Provider) Serivce model is defined in isco-controller-main.yang** and generated by the org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl plugin. You can see above that we marked all three of the leaf attributes on the toaster container as operational (config false), instead of configuration data. MD-SAL, along with some ietf drafts for restconf split the configuration and operational data into two separate data stores. Operational - Operational data stores are used to show the running state (read only) view of the devices, network, services, etc that you might be looking at. In our case we have a service called toaster which is available - the manufacture, model and status of the toaster are all provided by the underlying toaster and can not be configured (later we will add a configuration attribute). Think of the first two attributes as constants which are hardcoded into the physical device, while the third is a representation of current state, and changes as the toaster is used. Config - Config data stores are generally used to configure the device in someway. These configurations are user provided and is a way for the user to tell the device how to behave. For example if you wanted to configure the resource in some way, such as applying a policy or other configuration then you would use this data store. We will add some configuration data in part 3 of this tutorial. The file com.gtarc.isco.network.controller.isco_controller_main.ISCOControllerMainService is the generated service class from isco-controller-main.yang (without impl). source: http://docs.inocybe.com/dev-guide/content/_service_interfaces_generating.html ===== - Wire The Generated module and services to Config Subsystem ===== ==== - Define the provider service yang configuration ==== The configuration is defined in isco-controller-main-impl.yang. prefix "isco-controller-main-impl"; 7 import config { prefix config; revision-date 2013-04-05; } + import opendaylight-md-sal-binding { prefix mdsal; revision-date 2013-10-28; } 9 import isco-controller-main { prefix isco-controller-main; revision-date 2014-01-31; } 10 11 description 12 "This module contains the base YANG definitions for 13 isco-controller-main impl implementation."; 14 15 revision "2014-01-31" { 16 description 17 "Initial revision."; 18 } 19 20 // This is the definition of a service implementation 21 identity isco-controller-main-impl { 22 base config:module-type; 23 config:provided-service isco-controller-main:isco-controller-main; 24 config:java-name-prefix ISCOController; 25 } 26 27 augment "/config:modules/config:module/config:configuration" { 28 case isco-controller-main-impl { 29 when "/config:modules/config:module/config:type = 'isco-controller-main-impl'"; 30 31 leaf simple-attribute { 32 type uint32; 33 } 34 // module configuration, getter in stub used in createInstance to pass to Service instance. 35 container dto-attribute { 36 leaf inner-attribute { 37 type string; 38 } 39 } 40 41 // Dependency attribute demonstration, the config:required-identity points to a service type 42 // In this case it is the same service type as this implementation provides: isco-controller-main 43 container dependency-attribute { 44 uses config:service-ref { 45 refine type { 46 mandatory false; 47 config:required-identity isco-controller-main:isco-controller-main; 48 } 49 } 50 } 51 52 +//wires in the data-broker service 53 +container data-broker { 54 + uses config:service-ref { 55 + refine type { 56 + mandatory false; 57 + config:required-identity mdsal:binding-async-data-broker; 58 + } 59 + } 60 +} 61 62 } The toaster-provider-impl identity is a module-type identity that defines a global identifier for the toaster-provider service implementation so that it can be referred to. The augmentation of the modules/module/configuration hierarchy choice-type node adds schema nodes specific to the toaster-provider-impl module identity type (as indicated by the 'when' clause). This is where we define configuration information needed to initialize the toaster-provider-impl module; specifically, which external service dependencies are needed. We see that the OpendaylightToaster needs the DataBroker so we add a data-broker container node that defines a dependency on the MD-SAL's DataBroker service. Syntactically, it defines a reference (of type service-ref) to the particular service instance referred to by the mdsal:binding-async-data-broker service identity. The service instance is set at runtime by the MD-SAL. Referrence: http://sdntutorials.com/switching-to-config-subsystem-aware-bundles-in-opendaylight-helium/ ==== - Generate the Toaster yang provider source ==== To generate the java source files that facilitate the service wiring, we need to add another code generator, JmxGenerator, to the yang-maven-plugin configuration in the pom.xml in addition to the CodeGeneratorImpl, as well as an additional dependency to the yangtools plugins. Under the project toaster-provider, the pom.xml file should look like the following: org.opendaylight.controller config-api ${config.version} + + + org.opendaylight.controller + sal-binding-api + + + org.opendaylight.controller + sal-binding-config + + + org.opendaylight.controller + sal-common-util + + + After running mvn clean install you should see two files generated: ToasterProviderModule - concrete class whose createInstance() method provides the OpendaylightToaster instance. ToasterProviderModuleFactory - concrete class instantiated internally by MD-SAL that creates ToasterProviderModule instances. Note: these 2 classes are generated under src/main/java and are intended to be checked into Git as they will contain manually written code. ==== Implement the Service Provider Module ==== Only the ToasterProviderModule.createInstance() method needs to be implemented to instantiate and wire the OpendaylightToaster. The class is located under the package org.opendaylight.controller.config.yang.config.toaster_provider.impl. @Override public java.lang.AutoCloseable createInstance() { final OpendaylightToaster opendaylightToaster = new OpendaylightToaster(); DataBroker dataBrokerService = getDataBrokerDependency(); opendaylightToaster.setDataProvider(dataBrokerService); // Wrap toaster as AutoCloseable and close registrations to md-sal at // close(). The close method is where you would generally clean up thread pools // etc. final class AutoCloseableToaster implements AutoCloseable { @Override public void close() throws Exception { opendaylightToaster.close(); } } return new AutoCloseableToaster(); } In the above code, the DataBroker dependency has already been injected by the MD-SAL and is available via the getDataBrokerDependency() method defined in the generated base class. The automatic injection is facilitated by the dependency augmentation that we had defined in the toaster-provider-impl.yang file. The return type of createInstance() is AutoCloseable. We have to return an AutoCloseable object so MD-SAL can inform our logic when it is time to shutdown. We don't need to modify or implement anything in ToasterProviderModuleFactory for this example. Note 1: A future enhancement in this area may be to simplify the registration process here by performing more of the registrations etc automatically. Today this is how you need to perform these registrations. Sample module / provider implementation: https://wiki.opendaylight.org/view/OpenDaylight_OpenFlow_Plugin::Config_Subsystem **Note:** * createInstance returns AutoCloseable so the returned type needs to implement it. (You can make BarImpl implement AutoCloseable, or create a Wrapper class around the BarImpl instance that implements AutoCloseable, or even extend the BarImpl class and make it implement it.) * You can access all the configuration attributes by means of the getter methods. * In config-demo-impl.yang, we defined the bar-impl configuration as a container dto-attribute. The code generator creates a transfer object DtoAttribute that you can access by means of the getDtoAttribute() method, and retrieve configuration data from it. You can even add a new constructor to BarImpl that takes this transfer object, and reduces the number of arguments. FooImplModule We will not add any custom validation in this module. The createInstance method will look as follows: @Override public java.lang.AutoCloseable createInstance() { return new FooImpl(getStrAttribute(), getIntAttribute(), getBarDependencyDependency()); } ==== - Adding Support for Default Instance ==== In order to provide a default instance of module bar-impl, we need to further modify the generated code by the overriding method getDefaultModules in src/main/java/**/BarImplModuleFactory class. The body of this class is empty thus far, and it inherits the default behaviour from its parent abstract factory. Use the following code to replace the empty body: public static final ModuleIdentifier defaultInstance1Id = new ModuleIdentifier(NAME, "defaultInstance1"); @Override public Set getDefaultModules(DependencyResolverFactory dependencyResolverFactory, BundleContext bundleContext) { DependencyResolver depResolver1 = dependencyResolverFactory.createDependencyResolver(defaultInstance1Id); BarImplModule defaultModule1 = new BarImplModule(defaultInstance1Id, depResolver1); defaultModule1.setDtoAttribute(getDefaultConfiguration(bundleContext)); return Sets.newHashSet(defaultModule1); } private DtoAttribute getDefaultConfiguration(BundleContext bundleContext) { DtoAttribute defaultConfiguration = new DtoAttribute(); String property = bundleContext.getProperty("default.bool"); defaultConfiguration.setBoolAttribute(property == null ? false : Boolean.parseBoolean(property)); property = bundleContext.getProperty("default.int1"); defaultConfiguration.setIntAttribute(property == null ? 55 : Integer.parseInt(property)); property = bundleContext.getProperty("default.int2"); defaultConfiguration.setIntAttribute2(property == null ? 0 : Integer.parseInt(property)); return defaultConfiguration; } The getDefaultModules method now produces an instance of the bar-impl module with the name defaultInstance1. (It is possible to produce multiple default instances since the return type is a Set of module instances.) Note the getDefaultConfiguration method. It provides the default configuration for default instances by trying to retrieve system properties from bundleContext (or provides hardcoded values in case the system property is not present). For the controller distribution, system properties can be fed by means of config.ini file. The method getDefaultModules is called automatically after a bundle containing this factory is started in the OSGi environment. Its default implementation returns an empty Set. The default instances approach is similar to the Activator class approach in OSGi with the advantage of default instances being managed by the configuration subsystem. This approach can either replace the Activator class approach, or be used along with it. === Verifying the default instances in distribution === If we add the config-demo bundle to the opendaylight distribution, we can verify the presence of the default instance. The file pom.xml under the opendaylight/distribution/opendaylight folder needs to be modified by adding the config-demo dependency: ${project.groupId} config-demo 0.1.1-SNAPSHOT Now we need to rebuild the conf-demo module using mvn clean install. Then, we can build the distribution using the same mvn command under the opendaylight/distribution/opendaylight folder. If we go to the opendaylight/distribution/opendaylight/target/distribution.opendaylight-osgipackage/opendaylight folder, and execute run.sh, the opendaylight distribution should start. We can check the presence of the default instances by means of JMX using a tool such as jvisualvm. ==== - Define the initial XML configuration ==== **Note: ** https://wiki.opendaylight.org/view/Upgrades_And_Config_System#Configuration We have now defined the toaster data model (toaster.yang) and a provider implementation (toaster-provider-impl.yang). At this point, if the bundles were deployed, the configuration of the toaster data model (although we haven't defined any config attributes yet) would be accessible via restconf however the operational data and RPC provided by the OpenDaylightToaster service would not be accessible. What we have done so far is to define the service implementation. The last step is to actually tell MD-SAL to "deploy" the implementation, i.e. create an instance of the OpenDaylightToaster service, resolve its dependencies and advertise it for consumption/use. To do this, we need to create an xml file that defines the initial configuration of the toaster provider service deployment. The configuration is actually deployed internally using the netconf protocol. The xml is comprised of 2 main sections: configuration and required-capabilities. The required-capabilities section is needed for the netconf ""hello" message and describes the yang modules that are needed by the services in order for them to function properly. Under the data section of configuration is where you define your services, implementation modules and how to configure each implementation. This section is used in the subsequent netconf "edit-config" message. **Create new toaster-config project**. Under the toaster-config project, in the src/main/resources source, create a folder initial into which you create an xml file named "03-toaster-sample.xml" with the following: * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Toaster_Step-By-Step_krb toaster:toaster-provider-impl toaster-provider-impl binding:binding-async-data-broker binding-data-broker urn:opendaylight:params:xml:ns:yang:controller:config:toaster-provider:impl?module=toaster-provider-impl&revision=2014-01-31 Now you have to define the pom file for the toaster-config project, as below: 4.0.0 org.opendaylight.controller toaster-parent 0.0.1-SNAPSHOT toaster-config Configuration files for toaster jar org.codehaus.mojo build-helper-maven-plugin attach-artifacts attach-artifact package ${project.build.directory}/classes/initial/03-toaster-sample.xml xml config Compile the toaster-config project using mvn clean install . (**This step is quiestionable**) On startup, the XML files in the resources/initial directory are loaded by the ConfigPersisterActivator. A ConfigPusher instance is instantiated to push the configs via the NetConf subsystem to the ConfigRegistryImpl. In order to push the config file, we have to define it in the pom file of the ConfigPusher, which is located under controller/opendaylight/commons/opendaylight: ... 03-toaster-sample.xml ... Compile controller/opendaylight/commons/opendaylight using mvn clean install . **Continue...** You also have to specified the path of the config file in the feature.xml file of MDSAL. Go to controller/features/mdsal/src/main/resources/ and edit the features.xml file by adding the following: odl-yangtools-common odl-yangtools-binding odl-mdsal-broker mvn:org.opendaylight.controller.samples/toaster/${project.version} mvn:org.opendaylight.controller.samples/toaster-consumer/${project.version} mvn:org.opendaylight.controller.samples/toaster-provider/${project.version} mvn:org.opendaylight.controller.samples/toaster-config/${project.version}/xml/config Compile controller/features/mdsal using mvn clean install . When processing the toaster-provider-impl module in the toaster config file, the ToasterProviderModuleFactory class is located and instantiated and the createModule method is called to create a ToasterProviderModule instance. The ToasterProviderModule.createInstance method is then called to create and wire the OpenDaylightToaster. For a detailed walk-through on how to make a 'config-subsystem aware' project please visit https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Examples:Sample_Project ===== - Troubleshooting: ensure configfile is loaded ===== In karaf: opendaylight-user@root>feature:info odl-isco-controller-main Feature odl-isco-controller-main 0.1.0 Description: OpenDaylight :: isco-controller :: main Feature has no configuration Feature configuration files: etc/opendaylight/karaf/80-controller-main-config.xml <---------- This is it Feature depends on: odl-mdsal-broker 1.4.1-Boron-SR1 odl-mdsal-models 0.9.1-Boron-SR1 Feature contains followed bundles: mvn:com.gtarc.isco.network.controller/controller-main-impl/0.1.0 Feature has no conditionals. The file should be there too ls target/assembly/etc/opendaylight/karaf/80-controller-main-config.xml ==== - Continue Error loading configfile ==== java.lang.NullPointerException: Namespace urn:opendaylight:params:xml:ns:yang:controller:programming:impl, defined in: module isco-controller-main-impl of type isco-controller-main-impl not found, available namespaces: [urn:opendaylight:params:xml:ns:yang:controller:config:actor-system-provider:impl, urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:flexible, urn:opendaylight:params:xml:ns:yang:md:sal:config:impl:cluster-singleton-service, urn:opendaylight:params:xml:ns:yang:controller:config:legacy-entity-ownership-service-provider, urn:opendaylight:params:xml:ns:yang:controller:config:cluster-admin-provider, urn:opendaylight:params:xml:ns:yang:controller:netty:eventexecutor, urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:fixed, urn:opendaylight:params:xml:ns:yang:controller:netty:threadgroup, urn:opendaylight:params:xml:ns:yang:controller:config:concurrent-data-broker, urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:impl, urn:opendaylight:params:xml:ns:yang:controller:config:remote-rpc-connector, urn:opendaylight:params:xml:ns:yang:controller:shutdown:impl, urn:opendaylight:params:xml:ns:yang:controller:netty:timer, urn:opendaylight:params:xml:ns:yang:controller:inmemory-datastore-provider, urn:opendaylight:params:xml:ns:yang:controller:config:distributed-datastore-provider, urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl, urn:opendaylight:params:xml:ns:yang:controller:threadpool:impl:scheduled, urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:pingpong, urn:opendaylight:params:xml:ns:yang:controller:config:isco-controller-main:impl, urn:opendaylight:params:xml:ns:yang:controller:md:sal:binding:impl] ==== - Cannot inject rcp-registry Boron ==== 2017-09-20 00:33:29,747 | ERROR | config-pusher | ConfigPusherImpl | 131 - org.opendaylight.controller.config-persister-impl - 0.5.1.Boron-SR1 | Failed to apply configuration snapshot: 80-controller-main-config.xml(odl-isco-controller-main,odl-isco-controller-main). Config snapshot is not semantically correct and will be IGNORED. for detailed information see enclosed exception. org.opendaylight.controller.config.persist.impl.ConfigPusherImpl$ConfigSnapshotFailureException: Failed to apply config snapshot: 80-controller-main-config.xml(odl-isco-controller-main,odl-isco-controller-main) during phase: edit at org.opendaylight.controller.config.persist.impl.ConfigPusherImpl.pushConfig(ConfigPusherImpl.java:308)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] at org.opendaylight.controller.config.persist.impl.ConfigPusherImpl.pushConfigWithConflictingVersionRetries(ConfigPusherImpl.java:164)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] at org.opendaylight.controller.config.persist.impl.ConfigPusherImpl.internalPushConfigs(ConfigPusherImpl.java:129)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] at org.opendaylight.controller.config.persist.impl.ConfigPusherImpl.processSingle(ConfigPusherImpl.java:83)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] at org.opendaylight.controller.config.persist.impl.ConfigPusherImpl.process(ConfigPusherImpl.java:74)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] at org.opendaylight.controller.config.persist.impl.osgi.ConfigPersisterActivator$2.run(ConfigPersisterActivator.java:131)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] at java.lang.Thread.run(Thread.java:745)[:1.8.0_112] Caused by: org.opendaylight.controller.config.api.ValidationException: {isco-controller-main-impl={isco-controller-main-impl=Found module factory does not expose expected service interface. Module name is binding-data-compatible-broker : org.opendaylight.controller.config.yang.md.sal.binding.impl.ForwardedCompatibleDataBrokerImplModuleFactory@7954ad11, expected service interface interface org.opendaylight.controller.config.yang.md.sal.binding.DataBrokerServiceInterface, dependent module ON org.opendaylight.controller:instanceName=inmemory-binding-data-broker,type=Module,moduleFactoryName=binding-data-compatible-broker , attribute JmxAttribute{'DataBroker'}}} at org.opendaylight.controller.config.api.ValidationException.createFromCollectedValidationExceptions(ValidationException.java:61) at org.opendaylight.controller.config.manager.impl.ConfigTransactionControllerImpl.validateNoLocks(ConfigTransactionControllerImpl.java:379) at org.opendaylight.controller.config.manager.impl.ConfigTransactionControllerImpl.validateConfig(ConfigTransactionControllerImpl.java:353) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[:1.8.0_112] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)[:1.8.0_112] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[:1.8.0_112] at java.lang.reflect.Method.invoke(Method.java:498)[:1.8.0_112] at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)[:1.8.0_112] at sun.reflect.GeneratedMethodAccessor25.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[:1.8.0_112] at java.lang.reflect.Method.invoke(Method.java:498)[:1.8.0_112] at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)[:1.8.0_112] at com.sun.jmx.mbeanserver.ConvertingMethod.invokeWithOpenReturn(ConvertingMethod.java:193)[:1.8.0_112] at com.sun.jmx.mbeanserver.ConvertingMethod.invokeWithOpenReturn(ConvertingMethod.java:175)[:1.8.0_112] at com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(MXBeanIntrospector.java:117)[:1.8.0_112] at com.sun.jmx.mbeanserver.MXBeanIntrospector.invokeM2(MXBeanIntrospector.java:54)[:1.8.0_112] at com.sun.jmx.mbeanserver.MBeanIntrospector.invokeM(MBeanIntrospector.java:237)[:1.8.0_112] at com.sun.jmx.mbeanserver.PerInterface.invoke(PerInterface.java:138)[:1.8.0_112] at com.sun.jmx.mbeanserver.MBeanSupport.invoke(MBeanSupport.java:252)[:1.8.0_112] at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)[:1.8.0_112] at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:801)[:1.8.0_112] at com.sun.jmx.mbeanserver.MXBeanProxy$InvokeHandler.invoke(MXBeanProxy.java:150)[:1.8.0_112] at com.sun.jmx.mbeanserver.MXBeanProxy.invoke(MXBeanProxy.java:167)[:1.8.0_112] at javax.management.MBeanServerInvocationHandler.invoke(MBeanServerInvocationHandler.java:258)[:1.8.0_112] at com.sun.proxy.$Proxy35.validateConfig(Unknown Source)[118:org.opendaylight.controller.config-api:0.5.1.Boron-SR1] at org.opendaylight.controller.config.util.ConfigTransactionJMXClient.validateConfig(ConfigTransactionJMXClient.java:127)[123:org.opendaylight.controller.config-util:0.5.1.Boron-SR1] at org.opendaylight.controller.config.facade.xml.transactions.TransactionProvider.validateTestTransaction(TransactionProvider.java:198)[129:org.opendaylight.controller.config-manager-facade-xml:0.5.1.Boron-SR1] at org.opendaylight.controller.config.facade.xml.ConfigSubsystemFacade.test(ConfigSubsystemFacade.java:166)[129:org.opendaylight.controller.config-manager-facade-xml:0.5.1.Boron-SR1] at org.opendaylight.controller.config.facade.xml.ConfigSubsystemFacade.executeTests(ConfigSubsystemFacade.java:149)[129:org.opendaylight.controller.config-manager-facade-xml:0.5.1.Boron-SR1] at org.opendaylight.controller.config.facade.xml.ConfigSubsystemFacade.executeConfigExecution(ConfigSubsystemFacade.java:123)[129:org.opendaylight.controller.config-manager-facade-xml:0.5.1.Boron-SR1] at org.opendaylight.controller.config.persist.impl.ConfigPusherImpl.executeWithMissingModuleFactoryRetries(ConfigPusherImpl.java:329)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] at org.opendaylight.controller.config.persist.impl.ConfigPusherImpl.pushConfig(ConfigPusherImpl.java:305)[131:org.opendaylight.controller.config-persister-impl:0.5.1.Boron-SR1] ... 6 more 2017-09-20 00:33:29,750 | INFO | config-pusher | ConfigPersisterActivator | 131 - org.opendaylight.controller.config-persister-impl - 0.5.1.Boron-SR1 | Configuration Persister initialization completed. ===== - Initial config ===== Initial config is also used to inject configuration subsystem services, similar to blueprint. From Boron on, injection will only support for blueprint [check source]. The config subsystem is an activation and configuration framework, roughly similar to what Blueprint would do should it also handle statistics and allow for run-time rewiring. * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:MD-SAL_Document_Review:Config_SubSystem * ==== - How to use initial configuration xml file ==== Discussed here: * http://sdntutorials.com/notes-on-config-subsystem-in-opendaylight/ * https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:MD-SAL_Document_Review:Config_SubSystem * http://localhost/~dang/wiki/doku.php?id=work_dai_labor:projects:imovefan:imovefan_ws:openstack_odl:odl_tutorial:sal_binding&s[]=bindingawareprovider * https://github.com/onfsdn/atrium-odl/blob/master/bgprouter/impl/src/main/config/default-config.xml **Phase: Initial configuration push** In order to automate startup of infrastructural services, plugins and applications, a config-pusher mechanism was implemented. It is part of a config-persister component and detailed documentation can be found at: Initial configuration for ODL ( TODO might need an update for karaf based distribution). Initial configuration push just consists of a few edit-config rpcs sent to the northbound NETCONF interface for config subsystem. It does not differ from a user sending edit-configs via NETCONF. The format of files with initial configuration was chosen as XML since XML is the format of NETCONF rpcs and allows for trivial construction of edit-config rpcs, since the content is only copied from the files into the rpc. No transformation is needed in such case. Note: The NETCONF messages with initial config are not transferred over network, just inside the JVM Required capabilities in files with initial config The whole environment in ODL is fully dynamic as it uses OSGi container internally. This implies the dynamic nature of config subsystem as well as its NETCONF northbound interface. Bundles and features can appear at any time and config subsystem needs to pick them up as they come and go. These bundles may contain YANG models and the binding classes for config subsystem. Before a bundle is installed and picked up, initial config file that tries to spawn a Module defined in that bundle cannot be pushed. Config-pusher has to wait with such config files until required bundles are loaded. The required capabilities listed in the files ensure this behavior as config pusher waits until NETCONF server lists the required YANG models as capabilities. The required-capabilities in initial config files ensure that config subsystem is ready to spawn required modules in the dynamic environment of OSGi **Config push in karaf** This mechanism was slightly updated for the Karaf distribution. Initial config files are not picked up from file-system but rather from features containing bundles with initial config files. A hook for an install feature event was implemented to scan incoming features. Config-persister is an extensible component that can be configured to use any storage-adapter internally to load initial configuration and the feature-storage-adapter was implemented in this case. Controller initial configuration: https://wiki.opendaylight.org/view/OpenDaylight_Controller:Config:Configuration:Initial Data in config.xml are taken from module.yang: === Code: sample config.xml === prefix:task-provider-impl task-provider-impl binding:binding-rpc-registry binding-rpc-broker binding:binding-async-data-broker binding-data-broker binding:binding-notification-service binding-notification-broker urn:opendaylight:params:xml:ns:yang:controller:config:task-provider:impl?module=task-provider-impl&revision=2014-05-23 ==== - How does it work with MD-SAL ==== ==== - How Does MD-SAL Know about my Toaster? ==== The sample-toaster bundle only defines a yang file and has no bundle Activator and has no code other than the generated source files. If you are wondering how MD-SAL becomes aware of the toaster yang data model then read on. The magic is done via some files that are generated by the yang-maven-plugin under target/classes/META-INF that get inserted into the sample-toaster bundle. The src/main/yang/toaster.yang file is copied to target/classes/META-INF/yang/toaster.yang. The org.opendaylight.yangtools.yang.binding.YangModelBindingProvider file is generated in target/classes/META-INF/services and contains the fully-qualfied name of the toaster's generated $YangModelBindingProvider class. The MD-SAL's ModuleInfoBundleTracker class in the config subsystem scrapes the META-INF/services/org.opendaylight.yangtools.yang.binding.YangModelBindingProvider resource from bundles on startup and reads the class name(s) defined in the file. For each YangModelBindingProvider class specified, the MD-SAL creates an instance and calls getModuleInfo() to return the singleton $YangModuleInfoImpl instance. This class has methods to obtain static configuration information about the yang module, e.g. name, revision, imports etc, as well as a getModuleSourceStream() method that provides an input stream to the META-INF/yang/toaster.yang file. Once the MD-SAL knows about a yang module and its definitions, it can wire it up to RestConf and other parts of the system. ==== - How Do My Jar Files get Deployed in OSGI? ==== Now that you have created your projects you need to get the .jar files that are created into your OSGi container. You can manually copy the .jar file which is generated under your /target directory to the controller/opendaylight/distribution/opendaylight/target/distribution.opendaylight-osgipackage/opendaylight/plugins directory. To manually copy in the updated code, you can copy the jar file from the target directory to the plugins directory. For example from the toaster-provider project directory. To have your jars included automatically when you build your controller then you need to add your bundles as dependencies in controller/opendaylight/distribution/opendaylight/pom.xml. By just adding your bundles in the dependencies section your bundles will automatically be bundled up and copied to the plugins directory automatically when you build the distribution/opendaylight project. ===== - Wiring Module with Blueprint ===== * https://wiki.opendaylight.org/view/Upgrades_And_Config_System ===== - Implement the RPC methods ===== See the part on this link: https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL:Toaster_Step-By-Step#Prepare_the_Project_Structure As expected: * define rpc in model.yang * define binding-rpc-registry dependency in model-impl.yang * register model-rpc in creatInstance() @Override public java.lang.AutoCloseable createInstance() { final OpendaylightToaster opendaylightToaster = new OpendaylightToaster(); ... final BindingAwareBroker.RpcRegistration rpcRegistration = getRpcRegistryDependency() .addRpcImplementation(ToasterService.class, opendaylightToaster); final class AutoCloseableToaster implements AutoCloseable { @Override public void close() throws Exception { ... rpcRegistration.close(); ... } } return new AutoCloseableToaster(); } * Finally we need to add the dependency for the 'rpc-registry' to the toaster-provider-impl module in the initial configuration XML file (remember the 03-sample-toaster.xml file?) as we did earlier with the 'data-broker': prefix:toaster-provider-impl toaster-provider-impl binding:binding-rpc-registry binding-rpc-broker ... ===== - Test Run ===== * http://localhost/~dang/wiki/doku.php?id=work_dai_labor:projects:imovefan:imovefan_ws:openstack_odl:odl_tutorial:odl_operation ==== - Enable karaf log ==== log:set debug org.opendaylight.ovsdb.openstack.netvirt.impl.NeutronL3Adapter log:set TRACE org.opendaylight.controller.networkconfig.neutron log:set debug org.opendaylight.ovsdb ==== - Watch, reload bundle in running karaf ==== karaf@root()> bundle:watch * will monitor all bundles that have a location matching mvn:* and -SNAPSHOT in their URL. For not snapshot bundles use bundle:update bundle:update sd-rest more-bundle-name [ids, name/version] bundle:list | grep -i isco bundle:update com.gtarc.isco.network.controller.controller-main-impl Bundle:refresh should update all depending bundles: bundle:refresh sd-rest more-bundle-name [ids, name/version] list -t 0 -s | grep aries.transaction.manager ====== - Good resources ====== * http://www.packetdesign.com/blog/yang-mount-with-opendaylight-boron/ ===== - mdsal & yang & configfile ===== * https://wiki.opendaylight.org/view/Controller_Core_Functionality_Tutorials:Tutorials:Data_Store_Benchmarking_and_Data_Access_Patterns#The_Configuration_Model_and_Default_Config_File * https://github.com/opendaylight/controller/blob/release/boron-sr1/opendaylight/md-sal/sal-binding-config/src/main/yang/opendaylight-md-sal-binding.yang *