# ODTN LAB
###### tags: `unicamp`
This lab aims to create the foundation knowledge on using ONOS as an SDN controller for optical networks. To do so, ODTN-emulator is used to provide the necessary emulated equipment. The first part of this lab is based on https://github.com/opennetworkinglab/ODTN-emulator/blob/master/docs/README.md (some adaptations were made to make it work with newer versions of ONOS and some components of ODTN-emulator).
This lab is organized in 5 parts, as follows:
1. Platform - Installing ONOS and ODTN-emulator
2. Platform - Running ONOS and ODTN-emulator
3. NETCONF - Exploring ONOS and ODTN-emulator message exchange
4. NETCONF - Using netconf-console for reading
5. NETCONF - Using netconf-console for changing configuration
<!--
7. YANG Models, OpenConfig and NETCONF
-->
## PART 1: Platform - Installing ONOS and ODTN-emulator
:::info
If you have a pre-made virtual machine with all the necessary software, you can safely skip to PART 2. But if you want to bravely install one from scratch, PART 1 is for you.
:::
By the end of PART 1, you will have ONOS and ODTN-emulator compiled, installed, and ready to run. You need to have the following software installed on your system:
- docker
- docker-compose
- git
- bazel version 3.7.2
- python3
- pip3
- zip
Let's start by organizing the file structure of this lab by creating the directory `~/lab/odtn`:
```bash
$ mkdir -p ~/lab/odtn
```
Also, it's highly recommended to **prune** old docker images and containers related to ODTN-emulator (a less drastic alternative would be ```docker rm <CONTAINER_ID>``` and ```docker rmi <IMAGE_ID>```). Usually these images are:
- odtn-emulator_tapi_ols         latest
- odtn-emulator_openconfig_cassini_1 latest
- odtn-emulator_openconfig_cassini_2 latest
- ubuntu                 18.04
### Download and Compile ONOS
To assure everything works as expected, we must guarantee a certain version of ONOS is in use. To do so, line 4 does just that: it changes the ONOS repository to a specific version. So, open a terminal and type:
```bash=
$ cd ~/lab/odtn
$ git clone https://github.com/opennetworkinglab/onos.git
$ cd onos
$ git checkout 53733d577128d3fc2978d814afb04a3e692d37c9
$ export ONOS_APPS=odtn-service,roadm,gui2,optical-rest
$ bazel build onos
```
### Configure system PATH to include ONOS tools directory
Add the path `~/lab/odtn/onos/tools/package/runtime/bin/` to the global PATH environment variable (the correct way to do that depends on your Linux distribution). But for a local shell, and every local shell you open, open a terminal and execute the command:
```bash
$ export PATH=${PATH}:~/lab/odtn/onos/tools/package/runtime/bin/
```
> On some systems you can write this line in the file `~/.bashrc`
### Download and fix ODTN-emulator Dockerfile
ODTN-emulator runs inside several docker containers. In the process of creating docker images, some git repositories are cloned. Some of those repositories have suffered changes, and the Dockerfile representing the recipe to generate docker images weren't updated to comply with those changes. So, lines 5 and 6 make the necessary adjustments. And, again, to assure everything runs smoothly, line 4 changes the repository to a specific version. Open a terminal and type:
```bash=
$ cd ~/lab/odtn
$ git clone https://github.com/opennetworkinglab/ODTN-emulator.git
$ cd ODTN-emulator
$ git checkout faef96091a822bd9783ace9887f851c850de021b
$ sed -i 's+git clone git://git.cryptomilk.org+git clone https://git.cryptomilk.org+g' emulator-oc-cassini/Dockerfile
$ sed -i 's+cd Netopeer2/server \&\& git checkout v0.7-r1+cd Netopeer2 \&\& git checkout v0.7-r1 \&\& cd server+g' emulator-oc-cassini/Dockerfile
```
### Generating ODTN-emulator docker container images
Let's generate the ODTN-emulator containers. Open a terminal, change to the ODTN-emulator directory, and run `docker-compose`:
```bash
$ cd ~/lab/odtn/ODTN-emulator
$ docker-compose build
```
It will take a while. When it finish, inspect the generated container images. Type the following command:
```bash
$ docker images
```
You should see at least the following 4 images:
```
REPOSITORY TAG IMAGE ID CREATED SIZE
odtn-emulator_tapi_ols latest 7a50728a93a1 25 seconds ago 686MB
odtn-emulator_openconfig_cassini_1 latest 6987865ce96f 5 minutes ago 1.18GB
odtn-emulator_openconfig_cassini_2 latest 6987865ce96f 5 minutes ago 1.18GB
ubuntu 18.04 81bcf752ac3d 2 weeks ago 63.1MB
```
### Installing netconf-console
A very useful way to explore NETCONF and ODTN-emulator is through the use of `netconf-console`. So, let's go ahead and install it system-wide:
```bash
$ sudo pip3 install netconf-console==2.3.0
```
Make sure version 0.6.7 of ncclient, which is a netconf-console dependency, is installed. Open a terminal and type:
```bash
$ sudo pip3 install ncclient==0.6.7
```
That's it for the first part. Now let's proceed to PART 2.
## PART 2: Platform - Running ONOS and ODTN-emulator
By the end of PART 2, you will have the following running system:

In order to organize the data generated from executing commands, let's create a new directory. Open up a terminal and type:
```bash
$ mkdir ~/lab/odtn/inspect
```
:::warning
From now on, this directory will be refered to as `$INSPECT`.
:::
### Running ONOS
After compiling ONOS, it's time to run it. Open a terminal and type:
```bash
$ cd ~/lab/odtn/onos
$ export ONOS_APPS=odtn-service,roadm,gui2,optical-rest
$ bazel run onos-local -- clean
```
This terminal will keep showing ONOS log. So, don't type any other command here.
### Running ODTN-emulator
After generating the ODTN-emulator containers it's time to put them to run. Open a terminal, change to the ODTN-emulator directory, and run `docker-compose`:
```bash
$ cd ~/lab/odtn/ODTN-emulator
$ docker-compose up -d
```
Inspect running containers with the following command:
```bash
$ docker ps
```
You should see at least the following 3 containers:
```
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e0cddf773b09 odtn-emulator_openconfig_cassini_1 "sh /root/script/pus…" 4 days ago Up 4 days 22/tcp, 8080/tcp, 0.0.0.0:11002->830/tcp odtn-emulator_openconfig_cassini_1_1
12bcb9a68531 odtn-emulator_openconfig_cassini_2 "sh /root/script/pus…" 4 days ago Up 4 days 22/tcp, 8080/tcp, 0.0.0.0:11003->830/tcp odtn-emulator_openconfig_cassini_2_1
8ab6d4910815 odtn-emulator_tapi_ols "sh /root/script/ent…" 4 days ago Up 4 days 0.0.0.0:11000->1234/tcp odtn-emulator_tapi_ols_1
```
### Testing ODTN-emulator
Before informing ONOS about the ODTN devices, let's interact with ODTN-emulator using `netconf-console`. Open a terminal and type:
```bash
$ netconf-console --host localhost --port 11002 -u root -p root --hello
```
:::danger
If ODTN-emulator is working appropriately, you should see an XML message.
1. What is this message all about? Can you explain the message in detail?
:::
### Uploading configuration to ONOS
Up until now, ONOS and ODTN-emulator were strangers to each other. Let's introduce ODTN-emulator to ONOS by providing the configuration files to make it possible for **ONOS to connect to each emulated device**. Open a terminal and type:
```bash
$ onos-netcfg localhost ~/lab/odtn/ODTN-emulator/topo/with-rest-tapi/device.json
$ onos-netcfg localhost ~/lab/odtn/ODTN-emulator/topo/with-rest-tapi/link.json
```
> If the system PATH environment variable is not set correctly, `onos-netcfg` must be invoked providing the full script path: `~/lab/odtn/onos/tools/package/runtime/bin/onos-netcfg`
:::warning
Click on `Details` bellow to see the contents of files `device.json` and `link.json`
:::
:::spoiler
**Here's the contents of file `device.json`**
```source
{
"devices" : {
"netconf:127.0.0.1:11002" : {
"basic" : {
"name":"cassini2",
"driver":"cassini-openconfig"
},
"netconf" : {
"ip" : "127.0.0.1",
"port" : "11002",
"username" : "root",
"password" : "root",
"idle-timeout" : "0"
}
},
"rest:127.0.0.1:11000": {
"rest": {
"ip": "127.0.0.1",
"port": 11000,
"protocol": "http",
"testUrl":"/restconf/data/tapi-common:context",
"manufacturer": "tapi-swagger",
"hwVersion": "0",
"swVersion": "2.1"
},
"basic": {
"driver": "ols"
}
},
"netconf:127.0.0.1:11003" : {
"basic" : {
"name":"cassini1",
"driver":"cassini-openconfig"
},
"netconf" : {
"ip" : "127.0.0.1",
"port" : "11003",
"username" : "root",
"password" : "root",
"idle-timeout" : "0"
}
}
}
}
```
>It basically provides the endpoints to enable ONOS to connect to. Also, the driver to be used, the communication protocol, and protocol dependent parameters, such as username and password.
If you observe the field name, this is how you give a friendly name to your device to appear at ONOS Web interface;
:::
:::spoiler
**Here's the contents of file `link.json`**
```code
{
"links": {
"netconf:127.0.0.1:11002/201-rest:127.0.0.1:11000/100000035178": {
"basic": {
"type": "OPTICAL",
"metric": 1,
"durable": true,
"bidirectional": true
}
},
"rest:127.0.0.1:11000/100000035182-netconf:127.0.0.1:11003/201": {
"basic": {
"type": "OPTICAL",
"metric": 1,
"durable": true,
"bidirectional": true
}
}
}
}
```
:::
### See results
Now, open a web browser and type the following URL:
```
http://localhost:8181/onos/ui
```
> This URL should work as long as you are running a WEB Browser from the same system ONOS is running. If ONOS is running in another system you must replace `localhost` with the correct IP address.
If you succeeded in connecting to ONOS with a WEB Browser, you should see a login interface such as the one:

Type the following credentials:
```
USERNAME: onos
PASSWORD: rocks
```
After a succesful login, ONOS shows the optical network topology such as the one seen in the figure:

That's it for the first part. Now let's proceed to the next part.
## PART 3: NETCONF - Exploring ONOS and ODTN-emulator message exchange
The following sequence diagram shows several messages exchanged between ONOS and an emulated Cassini device (provided by the ODTN-emulator). We will explore and inspect some of these messages. To do so, we will access ONOS cli, enable NETCONF logging, and capture NETCONF messages. ONOS Gui from the web browser will be used to generate some of these messages.

### Connect to ONOS cli
Let's **connect to ONOS cli**, **enable NETCONF log**, and, at the same time, **save all console messages** to a local file for further investigation.
1. Connect to ONOS cli. To do so, open a terminal and type:
```bash
$ ssh -p 8101 onos@localhost | tee $INSPECT/onos.netconf.output
```
> Password is rocks.
> Please note the meaning of $INSPECT, described at the very begining of PART 2.
> :exclamation: In particular cases, you can face the following error:
> <font size="-1">
>>\@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
>> \@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! \@
>> \@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
>> IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING >> NASTY!
>> Someone could be eavesdropping on you right now (man-in-the-middle attack)!
>> It is also possible that a host key has just been changed.
>> The fingerprint for the RSA key sent by the remote host is
>> SHA256:nsasa432rCwaWgpwZfVDxyzdghas5sldadsda.
>> Please contact your system administrator.
>> Add correct host key in /home/odtn/.ssh/known_hosts to get rid of this message.
>> Offending RSA key in /home/odtn/.ssh/known_hosts:43
>> Host key for [localhost]:8101 has changed and you have requested strict checking.
>> Host key verification failed.
></font>
>
> This happens when the ssh fingerprint of ONOS has changed. In a non-threatening security case, it can happen if ONOS is restarted and/or recompiled.
>
> In this case, you can just remove entry 43, as informed by line **Offending RSA key in /home/odtn/.ssh/known_hosts:43**, using sed:
> ```bash
> $ sed -i '43d' ~/.ssh/known_hosts
> ```
> After thar just try the `ssh` command again.
After connecting to ONOS cli you should see a prompt like:
:::success
onos@root >
:::
2. Enable log output from NETCONF
:::success
onos@root > log:set TRACE org.onosproject.netconf
onos@root > log:set TRACE org.onosproject.drivers.netconf
onos@root > log:tail
:::
> As long as `log:tail` is executing, this shell will not be available to type other commands.
So, let's go ahead and open ONOS roadm-gui app.
### Open ONOS Web GUI - NETCONF get - reading values
1. Open a web browser and type the following URL:
```
http://localhost:8181/onos/ui/#/roadm-gui
```
2. Select **cassin1** and click the upper right PORT icon, just like the following image:

3. Observe the ONOS log output from the cli (ssh session) - it scrolls really fast, you would probably need to analyze the output offline (`onos.netconf.output`). This is how a *get* request for *input-power-range* should look like:
```xml
#374
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="720">
<get>
<filter>
<components xmlns="http://openconfig.net/yang/platform">
<component>
<name>xe6/1</name>
<optical-channel xmlns="http://openconfig.net/yang/terminal-device">
<state>
<input-power-range/>
</state>
</optical-channel>
</component>
</components>
</filter>
</get>
</rpc>
##
```
:::danger
2. Can you guess what `#374 ` and `##` are used for? If so, complement your response pointing out any other previous method to achieve the same purpose. Tip: It's related to framing.
:::
4. Stop `log:tail` at ONOS cli.
Access the ONOS cli, the one executing `onos@root > log:tail`, and type the keys `ctrl-c` simultaneously. It will stop `log:tail.` Now type `logout`. This should leave ONOS cli.
:::danger
3. Open file `onos.netconf.output` and try to find messages numbered 5 and 6 on the sequence diagram. Inform the element name and the value being returned.
:::
### Open ONOS Web GUI - NETCONF edit-config - changing configuration
Now it's time to explore NETCONF messages to change the device configuration. Let's start by repeating steps from [Connect to ONOS cli](https://hackmd.io/mx1y0hXFTtaHMHPzkYoEVw?both#Connect-to-ONOS-cli).
> IMPORTANT: If you don't want to overwrite the previous file, replace the filename `onos.netconf.output` with `onos.netconf.output2`.
Also, repeat steps 1 and 2 from [Open ONOS Web GUI - reading values](https://hackmd.io/mx1y0hXFTtaHMHPzkYoEVw?both#Open-ONOS-Web-GUI---reading-values).
After completing the previous steps, let's go ahead and observe what a NETCONF *edit-config* looks like:
1. Change the first port of **cassini1** Target Output Power to -5 dBm. To do so, fill in the indicated field (1) and press the Submit button (2), as indicated by the following figure:

> This action will take some time, so sit tight and wait for the action to take effect. After you see all other ports set to the same value, the action is complete.
2. Observe the ONOS log output from the cli (ssh session) - it scrolls really fast, you would probably need to analyze the output offline (`onos.netconf.output2`). This is how an *edit-config* request to change *target-output-power* should look like:
```xml=
#447
<?xml version="1.0" encoding="UTF-8"?>
<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="1943">
<edit-config>
<target>
<running/>
</target>
<config>
<components xmlns="http://openconfig.net/yang/platform">
<component>
<name>oe8/2</name>
<optical-channel xmlns="http://openconfig.net/yang/terminal-device">
<config>
<target-output-power>-5.0</target-output-power>
</config></optical-channel>
</component>
</components>
</config>
</edit-config>
</rpc>
##
```
:::danger
Observe the strucure of this call. Instead of `<get>` you have `<edit-config>`. `<edit-config>` is a NETCONF message to change a device's configuration - message 23 at the sequence diagram above.
4. Open file `onos.netconf.output2`. Could you find the response to this message? Tip: observe the message-id (which is probably different from this example).
5. Elaborate the meaning of `<target>` and `<running/>`. Provide all other possible values for `<target>`.
6. Explain the meaning of `xmlns="http://openconfig.net/yang/terminal-device"` and `xmlns="http://openconfig.net/yang/platform"`.
:::
## PART 4: NETCONF - Using netconf-console for reading
Moving on to cli (command line interface). Instead of use ONOS to read (NETCONF `get` operation) information from cassin1 device, you'll learn how to do that from cli. By the end of this part you will be able to create your own NETCONF `get` calls. So, let's get started.
Open up a terminal and type:
```bash
$ cd ~/lab/odtn/ODTN-emulator
```
Let's execute the first NETCONF `get` call. Using the same terminal, type the following command to request information regarding all terminal-devices available on one of the **cassinis**:
```bash
$ netconf-console --host=127.0.0.1 --port=11002 -u root -p root --rpc=emulator-test/get-terminal-device.xml
```
> If you want to save the output to the disk just append `> $INSPECT/cassini-terminal-devices.xml` at the end of the previous command. It will create the file `cassini-terminal-devices.xml`. Please note the meaning of $INSPECT, described at the very begining of PART 2.
>
:::danger
7. Can you tell which cassini device is this? cassini1 or 2? Tip: look at file `device.json`.
:::
It generates a huge output, roughly 685 lines and 29764 bytes. So, here's just the first few lines:
```xml=
<?xml version='1.0' encoding='UTF-8'?>
<nc:rpc-reply xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:34dd0290-8365-402d-b91e-2041f7b4d648">
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<terminal-device xmlns="http://openconfig.net/yang/terminal-device">
<logical-channels>
<channel>
<index>10101</index>
<config>
<index>10101</index>
<description>xe1/1</description>
<admin-state>ENABLED</admin-state>
<rate-class xmlns:oc-opt-types="http://openconfig.net/yang/transport-types">oc-opt-types:TRIB_RATE_100G</rate-class>
<trib-protocol xmlns:oc-opt-types="http://openconfig.net/yang/transport-types">oc-opt-types:PROT_100GE</trib-protocol>
<logical-channel-type xmlns:oc-opt-types="http://openconfig.net/yang/transport-types">oc-opt-types:PROT_ETHERNET</logical-channel-type>
</config>
<state>
<loopback-mode>NONE</loopback-mode>
</state>
<ingress>
<config>
<transceiver>xe1</transceiver>
</config>
</ingress>
<logical-channel-assignments>
<assignment>
<index>10101</index>
<config>
<index>10101</index>
<assignment-type>LOGICAL_CHANNEL</assignment-type>
<logical-channel>20101</logical-channel>
<allocation>100.0</allocation>
</config>
</assignment>
</logical-channel-assignments>
</channel>
....
```
Now let's take a look at file `emulator-test/get-terminal-device.xml`. This file provides a NETCONF `get` RPC call. Here's it's contents:
```xml
<?xml version="1.0"?>
<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<filter xmlns:td='http://openconfig.net/yang/terminal-device'>
<td:terminal-device/>
</filter>
</get>
```
:::danger
Now create your own file with the following call:
```xml
<?xml version="1.0"?>
<get xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<filter xmlns:td='http://openconfig.net/yang/terminal-device'>
<td:terminal-device>
<logical-channels>
<channel>
<index>20101</index>
</channel>
</logical-channels>
</td:terminal-device>
</filter>
</get>
```
8. What's the resulting output?
9. Read the output and provide the following information:
- `//terminal-device/logical-channels/channel/config/description`
- `//terminal-device/logical-channels/channel/logical-channel-assignments/assignment/config/assignment-type`
TIP: This path representation is called xPath and can be used in place of the more verbose XML document. XML documents are structured hierarchically, so is an xPath. So, just follow the XML document hierarchy according to the xPath to obtain the requested information.
10. Can you create another NETCONF `get` call (XML file) that filters `//components/component[name=oe1]`? If so, execute the call and provide the output. TIP: `[name=oe1]` only means that the `<name>` XML tag has the text `oe1`, just like `<index>` above has the value `20101`.
11. EXTRA: Mimicking Steve Jobs, "*one last thing*": Can you filter out all the NETCONF `sessions` (connections) to cassini? If so, provide the call and the output. The output should be something like:
```xml
...
<sessions>
<session>
<session-id>160</session-id>
<transport>netconf-ssh</transport>
<username>root</username>
<source-host>172.18.0.1</source-host>
...
```
:::
## Reading all available data
You have learned how to ask for small (not really) portions of information from a cassini device. What if you wanted all the available information? The next call does just that. So, open a terminal and type:
```bash
$ netconf-console --host=127.0.0.1 --port=11002 -u root -p root --get | less
```
> It's a 2743-line XML output (104241 bytes). It's huge. That's why we've used the `less` command. You can type `q` anytime to quit `less`. Another alternative is to write the output to a file. If it's your choice, just replace `less` with `$INSPECT/onos.netconf.output3`.
This is a really useful output. From that, you can learn: all aspects of the device; all installed modules; infer YANG models; learn what YANG modules are available; and so on. This kind of output is really important. **As a side note, I used this kind of output to develop 3 ONOS NETCONF drivers for a research project in collaboration with an optical equipment vendor ** - Therefore, I can't emphasize enough the importance of this output. Dig in and try to learn something else.
## PART 5: NETCONF - Using netconf-console for changing configuration
We have been using NETCONF with `netconf-console` for reading operations. This part will teach you how to change a cassini device configuration. To change configuration, you use NETCONF `edit-config` RPC calls. So, let's go ahead and start changing configuration.
Let's start observing what an `edit-config` call looks like:
```xml=
<?xml version="1.0"?>
<edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<target><running/></target>
<config>
<components xmlns="http://openconfig.net/yang/platform">
<component>
<name>oe1/2</name>
<optical-channel xmlns="http://openconfig.net/yang/terminal-device">
<config>
<target-output-power>0</target-output-power>
</config>
</optical-channel>
</component>
</components>
</config>
</edit-config>
```
Observe that the second line now has the `edit-config` call. There is also another entry observed on line 3, `<target>`. We have stumbled upon `edit-config` and `<target>` in one of the sections in [PART 3](https://hackmd.io/mx1y0hXFTtaHMHPzkYoEVw?both#Open-ONOS-Web-GUI---NETCONF-edit-config---changing-configuration). Back there, we have used ONOS to change a configuration as well.
<!--Another peculiarity is that the `edit-config` is not wrapped by a tag `<rpc>` as it is the case with `<get>`. But -->
Observe the return we receive when `edit-config` runs succescully:
```xml
<?xml version='1.0' encoding='UTF-8'?>
<nc:rpc-reply xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:beb279aa-a8d2-467f-b9b5-a4332fca4df5">
<nc:ok/>
</nc:rpc-reply>
```
As you can see, it's an `rpc-reply` message. Also, observe the tag `<nc:ok/>`.
## Change target-output-power
We are going to modify the previous `edit-config` call to alter the component `oe1/2` `target-output-power` to $-4dBm$.
Open up a terminal and type:
```bash
$ cd ~/lab/odtn/ODTN-emulator
$ mkdir my-calls
$ cd my-calls
```
Create file `set-oe1-target-output-power.xml` with the following content:
```xml
<?xml version="1.0"?>
<edit-config xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
<target><running/></target>
<config>
<components xmlns="http://openconfig.net/yang/platform">
<component>
<name>oe1/2</name>
<optical-channel xmlns="http://openconfig.net/yang/terminal-device">
<config>
<target-output-power>-4</target-output-power>
</config>
</optical-channel>
</component>
</components>
</config>
</edit-config>
```
Execute the following command to execute your `edit-config` call:
```bash
$ netconf-console --host=127.0.0.1 --port=11002 -u root -p root --rpc=set-oe1-target-output-power.xml
```
:::danger
11. In order to test your NETCONF `edit-config` call, create a NETCONF `get` call to observe the changing in value of `target-output-power`. Provide both the XML used for the call and the return message.
:::
<!--
## PART 6: YANG Models, OpenConfig and NETCONF
-->