Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

This applies to the obsolete Junco (1.9) release

Please see Cluster Configuration in Owl (1.14) for an updated guide. For an up-to-date tutorial for ONOS see the NG-SDN tutorial

Welcome to the Distributed ONOS Tutorial.

In this tutorial, you will learn to write a distributed ONOS application. The application you will be writing is called BYON (Build Your Own Network). This tutorial will teach you how to implement an ONOS service, an ONOS store (both trivial and distributed) , and how to use parts of the CLI and Northbound API. First, you will start with a single instance implementation which will be fully functional. After this, you will implement a distributed implementation of the service. The idea here will be for you to see how simple transitioning from a trivial single instance implementation to a distributed implementation is. Believe it or not, most of your code does not change. (smile)

(Here are some slides that can be used to accompany the tutorial: Slides)

Table of Contents

Introduction

Pre-requisites

 (smile)

(Here are some slides that can be used to accompany the tutorial: Slides)

Table of Contents

Introduction

Pre-requisites

You will need a computer You will need a computer with at least 2GB of RAM and at least 5GB of free hard disk space. A faster processor or solid-state drive will speed up the virtual machine boot time, and a larger screen will help to manage multiple terminal windows.

...

Email us if you’re stuck, think you’ve found a bug, or just want to send some feedback. Please have a look at the guidelines to learn how to efficiently submit a bug report.

Setup Set up your environment

Install required software

You will need to acquire two files: a VirtualBox installer and the the latest Tutorial VM from the Downloads page. 

After you have downloaded VirtualBox, install it, then go to the next section to verify that the VM is working on your system.

...

Double-click on the downloaded tutorial zipfile. This will give you an OVF file. Open the OVF file, this will open virtual box with an import dialog. Make sure you provision your VM with 4GB of RAM and if possible 4 CPUs if possible, if not 2 CPUs should be ok.

...

Click on import. When the import is finished start the VM and login using:

USERNAME: distributed

you will be automatically logged in to a running desktop session as show below:

Image AddedPASSWORD: distributed

Important Command Prompt Notes

...

indicates that you are in mininet.

Startup multiple docker instances

Docker is an open platform for developers and sysadmins to build, ship, and run distributed applications. In this tutorial we will use docker to provide encapsulated runtime environments for running instances of ONOS. For docker cli help visit: https://docs.docker.com/reference/commandline/cli/

We will be using docker to spawn multiple ONOS instances. So before we dive into the code, let's provision some docker instances that will run ONOS. First, you should see that there is already an ONOS distributed tutorial images present on your system:

Connecting to your tutorial cell

Obtain the IP address of the tutorial cell on from your instructor and setup a PPTP VPN connection using onos as a user and onos as password. The method to this will vary depending on the operating system of the developer machine. The example below shows the setup on OS/X:

Image Added

Then connect to the ONOS Cluster VPN.

Verifying that ONOS is deployed

Now let's quickly run some tests to make sure everything is ok. Let's start by openning a new terminal window and connecting to the ONOS cli by typing onos

Code Block
onos@onos-tutorial:~$ onos
Logging in as karaf
Welcome to Open Network Operating System (ONOS)!
     ____  _  ______  ____     
    / __ \/ |/ / __ \/ __/   
   / /_/ /    / /_/ /\ \     
   \____/_/|_/\____/___/     
Code Block
distributed@mininet-vm:~/onos$ sudo docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
onos/tutorial-dist   latest              666b3c862984        13 hours ago        666.1 MB
ubuntu-upstart Documentation: wiki.onosproject.org      14.10
Tutorials:     tutorials.onosproject.org 
Mailing lists: lists.onosproject.org     
Come help e2b2af39309aout! Find out how at: contribute.onosproject.org 
Hit '<tab>' 7for daysa agolist of available commands
and '[cmd] --help' for help on a 264.2 MB

At this point you are ready to spawn your ONOS instances. To do this we will spawn three docker instances that will be detached and running in the background. Later on we will run an instance of ONOS in each docker instance. Let's spawn our docker instances:

Code Block
distributed@mininet-vm:~/onos$ sudo docker run -t -P  -i -d --name onos-1 onos/tutorial-dist
	<docker-instance-id>
distributed@mininet-vm:~/onos$ sudo docker run -t -P  -i -d --name onos-2 onos/tutorial-dist
	<docker-instance-id>
distributed@mininet-vm:~/onos$ sudo docker run -t -P  -i -d --name onos-3 onos/tutorial-dist
	<docker-instance-id>

If you get the following error message:

Code Block
distributed@mininet-vm:~$ sudo docker run -t -P  -i -d --name onos-1 onos/tutorial-dist
2014/12/11 10:55:53 Error response from daemon: Conflict, The name onos-1 is already assigned to 26d8c84f8a50. You have to delete (or rename) that container to be able to assign onos-1 to a container again.
distributed@mininet-vm:~$ 

which should only happen if you have already build your docker instance then you only need to start it:

Code Block
distributed@mininet-vm:~$ sudo docker start onos-1

 Now you should have three docker instances up and running

Code Block
distributed@mininet-vm:~$ sudo docker ps
CONTAINER ID        IMAGE                       COMMAND             CREATED              STATUS              PORTS                                                                     NAMES
fc08370eb3d0        onos/tutorial-dist:latest   "/sbin/init"        About a minute ago   Up About a minute   0.0.0.0:49168->22/tcp, 0.0.0.0:49169->6633/tcp, 0.0.0.0:49170->8181/tcp   onos-3              
bc725b09deed        onos/tutorial-dist:latest   "/sbin/init"        About a minute ago   Up About a minute   0.0.0.0:49165->22/tcp, 0.0.0.0:49166->6633/tcp, 0.0.0.0:49167->8181/tcp   onos-2              
26d8c84f8a50        onos/tutorial-dist:latest   "/sbin/init"        About a minute ago   Up About a minute   0.0.0.0:49162->22/tcp, 0.0.0.0:49163->6633/tcp, 0.0.0.0:49164->8181/tcp   onos-1              
distributed@mininet-vm:~$ 

Ok now that we have all our docker instances up and running we simply need to set them up with ONOS. To do this we will use the standard ONOS toolset which would be the same set of commands if you were to deploy ONOS on a VM or bare metal machine.

Setting up ONOS in spawned docker instances (also known as docking the docker (big grin))

First, let's start by making sure our environment is correctly setup. 

Code Block
distributed@mininet-vm:~$ cell docker
ONOS_CELL=docker
OCI=172.17.0.2
OC1=172.17.0.2
OC2=172.17.0.3
OC3=172.17.0.4
OCN=localhost
ONOS_FEATURES=webconsole,onos-api,onos-core,onos-cli,onos-rest,onos-gui,onos-openflow,onos-app-fwd,onos-app-proxyarp,onos-app-mobility
ONOS_USER=root
ONOS_NIC=172.17.0.*

 

First thing we need to do is setup passwordless access to our instances, this will safe you a ton of time especially when developing and pushing your component frequently. ONOS provides a script that will push your local key to the instance: 

Code Block
distributed@mininet-vm:~$ onos-push-keys $OC1
root@172.17.0.5's password: onosrocks
distributed@mininet-vm:~$ onos-push-keys $OC2
root@172.17.0.5's password: onosrocks
distributed@mininet-vm:~$ onos-push-keys $OC3
root@172.17.0.5's password: onosrocks

The password for your instance is onosrocks. You will need to do this for each instance. Now we just need to package ONOS by running:

Code Block
distributed@mininet-vm:~$ onos-package
-rw-rw-r-- 1 distributed distributed 41940395 Dec 11 13:20 /tmp/onos-1.0.0.distributed.tar.gz

This prepares an ONOS installations which can now be shipped to the remote instances:

Code Block
distributed@mininet-vm:~$ onos-install $OC1
onos start/running, process 308
distributed@mininet-vm:~$ onos-install $OC2
onos start/running, process 302
distributed@mininet-vm:~$ onos-install $OC3
onos start/running, process 300
distributed@mininet-vm:~$

This has now installed ONOS on your docker instances.

 

Verifying that ONOS is deployed

Now ONOS is installed let's quickly run some tests to make sure everything is ok. Let's start by connecting to the ONOS cli:

Code Block
distributed@mininet-vm:~$ onos -w $OC1
Connection to 172.17.0.2 closed.
Logging in as karaf
Welcome to Open Network Operating System (ONOS)!
     ____  _  ______  ____   
    / __ \/ |/ / __ \/ __/    
   / /_/ /    / /_/ /\ \       
   \____/_/|_/\____/___/      
                             
Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown ONOS.
onos> 

You should drop into the cli. Now in another cli window let's start mininet.

 

Code Block
distributed@mininet-vm:~$ cd onos-byon && ./startmn.sh
mininet>

Now let's see if we have switches that are connected to ONOS:

Code Block
onos> devices
id=of:0000000100000001, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000100000002, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000200000001, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000200000002, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000300000001, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000300000002, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000010100000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000010200000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000020100000000, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000020200000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000030100000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000030200000000, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:1111000000000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:2222000000000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
 Let's see if we can forward traffic:

 

Code Block
mininet> pingall
*** Ping: testing ping reachability
h111 -> h112 h121 h122 h211 h212 h221 h222 h311 h312 h321 h322 
h112 -> h111 h121 h122 h211 h212 h221 h222 h311 h312 h321 h322 
h121 -> h111 h112 h122 h211 h212 h221 h222 h311 h312 h321 h322 
h122 -> h111 h112 h121 h211 h212 h221 h222 h311 h312 h321 h322 
h211 -> h111 h112 h121 h122 h212 h221 h222 h311 h312 h321 h322 
h212 -> h111 h112 h121 h122 h211 h221 h222 h311 h312 h321 h322 
h221 -> h111 h112 h121 h122 h211 h212 h222 h311 h312 h321 h322 
h222 -> h111 h112 h121 h122 h211 h212 h221 h311 h312 h321 h322 
h311 -> h111 h112 h121 h122 h211 h212 h221 h222 h312 h321 h322 
h312 -> h111 h112 h121 h122 h211 h212 h221 h222 h311 h321 h322 
h321 -> h111 h112 h121 h122 h211 h212 h221 h222 h311 h312 h322 
h322 -> h111 h112 h121 h122 h211 h212 h221 h222 h311 h312 h321 
*** Results: 0% dropped (132/132 received)

Finally let's see if we have switches connected to each instance of ONOS:

Code Block
onos> masters
172.17.0.2: 5 devices
  of:0000000100000001
  of:0000000200000001
  of:0000000300000001
  of:0000020100000000
  of:0000030200000000
172.17.0.3: 2 devices
  of:0000000100000002
  of:0000000300000002
172.17.0.4: 7 devices
  of:0000000200000002
  of:0000010100000000
  of:0000010200000000
  of:0000020200000000
  of:0000030100000000
  of:1111000000000000
  of:2222000000000000

The number of switches per ONOS instance may be different for you because mastership is simply obtained by the first controller which handshakes with the switch. If you would like to rebalance the switch-onos ratio simply run:

Code Block
onos> balance-masters 

And now the output of the masters command should give you something similar to this:

Code Block
onos> masters
172.17.0.2: 5 devices
  of:0000000100000001
  of:0000000200000001
  of:0000000300000001
  of:0000020100000000
  of:0000030200000000
172.17.0.3: 4 devices
  of:0000000100000002
  of:0000000300000002
  of:0000020200000000
  of:1111000000000000
172.17.0.4: 5 devices
  of:0000000200000002
  of:0000010100000000
  of:0000010200000000
  of:0000030100000000
  of:2222000000000000

 

At this point, you multi-instance ONOS deployment is functional. Let's move on to writing some code.

Writing 'Build Your Own Network'

We are now going to start building BYON. BYON is a service which allows you to spawn virtual networks in which each host is connected to every other host of that virtual network. Basically, each virtual network contains a full mesh of the hosts that make it up.

Part 1: Creating an application

 We have downloaded some starter code in the ~/onos-byon directory. It contains a root pom.xml file for the project, as well as a initial implementation of the CLI bundle. We can start by importing  the entire project into IntelliJ.

Before we fetch stuff from git, run the following to make git happy.

Code Block
distributed@mininet-vm:~/onos-byon/ $ git config --global user.email "you@example.com"
distributed@mininet-vm:~/onos-byon/ $ git config --global user.name "Your Name"

Before we start, make sure you have the latest version of the template code by running:

Code Block
distributed@mininet-vm:~/onos-byon/ $ git fetch && git pull

First start IntelliJ by double clicking on the IntelliJ icon on your desktop. When you get prompted with the following window.

Image Removed

Select "Import Project" and import the onos-byon project.

Image Removed

Import the project from external model, and select "Maven".

And now make sure you check "Sources" and "Documentation" in the Automatically download section:

Image Removed

And click 'Next' and click next as well on the following window. Now, make sure you pick Java 8 in the next window by first clicking on the green '+' sign and selecting 'java-8-openjdk-amd64' and click 'ok' followed by 'Next'.

Image Removed

Finally click on 'Finish'. 

You should see two top-level packages on the left sidebar: byon-app and byon-cli as you can see below. Expand the byon-app application, and find NetworkService.java.

Image Removed

 

This file contains the interface for the NetworkService that we will be implementing. Next, open NetworkManager.java. This file is a component that doesn't export any services. Start by making NetworkManager implement NetworkService.

Code Block
languagejava
...
public class NetworkManager implements NetworkService {
...

 

We will need to implement all of the methods contained in NetworkService. For now, we can leave most of them empty. However, to be able to test the CLI, you should have implement getNetworks() return Lists.of("test"), and and implement getHosts() return Collections.emptyList().

Code Block
languagejava
titlegetNetworks
 @Override
 public Set<String> getNetworks() {
 	return Sets.newHashSet("test");
 }

and 

Code Block
languagejava
titlegetHosts
 @Override
 public Set<HostId> getHosts(String network) {
 	return Collections.emptySet();
 }

Also remember to put the @Service 

Now that we have some code implemented lets try to run it, first though we must push the bundles we just coded. So let's start by building the code:

But before you build, unset JAVA_HOME as the default points to java7. You only need to do this step once.

Code Block
distributed@mininet-vm:~/onos-byon$ echo "unset JAVA_HOME" >> ~/.bashrc
distributed@mininet-vm:~/onos-byon$ source ~/.bashrc

Now run the build step:

Code Block
distributed@mininet-vm:~/onos-byon$ mci
[INFO] Scanning for projects...
...
[INFO] byon-app .......................................... SUCCESS [4.259s]
[INFO] byon-cli .......................................... SUCCESS [1.643s]
[INFO] byon .............................................. SUCCESS [0.104s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.719s
[INFO] Finished at: Fri Dec 12 14:28:16 PST 2014
[INFO] Final Memory: 30M/303M
[INFO] ------------------------------------------------------------------------
distributed@mininet-vm:~/onos-byon$ 

mci is an alias for maven clean install. Now that your project has successfully built your project let's push it up to the docker instances we launched earlier. 

Code Block
distributed@mininet-vm:~/onos-byon$ ./byon-push-bits
Pushing byon to 172.17.0.2
Pushing byon to 172.17.0.3
Pushing byon to 172.17.0.4

The byon-push-bits command will take the build bundles from your local maven repository and push them into the ONOS docker instances. The command will also load and start the bundles, in fact every time you update your code you simply need to run byon-push-bits and the new bundles will be loaded and started in the remote ONOS instances.

Let's check that everything works by heading into ONOS and running a couple commands:

Code Block
distributed@mininet-vm:~/onos-byon$ onos -w
Connection to 172.17.0.2 closed.
Logging in as karaf
Welcome to Open Network Operating System (ONOS)!
     ____  _  ______  ____   
    / __ \/ |/ / __ \/ __/    
   / /_/ /    / /_/ /\ \       
   \____/_/|_/\____/___/      
                             
Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown ONOS.
onos> list
...
159 | Active |  80 | 1.0.0.SNAPSHOT   | byon-app                              
160 | Active |  80 | 1.0.0.SNAPSHOT   | byon-cli 
onos> list-networks
test

So here we can see that ONOS have loaded the byon application and if we run the list-networks command  we see one fake network that we hard coded in this section.

Part 2: Make it so, Number one

In this part we are going to implement a trivial store for our network service as well as learn how to push intents. The store will be used to store the service's network state (duh!) while the intent framework will allow us to simply connect together every host in the virtual network we create. 

In order to be able to use the Intent Framework the caller must supply an Application ID. Application ID allow ONOS to identify who is consuming which resources as well as track applications. To achieve this, we have to add the following code to our NetworkManager class.

So first, we will need a reference to the CoreService, as it is the service that provides Application IDs. To do this add the follow code to the NetworkManager class.

Code Block
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected CoreService coreService;

This now gives you a reference to the CoreService at runtime. So let's pick up an application Id.

Code Block
languagejava
private ApplicationId appId;

// in activate method
appId = coreService.registerApplication("org.onos.byon");

Make sure to store the Application Id in a class field.

The NetworkManager is going to have to use the store (that we are going to build) to store information therefore we are going to need a reference on a NetworkStore:

Code Block
languagejava
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkStore store;

Notice that at this point IntelliJ is not happy because the NetworkStore class does not exist. Well, let's crerate it!

Code Block
languagejava
public interface NetworkStore {
    /**
     * Create a named network.
     *
     * @param network network name
     */
    void putNetwork(String network);

    /**
     * Removes a named network.
     *
     * @param network network name
     */
    void removeNetwork(String network);

    /**
     * Returns a set of network names.
     *
     * @return a set of network names
     */
    Set<String> getNetworks();


    /**
     * Adds a host to the given network.
     *
     * @param network network name
     * @param hostId host id
     * @return updated set of hosts in the network (or an empty set if the host
     *         has already been added to the network)
     */
    Set<HostId> addHost(String network, HostId hostId);

    /**
     * Removes a host from the given network.
     *
     * @param network network name
     * @param hostId host id
     */
    void removeHost(String network, HostId hostId);

    /**
     * Returns all the hosts in a network.
     *
     * @param network network name
     * @return set of host ids
     */
    Set<HostId> getHosts(String network);

	/**
     * Adds a set of intents to a network
     *
     * @param network network name
     * @param intents set of intents
     */
    void addIntents(String network, Set<Intent> intents);

    /**
     * Returns a set of intents given a network and a host.
     *
     * @param network network name
     * @param hostId host id
     * @return set of intents
     */
    Set<Intent> removeIntents(String network, HostId hostId);

    /**
     * Returns a set of intents given a network.
     * @param network network name
     * @return set of intents
     */
    Set<Intent> removeIntents(String network);
	
}

Alright so now you have an interface for the NetworkStore, that makes IntelliJ happy. But someone should implement that interface right? Let's create a new class which implements the NetworkStore interface.

Code Block
languagejava
@Component(immediate = true, enabled = true)
@Service
public class SimpleNetworkStore
        implements NetworkStore {

    private static Logger log = LoggerFactory.getLogger(SimpleNetworkStore.class);

    private final Map<String, Set<HostId>> networks = Maps.newHashMap();
    private final Map<String, Set<Intent>> intentsPerNet = Maps.newHashMap();

    @Activate
    protected void activate() {
        log.info("Started");
    }

    @Deactivate
    protected void deactivate() {
        log.info("Stopped");
    }
}

Now as an exercise you must implement the methods of SimpleNetworkStore. Don't hesitate to ask questions here!

Add some Intents

Now that we have a simple store implementation, let's have byon program the network when hosts are added. For this we are going to need the intent framework, so let's grab a reference  to it in the network manager.

Code Block
languagejava
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected IntentService intentService;

And we will need the following code to implement the mesh of the hosts in each virtual network.

specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown ONOS.
onos> 

You should drop into the cli. Now open another terminal window and start mininet as shown:

Code Block
onos@onos-tutorial:~$ cd onos-byon && ./startmn.sh
...
...
mininet>

Now let's see if we have switches that are connected to ONOS:

Code Block
onos> devices
id=of:0000000100000001, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000100000002, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000200000001, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000200000002, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000300000001, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000000300000002, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000010100000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000010200000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000020100000000, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000020200000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000030100000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:0000030200000000, available=true, role=MASTER, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:1111000000000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10
id=of:2222000000000000, available=true, role=STANDBY, type=SWITCH, mfr=Nicira, Inc., hw=Open vSwitch, sw=2.1.3, serial=None, protocol=OF_10

 You can also see which switches are connected to each instance of the ONOS cluster:

Code Block
onos> masters
172.17.0.2: 5 devices
  of:0000000100000001
  of:0000000200000001
  of:0000000300000001
  of:0000020100000000
  of:0000030200000000
172.17.0.3: 2 devices
  of:0000000100000002
  of:0000000300000002
172.17.0.4: 7 devices
  of:0000000200000002
  of:0000010100000000
  of:0000010200000000
  of:0000020200000000
  of:0000030100000000
  of:1111000000000000
  of:2222000000000000

The number of switches per ONOS instance may be different for you because mastership is simply obtained by the first controller which handshakes with the switch. If you would like to rebalance the switch-onos ratio simply run:

Code Block
onos> balance-masters 

And now the output of the masters command should give you something similar to this:

Code Block
onos> masters
172.17.0.2: 5 devices
  of:0000000100000001
  of:0000000200000001
  of:0000000300000001
  of:0000020100000000
  of:0000030200000000
172.17.0.3: 4 devices
  of:0000000100000002
  of:0000000300000002
  of:0000020200000000
  of:1111000000000000
172.17.0.4: 5 devices
  of:0000000200000002
  of:0000010100000000
  of:0000010200000000
  of:0000030100000000
  of:2222000000000000

At this point, you multi-instance ONOS deployment is functional. Let's move on to writing some code.

Writing 'Build Your Own Network'

We are now going to start building BYON. BYON is a service which allows you to spawn virtual networks in which each host is connected to every other host of that virtual network. Basically, each virtual network contains a full mesh of the hosts that make it up.

Lab 1: Importing and building an application

We have downloaded some starter code in the ~/onos-byon directory. It contains a root pom.xml file for the project, as well as a initial implementation of the CLI bundle. We can start by importing the entire project into IntelliJ.

First start IntelliJ by double clicking on the IntelliJ icon on your desktop. When you get prompted with the following window.

Image Added

Select "Import Project" and import the onos-byon project.

Image Added

Import the project from external model, and select "Maven".

And now make sure you check "Sources" and "Documentation" in the Automatically download section:

Image Added

And click 'Next' and click next as well on the following window. Now, make sure you pick Java 8 in the next window by first clicking on the green '+' sign and selecting 'java-8-openjdk-amd64' and click 'ok' followed by 'Next'.

Image Added

Finally click on 'Finish'. You should see the byon source tree on the left sidebar.

Next, we can return to a tutorial to build the project. 


Code Block
onos@onos-tutorial:~/onos-byon$ mci
[INFO] Scanning for projects...
...
[INFO] byon .............................................. SUCCESS [1.104s]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.719s
[INFO] Finished at: Fri Dec 12 14:28:16 PST 2014
[INFO] Final Memory: 30M/303M
[INFO] ------------------------------------------------------------------------
onos@onos-tutorial:~/onos-byon$

mci is an alias for mvn clean install. Now, that your project has successfully built your project let's push it up to the ONOS cluster..

Code Block
onos@onos-tutorial:~/onos-byon$ onos-app $OC1 install target/byon-1.0-SNAPSHOT.oar
{"name":"org.onos.byon","id":39,"version":"1.0-SNAPSHOT","description":"Build Your Own Network App","origin":"Apps-R-Us LLC, Inc. GmbH","permissions":"[]","featuresRepo":"mvn:org.onos.byon/byon/1.0-SNAPSHOT/xml/features","features":"[byon]","state":"INSTALLED"}


The onos-app command will take the oar file that is generated during the build and push it into the specified ONOS instance. The command can also activate the application if you replace install with install! as well as separately activate, deactivate, and uninstall the application. Every time you update your code you simply need to run onos-app $OC1 reinstall! target/byon-1.0-SNAPSHOT.oar and the new application will be loaded and started in the remote ONOS instances.

Let's check that everything works by heading into ONOS and running a couple commands:


Code Block
onos@onos-tutorial:~/onos-byon$ onos -w
Logging in as karaf
Welcome to Open Network Operating System (ONOS)!
     ____  _  ______  ____  
    / __ \/ |/ / __ \/ __/   
   / /_/ /    / /_/ /\ \      
   \____/_/|_/\____/___/     
                              
Hit '<tab>' for a list of available commands
and '[cmd] --help' for help on a specific command.
Hit '<ctrl-d>' or type 'system:shutdown' or 'logout' to shutdown ONOS.
onos> apps -s
...
 37 org.onos.byon 1.0.SNAPSHOT Build Your Own Network App

The application has been successfully installed, but it has not yet been activated. Next, we will activate it:

Code Block
onos> app activate org.onos.byon
onos> apps -s
...
* 37 org.onos.byon 1.0.SNAPSHOT Build Your Own Network App

The star next to the application indicates that it has been activated. We can try running the list-networks command to display the one fake network that is hard coded in the starter code.

Code Block
onos> list-networks 
my-network

Congratulations! You have successfully built, installed and activated the byon application.

Lab 2: Connect the Manager to the Store

In this part, we are going to implement some of the NetworkManager's methods using the provided store. The store will be used to store the service's network state; however, the implementation that is provided is not distributed. We will build a better store in a later part. 

The NetworkManager is going to have to use the store (that we are going to build) to store information therefore we are going to need a reference on a NetworkStore:

Code Block
languagejava
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected NetworkStore store;

There are six methods in the NetworkManager that should make calls to the network store. They are marked with "TODO Lab 2" in the starter code.

For example in the getNetworks() method, we will want to replace:

Code Block
return ImmutableSet.of("my-network"); // TODO remove this line before starting lab 2

with a call to the store:

Code Block
return store.getNetworks();

Next, let's verify that everything works.

First, you will need to compile byon again with mci and run onos-app $OC1 reinstall! target/byon-1.0-SNAPSHOT.oar again to get your latest bundle loaded into the ONOS docker instances.

Now at an ONOS shell, let's try some of the commands.

Code Block
onos> list-networks 
onos> create-network test
Created network test    
onos> add-host test 00:00:00:00:00:01/-1
Added host 00:00:00:00:00:01/-1 to test   
onos> add-host test 00:00:00:00:00:02/-1
Added host 00:00:00:00:00:02/-1 to test
onos> list-networks 
test
	00:00:00:00:00:01/-1
	00:00:00:00:00:02/-1

Lab 3: Add intents to allow traffic to flow in the virtual networks

In order to be able to use many of ONOS' services, the caller must supply an Application ID. An application ID allows ONOS to identify who is consuming which resources as well as track applications. To achieve this, we ask the CoreService for an application ID in the activate method. You should use the appId when constructing the intents for this part.

We will also need to get a reference to the IntentService. The reference will automatically be injected when the application is loaded after you uncomment the two lines near the top of the NetworkManager.

When addHost() is called, we will need to create an intent between the host and all other hosts that already exist in the network.

Info

You should only to add the intents if this is the first time that the host is added, so be sure to check the store's return value. true indicates that the add was successful i.e. this is the first time.

For this exercise, we will be using HostToHostIntents and you can build one like this:

Code Block
Intent intent = HostToHostIntent.builder()
                                .appId(appId)
Code Block
languagejava
    private Set<Intent> addToMesh(HostId src, Set<HostId> existing) {
        if (existing.isEmpty()) {
            return Collections.emptySet();
        }
        IntentOperations.Builder builder = IntentOperations.builder(appId);
        existing.forEach(dst -> {
            if (!src.equals(dst)) {
                builder.addSubmitOperation(new HostToHostIntent(appId, src, dst));
            }
        });
        IntentOperations ops = builder.build();
        intentService.execute(ops);

        return ops.operations().stream().map(IntentOperation::intent)
                .collect(Collectors.toSet());
    }

    private void removeFromMesh(Set<Intent> intents) {
        IntentOperations.Builder builder = IntentOperations.builder(appId);
.key(generateKey(network, src, dst))
          intents.forEach(intent -> builder.addWithdrawOperation(intent.id()));
        intentService.execute(builder.build());
    }

 

Verify that everything works

So make sure you compile byon again with mci and run byon-push-bits again to get your latest bundles loaded into the ONOS docker instances.

Now at an ONOS shell, play around with byon. and you should be able to forward traffic in mininet.

Code Block
onos> list-networks 
onos> create-network test
Created network test    
onos> add-host test 00:00:00:00:00:01/-1
Added host 00:00:00:00:00:01/-1 to test   
onos> add-host test 00:00:00:00:00:02/-1 #fixme
Added host 00:00:00:00:00:02/-1 to test
onos> list-networks 
test
	00:00:00:00:00:01/-1
	00:00:00:00:00:02/-1
onos> intents
id=0x0, state=INSTALLED, type=HostToHostIntent, appId=org.onos.byon
    constraints=[LinkTypeConstraint{inclusive=false, types=[OPTICAL]}]

Now check in mininet that you can actually communicated between the two hosts that you added to your virtual network.

Code Block
mininet> h1 ping h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=21.4 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.716 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.073 ms

Part 3: Flesh Out the CLI

Ok so the CLI allows you to add networks and hosts but not remove them. In part you will learn how to create CLI commands in ONOS. Start by creating two files in the byon-cli package "RemoveHostCommand.java" and "RemoveNetworkCommand.java". These CLI commands are simple and very similar to the add CLI commands so do this now as an exercise. When you have written these commands, you will need to add the following XML to the 'shell-config.xml' under resources.

Code Block
<command>
	<action class="org.onos.byon.cli.RemoveHostCommand"/>
    <completers>
		<ref component-id="networkCompleter"/>
        <ref component-id="hostIdCompleter"/>
        <null/>
	</completers>
</command>
<command>
	<action class="org.onos.byon.cli.RemoveNetwork"/>
	<completers>
		<ref component-id="networkCompleter"/>
        <null/>
    </completers>
</command>
 Once again if you recompile your code and use byon-push-bits you will update your bundles on the ONOS instances. You should now be able to add and remove networks as well as hosts. Try it out!

Part 4: Network Events

 In order to be able to communicate between ONOS instances we are going to make use of events. These events will be fired by the distributed store and caught by the manager in the peer ONOS instance. At this point the peer manager will notify any local listeners of the network event. 

Ok so let's start by adding a NetworkEvent class.

Code Block
languagejava
public class NetworkEvent extends AbstractEvent<NetworkEvent.Type, String> {

    enum Type {
        NETWORK_ADDED,
        NETWORK_REMOVED,
        NETWORK_UPDATED
    }

    public NetworkEvent(Type type, String subject) {
        super(type, subject);
    }

}

We are also going to need a couple interfaces that will be needed by the listeners and the and the delegates. The listeners are components which have registered to obtain events from this service, these are usually local. A delegate is a manager which is receiving events from a neighbouring store for the purpose of either taking action on the store event or notifying listeners. So we are going to add NetworkListener.

Code Block
languagejava
public interface NetworkListener extends EventListener<NetworkEvent> {}

 and a NetworkStoreDelegate interfaces

Code Block
languagejava
public interface NetworkStoreDelegate extends StoreDelegate<NetworkEvent> {}

Of course now if we want the store to be delegate-capable we need to make it extend the Store as shown below.

Code Block
languagejava
public interface NetworkStore extends Store<NetworkEvent, NetworkStoreDelegate> {

And since we changed the interface of the Store we will have to add something to the implementations, namely SimpleNetworkStore. This abstract class is used to define the methods for posting event to its delegates.

Code Block
public class SimpleNetworkStore extends AbstractStore<NetworkEvent, NetworkStoreDelegate>

We need to add the following methods to the NetworkService in order to allow components to add and remove listeners.

Code Block
languagejava
    /**
     * Register a listener for network events.
     *
     * @param listener listener
     */
    void addListener(NetworkListener listener);

    /**
     * Unregister a listener for network events.
     *
     * @param listener listener
     */
    void removeListener(NetworkListener listener);

Now since the store is making use of the delegates, we need to make the stores' implementation delegate-aware by notifying the delegates when network elements are added or removed. For example, the putNetwork method no notifies the delegate that a new network has been added.

Code Block
languagejava
    @Override
    public void putNetwork(String network) {
        intentsPerNet.putIfAbsent(network, Sets.<Intent>newHashSet());
        if (networks.putIfAbsent(network, Sets.<HostId>newHashSet()) == null) {
            notifyDelegate(new NetworkEvent(NETWORK_ADDED, network));
        }
    }

Try to change the implementation of the remaining methods to notify the delegates as well.

Alright so now that the stores emit events the managers should be made to receive them. For this we are going to have to add a reference on the EventDeliveryService an AbstractListenerRegistry to keep track of listeners and NetworkStoreDelegate as shown in the code snippet below.

            .one(src)
                                .two(dst)
                                .build();
intentService.submit(intent);

Make sure to submit a HostToHostIntent between the newly added host, and all other hosts that are already in the virtual network.

It's time to make sure that everything works; build with mci and run onos-app $OC1 reinstall! target/byon-1.0-SNAPSHOT.oar 

Code Block
onos> create-network test2
Created network test2    
onos> add-host test2 00:00:00:00:00:01/-1
Added host 00:00:00:00:00:01/-1 to test2   
onos> add-host test2 00:00:00:00:00:02/-1
Added host 00:00:00:00:00:02/-1 to test2
onos> list-networks 
test2
	00:00:00:00:00:01/-1
	00:00:00:00:00:02/-1
onos> intents
id=0x0, state=INSTALLED, type=HostToHostIntent, appId=org.onos.byon
    constraints=[LinkTypeConstraint{inclusive=false, types=[OPTICAL]}]

Now check in mininet that you can actually communicated between the two hosts that you added to your virtual network.

 

Code Block
mininet> h1 ping h2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=21.4 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=0.716 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=0.073 ms

Lab 4: Implementing removal of hosts from a virtual network

When a host is removed from a network or a network is removed entirely, we need to remove the intent related to that host or network from the IntentService. There are Lab 4 TODOs in the removeHost() and removeNetwork() methods. The starter code also contains a removeIntents() method that you will need to complete. In this method, you will need to get the intents from the IntentService, filter the relevant ones to remove, and then instruct the IntentService to withdrawn them. Note: For your convenience, we have provided a helper method, matches(), that can be used to filter the relevant intents.

Next, we will need to construct some CLI commands so that you can test your work, specifically, one command to remove hosts and another to remove networks.

Start by creating two files in the byon-cli package 'RemoveHostCommand.java'. This CLI command is simple and very similar to the add host CLI command, so do this now as an exercise. When you have written this command, you will need to add the following XML to the 'shell-config.xml' under resources.

Code Block
<command>
	<action class="org.onos.byon.cli.RemoveHostCommand"/>
    <completers>
		<ref component-id="networkCompleter"/>
        <ref component-id="hostIdCompleter"/>
        <null/>
	</completers>
</command>


 Once again if you recompile your code and use onos-app update your application on the ONOS instances. You should now be able to add and remove networks as well as hosts. Try it out!

Now, you should have a fairly complete implementation of the BYON app, but it will only work from one instance. (Try running list-networks on another instance! It will not show you any networks.)

Lab 5: Upgrading the store to make it distributed

The starter implementation of the DistributedNetworkStore uses a ConcurrentMap to store information about the virtual networks. This map only stores the data locally, and it knows nothing about other instances in the cluster. We will be replacing the map with a ConsistentMap which is cluster-aware, and one of the distributed primitives provided by ONOS.

Before we can instantiate a ConsistentMap, we need to get a reference to the StorageService; you can do this by uncommented in the StorageService reference near the top of the DistributedNetworkStore class.

First, start by defining a couple fields:

Code Block
 	/*
     * TODO Lab 5: Replace the ConcurrentMap with ConsistentMap
     */
    private Map<String, Set<HostId>> networks;
    private ConsistentMap<String, Set<HostId>> nets;


Once you change the ConcurrentMap to a ConsistentMap, we will need to update the way the map is created in the activate() method. Here is how we can ask the storage service for a ConcurrentMap:

Code Block
	 /*
      * TODO Lab 5: Replace the ConcurrentHashMap with ConsistentMap
      *
      * You should use storageService.consistentMapBuilder(), and the
      * serializer: Serializer.using(KryoNamespaces.API)
      */
      nets = storageService.<String, Set<HostId>>consistentMapBuilder()
                .withSerializer(Serializer.using(KryoNamespaces.API))
                .withName("byon-networks")
 
Code Block
languagejava
	@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
	protected EventDeliveryService eventDispatcher;

    private ApplicationId appId;
    private final AbstractListenerRegistry<NetworkEvent, NetworkListener>
            listenerRegistry = new AbstractListenerRegistry<>.build();

    private final NetworkStoreDelegate delegate = new InternalStoreDelegate();


Then we need to add a couple lines in the activate in order to register our Event type for our listeners and set our delegate.

Code Block
 eventDispatcher.addSink(NetworkEvent.class, listenerRegistry);
 store.setDelegate(delegate);
       

and vice versa deactivate

Code Block
eventDispatcher.removeSink(NetworkEvent.class);
store.unsetDelegate(delegate);
networks = nets.asJavaMap();

Now that you the networks field is a normal java map that is backed by a distributed map you do not have to change any code at all. And actually you can ignore all the lab 5 todos. (wink)

Next, let's recompile your application and push it to the ONOS cluster. You should now be able to create, update, and delete networks from any node, and your distributed store will the application running on all instances in sync. Open a few terminals and test this on the different instances. 

Lab 6: Network Events

Components in ONOS can use events to asynchronously notify other components when their state has changed. We will demonstrate how this can be done by creating a new event type, NetworkEvent, to notify listeners when a virtual network has been updated. These events will be fired by the distributed store and forwarded by the manager to listeners in the peer ONOS instance. 

Let's start by inspecting the NetworkEvent classFinally add the methods to add listeners and dispatch events coming from the store:

Code Block
languagejava
    @Override
    public voidclass addListener(NetworkListener listener) {
        listenerRegistry.addListener(listener);
    }NetworkEvent extends AbstractEvent<NetworkEvent.Type, String> {

    @Override
enum    public void removeListener(NetworkListener listener) {
   Type {
     listenerRegistry.removeListener(listener);
    }NETWORK_ADDED,

    private class InternalStoreDelegate implements NetworkStoreDelegate { NETWORK_REMOVED,
        @OverrideNETWORK_UPDATED
    }

    public void notify(NetworkEvent eventNetworkEvent(Type type, String subject) {
            eventDispatcher.post(eventsuper(type, subject);
        }
    }

To test this you will need to complete the extra credit section (big grin).

 

Part 5: Distributed Store

So we can finally start implementing DistributedStore. To this start by disabling your SimpleNetworkStore:

Code Block
@Component(immediate = false, enabled = false)
@Service
public class SimpleNetworkStore

 

Then copy the contents of the SimpleNetworkStore to a new file called DistributedNetworkStore (surprise!). First start by striping all the events that you generate in the methods of the store. These will be generated by the Remote Listener. Start by adding the Remote Listener to the distributed store class as an internal class.


}

Let's also inspect the provided abstraction for listeners capable of receiving network events, which is the NetworkListener class:

Code Block
languagejava
public interface NetworkListener extends EventListener<NetworkEvent> {}

With ONOS being a clustered system, it needs to allow managers to learn about events that may have occurred on other cluster nodes. For this reason, in every ONOS subsystem the events are generated within the distributed stores and then are passed to the respective manager components via StoreDelegate interface. Upon receiving the event via the delegate interface, the manager can then take local action and then disseminate the event to its event listeners.

Let's inspect the provided NetworkStoreDelegate interface:

Code Block
languagejava
public interface NetworkStoreDelegate extends StoreDelegate<NetworkEvent> {}

To enable the store/manager delegate relationship, we need to make the existing store interface extend the abstract Store interface as shown below. It basically states that the NetworkStore is capable of having a NetworkStoreDelegate to which it will send NetworkEvent notifications:

Code Block
languagejava
public interface NetworkStore extends Store<NetworkEvent, NetworkStoreDelegate> {

And since we changed the interface of the Store we will have to make sure that the implementation, namely DistributedNetworkStore, properly adheres the revised contract. The AbstractStore base class provides methods for setting delegate and for posting events to that delegate.

Code Block
public class DistributedNetworkStore extends AbstractStore<NetworkEvent, NetworkStoreDelegate>

The ConsistentMap will already generate notifications when updates are made locally and remotely, so we can mostly leverage that feature to regenerate our events.

To do this, we will need to create a MapEventListener<String, Set<HostId>> class in the DistributedNetworkStore.

Code Block
private class InternalListener implements MapEventListener<String, Set<HostId>> {
    @Override
    public void event(MapEvent<String, Set<HostId>> mapEvent
Code Block
languagejava
 private class RemoteListener extends EntryAdapter<String, Set<HostId>> {
        @Override
        public void entryAdded(EntryEvent<String, Set<HostId>> event) {
            notifyDelegate(new NetworkEvent(NETWORK_ADDED, event.getKey()));
        }

        @Override
        public void entryUpdated(EntryEvent<String, Set<HostId>> event) {
            notifyDelegate(new NetworkEvent(NETWORK_UPDATED, event.getKey()));
        }

        @Override
        public void entryRemoved(EntryEvent<String, Set<HostId>> event) {
            notifyDelegate(newfinal NetworkEvent(NETWORK_REMOVED, event.getKey())).Type type;
        }
    }

Replace your maps, activate and deactivate methods by the following code snippet:

Code Block
languagejava
    private SMap<String, Set<HostId>> networks;switch (mapEvent.type()) {
    private SMap<String, Set<Intent>> intentsPerNet;

    private String listenerId;
case INSERT:
    @Activate
    public void activate() {
     type   super.activate();

= NETWORK_ADDED;
           networks = new SMap<>(theInstance.<byte[], byte[]>getMap("byon-networks"), this.serializer) break;
        intentsPerNet = new SMap<>(theInstance.<byte[], byte[]>getMap("byon-network-intents"), this.serializer); case UPDATE:
        EntryListener<String, Set<HostId>> listener        type = new RemoteListener()NETWORK_UPDATED;
             listenerId = networks.addEntryListener(listener, true) break;
             log.info("Started");
case REMOVE:
     }

       @Deactivatedefault:
       public void deactivate() {
      type  networks.removeEntryListener(listenerId)= NETWORK_REMOVED;
        log.info("Stopped");
      }


 

Verify your implementation

You can do this by recompiling, deploying, and launching another console to a second onos instance:

Code Block
distributed@mininet-vm:~/ $ onos $OC2

When you add and remove networks and/or hosts you should see this being reflected on the other ONOS instances.

 

Part 6: Event Listener

 

We can create the byon application using a Maven archetype:

 

 

 

Code Block
distributed@mininet-vm:~/onos-byon/ $ mvn archetype:generate -DarchetypeGroupId=org.onosproject  -DarchetypeArtifactId=onos-bundle-archetype -DarchetypeVersion=1.0.0p1

 

Make sure you are in the onos-byon directory

When prompted for the groupId, enter org.onos.byon. When prompted for the artifactId, enter byon-monitor. The remaining fields can the default values that maven suggests.

This will create a directory called byon-monitor that contains a pom.xml file as well as some starter Java files. Let's delete the "test" subtree for now.<w

Info

IntelliJ should detect the new module and ask you to import the changes but if it does not then right click on the top-level project, select Maven followed by re-import.

Open the pom.xml in the byon-monitor module and add the following snippet to the bottom of the dependencies block: (can either be done in IntelliJ or by hand)

Code Block
<dependency>
   <groupId>org.onos.byon</groupId>
   <artifactId>byon-app</artifactId>
   <version>1.0-SNAPSHOT</version>
</dependency>

The archetype comes with a file called AppComponent.java. Refactor this class and call it NetworkEventMonitor. And add the following code to it.

 

 

 break;
        }
        notifyDelegate(new NetworkEvent(type, mapEvent.key()));
    }
}

We will need to construct an instance of the InternalListener.

Code Block
private final InternalListener listener = new InternalListener();

This listener will need to be added and removed to the networks map during activation and deactivation respectively.

Alright, so now that the stores emit events the managers should be made to receive them. For this we are going to have to add a reference on the EventDeliveryService an ListenerRegistry to keep track of listeners and NetworkStoreDelegate as shown in the code snippet below.

Code Block
languagejava
	@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
	protected EventDeliveryService eventDispatcher;

    private final ListenerRegistry<NetworkEvent, NetworkListener>
            listenerRegistry = new ListenerRegistry<>();

    private final NetworkStoreDelegate delegate = new InternalStoreDelegate();

Then we need to add a couple lines in the activate in order to register our Event type for our listeners and set our delegate.

Code Block
 eventDispatcher.addSink(NetworkEvent.class, listenerRegistry);
 store.setDelegate(delegate);
       

and vice versa deactivate

Code Block
eventDispatcher.removeSink(NetworkEvent.class);
store.unsetDelegate(delegate);

Finally add the methods to add listeners and dispatch events coming from the store:

Code Block
languagejava
    @Override
    public void addListener(NetworkListener listener) {
        listenerRegistry.addListener(listener);
    }

    @Override
    public void removeListener(NetworkListener listener) {
        listenerRegistry.removeListener(listener);
    }

    private class InternalStoreDelegate implements NetworkStoreDelegate
Code Block
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
   protected NetworkService service;

   private final Listener listener = new Listener();

   @Activate
   protected void activate() {
       service.addListener(listener);
       log.info("Started");
   }

   @Deactivate
   protected void deactivate() {
       service.removeListener(listener);
       log.info("Stopped");
   }

   private class Listener implements NetworkListener {
        @Override
        public void eventnotify(NetworkEvent event) {
            logeventDispatcher.info("{}", post(event);
        }
    }

 

Then update the bundles line in byon-push-bits:     

Code Block
bundles="byon-app byon-cli byon-monitor"

Verify what you built

So once again recompile and redeploy byon by using the byon-push-bits command.

To test this, you will need to create a new component that registers to the NetworkEvents with the NetworkService. We will leave this as an extra credit exercise. 

Info

Make sure to use the @Component annotation to start an instance of the class automatically.You will also want to register and deregister with the NetworkService in the activate and deactivate methods of your component.

Finally, build and push the app to the ONOS cluster. You should try to test your implementation. When you add and remove networks and/or hosts you should see this being reflected on the other ONOS instances.

Furthermore, you add, remove, or update When you add a network you should see the following line in the ONOS log. You can get the ONOS log by running:

Code Block
distributed@mininet-vm:~/ $ ol

 

And you should see a line that look like:

Code Block
INFO  | event-dispatch-0 | NetworkEventMonitor    | 187 - org.onos.byon.monitor - 1.0.0.SNAPSHOT | NetworkEvent{time=<time&date>, type=NETWORK_ADDED, subject=test}

 

Conclusion

Congratulations! You have learned how to develop an ONOS distributed application on ONOS. 

Source Code Download

If you wish to have access to the full source code with the complete solution, and without downloading the entire tutorial VM, you can download the source code directly from GitHub as follows:

Code Block
git clone https://github.com/bocon13/onos-byon.git && cd onos-byon && git checkout solution