The documentation set for this product strives to use bias-free language. For the purposes of this documentation set, bias-free is defined as language that does not imply discrimination based on age, disability, gender, racial identity, ethnic identity, sexual orientation, socioeconomic status, and intersectionality. Exceptions may be present in the documentation due to language that is hardcoded in the user interfaces of the product software, language used based on RFP documentation, or language that is used by a referenced third-party product. Learn more about how Cisco is using Inclusive Language.
The OSC UI is a Karaf-based UI platform that allows you to create new applications and install those applications within the OSC UI via the OSGi Blueprint Container specification. Applications consist of the following components, which are typically packaged as separate Maven JAR files:
Application module—Contains all of the JS and HTML code for an application.
Application bundle—Contains an application’s configuration file (blueprint.xml), which is read by the Karaf container and used to deploy that application within the OSC UI. The application bundle embeds the contents of the corresponding application module to ensure that they can be accessed from a browser. Note that only the application bundle, and not the application module, is deployed in Karaf.
You also have the option of packaging an application’s module and bundle files together in one Maven JAR file. By doing so, you will not need to embed the module’s content within the bundle.
To add a new application to the OSC UI, you will need to do the following:
Create a new JavaScript module.
Add a new OSGi Blueprint bundle.
Add a new Karaf feature for your application.
Before you proceed, note the following:
You can deploy your application's installer (.kar) file from the Features page. See Installing New Features for more information.
We recommend that you use both angularJS and requireJS when writing the code for your application.
The following topics assume that you have a basic understanding of Karaf. Refer to the Karaf Developer’s Guide for more information.
To learn more about Blueprint, click here.
OSC and its UI run on separate Karaf instances.
The first thing you need to do when developing a new OSC UI application is create a JavaScript module. This module should be coded using angularJS and requireJS and packaged as a Maven JAR file. The structure for your project should look similar to the following example:
<module-name>-resources -- src ---- main ------resources -------- <module-name> ----------- <module-name>.module.js ----------- <module-name>.controller.js ----------- <module-name>.services.js ----------- <module-name>.directives.js ----------- <module-name>.filter.js ----------- index.tpl.html ----------- <module-stylesheet>.css -- pom.xml
Note that all of the code for your module will reside under your project’s resources folder.
To create a new JavaScript module, do the following:
Defining your JavaScript module is a five-step process that involves the following tasks:
Create a new file and save it with a name such as topology.module.js. The following example illustrates the contents of a standard module.js file:
define(['angularAMD','app/routingConfig', 'angular-ui-router','app/core/core.services'], function(ng) { var module = angular.module('app.a_module', ['ui.router.state', 'app.core']); // module configuration module.config(function() { [...] }); return module; });
In this example, the angularJS module is surrounded by a define function. This allows requireJS to see our module.js files. The first argument of the define function is an array which contains all of the module dependencies. The second argument is a callback function whose body contains the angularJS module code. The function parameters correspond with the order of dependencies, and each dependency is inserted into a parameter (if it is provided). Finally, the angular module is returned in order to enable its insertion as a parameter in any other modules you create.
For each new module, you must have at least two dependencies:
angularAMD—This is a angularJS wrapper that provides Asynchronous Module Definition (AMD) support, which is used by requireJS. For more information, click here.
app/core/core.services—This dependency is mandatory if you want to add content to the navigation menu, the left bar, or the top bar.
The following dependencies are not mandatory but are used often:
If your module is required by the main application, you need to register your angular components because the parent OSC UI application will be already be bootstrapped. The OSC UI will not see your components at runtime unless you add the following code.
Note | If your module is only used by another module, you can skip this step. |
module.config(function($compileProvider, $controllerProvider, $provide) { module.register = { controller : $controllerProvider.register, directive : $compileProvider.directive, factory : $provide.factory, service : $provide.service };
Next, set up your module’s route by adding the $stateProvider parameter to your module’s configuration method.
module.config(function($stateProvider) { var access = routingConfig.accessLevels; $stateProvider.state('main.module', { url: 'module', views : { 'content' : { templateUrl: 'src/app/module/module.tpl.html', controller: 'ModuleCtrl' } } }); });
In order to add an item to the navigation menu, the NavMenuHelper parameter in your module’s configuration method must be set, as illustrated in the following example. The first parameter is an ID that refers to a level of your menu and the second parameter is an object.
var module = angular.module('app.a_module', ['app.core']); module.config(function(NavMenuHelper) { NavMenuHelper.addToMenu('myFirstModule', { "link" : "#/module/index", "active" : "module", "title" : "My First Module", "icon" : "icon-sitemap", "page" : { "title" : "My First Module", "description" : "My first module" } }); });
Currently, two levels of ID parameter support are provided. For example, if your module’s ID is rootNode.childNode, the helper will look for a node named rootNode and append it with childNode. If the root node does not exist, it will create it automatically.
To link to the controller file, use the NavHelperProvider. It contains a method that will load the specified file.
[...] NavHelperProvider.addControllerUrl('<path-to-module-folder>/ <module-name>.controller');
Setup of the module.js file is now complete.
The process for creating the controller and other necessary components is similar to that for defining your module.
Add the module definition.
Specify the relative path to the module definition.
Create your methods using angularJs.
In the following example, we are setting up the register controller module:
define(['<relative-path-to-module>/<module-name>.module'], function(module) { module.register.controller('ModuleCtrl', function($rootScope, $scope) { }); });
Remember that you don’t need to register your angular components if your module only refers to another module.
The last thing you need to do to create a new JavaScript module is modify the POM.xml files that reside in the main OSC UI directory (dlux/), the modules directory (dlux/modules/), and the dlux-web directory (dlux/dlux-web/). Edit the files as follows:
Note | If you are writing an application that will reside outside of the OSC UI repository, you do not need to make the changes described in this topic. |
<properties> <nexus.repository.release>opendaylight.release </nexus.repository.release> <nexus.repository.snapshot>opendaylight.snapshot </nexus.repository.snapshot> <application-name.resources.version><Version> </application-name.resources.version> ........... <properties>
<modules> <module>{application-directory-name}</module> //For example "grouppolicy-resources" or "loader-resources" ....... </modules>
<dependencies> <dependency> <groupId>org.opendaylight.dlux</groupId> <artifactId>dlux.{application-name}.resources</artifactId> <version>${{application-name}.resources.version}</version> </dependency> </dependencies> ...... <includeArtifactIds> //Line 183 dlux.{application-name}.resources //for example "dlux.grouppolicy.resources" or "dlux.topology.resources" ..... </includeArtifactIds>
The OSGi Blueprint Container specification allows you to use dependency injection in your OSGi environment. Each OSC UI application module registers itself via its Blueprint configuration. Each application will have its own blueprint.xml file in which to place its configuration.
Create a Maven project with the following structure to place you Blueprint configuration:
AppModuleName src main resources OSGI-INF blueprint blueprint.xml pom.xml
In the pom.xml file, add a Maven plug-in to unpack your module’s code under this project’s generated-resources directory.
The following sample POM file is provided for your reference.
<?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/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.opendaylight.dlux</groupId> <artifactId>bundles</artifactId> <version>0.3.0-SNAPSHOT</version> <relativePath>../</relativePath> </parent> <groupId>org.opendaylight.dlux</groupId> <artifactId>dlux.topology</artifactId> <packaging>bundle</packaging> <dependencies> <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.core</artifactId> </dependency> <dependency> <groupId>org.osgi</groupId> <artifactId>org.osgi.compendium</artifactId> </dependency> <dependency> <groupId>org.apache.felix</groupId> <artifactId>org.osgi.compendium</artifactId> <version>${apache.felix.osgi.compendium.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>jcl-over-slf4j</artifactId> </dependency> <dependency> <groupId>org.opendaylight.dlux</groupId> <artifactId>loader</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.opendaylight.dlux</groupId> <artifactId>dlux.topology.resources</artifactId> <version>${topology.resources.version}</version> </dependency> </dependencies> <build> <resources> <resource> <directory>target/generated-resources</directory> </resource> <resource> <directory>src/main/resources</directory> </resource> </resources> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <version>2.6</version> <executions> <!--loader Resources--> <execution> <id>unpack-loader-resources</id> <goals> <goal>unpack-dependencies</goal> </goals> <phase>generate-resources</phase> <configuration> <outputDirectory>${project.build.directory}/ generated-resources</outputDirectory> <groupId>org.opendaylight.dlux</groupId> <includeArtifactIds>dlux.topology.resources</includeArtifactIds> <excludes>META-INF\/**</excludes> <excludeTransitive>true</excludeTransitive> <ignorePermissions>false</ignorePermissions> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <extensions>true</extensions> <configuration> <instructions> <Import-Package>org.osgi.service.http, org.osgi.framework;version="1.0.0", org.opendaylight.dlux.loader, org.slf4j </Import-Package> <Export-Package></Export-Package> </instructions> </configuration> </plugin> </plugins> </build> <scm> <connection>scm:git:ssh://git.opendaylight.org:29418/ dlux.git</connection> <developerConnection>scm:git:ssh://git.opendaylight.org:29418/dlux.git</developerConnection> <tag>HEAD</tag> <url>https://wiki.opendaylight.org/view/OpenDaylight_dlux:Main</url> </scm> </project>
Since your bundle will eventually be deployed in Karaf as a feature, your bundle should contain all of your module’s code. You should not encounter any problems if you choose to combine both the module and bundle together into one project.
Create the blueprint.xml configuration file in the src/main/resources/OSGI-INF/blueprint directory.
When creating a new application’s configuration file, ensure that it is formatted in a similar fashion to the following example:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> <reference id="httpService" availability="mandatory" activation="eager" interface="org.osgi.service.http.HttpService"/> <reference id="loader" availability="mandatory" activation="eager" interface="org.opendaylight.dlux.loader.DluxModuleLoader"/> <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.opendaylight.dlux.loader.DluxModule"> <property name="httpService" ref="httpService"/> <property name="loader" ref="loader"/> <property name="moduleName" value="topology "/> <property name="url" value="/src/app/topology"/> <property name="directory" value="/topology"/> <property name="requireJs" value="app/topology/topology.module"/> <property name="angularJs" value="app.topology"/> <property name="cssDependencies"> <list> <value>http://yui.yahooapis.com/3.18.1/build/cssreset/cssreset-min.css</value> <value>src/app/topology/topology-custom.css</value> </list> </property> </bean> </blueprint>
In the configuration above, two references with IDs are listed: httpService and loader. These two beans will have already been initialized by dlux-core, so any new application can use them. Without these two bean references, a new application will not be able to register.
Initialize your application bean, which will be an instance of class org.opendaylight.dlux.loader.DluxModule.
In addition to httpService and loader, there are 6 properties that you should specify for this bean:
moduleName—Name of your module. This name should be unique in the OSC UI.
url—This is the URL requireJS will use to load your module’s .js and .html files into the OSC UI. This is also the URL that a browser will use to load static .html, .js, and .css files. Since requireJS in the OSC UI has a base path of src, every URL you specify for this step should start with /src so that requireJS and browsers can access the appropriate files.
directory—In your bundle's pom.xml file, you unpack your module’s code. The directory you specify here is where your actual static files reside. The URL you specified in the previous bullet is registered with httpService, so when a browser makes a call to that URL, it will be redirected to the directory specified here.
requireJS—The path to your requireJS module. If you look closely in the previous example, you will see that the initial path of requireJS app/topology matches with the last part of the URL. This is the path that will be used by requireJS.
angularJS—Name of your angularJS module.
cssDependencies—If your application has any internal or external CSS dependencies, then those can be added here. If you create your own .css files, point to those files.
After you deploy your bundle in Karaf, Karaf will read your application's blueprint.xml file and register the application with the OSC UI. Once successful, refresh the OSC UI and you will see your application in the Applications pane.
At this point, you have written your JavaScript code and created a bundle that Karaf can understand. The final step is to test and deploy your bundle. Before you proceed, ensure that the odl-dlux-core feature is already enabled in Karaf.
Copy your bundle JAR file and place it in the deploy directory of your Karaf-based controller.
From the Karaf Console, install your bundle:
root@karaf> bundles:install -s mvn:mvn:org.opendaylight.dlux/dlux.topology/0.3.0
You may want to create your own Karaf feature in a production environment, which may deploy one or more bundles. All the Karaf-based features in the OSC UI are defined in the features.xml file, which can be found in the features/src/main/resources/ directory. A standard feature definition can have a dependency on another feature and one or more bundles, as is the case in the following example:
<feature name="odl-dlux-node" version='${project.version}' description="Enable nodes in Opendaylight dlux"> <feature>odl-dlux-core</feature> <bundle>mvn:org.opendaylight.dlux/dlux.node/${project.version}</bundle> </feature>
If you are updating code in the OSC UI repository, you can update the existing features.xml file. If your project resides outside of the repository, you can create a new feature there that includes your application bundle and has a dependency on the odl-dlux-core feature.