Test suite for O1 interface

This commit adds a test suite that can be run to validate the O1 interface.

Issue-ID: SMO-12

Signed-off-by: Mahesh Jethanandani <mjethanandani@gmail.com>
Change-Id: Iba846790c050498b2d3002fcab2470edd255eed9
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e79fce6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,14 @@
+This README is for running the tests. See the README in the client
+folder for how the SMO NETCONF client is setup.
+
+The run_tests.sh script 
+1. Brings up simulators(RU & DU)
+2. Brings up SDNR
+3. Add simulators to SDNR
+4. Checks connectivity status by fetching the capabilities
+5. Updates DU and RU config and prints the output
+6. Teardown the services
+
+The environmental variables used to setup and run the simulators is stored in a .env file.
+
+Note: This script should be executed from the root of the repository.
diff --git a/client/tests/.env b/client/tests/.env
new file mode 100644
index 0000000..58e0892
--- /dev/null
+++ b/client/tests/.env
@@ -0,0 +1,33 @@
+DOCKER_REPO=nexus3.o-ran-sc.org:10004/o-ran-sc/
+NTS_MANAGER_PORT=8300
+NTS_BUILD_VERSION=1.3.3
+ 
+IPv6_ENABLED=true
+SSH_CONNECTIONS=1
+TLS_CONNECTIONS=0
+# NTS_HOST_IP=10.20.11.136
+NTS_HOST_IP=2001:db8:1::1
+NTS_HOST_BASE_PORT=50000
+NTS_HOST_NETCONF_SSH_BASE_PORT=50000
+NTS_HOST_NETCONF_TLS_BASE_PORT=52000
+NTS_HOST_TRANSFER_FTP_BASE_PORT=54000
+NTS_HOST_TRANSFER_SFTP_BASE_PORT=56000
+NTS_BUILD_DATE=2021-05-01T08:20:54.9Z
+ 
+NTS_NF_MOUNT_POINT_ADDRESSING_METHOD=docker-mapping
+NTS_NF_STANDALONE_START_FEATURES="datastore-populate ves-heartbeat ves-file-ready ves-pnf-registration netconf-call-home web-cut-through"
+ 
+SDN_CONTROLLER_PROTOCOL=http
+SDN_CONTROLLER_IP=172.40.0.21
+SDN_CONTROLLER_PORT=8181
+SDN_CONTROLLER_CALLHOME_PORT=6666
+SDN_CONTROLLER_USERNAME=admin
+SDN_CONTROLLER_PASSWORD=admin
+ 
+VES_COMMON_HEADER_VERSION=7.1
+VES_ENDPOINT_PROTOCOL=http
+VES_ENDPOINT_IP=172.40.0.90
+VES_ENDPOINT_PORT=8080
+VES_ENDPOINT_AUTH_METHOD=no-auth
+VES_ENDPOINT_USERNAME=sample1
+VES_ENDPOINT_PASSWORD=sample1
\ No newline at end of file
diff --git a/client/tests/docker-compose.yml b/client/tests/docker-compose.yml
new file mode 100644
index 0000000..7a83bf5
--- /dev/null
+++ b/client/tests/docker-compose.yml
@@ -0,0 +1,78 @@
+version: '3.8'
+services:
+  ntsim-ng-o-ru:
+    image: "${DOCKER_REPO}nts-ng-o-ran-ru-fh:${NTS_BUILD_VERSION}"
+    cap_add:
+      - SYS_ADMIN
+    stop_grace_period: 5m
+    hostname: "O-RAN-O-RU-1"
+    ports:
+     - "::18300:830"
+    environment:
+      IPv6_ENABLED: ${IPv6_ENABLED}
+      SSH_CONNECTIONS: ${SSH_CONNECTIONS}
+      TLS_CONNECTIONS: ${TLS_CONNECTIONS}
+      NTS_NF_STANDALONE_START_FEATURES: "datastore-populate netconf-call-home web-cut-through"
+      NTS_NF_MOUNT_POINT_ADDRESSING_METHOD: ${NTS_NF_MOUNT_POINT_ADDRESSING_METHOD}
+ 
+      NTS_HOST_IP: ${NTS_HOST_IP}
+      NTS_HOST_BASE_PORT: ${NTS_HOST_BASE_PORT}
+      NTS_HOST_NETCONF_SSH_BASE_PORT: ${NTS_HOST_NETCONF_SSH_BASE_PORT}
+      NTS_HOST_NETCONF_TLS_BASE_PORT: ${NTS_HOST_NETCONF_TLS_BASE_PORT}
+      NTS_HOST_TRANSFER_FTP_BASE_PORT: ${NTS_HOST_TRANSFER_FTP_BASE_PORT}
+      NTS_HOST_TRANSFER_SFTP_BASE_PORT: ${NTS_HOST_TRANSFER_SFTP_BASE_PORT}
+      NTS_BUILD_DATE: ${NTS_BUILD_DATE}
+         
+      SDN_CONTROLLER_PROTOCOL: ${SDN_CONTROLLER_PROTOCOL}
+      SDN_CONTROLLER_IP: ${SDN_CONTROLLER_IP}
+      SDN_CONTROLLER_PORT: ${SDN_CONTROLLER_PORT}
+      SDN_CONTROLLER_CALLHOME_PORT: ${SDN_CONTROLLER_CALLHOME_PORT}
+      SDN_CONTROLLER_USERNAME: ${SDN_CONTROLLER_USERNAME}
+      SDN_CONTROLLER_PASSWORD: ${SDN_CONTROLLER_PASSWORD}
+   
+      VES_COMMON_HEADER_VERSION: ${VES_COMMON_HEADER_VERSION}
+      VES_ENDPOINT_PROTOCOL: ${VES_ENDPOINT_PROTOCOL}
+      VES_ENDPOINT_IP: ${VES_ENDPOINT_IP}
+      VES_ENDPOINT_PORT: ${VES_ENDPOINT_PORT}
+      VES_ENDPOINT_AUTH_METHOD: ${VES_ENDPOINT_AUTH_METHOD}
+      VES_ENDPOINT_USERNAME: ${VES_ENDPOINT_USERNAME}
+      VES_ENDPOINT_PASSWORD: ${VES_ENDPOINT_PASSWORD}
+ 
+  ntsim-ng-o-du:
+    image: "${DOCKER_REPO}nts-ng-o-ran-du:${NTS_BUILD_VERSION}"
+    cap_add:
+      - SYS_ADMIN
+    stop_grace_period: 5m
+    hostname: "O-RAN-O-DU-1"
+    ports:
+     - "::18310:830"
+    environment:
+      IPv6_ENABLED: ${IPv6_ENABLED}
+      SSH_CONNECTIONS: ${SSH_CONNECTIONS}
+      TLS_CONNECTIONS: ${TLS_CONNECTIONS}
+      NTS_NF_STANDALONE_START_FEATURES: "datastore-populate ves-heartbeat ves-file-ready ves-pnf-registration web-cut-through"
+      NTS_NF_MOUNT_POINT_ADDRESSING_METHOD: ${NTS_NF_MOUNT_POINT_ADDRESSING_METHOD}
+ 
+      NTS_HOST_IP: ${NTS_HOST_IP}
+      NTS_HOST_BASE_PORT: ${NTS_HOST_BASE_PORT}
+      NTS_HOST_NETCONF_SSH_BASE_PORT: ${NTS_HOST_NETCONF_SSH_BASE_PORT}
+      NTS_HOST_NETCONF_TLS_BASE_PORT: ${NTS_HOST_NETCONF_TLS_BASE_PORT}
+      NTS_HOST_TRANSFER_FTP_BASE_PORT: ${NTS_HOST_TRANSFER_FTP_BASE_PORT}
+      NTS_HOST_TRANSFER_SFTP_BASE_PORT: ${NTS_HOST_TRANSFER_SFTP_BASE_PORT}
+      NTS_BUILD_DATE: ${NTS_BUILD_DATE}
+       
+      SDN_CONTROLLER_PROTOCOL: ${SDN_CONTROLLER_PROTOCOL}
+      SDN_CONTROLLER_IP: ${SDN_CONTROLLER_IP}
+      SDN_CONTROLLER_PORT: ${SDN_CONTROLLER_PORT}
+      SDN_CONTROLLER_CALLHOME_PORT: ${SDN_CONTROLLER_CALLHOME_PORT}
+      SDN_CONTROLLER_USERNAME: ${SDN_CONTROLLER_USERNAME}
+      SDN_CONTROLLER_PASSWORD: ${SDN_CONTROLLER_PASSWORD}
+ 
+      VES_COMMON_HEADER_VERSION: ${VES_COMMON_HEADER_VERSION}
+      VES_ENDPOINT_PROTOCOL: ${VES_ENDPOINT_PROTOCOL}
+      VES_ENDPOINT_IP: ${VES_ENDPOINT_IP}
+      VES_ENDPOINT_PORT: ${VES_ENDPOINT_PORT}
+      VES_ENDPOINT_AUTH_METHOD: ${VES_ENDPOINT_AUTH_METHOD}
+      VES_ENDPOINT_USERNAME: ${VES_ENDPOINT_USERNAME}
+      VES_ENDPOINT_PASSWORD: ${VES_ENDPOINT_PASSWORD}
+ 
\ No newline at end of file
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000..4d10051
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+
+cd client/tests
+# cleanup any previous (unsuccessful) runs.
+docker-compose down
+
+# bringup simulators
+docker-compose up -d
+
+cd ../
+# cleanup any previous (unsuccessful) runs.
+docker-compose down
+# Bringup sdnr
+docker-compose up -d
+
+# wait until sdnr up & running
+for i in {1..60}; do
+   res=$(curl -o /dev/null -sw %{http_code} http://localhost:8181/odlux/index.html)
+   echo "$res"
+   expect="200"
+   if [ "$res" == "$expect" ]; then
+      echo -e "SDNR is up and running\n"
+      break;
+   else
+      sleep $i
+   fi
+done
+
+# check RU 
+nc -z localhost 18300
+if [ $? == 0 ]; then
+    echo -e "RU is up.\n"
+else
+    echo -e "RU failed to connect.\n"
+    exit 1
+fi
+
+# check DU status
+nc -z localhost 18310
+if [ $? == 0 ]; then
+    echo -e "DU is up.\n"
+else
+    echo -e "DU failed to connect.\n"
+    exit 1
+fi
+
+# Adding delay to avoid curl failures
+sleep 30
+
+HOST_IP=$(hostname -I | awk '{print $1}')
+USER_PWD=admin:Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U
+
+# Add DU
+echo "Adding DU simulator"
+res=$(curl -o /dev/null -sw %{http_code} -u $USER_PWD  -X POST "http://localhost:8181/rests/operations/netconf-node-topology:create-device" -H "accept: */*" -H "Content-Type: application/json" -d '{"input":{"pass-through":{},"login-password":{"username":"netconf","password":"netconf!"},"host":"'"$HOST_IP"'","port":"18310","node-id":"du_sim"}}')
+
+if [ "$res" == "204" ]; then
+    echo -e "Successfully added device DU \n"
+else
+    echo -e "Failed to add DU.\n"
+    exit 1
+fi
+sleep 2
+
+# check DU connection
+echo -e "Checking DU simulator \n"
+res=$(curl -o /dev/null -sw '%{http_code}' -u $USER_PWD -X GET "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=du_sim/netconf-node-topology:available-capabilities?content=nonconfig" -H "accept: application/xml")
+if [ $res == 200 ]; then
+    echo -e "DU simulator is alive.\n"
+else
+    echo -e "DU simulator is not responding.\n"
+    exit 1
+fi
+
+# Add RU
+echo ""
+echo "Adding RU Simulator\n"
+res=$(curl -o /dev/null -sw %{http_code} -u $USER_PWD -X POST "http://localhost:8181/rests/operations/netconf-node-topology:create-device" -H "accept: */*" -H "Content-Type: application/json" -d '{"input":{"pass-through":{},"login-password":{"username":"netconf","password":"netconf!"},"host":"'"$HOST_IP"'","port":"18300","node-id":"ru_sim"}}')
+
+if [ "$res" == "204" ]; then
+    echo -e "Successfully added device RU \n"
+else
+    echo -e "Failed to add RU.\n"
+    exit 1
+fi
+sleep 2
+
+# check RU connection
+echo -e "Checking RU simulator connection"
+res=$(curl -o /dev/null -sw '%{http_code}' -u $USER_PWD -X GET "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=ru_sim/netconf-node-topology:available-capabilities?content=nonconfig" -H "accept: application/xml")
+if [ $res == 200 ]; then
+    echo -e "RU simulator is alive.\n"
+else
+    echo -e "RU simulator is not responding.\n"
+    exit 1
+fi
+
+echo -e "\nRU cofig before update"
+res=$(curl -o /dev/null -sw '%{http_code}' -u $USER_PWD -X GET "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=ru_sim/yang-ext:mount/o-ran-delay-management:delay-management/adaptive-delay-configuration/transport-delay" -H "accept: application/xml")
+if [ $res == 200 ]; then
+    echo -e "RU config check before update succeeded.\n"
+else
+    echo -e "RU config check before update failed.\n"
+    exit 1
+fi
+
+echo -e "\nUpdating RU config"
+res=$(curl -o /dev/null -sw %{http_code} -u $USER_PWD -X PUT "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=ru_sim/yang-ext:mount/o-ran-delay-management:delay-management/adaptive-delay-configuration/transport-delay" -H "accept: */*" -H "Content-Type: application/json" -d '{"transport-delay":{"t12-min":1000,"t12-max":66666,"t34-min":2000,"t34-max":55555}}')
+if [ "$res" == "204" ]; then
+    echo -e "Successfully updated RU config.\n"
+else
+    echo -e "Failed to update RU config.\n"
+    exit 1
+fi
+
+echo -e "\nRU config afer update."
+res=$(curl -o /dev/null -sw '%{http_code}' -u $USER_PWD -X GET "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=ru_sim/yang-ext:mount/o-ran-delay-management:delay-management/adaptive-delay-configuration/transport-delay" -H "accept: application/xml")
+if [ $res == 200 ]; then
+    echo -e "RU config after update succeeded.\n"
+else
+    echo -e "RU config after update failed.\n"
+    exit 1
+fi
+
+# get DU config
+echo -e "\nDU config before update."
+res=$(curl -o /dev/null -sw '%{http_code}' -u $USER_PWD -X GET "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=du_sim/yang-ext:mount/o-ran-sc-du-hello-world:network-function/du-to-ru-connection=O-RU-1" -H "accept: application/xml")
+if [ $res == 200 ]; then
+    echo -e "DU config before update succeeded.\n"
+else
+    echo -e "DU config before update failed.\n"
+    exit 1
+fi
+
+# Update DU config
+echo -e "Updating DU config"
+res=$(curl -o /dev/null -sw %{http_code} -u $USER_PWD -X PUT "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=du_sim/yang-ext:mount/o-ran-sc-du-hello-world:network-function/du-to-ru-connection=O-RU-1" -H "accept: */*" -H "Content-Type: application/json" -d "{"du-to-ru-connection":[{"name":"O-RU-1","administrative-state":"UNLOCKED"}]}")
+if [ "$res" == "204" ]; then
+    echo -e "Successfully updated DU config.\n"
+else
+    echo -e "Failed to update DU config.\n"
+    exit 1
+fi
+
+# Validate DU config after update
+echo -e "DU cofig afer update"
+res=$(curl -o /dev/null -sw '%{http_code}' -u $USER_PWD -X GET "http://localhost:8181/rests/data/network-topology:network-topology/topology=topology-netconf/node=du_sim/yang-ext:mount/o-ran-sc-du-hello-world:network-function/du-to-ru-connection=O-RU-1" -H "accept: application/xml")
+if [ $res == 200 ]; then
+    echo -e "DU config after update succeeded.\n"
+else
+    echo -e "DU config after update failed.\n"
+    exit 1
+fi
+
+# Bring down the sdnr
+docker-compose down
+
+# bring down the simulators
+cd tests/
+docker-compose down
+echo -e "\nTests completed"
+exit 0