This is an archive of the ONOS 1.1 wiki. For the current ONOS wiki, look here.

Work-in-progress. Completed page will be linked into the Tutorials and Walkthroughs page.

Overview

ONOS interacts with the underlying network with the help of its Providers. Since the southbound is modular, new Providers may be loaded to enhance ONOS's ability to interact with networks. This tutorial illustrates how to create a new Provider, using the null link test Provider (NullLinkProvider) as an example. This provider generates LinkDescriptions to stress-test the ONOS core. The OpenFlowDeviceProvider is also referenced to supplement this tutorial with key Provider features not implemented by NullLinkProvider.

By completing this tutorial, you will understand:

  • The organization and structure of a Provider
  • How to load a Provider

Project skeleton setup

Provider implementations are placed under ${ONOS_ROOT}/providers/. The layout of the Provider is summarized below:

${ONOS_ROOT}/providers/pom.xml (provider parent POM file)
                      |  
                      /null/pom.xml (null providers POM)
                           |
                           /link/pom.xml (provider-specific POM)
                                |
                                /src/main/java/org/onosproject/provider/nil/link/impl/NullLinkProvider.java (the provider)
                                    |                                                |
                                    |                                                /package-info.java (optional package-wide documentation/annotations)
                                    |
                                    /test/java/org/onosproject/providers/null/link/impl/ (Unit tests should go here)

If you have completed the Application tutorial, you will notice that the following steps are very similar.

1. Set up a directory layout

We first build a directory tree following Maven conventions.

$ cd ${ONOS_ROOT}/providers/
$ mkdir -p null/link/src/main/java/org/onosproject/provider/nil/link/impl/
$ mkdir -p null/link/src/test/java/org/onosproject/provider/nil/link/impl/

2. Add and edit POM files

There are three key POM files to keep in mind for Providers:

  • Provider-specific : the Provider you are writing
  • Providers : the set of Providers associated with a mechanism for interacting with the network, e.g. a certain protocol
  • Provider root : all Providers

We take a look at each below.

Provider-specific POM

A Provider tends to have a relatively simple provider-specific POM file. For the NullLinkProvider:

Provider-specific pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright 2014 Open Networking Laboratory
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<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>org.onosproject</groupId>
        <artifactId>onos-null-providers</artifactId>
        <version>1.1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>onos-null-provider-link</artifactId>
    <packaging>bundle</packaging>

    <description>ONOS Null link provider</description>
</project>

Providers POM

Conventionally, a single Provider is associated with one subsystem, such as Device, Packet, or Link. A protocol supported by the southbound will have a Provider for each subsystem it interacts with. The Providers-level POM describes the group of Providers required to support a protocol. This POM is the parent POM to the Provider-specific file, and is in ${ONOS_ROOT}/providers/null/ .

Providers pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright 2014 Open Networking Laboratory
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~     http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<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>org.onosproject</groupId>
        <artifactId>onos-providers</artifactId>
        <version>1.1.0-SNAPSHOT</version>
        <relativePath>../pom.xml</relativePath>
    </parent>

    <artifactId>onos-null-providers</artifactId>
    <packaging>pom</packaging>

    <description>ONOS null protocol adapters</description>

    <modules>
        <module>device</module>
        <module>link</module>       <-----here
        <module>host</module>
        <module>packet</module>
        <module>flow</module>
    </modules>

    <dependencies>

        <dependency>
            <groupId>org.onosproject</groupId>
            <artifactId>onos-api</artifactId>
            <classifier>tests</classifier>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

As seen in the modules section of the above pom.xml, the NullLinkProvider is part of a set of five Providers associated with different aspects of the Null southbound.

Provider root POM

This POM file is used to build all available Providers in ${ONOS_ROOT}/providers. Therefore, it must include all Provider sets in its modules section, including our set of null Providers:

Provider root pom.xml
    <description>ONOS information providers &amp; control/management protocol adapter</description>

    <modules>
        <module>openflow</module>
        <module>lldp</module>
        <module>host</module>
        <module>null</module>       <-----here
    </modules>

3. Register with Karaf

Providers grouped together in the Providers POM should be loaded together in a single feature. To do so, we add this provider under feature name="onos-null" in features.xml, as part of the onos-null feature. This allows it to be loaded when onos-null is installed with feature:install.

features.xml
    <feature name="onos-null" version="@FEATURE-VERSION"
            description="ONOS Null providers">
        <feature>onos-api</feature>
        <bundle>mvn:org.onosproject/onos-null-provider-device/@ONOS-VERSION</bundle>
        <bundle>mvn:org.onosproject/onos-null-provider-link/@ONOS-VERSION</bundle>         <------here
        <bundle>mvn:org.onosproject/onos-null-provider-host/@ONOS-VERSION</bundle>
        <bundle>mvn:org.onosproject/onos-null-provider-packet/@ONOS-VERSION</bundle>
        <bundle>mvn:org.onosproject/onos-null-provider-flow/@ONOS-VERSION</bundle>
    </feature>

Creating the Provider

1. Create a minimal skeleton.

A Provider is a derived class of AbstractProvider that implements some Provider API, and an OSGi bundle. The class NullLinkProvider deals with Links, so will implement the LinkProvider interface. We begin with the following skeleton.

NullLinkProvider.java
/*
 * Copyright 2015 Open Networking Laboratory
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.onosproject.provider.nil.packet.impl;

import static org.slf4j.LoggerFactory.getLogger;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onosproject.net.link.LinkProvider;
import org.onosproject.net.link.LinkProviderRegistry;
import org.onosproject.net.link.LinkProviderService;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;

/**
 * Provider which advertises fake/nonexistent links to the core. To be used for
 * benchmarking only.
 */
@Component(immediate = true)
public class NullLinkProvider extends AbstractProvider implements LinkProvider {

    private final Logger log = getLogger(getClass());   

    // The Manager serving as communication point between the ONOS core and this Provider
    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected LinkProviderRegistry providerRegistry;

    // The interface that defines how this Provider can interact with the core
    private LinkProviderService providerService;

    public NullLinkProvider() {
        // ProviderId is used by the core to identify this Provider
        // The schema "null" and FQDN of the Null southbound comprise this ID
        super(new ProviderId("null", "org.onosproject.provider.nil"));
    }

    @Activate
    public void activate() {
        // Request interface from Manager
        providerService = providerRegistry.register(this); 
        log.info("started");
    }

    @Deactivate
    public void deactivate() {
        providerRegistry.unregister(this);
        log.info("stopped");
    }
	
 }

2. Register with Services.

Our Provider is part of the Link subsystem, but needs two pieces of information from other subsystems: available devices and mastership (write permissions) for each device. We register as a listener to the DeviceService, and make the MastershipService a dependency.

NullLinkProvider.java
// new imports - service dependencies
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceEvent;

// ...<snip>...

public class NullLinkProvider extends AbstractProvider implements LinkProvider {

    private final Logger log = getLogger(getClass());   

    // For subscribing to device-related events
    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected DeviceService deviceService;

    // For checking write permissions for devices
    @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
    protected MastershipService roleService;

    // For handling DeviceEvents that we've subscribed to
    private final InternalLinkProvider linkProvider = new InternalLinkProvider();

    // ...<snip>...

    @Activate
    public void activate() {
        providerService = providerRegistry.register(this); 
        deviceService.addListener(linkProvider);
        log.info("started");
    }

    @Deactivate
    public void deactivate() {
        deviceService.removeListener(linkProvider);
        providerRegistry.unregister(this);
        log.info("stopped");
    }

    /**
     * Listener as an inner class 
     * Listens for devices being added/removed, and generates LinkDescriptions.
     */
    private class InternalLinkProvider implements DeviceListener {
        @Override
        public void event(DeviceEvent event) {
        }
    }
 }

3. Notify Core about links.

We now have a providerService for notifying the Core about links and their states, and a linkProvider for learning about available devices that can be interconnected by links. The method event() in the latter intercepts DeviceEvents from the DeviceService. We can implement how this Provider "creates" links between devices that it finds in this method. To keep things organized, we break up event detection and link creation/destruction into multiple methods within the InternalLinkProvider:

NullLinkProvider.java - class InternalLinkProvider
// more new imports
import java.util.List;
import java.util.concurrent.ConcurrentMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import org.onosproject.net.ConnectPoint;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.PortNumber;
import org.onosproject.net.link.DefaultLinkDescription;
import org.onosproject.net.link.LinkDescription;

import static org.onosproject.net.MastershipRole.MASTER;

// ...<snip>...

public class NullLinkProvider extends AbstractProvider implements LinkProvider {

    // ...<snip>...

    private final InternalLinkProvider linkProvider = new InternalLinkProvider();

    // LinkDescriptions for Links that this Provider generates
    private final ConcurrentMap<ConnectPoint, LinkDescription> descriptions = Maps
            .newConcurrentMap();

    // Device IDs that have been seen so far
    private final List<DeviceId> devices = Lists.newArrayList();

    // Switch port values used as Link endpoints
    private static final PortNumber SRCPORT = PortNumber.portNumber(5);
    private static final PortNumber DSTPORT = PortNumber.portNumber(6);

    // ...<snip>...

    /**
     * Listens for devices being added/removed, and generates LinkDescriptions.
     */
    private class InternalLinkProvider implements DeviceListener {

        @Override
        public void event(DeviceEvent event) {
            // extract the device that the event is associated with
            Device dev = event.subject();

            // check if our ONOS instance has permission to configure this 
            // device - if not, ignore the event
            if (!MASTER.equals(roleService.getLocalRole(dev.id()))) {
                return;
            }

            // extract the event type, act accordingly 
            switch (event.type()) {
            case DEVICE_ADDED:
                addLink(dev);
                break;
            case DEVICE_REMOVED:
                removeLink(dev);
                break;
            default:
                break;
            }
        }

        /**
         * "create" links by passing LinkDescriptors to the Core with
         * "link found" context, via the linkDetected() method
         */
        private void addLink(Device current) {
            // update our list of configurable devices
            devices.add(current.id());
            // No link if only one device
            if (devices.size() == 1) {
                return;
            }

            // Connect new device to the last-seen device with a link
            DeviceId prev = devices.get(devices.size() - 2);
            // Create endpoints (ConnectPoints) on each device
            ConnectPoint src = new ConnectPoint(prev, SRCPORT);
            ConnectPoint dst = new ConnectPoint(current.id(), DSTPORT);

            // Create LinkDescriptions for the two 'halves' of the duplex
            // Link: src --> dst and dst --> src 
            LinkDescription fdesc = new DefaultLinkDescription(src, dst,
                    Link.Type.DIRECT);
            LinkDescription rdesc = new DefaultLinkDescription(dst, src,
                    Link.Type.DIRECT);

            // Save created Description for later use (for flickering, step 4)
            descriptions.put(src, fdesc);
            descriptions.put(dst, rdesc);

            // Tell the Core that we have detected a link
            providerService.linkDetected(fdesc);
            providerService.linkDetected(rdesc);
        }

        /*
         * "remove" a link by passing a LinkDescriptor to the Core with
         * "link removed" context, via the linkVanished() method
         */
        private void removeLink(Device device) {
            providerService.linksVanished(device.id());
            devices.remove(device.id());
        }
    }
 }

Several things to note here are:

  • A LinkDescription is a summary of a network link that the Provider has discovered. While this Provider fakes the discovery of a link, a proper Provider would also implement some mechanism to communicate with the network. For example, the LLDPLinkProvider maintains a LinkDiscovery object per known device. LinkDiscovery sends and receives LLDPs to and from its network device via its reference to the PacketService.

  • The Mastership check is needed for function in a cluster.The Provider cannot create a link between devices that the ONOS instance it runs on isn't master for, or if the instance isn't master for one endpoint.

4. Add the LinkDriver.

In addition to creating the illusion of a topology, our Provider generates a steady stream of LinkDescriptions for the core to handle during a stress test. We add this function as a internal LinkDriver class that sends previously-created LinkDescriptions to the core with the premise that the links that they describe are flapping.

NullLinkProvider.java - class LinkDriver
// more new imports
import static org.onlab.util.Tools.delay;

// ...<snip>...

public class DemoProvider extends AbstractProvider implements LinkProvider { 

    // ...<snip>...

    private static final PortNumber DSTPORT = PortNumber.portNumber(6);

    // Default values for tunable parameters
    private static final boolean FLICKER = true;
    private static final int DEFAULT_RATE = 3000;

    // true: cause links to appear to flap
    private boolean flicker = FLICKER;
    // cause link to flap every specified number of milliseconds
    private int eventRate = DEFAULT_RATE;

    // ...<snip>...

    /**
     * Generate Descriptions to make Links appear to be flapping
     */
    private class LinkDriver implements Runnable {

        @Override
        public void run() {
            log.info("LinkDriver started");

            // continue generating Descriptions until thread is torn down
            while (!linkDriver.isShutdown()) {
                for (LinkDescription desc : descriptions.values()) {
                    // link went down
                    providerService.linkVanished(desc);
                    delay(eventRate);
                    // link came back
                    providerService.linkDetected(desc);
                    delay(eventRate);
                }
            }
        }
    }

}

The LinkDriver is launched as a task in a separate thread, so we must wire it into the Provider so that it is started and stopped with the Provider.

NullLinkProvider.java - class LinkDriver
// more new imports
import static org.onlab.util.Tools.namedThreads;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

// ...<snip>...

public class DemoProvider extends AbstractProvider implements LinkProvider { 

    // ...<snip>...

    private int eventRate = DEFAULT_RATE;
    
    // Single-threaded executor. Thread is named to make it easier to  
    // find in logs.
    private ExecutorService linkDriver = Executors.newFixedThreadPool(1,
            namedThreads("null-link-driver"));

    // ...<snip>...

    @Activate
    public void activate() {
        providerService = providerRegistry.register(this);
        deviceService.addListener(linkProvider);
        linkDriver.submit(new LinkDriver());
        log.info("started");
    }

    @Deactivate
    public void deactivate() {
        if (flicker) {
            try {
                linkDriver.awaitTermination(1000, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                log.error("LinkBuilder did not terminate");
            }
            linkDriver.shutdownNow();
        }
        deviceService.removeListener(linkProvider);
        providerRegistry.unregister(this);
        deviceService = null;

        log.info("stopped");
    }   

    // ...<rest of class>...
}

5. Build the Provider.

Since we have modified several POM files up to the Provider root pom.xml, we build the project from ${ONOS_ROOT}/providers/ .

$ cd ${ONOS_ROOT}/providers && mvn clean install

Once the build process finishes, we can test out our new provider.

Loading the Provider(s)

Since the protocol-specific southbound module is just another feature, it can be loaded in the same way as an application.

Dynamically (at runtime)

To enable the (set of) Providers dynamically, type the following commands in the ONOS CLI:

loading reactive forwarding application from CLI
onos> feature:uninstall onos-openflow
onos> feature:install onos-null

The first line uninstalls the OpenFlow southbound, loaded by out-of-the-box configurations. Note that we don't load specific Providers, e.g. onos-null-provider-link, but the set of Providers that it is part of. You can verify that the new Providers are running via list:

onos> list | grep onos-null
118 | Active |  80 | 1.1.0.SNAPSHOT   | onos-null-provider-device             
119 | Active |  80 | 1.1.0.SNAPSHOT   | onos-null-provider-link               
120 | Active |  80 | 1.1.0.SNAPSHOT   | onos-null-provider-host               
121 | Active |  80 | 1.1.0.SNAPSHOT   | onos-null-provider-packet             
122 | Active |  80 | 1.1.0.SNAPSHOT   | onos-null-provider-flow               

Statically (at startup)

The Provider set can be loaded by modifying org.apache.karaf.features.cfg placed in the /etc folder of the Karaf installation. This configuration file contains a list of features that Karaf loads when is started (featuresBoot). We can simply add the feature onos-null to this list:

org.apache.karaf.features.cfg
featuresBoot=config,standard,region,package,kar,ssh,management,webconsole,onos-api,onos-core,onos-cli,onos-gui,onos-rest,onos-app-ifwd,onos-null

ONOS must be restarted in this case.

What next?

 

  • No labels