Create testing for ODTN, including line-side/client-side connectivity creation and deletion. Testing succeeds on single ONOS instance.

Change-Id: I7d0eb4ff0e9160ef2fdd5fba9567399a4f1777eb
diff --git a/tools/test/scenarios/bin/execute-tapi-delete-call.py b/tools/test/scenarios/bin/execute-tapi-delete-call.py
old mode 100644
new mode 100755
diff --git a/tools/test/scenarios/bin/execute-tapi-post-call.py b/tools/test/scenarios/bin/execute-tapi-post-call.py
index 59f535f..f6d419a 100755
--- a/tools/test/scenarios/bin/execute-tapi-post-call.py
+++ b/tools/test/scenarios/bin/execute-tapi-post-call.py
@@ -6,7 +6,7 @@
 
 if len(sys.argv) < 4:
     print "usage: execute-tapi-post-call <onos-node> <context> <empty> [uuid]."
-    print "\t- If <empty> is \"empty\", it measn that it shoudl be no devices, links or ports"
+    print "\t- If <empty> is \"empty\", it measn that it should be no devices, links or ports"
     print "\t- Uuid is optional and defaults to empty"
     print "\t- For example:\n\t\t- line-side connectivity creation: %s\n\t\t- client-side connectivity creation: %s" % \
           ("python execute-tapi-post-call.py 127.0.0.1 tapi-connectivity:create-connectivity-service line-side",
@@ -30,7 +30,13 @@
     print tapi_connection_json
     if not tapi_connection_json["tapi-connectivity:output"] and empty != "empty":
        print "No connection was established"
-       sys.exit(1)
+       sys.exit(0)
+    if empty == "empty":
+        if not tapi_connection_json["tapi-connectivity:output"]:
+            sys.exit(0)
+        else:
+            print "There exist some connectivities!!!"
+            sys.exit(1)
     if uuid == "":
         # verify empty connection
         print tapi_connection_json
diff --git a/tools/test/scenarios/net-odtn-presmoke.xml b/tools/test/scenarios/net-odtn-presmoke.xml
new file mode 100644
index 0000000..dbff113
--- /dev/null
+++ b/tools/test/scenarios/net-odtn-presmoke.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2018-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<scenario name="net-odtn-presmoke" description="Start ODTN docker containers and set env variables">
+  <group name="ODTN-Net-Prepare">
+    <step name="ODTN-Net-Prepare.clean-docker" exec="bash ${ONOS_SCENARIOS}/odtn/destroyOdtnCell.sh"/>
+    <step name="ODTN-Net-Prepare.build-docker-img" exec="bash ${ONOS_SCENARIOS}/odtn/buildOdtnDocker.sh" requires="ODTN-Net-Prepare.clean-docker"/>
+    <step name="ODTN-Net-Prepare.create-docker-containers" requires="ODTN-Net-Prepare.build-docker-img" exec="source ${ONOS_SCENARIOS}/odtn/createOdtnCell.sh"/>
+  </group>
+</scenario>
diff --git a/tools/test/scenarios/net-odtn-restconf.xml b/tools/test/scenarios/net-odtn-restconf.xml
index 93d2095..571bc0b 100644
--- a/tools/test/scenarios/net-odtn-restconf.xml
+++ b/tools/test/scenarios/net-odtn-restconf.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0"?>
 <!--
   ~ Copyright 2015-present Open Networking Foundation
   ~
@@ -13,42 +14,35 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-<scenario name="net-odtn-restconf"
-          description="ODTN RESTCONF API test">
-    <group name="Net-ODTN-Restconf">
-
-        <!-- Verify the correct topology is present -->
-
-        <step name="Net-ODTN-Restconf.Tapi-context"
-              exec="execute-tapi-context-get-call.py ${OC1} empty"/>
-
-        <!-- Verify empty connectivity service -->
-
-        <step name="Net-ODTN-Restconf.Tapi-connectivity" requires="Net-ODTN-Restconf.Tapi-context"
-              exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:get-connectivity-service-list empty"/>
-
-        <!-- Push the connectivity service request -->
-
-        <!--<step name="ODTN-Net-Setup.Tapi-connectivity" requires="ODTN-Net-Setup.Tapi-context"
-              exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:get-connectivity-service-list true"/>
-
-        <group name="ODTN-Net-Setup.Verify-Logs-3" requires="ODTN-Net-Setup.Tapi-connectivity">
-            <parallel var="${OC#}">
-                <step name="Check-Logs-3-${#}" exec="onos-check-logs ${OC#}"/>
-            </parallel>
-        </group>
-
-        <step name="ODTN-Net-Setup.Tapi-context" requires="ODTN-Net-Setup.Verify-Logs"
-              exec="execute-tapi-context-get-call.py ${OC1} empty"/>
-        <step name="ODTN-Net-Setup.Tapi-connectivity" requires="ODTN-Net-Setup.Tapi-context"
-              exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:get-connectivity-service-list empty true"/>
-
-        <group name="ODTN-Net-Setup.Verify-Logs-4" requires="ODTN-Net-Setup.Tapi-connectivity">
-            <parallel var="${OC#}">
-                <step name="Check-Logs-4-${#}" exec="onos-check-logs ${OC#}"/>
-            </parallel>
-        </group>-->
-
-
+<scenario name="net-odtn-restconf" description="ODTN RESTCONF API test">
+  <group name="Net-ODTN-Restconf">
+    <!-- Verify the correct topology is present -->
+    <step name="Net-ODTN-Restconf.Tapi-context" exec="execute-tapi-context-get-call.py ${OC1} empty"/>
+    <!-- Verify empty connectivity service -->
+    <step name="Net-ODTN-Restconf.Confirm-conn-empty" requires="Net-ODTN-Restconf.Tapi-context" exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:get-connectivity-service-list empty"/>
+    <!-- Push the line-side connectivity service request -->
+    <group name="Net-ODTN-Restconf.Line-side-test" requires="Net-ODTN-Restconf.Confirm-conn-empty">
+      <step name="Net-ODTN-Restconf.Create-line-side" exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:create-connectivity-service line-side"/>
+      <step name="Net-ODTN-Restconf.Check-line-side" requires="Net-ODTN-Restconf.Create-line-side" exec="${ONOS_SCENARIOS}/odtn/checkUntilSucc.sh 'execute-tapi-post-call.py+${OC1}+tapi-connectivity:get-connectivity-service-list+empty+|+grep+'tapi-connectivity:output'+|+grep+connection-uuid+-o+|+wc+-l' 1" />
+      <step name="Net-ODTN-Restconf.Delete-line-conn" requires="Net-ODTN-Restconf.Check-line-side" exec="execute-tapi-delete-call.py ${OC1} line"/>
+      <step name="Net-ODTN-Restconf.Confirm-conn-empty-2" requires="Net-ODTN-Restconf.Delete-line-conn" exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:get-connectivity-service-list empty"/>
+      <group name="Net-ODTN-Restconf.Verify-Logs" requires="Net-ODTN-Restconf.Confirm-conn-empty-2">
+        <parallel var="${OC#}">
+          <step name="Check-Logs-Restconf-1-${#}" exec="onos-check-logs ${OC#}"/>
+        </parallel>
+      </group>
     </group>
+    <!-- Push the client-side connectivity service request -->
+    <group name="Net-ODTN-Restconf.Client-side-test" requires="Net-ODTN-Restconf.Line-side-test" delay="10" >
+      <step name="Net-ODTN-Restconf.Create-client-side" exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:create-connectivity-service client-side"/>
+      <step name="Net-ODTN-Restconf.Check-client-side" requires="Net-ODTN-Restconf.Create-client-side" exec="${ONOS_SCENARIOS}/odtn/checkUntilSucc.sh 'execute-tapi-post-call.py+${OC1}+tapi-connectivity:get-connectivity-service-list+empty+|+grep+'tapi-connectivity:output'+|+grep+connection-uuid+-o+|+wc+-l' 1" />
+      <step name="Net-ODTN-Restconf.Delete-client-conn" requires="Net-ODTN-Restconf.Check-client-side" exec="execute-tapi-delete-call.py ${OC1} both"/>
+      <step name="Net-ODTN-Restconf.Confirm-conn-empty-3" requires="Net-ODTN-Restconf.Delete-client-conn" exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:get-connectivity-service-list empty"/>
+      <group name="Net-ODTN-Restconf.Verify-Logs-2" requires="Net-ODTN-Restconf.Confirm-conn-empty-3">
+        <parallel var="${OC#}">
+          <step name="Check-Logs-Restconf-2-${#}" exec="onos-check-logs ${OC#}"/>
+        </parallel>
+      </group>
+    </group>
+  </group>
 </scenario>
diff --git a/tools/test/scenarios/net-odtn-smoke.xml b/tools/test/scenarios/net-odtn-smoke.xml
index e49ca73..f1e1f7d 100644
--- a/tools/test/scenarios/net-odtn-smoke.xml
+++ b/tools/test/scenarios/net-odtn-smoke.xml
@@ -14,24 +14,10 @@
   ~ limitations under the License.
   -->
 
-
-<scenario name="net-odtn-smoke"
-          description="ODTN test steps">
-
-    <import file="${ONOS_SCENARIOS}/net-setup-odtn.xml"/>
-
-    <!--TODO check proper topology-->
-    <!--<step name="Net-ODTN-Smoke.Check-Summary"
-          requires="ODTN-Net-Setup.Verify-Logs-2" delay="5"
-          exec="onos-check-summary ${OC1} [0-9]* 4 8 0 112"/>-->
-
-    <import file="${ONOS_SCENARIOS}/net-odtn-restconf.xml"/>
-    <dependency name="Net-ODTN-Restconf" requires="ODTN-Net-Setup"/>
-
-    <import file="${ONOS_SCENARIOS}/net-teardown-odtn.xml"/>
-    <dependency name="Net-Teardown-ODTN" requires="Net-ODTN-Restconf"/>
-
-    <import file="${ONOS_SCENARIOS}/shutdown.xml"/>
-    <dependency name="Shutdown-ONOS" requires="Net-Teardown-ODTN"/>
-
+<scenario name="net-odtn-smoke" description="ODTN test steps">
+  <import file="${ONOS_SCENARIOS}/net-setup-odtn.xml"/>
+  <import file="${ONOS_SCENARIOS}/net-odtn-restconf.xml"/>
+  <dependency name="Net-ODTN-Restconf" requires="ODTN-Net-Setup"/>
+  <import file="${ONOS_SCENARIOS}/net-teardown-odtn.xml"/>
+  <dependency name="Net-Teardown-ODTN" requires="Net-ODTN-Restconf"/>
 </scenario>
diff --git a/tools/test/scenarios/net-setup-odtn.xml b/tools/test/scenarios/net-setup-odtn.xml
index 78987a2..1f69aed 100644
--- a/tools/test/scenarios/net-setup-odtn.xml
+++ b/tools/test/scenarios/net-setup-odtn.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0"?>
 <!--
   ~ Copyright 2015-present Open Networking Foundation
   ~
@@ -13,81 +14,58 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
 <scenario name="net-setup-odtn" description="ODTN network setup steps">
-
-    <group name="ODTN-Net-Setup">
-
-        <!-- Clean -->
-        <step name="ODTN-Net-Setup.Wipe-Out-Data-Before" exec="onos-wipe-out"/>
-
-        <!-- Make sure that there is no data in the system -->
-        <step name="ODTN-Net-Setup.Initial-Summary-Check" requires="~ODTN-Net-Setup.Wipe-Out-Data-Before"
-              exec="onos-check-summary ${OC1} [0-9]* 0 0 0"/>
-
-        <!-- Deactivate unneeded apps -->
-        <group name="ODTN-Net-Setup.Deactivate-Apps" requires="ODTN-Net-Setup.Initial-Summary-Check">
-            <step name="App-Deactivate-fwd"
-                  exec="onos ${OCI} app deactivate org.onosproject.fwd"
-                  requires="ODTN-Net-Setup.Initial-Summary-Check"/>
-            <step name="App-Deactivate-Openflow"
-                  exec="onos ${OCI} app deactivate org.onosproject.openflow"
-                  requires="ODTN-Net-Setup.Initial-Summary-Check"/>
-            <step name="App-Deactivate-Drivers"
-                  exec="onos ${OCI} app deactivate org.onosproject.drivers"
-                  requires="ODTN-Net-Setup.Initial-Summary-Check"/>
-            <step name="App-Deactivate-Mobility"
-                  exec="onos ${OCI} app deactivate org.onosproject.mobility"
-                  requires="ODTN-Net-Setup.Initial-Summary-Check"/>
-            <step name="App-Deactivate-ProxyArp"
-                  exec="onos ${OCI} app deactivate org.onosproject.proxyarp"
-                  requires="ODTN-Net-Setup.Initial-Summary-Check"/>
-            <step name="App-Deactivate-RouteService"
-                  exec="onos ${OCI} app deactivate org.onosproject.route-service"
-                  requires="ODTN-Net-Setup.Initial-Summary-Check"/>
-        </group>
-
-        <!-- Active required apps and yang models-->
-        <group name="ODTN-Net-Setup.Activate-Apps" requires="ODTN-Net-Setup.Deactivate-Apps">
-            <step name="App-Activate-Odtn-Service"
-                  exec="onos ${OCI} app activate org.onosproject.odtn-service"
-                  requires="ODTN-Net-Setup.Deactivate-Apps"/>
-        </group>
-
-        <group name="ODTN-Net-Setup.Verify-Apps" requires="ODTN-Net-Setup.Activate-Apps" delay="40">
-            <parallel var="${OC#}">
-                <step name="Check-Apps-${#}" exec="onos-check-apps ${OC#} optical-model,yang,config,configsync,faultmanagement,
-                netconf,configsync-netconf,drivers,drivers.netconf,drivers.optical,restconf,protocols.restconfserver,
-                odtn-api,drivers.odtn-driver,odtn-service,models.tapi,models.ietf,models.openconfig,models.openconfig-infinera,
-                models.openconfig-odtn includes"
-                      requires="ODTN-Net-Setup.Activate-Apps"/>
-            </parallel>
-        </group>
-
-        <group name="ODTN-Net-Setup.Verify-Logs" requires="ODTN-Net-Setup.Verify-Apps">
-            <parallel var="${OC#}">
-                <step name="Check-Logs-${#}" exec="onos-check-logs ${OC#}"/>
-            </parallel>
-        </group>
-
-        <step name="ODTN-Net-Setup.Tapi-context" requires="ODTN-Net-Setup.Verify-Logs"
-              exec="execute-tapi-context-get-call.py ${OC1} empty"/>
-
-        <group name="ODTN-Net-Setup.Verify-Logs-2" requires="ODTN-Net-Setup.Tapi-context">
-            <parallel var="${OC#}">
-                <step name="Check-Logs-2-${#}" exec="onos-check-logs ${OC#}"/>
-            </parallel>
-        </group>
-
-        <!-- Verify empty connectivity service -->
-
-        <step name="ODTN-Net-Setup.Tapi-connectivity" requires="ODTN-Net-Setup.Tapi-context"
-              exec="execute-tapi-post-call.py ${OC1} tapi-connectivity:get-connectivity-service-list empty"/>
-
-        <!--TODO include check for empty connectivity service-->
-        <!--TODO include 2 or more docker emulators to setup the network for phase 1.0-->
-        <!--TODO push netcfg links between emulated devices -->
-        <!--TODO push emulated links between emulated devices -->
-        <!--TODO check logs -->
+  <group name="ODTN-Net-Setup">
+    <!-- Clean -->
+    <step name="ODTN-Net-Setup.Wipe-Out-Data-Before" exec="onos-wipe-out"/>
+    <!-- Make sure that there is no data in the system -->
+    <step name="ODTN-Net-Setup.Initial-Summary-Check" requires="~ODTN-Net-Setup.Wipe-Out-Data-Before" exec="onos-check-summary ${OC1} [0-9]* 0 0 0"/>
+    <step name="ODTN-Net-Setup.Activate-Apps" exec="onos ${OC1} app activate odtn-service" requires="ODTN-Net-Setup.Initial-Summary-Check" delay="30" />
+    <group name="ODTN-Net-Setup.Verify-Apps" requires="ODTN-Net-Setup.Activate-Apps" delay="30">
+      <parallel var="${OC#}">
+        <step name="Check-Apps-${#}" exec="onos-check-apps ${OC#} optical-model,yang,config,configsync,faultmanagement,netconf,configsync-netconf,drivers,drivers.netconf,drivers.optical,restconf,protocols.restconfserver,odtn-api,drivers.odtn-driver,odtn-service,models.tapi,models.ietf,models.openconfig,models.openconfig-infinera,models.openconfig-odtn includes" requires="ODTN-Net-Setup.Activate-Apps"/>
+      </parallel>
     </group>
+    <group name="ODTN-Net-Setup.Tapi-context" requires="ODTN-Net-Setup.Verify-Apps">
+      <parallel var="${OC#}">
+        <step name="ODTN-Net-Setup.Tapi-context-${#}" exec="${ONOS_SCENARIOS}/odtn/checkUntilSucc.sh ${ONOS_SCENARIOS}/bin/execute-tapi-context-get-call.py+${OC#}+empty"/>
+      </parallel>
+    </group>
+    <!-- Verify empty connectivity service -->
+    <step name="ODTN-Net-Setup.Tapi-connectivity" requires="ODTN-Net-Setup.Tapi-context" exec="${ONOS_SCENARIOS}/odtn/checkUntilSucc.sh ${ONOS_SCENARIOS}/bin/execute-tapi-post-call.py+${OC1}+tapi-connectivity:get-connectivity-service-list+empty"/>
+    <group name="ODTN-Net-Setup.Verify-Logs-1" requires="ODTN-Net-Setup.Tapi-connectivity">
+      <parallel var="${OC#}">
+        <step name="Check-Logs-Odtn-Setup-1-${#}" exec="onos-check-logs ${OC#}"/>
+      </parallel>
+    </group>
+    <!-- include 2 or more docker emulators to setup the network for phase 1.0-->
+    <group name="ODTN-Net-Setup.Init-network" requires="ODTN-Net-Setup.Verify-Logs-1">
+      <step name="ODTN-Net-Setup.Start-emulators" exec="docker-compose -f ${HOME}/emulator/docker-compose.yaml up -d"/>
+      <step name="ODTN-Net-Setup.Generate-cfg-files" requires="ODTN-Net-Setup.Start-emulators" exec="${ONOS_SCENARIOS}/odtn/createNetCfg.sh"/>
+      <step name="ODTN-Net-Setup.Put-nodes" requires="ODTN-Net-Setup.Generate-cfg-files" exec="onos-netcfg ${OC1} /tmp/odtn/openconfig-devices.json"/>
+      <group name="ODTN-Net-Setup.Wait-nodes" requires="ODTN-Net-Setup.Put-nodes">
+        <parallel var="${OC#}">
+          <step name="ODTN-Net-Setup.Wait-node-${#}" exec="${ONOS_SCENARIOS}/odtn/checkNetInit.sh device ${OC#}"/>
+          <step name="ODTN-Net-Setup.Wait-port-${#}" exec="${ONOS_SCENARIOS}/odtn/checkNetInit.sh port ${OC#}" requires="ODTN-Net-Setup.Wait-node-${#}"/>
+        </parallel>
+      </group>
+      <group name="ODTN-Net-Setup.Verify-Logs-2" requires="ODTN-Net-Setup.Wait-nodes">
+        <parallel var="${OC#}">
+          <step name="Check-Logs-Odtn-Setup-2-${#}" exec="onos-check-logs ${OC#}"/>
+        </parallel>
+      </group>
+      <step name="ODTN-Net-Setup.Put-links" requires="ODTN-Net-Setup.Verify-Logs-2" exec="onos-netcfg ${OC1} /tmp/odtn/openconfig-device-link.json"/>
+      <group name="ODTN-Net-Setup.Wait-links" requires="ODTN-Net-Setup.Put-links" delay="30">
+        <parallel var="${OC#}">
+          <step name="ODTN-Net-Setup.Wait-link-${#}" exec="${ONOS_SCENARIOS}/odtn/checkNetInit.sh link ${OC#}"/>
+        </parallel>
+      </group>
+    </group>
+    <!-- check logs to verify -->
+    <group name="ODTN-Net-Setup.Verify-Logs-3" requires="ODTN-Net-Setup.Wait-links">
+      <parallel var="${OC#}">
+        <step name="Check-Logs-Odtn-Setup-3-${#}" exec="onos-check-logs ${OC#}"/>
+      </parallel>
+    </group>
+  </group>
 </scenario>
diff --git a/tools/test/scenarios/net-teardown-odtn.xml b/tools/test/scenarios/net-teardown-odtn.xml
index be063c1..be78544 100644
--- a/tools/test/scenarios/net-teardown-odtn.xml
+++ b/tools/test/scenarios/net-teardown-odtn.xml
@@ -1,3 +1,4 @@
+<?xml version="1.0"?>
 <!--
   ~ Copyright 2015-present Open Networking Foundation
   ~
@@ -14,11 +15,8 @@
   ~ limitations under the License.
   -->
 <scenario name="net-teardown-odtn" description="Network teardown steps">
-    <group name="Net-Teardown-ODTN">
-        <!-- TODO remove docker simulators docker stop $(docker ps -aq) -->
-        <group name="ODTN-Net-Teardown.Deactivate-Apps-2">
-            <step name="App-Dectivate-Odtn-Service"
-                  exec="onos ${OCI} app deactivate org.onosproject.odtn-service"/>
-        </group>
-    </group>
-</scenario>
\ No newline at end of file
+  <group name="Net-Teardown-ODTN">
+    <step name="App-Dectivate-Odtn-Service" exec="onos ${OCI} app deactivate org.onosproject.odtn-service"/>
+    <step name="Remove-onos-images-stop-emulators" requires="App-Dectivate-Odtn-Service" exec="${ONOS_SCENARIOS}/odtn/destroyOdtnCell.sh"/>
+  </group>
+</scenario>
diff --git a/tools/test/scenarios/odtn/README.md b/tools/test/scenarios/odtn/README.md
new file mode 100644
index 0000000..61b68f9
--- /dev/null
+++ b/tools/test/scenarios/odtn/README.md
@@ -0,0 +1,44 @@
+## Description
+
+This directory contains several Shell scripts for ODTN project testing in STC environment.
+
+### The normal steps of single-instance ODTN testing are listed:
+
+#### 1. "./testOnSingleONOS.sh"
+
+This command do odtn-setvice startup on local ONOS instance, and line-side/client-side connectivity creation and deletion.
+The requirement for this script's invocation is explained in it's comment.
+
+### The normal steps of multi-instance ODTN testing are listed:
+
+#### 1. `stc net-odtn-presmoke`
+This command completes preparation for testing, including latest onos image build, and onos/atomix cluster containers startup. Also, because of dynamic IP Address for containers, some related environment variables are stored in /tmp/odtn/OCvar.sh. All subsequent stc command should source this file as default environment. An example of this file is:
+
+```shell
+#!/bin/bash
+export OC1=172.17.0.5
+export OC2=172.17.0.6
+export OC3=172.17.0.7
+export OCI=172.17.0.5
+export ONOS_INSTANCES="172.17.0.5 172.17.0.6 172.17.0.7"
+export ONOS_USER=root
+export ONOS_INSTALL_DIR=/root/onos/apache-karaf-4.2.3/data
+```
+
+Besides, the access for each onos container are very easy (`source /tmp/odtn/OCvar` firstly):
+* If you want to login ONOS CLI directly, please type `onos $OC1/2/3`.
+* If you want to login ONOS container, please type `ssh root@OC1/2/3`.
+
+#### 2. `stc -ENV_DEFAULT=/tmp/odtn/OCvar.sh net-odtn-smoke`
+
+This command contains thress substeps in order, you can use these steps one by one:
+* `stc -ENV_DEFAULT=/tmp/odtn/OCvar.sh net-setup-odtn`
+In this step, `odtn-service` and related multiple apps are installed and checked. Then emulator containers are started, whose topology is pushed into onos/atomix cluster via `onos-netcfg`.
+
+This command invokes script `CheckNetInit.sh`, which need file "~/emulator/net-summary.json" to load number of device/port/link.
+
+* `stc -ENV_DEFAULT=/tmp/odtn/OCvar.sh net-odtn-restconf`
+In this step, line-side and client-side connectivity creation and deletion are tested.
+
+* `stc -ENV_DEFAULT=/tmp/odtn/OCvar.sh net-teardown-odtn`
+In this step, all onos/atomix/emulator containers are stopped and removed, and onos images are removed.
diff --git a/tools/test/scenarios/odtn/buildOdtnDocker.sh b/tools/test/scenarios/odtn/buildOdtnDocker.sh
new file mode 100755
index 0000000..1844714
--- /dev/null
+++ b/tools/test/scenarios/odtn/buildOdtnDocker.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+# This script is used to build onos by using bazel, and create related docker image from ${ONOS_ROOT}/tools/dev/Dockerfile-dev
+
+# Initialize the environment
+shopt -s expand_aliases
+export PATH="$PATH:$HOME/bin:onos/bin"
+export ONOS_ROOT=~/onos
+source ${ONOS_ROOT}/tools/dev/bash_profile
+
+# Compile and Package ONOS
+cd ${ONOS_ROOT}
+# ob is replaced by bazel build onos
+bazel build onos
+rtn=$?
+if [[ ${rtn} -ne 0 ]]
+then
+    exit ${rtn}
+fi
+# Re-deploy ONOS
+[ -f tools/dev/onos.tar.gz ] && rm -f tools/dev/onos.tar.gz
+cp bazel-bin/onos.tar.gz tools/dev/
+# Build ONOS's docker image, and start ONOS cluster through docker
+cd tools/dev/
+docker build -t onos -f Dockerfile-dev .
+rm -f onos.tar.gz
+exit $?
diff --git a/tools/test/scenarios/odtn/checkNetInit.sh b/tools/test/scenarios/odtn/checkNetInit.sh
new file mode 100755
index 0000000..d000c1a
--- /dev/null
+++ b/tools/test/scenarios/odtn/checkNetInit.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+
+# Two input parameters:
+# $1 - one of {device, port, link}, specify what needs to be checked.
+# $2 - IP address of ONOS instance.
+
+# remove all spaces of first input parameter
+# In Mac or other systems, there maybe extra whitespaces before the result of `wc -l`
+function strip_space()
+{
+    tmp=$1
+    tmp="${tmp// /}"
+    return $tmp
+}
+
+strip_space $(cat ~/emulator/net-summary.json | wc -l)
+line_num=$?
+if [[ $line_num != 1 ]]; then
+    echo "JSON file should have only 1 line."
+    exit 1
+fi
+
+# Extract specific value from returned json string under onos command "odtn-show-tapi-context"
+function get_json_value()
+{
+    local json=$1
+    local key=$2
+
+    if [[ -z "$3" ]]; then
+    local num=1
+    else
+    local num=$3
+    fi
+
+    local value=$(echo "${json}" | awk -F"[,:}]" '{for(i=1;i<=NF;i++){if($i~/'${key}'\042/){print $(i+1)}}}' | tr -d '"' | sed -n ${num}p)
+
+    return ${value}
+}
+
+tried=0
+case "$1" in
+    "device" )
+        get_json_value $( cat ~/emulator/net-summary.json) device_num
+        device_num=$?
+        strip_space $(onos $2 devices | wc -l)
+        num_in_topo=$?
+        strip_space $(onos $2 odtn-show-tapi-context | grep "<node>" | wc -l)
+        num_in_tapi=$?
+        while [[ "$num_in_topo" != "$device_num" || "$num_in_tapi" != "$device_num" ]]
+        do
+            echo "On ONOS $2, current device num in topo:$num_in_topo, num in tapi:$num_in_tapi, expected $device_num. Waiting..."
+            sleep 10
+            strip_space $(onos $2 devices | wc -l)
+            num_in_topo=$?
+            strip_space $(onos $2 odtn-show-tapi-context | grep "<node>" | wc -l)
+            num_in_tapi=$?
+            let "tried=tried+1"
+            if [[ "$tried" == "10" ]]; then
+                exit 99
+            fi
+        done
+        ;;
+    "port" )
+        get_json_value $( cat ~/emulator/net-summary.json) port_num
+        port_num=$?
+        get_json_value $( cat ~/emulator/net-summary.json) device_num
+        device_num=$?
+        strip_space $(onos $2 ports | wc -l)
+        num_in_topo=$?
+        strip_space $(onos $2 odtn-show-tapi-context | grep "<owned-node-edge-point>" | wc -l)
+        num_in_tapi=$?
+        num_in_topo=$[num_in_topo-device_num]
+        while [[ "$num_in_topo" != "$port_num" || "$num_in_tapi" != "$port_num" ]]
+            do
+            echo "On ONOS $2, current port num in topo: $num_in_topo, num in tapi: $num_in_tapi, expected $port_num. Waiting..."
+                    sleep 10
+            strip_space $(onos $2 ports | wc -l)
+            num_in_topo=$?
+            strip_space $(onos $2 odtn-show-tapi-context | grep "<owned-node-edge-point>" | wc -l)
+            num_in_tapi=$?
+            num_in_topo=$[num_in_topo-device_num]
+            let "tried=tried+1"
+            if [[ "$tried" == "10" ]]; then
+                exit 99
+            fi
+            done
+            ;;
+    "link" )
+        get_json_value $( cat ~/emulator/net-summary.json) link_num
+        link_num=$?
+        strip_space $(onos $2 links | wc -l)
+        num_in_topo=$?
+        strip_space $(onos $2 odtn-show-tapi-context | grep "<link>" | wc -l)
+        num_in_tapi=$?
+        while [[ "$num_in_topo" != "$link_num" || "$num_in_tapi" != "$link_num" ]]
+            do
+                    echo "On ONOS $2, current link num in topo: $num_in_topo, num in tapi: $num_in_tapi, expected $link_num. Waiting..."
+            sleep 10
+                    num_in_topo=`onos $2 links | wc -l`
+            strip_space $(onos $2 links | wc -l)
+            num_in_topo=$?
+            strip_space $(onos $2 odtn-show-tapi-context | grep "<link>" | wc -l)
+            num_in_tapi=$?            
+            let "tried=tried+1"
+            if [[ "$tried" == "10" ]]; then
+                exit 99
+            fi
+            done
+esac
diff --git a/tools/test/scenarios/odtn/checkUntilSucc.sh b/tools/test/scenarios/odtn/checkUntilSucc.sh
new file mode 100755
index 0000000..5dee974
--- /dev/null
+++ b/tools/test/scenarios/odtn/checkUntilSucc.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# This script is used to execute some checking commands in period to confirm whether specific requirement is satisfied.
+# $1 - the command to be executed in this script, whose parameter splitter is +, but ont space. This command could use |, &&, || to concatenate multiple shell commands.
+# $2 - Optional. If exists, it means the output (Note: not returned value) of $1 should equals $2.
+
+cmd=${1//'+'/' '}
+if [ $# == 1 ]; then
+    for i in {1..60}; do
+        eval ${cmd}
+        rtn=$?
+        if [[ ${rtn} -ne 0 ]]
+        then
+            echo "$i-th execution returns $rtn"
+            sleep 3
+        else
+            exit 0
+        fi
+    done
+elif [ $# == 2 ]; then
+    for i in {1..60}; do
+        out=`eval ${cmd}`
+        rtn=$?
+        out="${out// /}"
+        if [[ ${rtn} -ne 0 || "$out" != $2 ]]; then
+            echo "$i-th execution fails"
+            sleep 3
+        else
+            exit 0
+        fi
+    done
+fi
diff --git a/tools/test/scenarios/odtn/createNetCfg.sh b/tools/test/scenarios/odtn/createNetCfg.sh
new file mode 100755
index 0000000..9bbd456
--- /dev/null
+++ b/tools/test/scenarios/odtn/createNetCfg.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+# Get first IPv4 Address on local machine, even the machine has lots of NICs.
+localIP=`ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:" | head -n1`
+mkdir /tmp/odtn
+# Replace IP address in openconfig json files with real ones.
+cat ${ONOS_ROOT}/apps/odtn/api/src/test/resources/openconfig-devices.json | sed "s/127.0.0.1/${localIP}/g" > /tmp/odtn/openconfig-devices.json
+cat ${ONOS_ROOT}/apps/odtn/api/src/test/resources/openconfig-device-link.json | sed "s/127.0.0.1/${localIP}/g" > /tmp/odtn/openconfig-device-link.json
+sleep 30
diff --git a/tools/test/scenarios/odtn/createOdtnCell.sh b/tools/test/scenarios/odtn/createOdtnCell.sh
new file mode 100755
index 0000000..91f9197
--- /dev/null
+++ b/tools/test/scenarios/odtn/createOdtnCell.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+
+# Create onos and atomix containers from related images.
+
+# Initialize the environment
+shopt -s expand_aliases
+export PATH="$PATH:$HOME/bin:onos/bin"
+export ONOS_ROOT=~/onos
+source ${ONOS_ROOT}/tools/dev/bash_profile
+KARAF_VERSION=`cat ${ONOS_ROOT}/tools/build/envDefaults | grep KARAF_VERSION= | awk -F '=' '{print $2}'`
+
+# Start ONOS cluster through docker
+cd ~/onos/tools/tutorials/vm
+SSH_KEY=$(cut -d\  -f2 ~/.ssh/id_rsa.pub)
+FULL_SSH_KEY=$(cat ~/.ssh/id_rsa.pub)
+echo "The public key in local host is: $SSH_KEY"
+
+# Create Atomix cluster using Atomix docker image
+ATOMIX_IMAGE=atomix/atomix:3.1.5
+for i in {1..3}; do
+    echo "Setting up atomix-$i..."
+    docker container run --detach --name atomix-$i --hostname atomix-$i \
+        --restart=always -v /home/sdn/onos/tools/tutorials/vm/config:/atomix/config $ATOMIX_IMAGE \
+        --config /atomix/config/atomix-$i.conf
+done
+wait
+
+# Create and start  ONOS cluster using ONOS docker image
+ONOS_IMAGE=onos:latest
+for i in {1..3}; do
+    echo "Setting up onos-$i..."
+    docker container run --detach --name onos-$i --hostname onos-$i --restart=always $ONOS_IMAGE
+    docker exec -i onos-$i /bin/bash -c "mkdir config; cat > config/cluster.json" < $ONOS_ROOT/tools/tutorials/vm/config/cluster-$i.json
+    docker exec -i onos-$i /bin/bash -c "touch /root/onos/apache-karaf-${KARAF_VERSION}/etc/keys.properties"
+    docker exec -i onos-$i /bin/bash -c "echo 'sdn=$SSH_KEY,_g_:admingroup' >> /root/onos/apache-karaf-${KARAF_VERSION}/etc/keys.properties"
+    docker exec -i onos-$i /bin/bash -c "/root/onos/bin/onos-user-password onos rocks"
+    docker exec -i onos-$i /bin/bash -c "ssh-keygen -f /root/.ssh/id_rsa -t rsa -N ''"
+    docker exec -i onos-$i /bin/bash -c "/etc/init.d/ssh start"
+    docker exec -i onos-$i /bin/bash -c "echo '$FULL_SSH_KEY' >> /root/.ssh/authorized_keys"
+done
+
+# Start onos and atomix docker containers
+function waitForStart {
+    sleep 5
+    for i in {1..3}; do
+        echo "Waiting for onos-$i startup..."
+        ip=$(docker container inspect onos-$i | grep \"IPAddress | cut -d: -f2 | sort -u | tr -d '", ')
+        echo "IP is: $ip"
+    for t in {1..60}; do
+        echo "$t-th times curl request"
+            curl --fail -sS http://$ip:8181/onos/v1/applications --user "onos:rocks" 1>/dev/null 2>&1 && break;
+            sleep 1;
+        done
+    echo
+        onos $ip summary >/dev/null 2>&1
+    done
+}
+
+# Extract the IP addresses of the ONOS nodes
+export OC1=$(docker container inspect onos-1 | grep \"IPAddress | cut -d: -f2 | sort -u | tr -d '", ')
+export OC2=$(docker container inspect onos-2 | grep \"IPAddress | cut -d: -f2 | sort -u | tr -d '", ')
+export OC3=$(docker container inspect onos-3 | grep \"IPAddress | cut -d: -f2 | sort -u | tr -d '", ')
+export ONOS_INSTANCES="\"$OC1 $OC2 $OC3\""
+
+waitForStart
+
+# remove known hosts
+ssh-keygen -R $OC1
+ssh-keygen -R $OC2
+ssh-keygen -R $OC3
+# add to known-hosts list
+ssh-keyscan $OC1 >> ~/.ssh/known_hosts
+ssh-keyscan $OC2 >> ~/.ssh/known_hosts
+ssh-keyscan $OC3 >> ~/.ssh/known_hosts
+
+echo "#!/bin/bash" > /tmp/odtn/OCvar.sh
+echo "export OC1=$OC1" >> /tmp/odtn/OCvar.sh
+echo "export OC2=$OC2" >> /tmp/odtn/OCvar.sh
+echo "export OC3=$OC3" >> /tmp/odtn/OCvar.sh
+echo "export OCI=$OC1" >> /tmp/odtn/OCvar.sh
+echo "export ONOS_INSTANCES=$ONOS_INSTANCES" >> /tmp/odtn/OCvar.sh
+echo "export ONOS_USER=root" >> /tmp/odtn/OCvar.sh
+echo "export ONOS_INSTALL_DIR=/root/onos/apache-karaf-${KARAF_VERSION}/data" >> /tmp/odtn/OCvar.sh
+# sleep to wait onos instances start up
+sleep 20
diff --git a/tools/test/scenarios/odtn/destroyOdtnCell.sh b/tools/test/scenarios/odtn/destroyOdtnCell.sh
new file mode 100755
index 0000000..edb640d
--- /dev/null
+++ b/tools/test/scenarios/odtn/destroyOdtnCell.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+# remove existed onos and atomix containers and images, stop emulator containers
+containers=(`docker ps -a | grep -E 'onos-|atomix-|emulator' | awk '{print $1}'`)
+if [[ ${#containers} != 0 ]]; then
+    for var in  ${containers[*]}; do
+        docker stop ${var} &
+    done
+    wait
+else
+    echo "There is no container existed."
+fi
+if [[ $# != 1 || "$1" != "stop-docker" ]]; then
+    if [[ ${#containers} != 0 ]]; then
+        for var in ${containers[*]}; do
+            docker rm ${var} &
+        done
+        wait
+    fi
+    images=(`docker images | grep -e onos -e none -e "<none>" | awk '{print $3}'`)
+    if [[ ${#images} != 0 ]]; then
+        for var in ${images[*]}; do
+            docker rmi ${var} &
+        done
+        wait
+    else
+        echo "There is no onos/atomix docker image existed."
+    fi
+else
+    echo "There is no container and image to be removed."
+fi
diff --git a/tools/test/scenarios/odtn/testOnSingleONOS.sh b/tools/test/scenarios/odtn/testOnSingleONOS.sh
new file mode 100755
index 0000000..4f223ac
--- /dev/null
+++ b/tools/test/scenarios/odtn/testOnSingleONOS.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+# This scrip is used to test ODTN on single ONOS instance in branch 2.0
+# Before this script:
+#   1. Make sure the default Python version is 2.x
+#   2. Run ONOS locally (bazel run onos-local -- clean)
+#   3. Start sshd service, and make sure "ssh $USER@localhost" operation doesn't need passwd
+#   4. Emulator configuration could be found under directory $HOME/emulator
+
+# env configuration
+ONOS_ROOT="${ONOS_ROOT:-~/onos}"
+source ${ONOS_ROOT}/tools/dev/bash_profile
+source ${ONOS_ROOT}/tools/build/envDefaults
+unset OC2
+unset OC3
+export OC1="127.0.0.1"
+export OCI="$OC1"
+export ONOS_INSTANCES="$OC1"
+export ONOS_USER=$USER
+
+OV=`echo $ONOS_VERSION | sed "s/\.$USER/-SNAPSHOT/g" `
+export ONOS_INSTLL_DIR=/tmp/onos-${OV}/apache-karaf-${KARAF_VERSION}/data
+export EMULATOR_ROOT="${EMULATOR_ROOT:-$HOME/emulator}"
+
+for t in {1..60}; do
+    echo "$t-th times curl request"
+    curl --fail -sS http://localhost:8181/onos/v1/applications --user "onos:rocks" 1>/dev/null 2>&1 && break;
+    sleep 2
+done
+
+# activate odtn-service
+# run emulator, and push topo into local onos instance
+cd ${EMULATOR_ROOT}
+docker-compose up -d
+stc net-setup-odtn
+if [[ $? == 0 ]]; then
+    stc net-odtn-restconf
+fi
diff --git a/tools/test/scenarios/stop-docker.xml b/tools/test/scenarios/stop-docker.xml
new file mode 100644
index 0000000..fc95cc6
--- /dev/null
+++ b/tools/test/scenarios/stop-docker.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!--
+  ~ Copyright 2015-present Open Networking Foundation
+  ~
+  ~ 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.
+  -->
+<scenario name="Stop-docker" description="Stop all docker containers with prefix onos- atomix- emulator-">
+    <step name="Stop-docker.stop-all" exec="${ONOS_SCENARIOS}/odtn/destroyOdtnCell.sh stop-docker"/>
+</scenario>