Include testsuite 22/15122/1
authorRavi Pendurty <ravi.pendurty@highstreet-technologies.com>
Tue, 7 Oct 2025 09:26:07 +0000 (14:56 +0530)
committerRavi Pendurty <ravi.pendurty@highstreet-technologies.com>
Tue, 7 Oct 2025 09:26:07 +0000 (14:56 +0530)
deploy docker images and run tests

Issue-ID: OAM-513
Change-Id: Ic36c8355ade7c6adae3a353fa5315285cefda9d9
Signed-off-by: Ravi Pendurty <ravi.pendurty@highstreet-technologies.com>
35 files changed:
.github/workflows/gerrit-verify.yaml
.gitignore
CICD/bin/setup.sh [new file with mode: 0755]
CICD/config/.env [new file with mode: 0644]
CICD/config/certs/certs.properties [new file with mode: 0644]
CICD/config/certs/keys0.zip [new file with mode: 0644]
CICD/config/certs/keys1.zip [new file with mode: 0644]
CICD/config/docker-compose-o-ru-mplane.yaml [new file with mode: 0644]
CICD/config/docker-compose-sdnr-mariadb.yaml [new file with mode: 0644]
CICD/config/ietf-netconf-server-ssh-callhome.json [new file with mode: 0644]
CICD/config/ietf-netconf-server-tls-callhome.json [new file with mode: 0644]
CICD/config/ietf-netconf-server-tls-listen.json [new file with mode: 0644]
CICD/config/sdnc-web/cacert.pem [new file with mode: 0644]
CICD/config/sdnc-web/cert.pem [new file with mode: 0644]
CICD/config/sdnc-web/key.pem [new file with mode: 0644]
CICD/config/sdnr/devicemanager.properties [new file with mode: 0644]
CICD/config/sdnr/mountpoint-registrar.properties [new file with mode: 0644]
CICD/config/sdnr/mountpoint-registrar.properties.bkup [new file with mode: 0644]
CICD/config/sdnr/oauth-aaa-app-config.xml [new file with mode: 0644]
CICD/config/sdnr/oauth-provider.config.json [new file with mode: 0644]
CICD/config/sdnrconfig.json [new file with mode: 0644]
CICD/config/testsuiteconfig.json [new file with mode: 0644]
CICD/log/.gitkeep [new file with mode: 0644]
CICD/src/ApiRequest.py [new file with mode: 0644]
CICD/src/Main.py [new file with mode: 0644]
CICD/src/SdnrConfigModel.py [new file with mode: 0644]
CICD/src/TestPlanModel.py [new file with mode: 0644]
CICD/src/TestPlanRunner.py [new file with mode: 0644]
CICD/src/TestSuiteConfig.py [new file with mode: 0644]
CICD/testplans/001/CREATE-MOUNTPOINT-TLS.json [new file with mode: 0644]
CICD/testplans/001/testplan.json [new file with mode: 0644]
CICD/testplans/002/CREATE-MOUNTPOINT-SSH.json [new file with mode: 0644]
CICD/testplans/002/testplan.json [new file with mode: 0644]
CICD/testplans/003/testplan.json [new file with mode: 0644]
CICD/testplans/004/testplan.json [new file with mode: 0644]

index 7f31196..de8ee9f 100644 (file)
@@ -82,19 +82,29 @@ jobs:
 
   maven-verify:
     needs: [prepare, sanitize]
-    # use compose-jjb-verify from the v0.4 series of releng-reusable-workflows
-    # yamllint disable-line rule:line-length
-    uses: lfit/releng-reusable-workflows/.github/workflows/compose-make-verify.yaml@f318aaac9f4fce73a8059fe95ba9080833e018c2
-    with:
-      GERRIT_BRANCH: ${{ inputs.GERRIT_BRANCH }}
-      GERRIT_CHANGE_ID: ${{ inputs.GERRIT_CHANGE_ID }}
-      GERRIT_PROJECT: ${{ github.repository_owner }}/${{ needs.sanitize.outputs.sanitized_project }}
-      GERRIT_REFSPEC: ${{ inputs.GERRIT_REFSPEC }}
-      JDK_VERSION: "21"
-      MVN_VERSION: "3.9.6"
-      MAKE_TARGETS: "build"
-      # yamllint enable rule:line-length
-      ENV_VARS: ${{ toJSON(vars) }}
+    runs-on: ubuntu-latest
+    steps:
+      - name: Run make
+        # yamllint disable-line rule:line-length
+        uses: lfit/releng-reusable-workflows/.github/workflows/compose-make-verify.yaml@f318aaac9f4fce73a8059fe95ba9080833e018c2
+        with:
+          GERRIT_BRANCH: ${{ inputs.GERRIT_BRANCH }}
+          GERRIT_CHANGE_ID: ${{ inputs.GERRIT_CHANGE_ID }}
+          GERRIT_PROJECT: ${{ github.repository_owner }}/${{ needs.sanitize.outputs.sanitized_project }}
+          GERRIT_REFSPEC: ${{ inputs.GERRIT_REFSPEC }}
+          JDK_VERSION: "21"
+          MVN_VERSION: "3.9.6"
+          MAKE_TARGETS: "build"
+          # yamllint enable rule:line-length
+          ENV_VARS: ${{ toJSON(vars) }}
+      - name: Run tests
+        run: CICD/bin/setup.sh 
+      - name: Print test log
+        if: always()
+        run: |
+          echo "Test log dump - START"
+          cat CICD/log/testsuite.log
+          echo "Test log dump - END"
 
   vote:
     if: ${{ always() }}
index d789c1e..abbe4b5 100644 (file)
@@ -25,7 +25,7 @@ buildNumber.properties
 *.pyc
 append_to_etc_hosts.txt
 
-*/**/bin/
+#*/**/bin/
 
 # Maven
 target/
@@ -54,38 +54,12 @@ venv
 # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
 !/.mvn/wrapper/maven-wrapper.jar
 
-# VES related
-*/**/json/examples
-*/**/__pycache__
-*.log
-
-# avoid copyright issues with 3GPP
-*/**/3gpp/**/*.yaml
-
 # node js
 node_modules
 package-lock.json
 
-# wireshark
-.cache
-
 # ODLUX cache
 */**/O D L UX*
 
-# nodered
-*.backup
-
-# container-analyis
-*.sbom.spdx.json
-*.vulnerabilities.vex.json
-
-# jenkins
-*/**/apps/jenkins/*
-
-# wireshark
-*/**/apps/wireshark/*
-
-# documentation
-.tox
-docs/_build/
-ci-management/
+#tmp directory
+CICD/tmp
diff --git a/CICD/bin/setup.sh b/CICD/bin/setup.sh
new file mode 100755 (executable)
index 0000000..47db5c9
--- /dev/null
@@ -0,0 +1,130 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+
+#!/bin/bash
+
+# Log output of each command executed in the script for easier debugging
+#exec > >(stdbuf -oL awk '{ print strftime("[%Y-%m-%d %H:%M:%S]"), $0; fflush(); }' | tee -a setup.log) 2>&1
+
+# 7. Dump the log into the Github window. How ?
+
+
+DOCKER_NETWORK_SUB="172.18.0.0/16"
+DOCKER_NETWORK_GW="172.18.0.1"
+DOCKER_NETWORK_NAME="CICD-Network"
+CUSTOM_O_RU_MPLANE_DC_FILE="docker-compose-o-ru-mplane.yaml"
+CUSTOM_O_RU_MPLANE_SSH_CALLHOME="ietf-netconf-server-ssh-callhome.json"
+CUSTOM_O_RU_MPLANE_TLS_CALLHOME="ietf-netconf-server-tls-callhome.json"
+CUSTOM_O_RU_MPLANE_TLS_NON_CALLHOME="ietf-netconf-server-tls-listen.json"
+SDNR_COMPONENTS_DC_FILE="docker-compose-sdnr-mariadb.yaml"
+# Code everything relative to the BINDIR
+BINDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+ROOTDIR="$(cd "${BINDIR}/.." && pwd)"
+CONFIGDIR=${ROOTDIR}/config
+TMPDIR=${ROOTDIR}/tmp
+LOGDIR=${ROOTDIR}/log
+LOGFILE=testsuite.log
+
+log() {
+    echo "$1" | tee -a ${LOGDIR}/${LOGFILE}
+}
+
+cleanup() {
+    log "## Deleting any running/stopped docker containers ##\n"
+    container_names=$(docker ps --format {{.Names}})
+    if [[ "$container_names" != "" ]]; then
+        docker rm -f $(docker ps -aq)
+    fi
+
+    log "## Checking if docker network with name - ${DOCKER_NETWORK_NAME} - exists ##\n"
+    docker_nw=$(docker network ls --filter name=^${DOCKER_NETWORK_NAME}$ --format {{.Name}})
+    if [[ "$docker_nw" != "" ]]; then
+        log "## Docker network with name ${DOCKER_NETWORK_NAME} exists. Deleting it ...##\n"
+        docker network rm ${DOCKER_NETWORK_NAME}
+    else
+        log "## Docker network with name ${DOCKER_NETWORK_NAME} DOES NOT exist ##"
+    fi
+
+    if [[ -d ${TMPDIR} ]]; then
+        log "## Deleting tmp directory ..##\n"
+        sudo rm -rf ${TMPDIR}
+    fi
+
+    if [[ -f ${LOGDIR}/setup.log ]]; then
+        rm ${LOGDIR}/setup.log
+    fi
+}
+
+build_pynts_images() {
+    log "## Start Building PyNTS ... ##\n"
+    log "## Cloning PyNTS simulator from https://github.com/o-ran-sc/sim-o1-ofhmp-interfaces ##\n"
+    git clone https://github.com/o-ran-sc/sim-o1-ofhmp-interfaces.git ${TMPDIR}/sim-o1-ofhmp-interfaces
+    cd ${TMPDIR}/sim-o1-ofhmp-interfaces
+    make
+    if [ $? = 0 ]; then
+        log "\n## Build of PyNTS Successful ##\n"
+        log "## Following docker images were built ##\n"
+        docker images | grep pynts
+    else
+        log "\n### Build failed ###\n"
+        exit 1
+    fi
+}
+
+create_docker_network() {
+    log "\n## Creating docker network - ${DOCKER_NETWORK_NAME} ##\n"
+    docker network create --subnet ${DOCKER_NETWORK_SUB} --gateway ${DOCKER_NETWORK_GW} ${DOCKER_NETWORK_NAME}
+}
+
+start_pynts() {
+    log "## Overwriting file - docker-compose-o-ru-mplane.yaml - with custom file ##\n"
+    cp ${CONFIGDIR}/${CUSTOM_O_RU_MPLANE_DC_FILE} ${TMPDIR}/sim-o1-ofhmp-interfaces
+    cp ${CONFIGDIR}/${CUSTOM_O_RU_MPLANE_SSH_CALLHOME} ${TMPDIR}/sim-o1-ofhmp-interfaces/o-ru-mplane/data
+    cp ${CONFIGDIR}/${CUSTOM_O_RU_MPLANE_TLS_CALLHOME} ${TMPDIR}/sim-o1-ofhmp-interfaces/o-ru-mplane/data
+    cp ${CONFIGDIR}/${CUSTOM_O_RU_MPLANE_TLS_NON_CALLHOME} ${TMPDIR}/sim-o1-ofhmp-interfaces/o-ru-mplane/data
+    log "## Starting PyNTS simulator ##\n"
+    docker compose -f ${TMPDIR}/sim-o1-ofhmp-interfaces/${CUSTOM_O_RU_MPLANE_DC_FILE} up -d 
+}
+
+start_sdnr_components() {
+    log "## Starting SDNR Components (MARIADB, SDNR, SDNC-WEB) ##\n"
+    log "## Copying certs directory to $TMPDIR ##\n"
+    cp -r ${CONFIGDIR}/certs ${TMPDIR}
+    log "## Copying sdnr directory to $TMPDIR ##\n"
+    cp -r ${CONFIGDIR}/sdnr ${TMPDIR}
+    log "## Copying sdnc-web directory to $TMPDIR ##\n"
+    cp -r ${CONFIGDIR}/sdnc-web ${TMPDIR}
+    log "## Copying docker compose file to $TMPDIR ##\n"
+    cp ${CONFIGDIR}/${SDNR_COMPONENTS_DC_FILE} ${TMPDIR}
+    cd ${TMPDIR}
+    docker-compose -f ${TMPDIR}/${SDNR_COMPONENTS_DC_FILE} --env-file ${CONFIGDIR}/.env up -d 
+    cd ${ROOTDIR}
+}
+
+start_test_suite() {
+    python3 ${ROOTDIR}/src/Main.py --logFile ${LOGDIR}/${LOGFILE}
+}
+
+#Main
+log "## Starting CI/CD ##\n"
+cleanup
+mkdir $TMPDIR
+build_pynts_images
+create_docker_network
+start_sdnr_components
+start_pynts
+sleep 20
+start_test_suite
diff --git a/CICD/config/.env b/CICD/config/.env
new file mode 100644 (file)
index 0000000..960f1dd
--- /dev/null
@@ -0,0 +1,12 @@
+SDNC_IMAGE=o-ran-sc/oam-oam-controller/sdnr-image:latest
+ADMIN_PASSWORD=admin
+SDNC_REST_PORT=8181
+SDNC_CERT_DIR=/opt/opendaylight/current/certs
+
+SDNRDB_IMAGE_ES=docker.elastic.co/elasticsearch/elasticsearch-oss:7.9.3
+SDNC_DB_IMAGE=docker.io/library/mariadb:11.1.2
+SDNC_DB_ROOT_PASSWORD=sdnrdb
+SDNC_DB_USERNAME=sdnrdb
+SDNC_DB_PASSWORD=sdnrdb
+
+SDNC_WEB_IMAGE=o-ran-sc/oam-oam-controller/sdnr-web-image:latest
diff --git a/CICD/config/certs/certs.properties b/CICD/config/certs/certs.properties
new file mode 100644 (file)
index 0000000..0711ad1
--- /dev/null
@@ -0,0 +1,4 @@
+keys0.zip
+***********
+keys1.zip
+***********
diff --git a/CICD/config/certs/keys0.zip b/CICD/config/certs/keys0.zip
new file mode 100644 (file)
index 0000000..28a34bd
Binary files /dev/null and b/CICD/config/certs/keys0.zip differ
diff --git a/CICD/config/certs/keys1.zip b/CICD/config/certs/keys1.zip
new file mode 100644 (file)
index 0000000..b046c79
Binary files /dev/null and b/CICD/config/certs/keys1.zip differ
diff --git a/CICD/config/docker-compose-o-ru-mplane.yaml b/CICD/config/docker-compose-o-ru-mplane.yaml
new file mode 100644 (file)
index 0000000..16005a3
--- /dev/null
@@ -0,0 +1,107 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+services:
+  pynts-ssh:
+    image: pynts-o-ru-mplane:${NTS_VERSION}
+    container_name: pynts-ssh
+    hostname: pynts-ssh
+    privileged: true
+    environment:
+      - ENDPOINT_COUNT=1
+
+      - SDNR_RESTCONF_URL=http://172.18.0.3:8181
+      - SDNR_USERNAME=admin
+      - SDNR_PASSWORD=admin
+    ports:
+      - "8300:830"
+      - "65130:6513"
+    volumes:
+      - ./o-ru-mplane/data:/data
+      - ./o-ru-mplane/src:/app/extensions/o-ru-mplane
+      - ./o-ru-mplane/data/ietf-netconf-server-ssh-listen.json:/data/ietf-netconf-server-running.json
+    networks:
+      CICD-Network:
+        ipv4_address: 172.18.0.10
+
+  pynts-tls:
+    image: pynts-o-ru-mplane:${NTS_VERSION}
+    container_name: pynts-tls
+    hostname: pynts-tls
+    privileged: true
+    environment:
+      - ENDPOINT_COUNT=1
+
+      - SDNR_RESTCONF_URL=http://172.18.0.3:8181
+      - SDNR_USERNAME=admin
+      - SDNR_PASSWORD=admin
+    ports:
+      - "8301:830"
+      - "65131:6513"
+    volumes:
+      - ./o-ru-mplane/data:/data
+      - ./o-ru-mplane/src:/app/extensions/o-ru-mplane
+      - ./o-ru-mplane/data/ietf-netconf-server-tls-listen.json:/data/ietf-netconf-server-running.json
+    networks:
+      CICD-Network:
+        ipv4_address: 172.18.0.11
+
+  pynts-callhome-tls:
+    image: pynts-o-ru-mplane:${NTS_VERSION}
+    container_name: pynts-callhome-tls
+    hostname: CICD-CALLHOME-TLS-NODE
+    privileged: true
+    environment:
+      - ENDPOINT_COUNT=1
+      - SDNR_CERTIFICATE_MARKERS=True
+      - SDNR_RESTCONF_URL=http://172.18.0.3:8181
+      - SDNR_USERNAME=admin
+      - SDNR_PASSWORD=admin
+    ports:
+      - "8302:830"
+      - "65132:6513"
+    volumes:
+      - ./o-ru-mplane/data:/data
+      - ./o-ru-mplane/src:/app/extensions/o-ru-mplane
+      - ./o-ru-mplane/data/ietf-netconf-server-tls-callhome.json:/data/ietf-netconf-server-running.json
+    networks:
+      CICD-Network:
+        ipv4_address: 172.18.0.12
+
+  pynts-callhome-ssh:
+    image: pynts-o-ru-mplane:${NTS_VERSION}
+    container_name: pynts-callhome-ssh
+    hostname: CICD-CALLHOME-SSH-NODE
+    privileged: true
+    environment:
+      - ENDPOINT_COUNT=1
+
+      - SDNR_RESTCONF_URL=http://172.18.0.3:8181
+      - SDNR_USERNAME=admin
+      - SDNR_PASSWORD=admin
+    ports:
+      - "8303:830"
+      - "65133:6513"
+    volumes:
+      - ./o-ru-mplane/data:/data
+      - ./o-ru-mplane/src:/app/extensions/o-ru-mplane
+      - ./o-ru-mplane/data/ietf-netconf-server-ssh-callhome.json:/data/ietf-netconf-server-running.json
+    networks:
+      CICD-Network:
+        ipv4_address: 172.18.0.13
+
+networks:
+  CICD-Network:
+    external: true
diff --git a/CICD/config/docker-compose-sdnr-mariadb.yaml b/CICD/config/docker-compose-sdnr-mariadb.yaml
new file mode 100644 (file)
index 0000000..2a4697b
--- /dev/null
@@ -0,0 +1,102 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+
+services:
+  sdnr:
+    image: ${SDNC_IMAGE}
+    container_name: sdnr
+    hostname: sdnr
+    depends_on:
+      - sdnrdb
+    healthcheck:
+      test: wget --no-verbose --tries=1 --spider http://localhost:${SDNC_REST_PORT}/ready || exit 1
+      start_period: 10s
+      interval: 10s
+      timeout: 5s
+      retries: 10
+    ports:
+      - ${SDNC_REST_PORT}:8181
+      - 8101:8101
+    environment:
+      - SDNC_CONFIG_DIR=/opt/onap/ccsdk/data/properties
+      - ODL_CERT_DIR=${SDNC_CERT_DIR}
+      - ENABLE_ODL_CLUSTER=false
+      - SDNC_REPLICAS=0
+      - CCSDK_REPLICAS=0
+      - DOMAIN=""
+      - SDNRWT=true
+      - SDNRDM=true
+      - SDNRINIT=true
+      - SDNRONLY=true
+      - SDNRDBTYPE=MARIADB
+      - SDNRDBURL=jdbc:mysql://sdnrdb:3306/sdnrdb
+      - SDNRDBUSERNAME=sdnrdb
+      - SDNRDBPASSWORD=sdnrdb
+      - JAVA_OPTS=-Xms2g -Xmx5g
+      - ODL_ADMIN_PASSWORD=${ADMIN_PASSWORD}
+      - SDNR_NETCONF_CALLHOME_ENABLED=true
+      - ENABLE_OAUTH=true
+      - SDNR_WEBSOCKET_PORT=8192
+    volumes:
+        - ./sdnr/oauth-provider.config.json:/opt/opendaylight/etc/oauth-provider.config.json
+        - ./sdnr/oauth-aaa-app-config.xml:/opt/opendaylight/etc/oauth-aaa-app-config.xml
+        - ./certs/keys0.zip:${SDNC_CERT_DIR}/keys0.zip
+        - ./certs/keys1.zip:${SDNC_CERT_DIR}/keys1.zip
+        - ./certs/certs.properties:${SDNC_CERT_DIR}/certs.properties
+    networks:
+      CICD-Network:
+        ipv4_address: 172.18.0.3
+
+  sdnc-web:
+    image: ${SDNC_WEB_IMAGE}
+    container_name: sdnc-web
+    ports:
+      - 8080:8080
+    depends_on:
+      sdnr:
+        condition: service_healthy
+    environment:
+      - ENABLE_OAUTH=true
+      - WEBPROTOCOL=HTTP
+      - WEBPORT=8080
+      - SDNRPROTOCOL=http
+      - SDNRHOST=172.18.0.3
+      - SDNRPORT=8181
+      - SSL_CERT_DIR=/opt/app/osaaf/local/certs
+      - SSL_CERTIFICATE=cert.pem
+      - SSL_CERTIFICATE_KEY=key.pem
+      - SDNRWEBSOCKETPORT=8192
+    volumes:
+      - ./sdnc-web:/opt/app/osaaf/local/certs
+    networks:
+      CICD-Network: 
+        ipv4_address: 172.18.0.4         
+
+  sdnrdb:
+    image: ${SDNC_DB_IMAGE}
+    container_name: sdnrdb
+    environment:
+      - MARIADB_ROOT_PASSWORD=${SDNC_DB_ROOT_PASSWORD}
+      - MARIADB_DATABASE=sdnrdb
+      - MARIADB_USER=${SDNC_DB_USERNAME}
+      - MARIADB_PASSWORD=${SDNC_DB_PASSWORD}
+    networks:
+      CICD-Network:  
+        ipv4_address: 172.18.0.2        
+
+networks:
+  CICD-Network:
+    external: true
diff --git a/CICD/config/ietf-netconf-server-ssh-callhome.json b/CICD/config/ietf-netconf-server-ssh-callhome.json
new file mode 100644 (file)
index 0000000..3154eda
--- /dev/null
@@ -0,0 +1,49 @@
+{
+  "ietf-netconf-server:netconf-server": {
+    "call-home": {
+      "netconf-client": [
+        {
+          "name": "default-client",
+          "endpoints": {
+            "endpoint": [
+              {
+                "name": "default-ssh-callhome",
+                "ssh": {
+                  "tcp-client-parameters": {
+                    "remote-address": "172.18.0.3",
+                    "remote-port": 4334
+                  },
+                  "ssh-server-parameters": {
+                    "server-identity": {
+                      "host-key": [
+                        {
+                          "name": "melacon-key",
+                          "public-key": {
+                            "central-keystore-reference": "serverkey-ssh"
+                          }
+                        }
+                      ]
+                    },
+                    "client-authentication": {
+                      "users": {
+                        "user": [
+                          {
+                            "name": "netconf",
+                            "password": "$0$netconf!"
+                          }
+                        ]
+                      }
+                    }
+                  }
+                }
+              }
+            ]
+          },
+          "connection-type": {
+            "persistent": {}
+          }
+        }
+      ]
+    }
+  }
+}
\ No newline at end of file
diff --git a/CICD/config/ietf-netconf-server-tls-callhome.json b/CICD/config/ietf-netconf-server-tls-callhome.json
new file mode 100644 (file)
index 0000000..68ee381
--- /dev/null
@@ -0,0 +1,53 @@
+{
+  "ietf-netconf-server:netconf-server": {
+    "call-home": {
+      "netconf-client": [
+        {
+          "name": "default-client",
+          "endpoints": {
+            "endpoint": [
+              {
+                "name": "tls-auth-endpt",
+                "tls": {
+                  "tcp-client-parameters": {
+                    "remote-address": "172.18.0.3",
+                    "remote-port": 4335
+                  },
+                  "tls-server-parameters": {
+                    "server-identity": {
+                      "certificate": {
+                        "central-keystore-reference": {
+                          "asymmetric-key": "serverkey-tls",
+                          "certificate": "servercert-smo"
+                        }
+                      }
+                    },
+                    "client-authentication": {
+                      "ca-certs": {
+                        "central-truststore-reference": "cacerts"
+                      }
+                    }
+                  },
+                  "netconf-server-parameters": {
+                    "client-identity-mappings": {
+                      "cert-to-name": [
+                        {
+                          "id": 1,
+                          "fingerprint": "02:DC:CB:E3:29:E2:65:04:A8:DF:B3:63:E7:E4:1A:06:81:64:C6:DA:37",
+                          "map-type": "ietf-x509-cert-to-name:san-rfc822-name"
+                        }
+                      ]
+                    }
+                  }
+                }
+              }
+            ]
+          },
+          "connection-type": {
+            "persistent": {}
+          }
+        }
+      ]
+    }
+  }
+}
diff --git a/CICD/config/ietf-netconf-server-tls-listen.json b/CICD/config/ietf-netconf-server-tls-listen.json
new file mode 100644 (file)
index 0000000..b12b65a
--- /dev/null
@@ -0,0 +1,45 @@
+{
+  "ietf-netconf-server:netconf-server": {
+    "listen": {
+      "endpoints": {
+        "endpoint": [
+          {
+            "name": "tls-endpoint-6513",
+            "tls": {
+              "tcp-server-parameters": {
+                "local-address": "0.0.0.0",
+                "local-port": 6513
+              },
+              "tls-server-parameters": {
+                "server-identity": {
+                  "certificate": {
+                    "central-keystore-reference": {
+                      "asymmetric-key": "serverkey-tls",
+                      "certificate": "servercert-smo"
+                    }
+                  }
+                },
+                "client-authentication": {
+                  "ca-certs": {
+                    "central-truststore-reference": "cacerts"
+                  }
+                }
+              },
+              "netconf-server-parameters": {
+                "client-identity-mappings": {
+                  "cert-to-name": [
+                    {
+                      "id": 1,
+                                 "fingerprint": "02:DC:CB:E3:29:E2:65:04:A8:DF:B3:63:E7:E4:1A:06:81:64:C6:DA:37",
+                      "map-type": "ietf-x509-cert-to-name:san-rfc822-name"
+                    }
+                  ]
+                }
+              }
+            }
+          }
+        ]
+      }
+    }
+  }
+}
diff --git a/CICD/config/sdnc-web/cacert.pem b/CICD/config/sdnc-web/cacert.pem
new file mode 100644 (file)
index 0000000..0146e6b
--- /dev/null
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFPjCCAyagAwIBAgIJAJ6u7cCnzrWdMA0GCSqGSIb3DQEBCwUAMCwxDjAMBgNV\r
+BAsMBU9TQUFGMQ0wCwYDVQQKDARPTkFQMQswCQYDVQQGEwJVUzAeFw0xODA0MDUx\r
+NDE1MjhaFw0zODAzMzExNDE1MjhaMCwxDjAMBgNVBAsMBU9TQUFGMQ0wCwYDVQQK\r
+DARPTkFQMQswCQYDVQQGEwJVUzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC\r
+ggIBAMA5pkgRs7NhGG4ew5JouhyYakgYUyFaG121+/h8qbSdt0hVQv56+EA41Yq7\r
+XGie7RYDQK9NmAFF3gruE+6X7wvJiChp+Cyd7sFMnb65uWhxEdxWTM2BJFrgfzUn\r
+H8ZCxgaCo3XH4PzlKRy2LQQJEJECwl/RZmRCXijMt5e9h8XoZY/fKkKcZZUsWNCM\r
+pTo266wjvA9MXLmdgReRj0+vrCjrNqy+htwJDztoiHWiYPqT6o8EvGcgjNqjlZx7\r
+NUNf8MfLDByqKF6+wRbHv1GKjn3/Vijd45Fv8riyRYROiFanvbV6jIfBkv8PZbXg\r
+2VDWsYsgp8NAvMxK+iV8cO+Ck3lBI2GOPZbCEqpPVTYbLUz6sczAlCXwQoPzDIZY\r
+wYa3eR/gYLY1gP2iEVHORag3bLPap9ZX5E8DZkzTNTjovvLk8KaCmfcaUMJsBtDd\r
+ApcUitz10cnRyZc1sX3gE1f3DpzQM6t9C5sOVyRhDcSrKqqwb9m0Ss04XAS9FsqM\r
+P3UWYQyqDXSxlUAYaX892u8mV1hxnt2gjb22RloXMM6TovM3sSrJS0wH+l1nznd6\r
+aFXftS/G4ZVIVZ/LfT1is4StoyPWZCwwwly1z8qJQ/zhip5NgZTxQw4mi7ww35DY\r
+PdAQOCoajfSvFjqslQ/cPRi/MRCu079heVb5fQnnzVtnpFQRAgMBAAGjYzBhMB0G\r
+A1UdDgQWBBRTVTPyS+vQUbHBeJrBKDF77+rtSTAfBgNVHSMEGDAWgBRTVTPyS+vQ\r
+UbHBeJrBKDF77+rtSTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAN\r
+BgkqhkiG9w0BAQsFAAOCAgEAPx/IaK94n02wPxpnYTy+LVLIxwdq/kawNd6IbiMz\r
+L87zmNMDmHcGbfoRCj8OkhuggX9Lx1/CkhpXimuYsZOFQi5blr/u+v4mIbsgbmi9\r
+7j+cUHDP0zLycvSvxKHty51LwmaX9a4wkJl5zBU4O1sd/H9tWcEmwJ39ltKoBKBx\r
+c94Zc3iMm5ytRWGj+0rKzLDAXEWpoZ5bE5PLJauA6UDCxDLfs3FwhbS7uDggxYvf\r
+jySF5FCNET94oJ+m8s7VeHvoa8iPGKvXrIqdd7XDHnqJJlVKr7m9S0fMbyEB8ci2\r
+RtOXDt93ifY1uhoEtEykn4dqBSp8ezvNMnwoXdYPDvTd9uCAFeWFLVreBAWxd25h\r
+PsBTkZA5hpa/rA+mKv6Af4VBViYr8cz4dZCsFChuioVebe9ighrfjB//qKepFjPF\r
+CyjzKN1u0JKm/2x/ORqxkTONG8p3uDwoIOyimUcTtTMv42bfYD88RKakqSFXE9G+\r
+Z0LlaKABqfjK49o/tsAp+c5LoNlYllKhnetO3QAdraHwdmC36BhoghzR1jpX751A\r
+cZn2VH3Q4XKyp01cJNCJIrua+A+bx6zh3RyW6zIIkbRCbET+UD+4mr8WIcSE3mtR\r
+ZVlnhUDO4z9//WKMVzwS9Rh8/kuszrGFI1KQozXCHLrce3YP6RYZfOed79LXaRwX\r
+dYY=
+-----END CERTIFICATE-----
diff --git a/CICD/config/sdnc-web/cert.pem b/CICD/config/sdnc-web/cert.pem
new file mode 100644 (file)
index 0000000..1195a12
--- /dev/null
@@ -0,0 +1,102 @@
+Bag Attributes
+    friendlyName: sdnc@sdnc.onap.org
+    localKeyID: 54 69 6D 65 20 31 36 31 31 36 38 33 32 39 38 35 32 32 
+Key Attributes: <No Attributes>
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIk4s7xBaGXdgCAggA
+MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECI7qjnyFxa3mBIIEyHgvXzCmhOT/
+atHsneicZqaGXxz57z1mPosvGwf2w6WerxOyW+cFejNepgMH+c5MQAfTuTD0H5g3
+SoyC9TKfHBld439sKO3hnDBROsypugO2uP4ownGiKQxOdwHNCOS8nC1EbUYC6nHR
+B5QtRpDHZzB2t9dd6+RU1PuPUFRxjIPZsCM1DdKKhMHQHr8WDSbi170XfzC6O2Ko
+23tgDq9cQYTqmgFedjyWpEmxfcibaujGOZ4VQej+tn60A03cXHG05tN/XvDCbQty
+9bw1kiS8e+qmdUhkEIhR2aY0Z9sCUOLwJPDg/1vE0ZwK/bRGRD432PD+dmSLFV+v
+m2m/ec8IQer8hCleI6GBaFI28QZyS2jznbzS7b/hU+nyxkZXejAymU1OBcNV4H8M
+qf5ITWs+Ma5fc/8X90MlJacmBo3JuusTvDImLFP+5Nn5Yo3cnDeiAyMo0vFuywrv
+bATYAncPlzksb6py3D5iXmLxREgLI14/TdZLhcYXBHw76oPz+/CH0A2P/HICPIjr
+zF8U6zNI4bIguBTfVmm6YLjzOkVkKx4e/0fJiQO1yhzIsghhByIMg1uPcm0olNQz
+r+YRPKTqFCPRxyGgPMleN56qeLhN8Q1WyJzIJoVVpDFc+4Stbv71C/po6/6A3v4r
+hGUPCSsj8wJN+ozdamDWpeyRVCwXmmKwJU96pbnhdH/l6CPjmniAuKLzOMLJH1AD
+FJEm1Frpz40BDC2U/165+nlfcHZfePWVPpNuqWzUfywqu8ORS/pYhapFoLLafQIn
+22KhnPnbNXclIzuI0wiKjoNAfzJM0S9hysdojK/bptaZXUFeBEe41A8exuOjOxRh
+pJqqgq2cRx6cmnAy0dr54+GoZr3haQCqTk814cxumOHqQdWllblA1D9b5Wd+8T5+
+tYdzYatsvjeY/VDH5czAbUhgBHGO3Vkxm49QemcopNDeCZ7RXmSwl2X6HLfCwnfH
+9zsdNrIMfSTdRk9H4iHTyFRyoPViX6wYxlD7B9hJWggEyDVg9RvJImOSKyWk+NKd
+WmfxaywgUJkxsJeEBCso8V690lm//oWrc17qWEd29h/9J0MPNa2zXJyG1Yq1RohZ
+JW3IcnE8gCoyHVjxBCVjKPA6dXtfDNfmCDyEG+GgPglQUj9TBIRGmn9e067M6dB9
+vhnK12SBQNTyoH5vWSbP2u5wk6jV5QKYyqMbNHAD1uvsBbWy4FpoI+epp2Y3XcAr
+kgBzopKSaCbj1thxxUHsMYBspVDR+D0SR4fsHhj+Y3jmr37s3q1NOSkWUMcMa7Hi
+zaozRSo8hYUwP5RHpPcNCTcK0dhFPAWXyGPtP7IribGQDhUAdde/s4yPuSLa28zy
+uTKWU5vSlaE+9WRSKvbA8HdKTPv45dV/qUCHITvPOdsqkWJ3qhNQMrrD9Mx17oj/
+zGZTcRGQG62dfYUR9v2rGE6Gj7MzaE1SrQgYqtCzWfJ9e3fBqOlko5jtZ5Kz86DU
+ZFISrkb1sATngzWFtvyNgHFx4fUsYDJbc8XlB47H5Byyj/6SypS6dMpwqpNezn8s
+xQi3yUfpg7GQzTFKdwmqK/KVeh0KZF/0FM+ZCUPh6IaRDcp8+Hm7H72ZD4vPxs4s
+8bvLx9bxfiI+/6Om51IQrQ==
+-----END ENCRYPTED PRIVATE KEY-----
+Bag Attributes
+    friendlyName: sdnc@sdnc.onap.org
+    localKeyID: 54 69 6D 65 20 31 36 31 31 36 38 33 32 39 38 35 32 32 
+subject=CN = sdnc, OU = sdnc@sdnc.onap.org:DEV, OU = OSAAF, O = ONAP, C = US
+
+issuer=C = US, O = ONAP, OU = OSAAF, CN = intermediateCA_9
+
+-----BEGIN CERTIFICATE-----
+MIIE6zCCA9OgAwIBAgIJALyx+8HFJuYTMA0GCSqGSIb3DQEBCwUAMEcxCzAJBgNV
+BAYTAlVTMQ0wCwYDVQQKDARPTkFQMQ4wDAYDVQQLDAVPU0FBRjEZMBcGA1UEAwwQ
+aW50ZXJtZWRpYXRlQ0FfOTAeFw0yMTAxMjYxNzQ4MThaFw0yMjAxMjYxNzQ4MTha
+MFwxDTALBgNVBAMMBHNkbmMxHzAdBgNVBAsMFnNkbmNAc2RuYy5vbmFwLm9yZzpE
+RVYxDjAMBgNVBAsMBU9TQUFGMQ0wCwYDVQQKDARPTkFQMQswCQYDVQQGEwJVUzCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALQtwrW3Eiru18BADUiBIbEU
+sruiLu63h6jDprLqrxGt57d5MSpxaRoOPoWDPE/Z6LvMwTGfiQIPsFnZ2H3YtDsI
+/p1fOFAHxCUl5Cs4HnYjJUkDs2U7dXUYZR8enNPZRfFZNUOajxERUgyE/0g+yHS1
+AlysMInFDblmNEYgQoNiN996FpBamHivCDXw612bBkkZQOTeKJaCZ0DPGIYGAJtf
+Q1kIL7Y1D3c3C0VD39homtxqIb21rje63YVISprbfKX0RxijkWw0wXjaRDwxPGwH
+TrDHgsrPH/zv9Hak6cJkTw5e7VBHHlL1sHYgPSDLd/8PFGkmD4a/N/IKLy/14KMC
+AwEAAaOCAcMwggG/MAkGA1UdEwQCMAAwDgYDVR0PAQH/BAQDAgXgMCAGA1UdJQEB
+/wQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBUBgNVHSMETTBLgBSB95lbELnIjN7z
+Ul7qTmmgQz6s3aEwpC4wLDEOMAwGA1UECwwFT1NBQUYxDTALBgNVBAoMBE9OQVAx
+CzAJBgNVBAYTAlVTggEHMB0GA1UdDgQWBBRUhHQPlauGoN9fiGiB7WYr+oIHJDCC
+AQkGA1UdEQSCAQAwgf2BH21hcmsuZC5tYW5hZ2VyQHBlb3BsZS5vc2FhZi5jb22C
+BHNkbmOCG2MxLnZtMS5zZG5jLnNpbXBsZWRlbW8ub25hcIIbYzIudm0xLnNkbmMu
+c2ltcGxlZGVtby5vbmFwghtjMy52bTEuc2RuYy5zaW1wbGVkZW1vLm9uYXCCG2M0
+LnZtMS5zZG5jLnNpbXBsZWRlbW8ub25hcIIJb25hcC1zZG5jgg5vbmFwLXNkbmMu
+b25hcIIcc2RuYy5hcGkuc2ltcGxlZGVtby5vbmFwLm9yZ4IJc2RuYy5vbmFwghx2
+bTEuc2RuYy5zaW1wbGVkZW1vLm9uYXAub3JnMA0GCSqGSIb3DQEBCwUAA4IBAQAg
+7fybHysjWyKejSfPnodYuLfQoCIaXe5C4JbwLGKweAost5E6ud2rscN/c5UYNPs/
+IskfnMxULLzJpEXdUHwLQaLJj0fQQBRHq23s8P7Emu44ZeEzxAQfI+4pKRzTYxag
+4dIitf91nhUq5SQI/pcki+/ElkwfeKHYQLBDU8ygG/gZKh1UHxIjfva7v/ENqL2h
+H8UDXsLhOx/guaJzH2CRQdKMminsdtnNgSRRPzWRe4EMc2ah6G6E4B/Za/n7Rhq5
+r6jpvM/XIxPCY4ci5jJIbvdahS4I54kMaLRTSl4gT8+n8ie/GzhZlXX+1MR8HCZc
+8SWDYxmc8MkJ20iekiSc
+-----END CERTIFICATE-----
+Bag Attributes
+    friendlyName: CN=intermediateCA_9,OU=OSAAF,O=ONAP,C=US
+subject=C = US, O = ONAP, OU = OSAAF, CN = intermediateCA_9
+
+issuer=OU = OSAAF, O = ONAP, C = US
+
+-----BEGIN CERTIFICATE-----
+MIIEdTCCAl2gAwIBAgIBBzANBgkqhkiG9w0BAQsFADAsMQ4wDAYDVQQLDAVPU0FB
+RjENMAsGA1UECgwET05BUDELMAkGA1UEBhMCVVMwHhcNMTgwODE3MTg1MTM3WhcN
+MjMwODE3MTg1MTM3WjBHMQswCQYDVQQGEwJVUzENMAsGA1UECgwET05BUDEOMAwG
+A1UECwwFT1NBQUYxGTAXBgNVBAMMEGludGVybWVkaWF0ZUNBXzkwggEiMA0GCSqG
+SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv0HHUkba3uNtNI3jPKimUcd6RNwmhSCJL
+neMWpnjqp5/A+HCKyNsEaT4y177hNLmCm/aMm1u2JIfikc+8wEqLCSBBPz+P0h+d
+o+sZ7U+4oeQizdYYpEdzHJ2SieHHa8vtu80rU3nO2NEIkuYC20HcKSEtl8fFKsk3
+nqlhY+tGfYJPTXcDOQAO40BTcgat3C3uIJHkWJJ4RivunE4LEuRv9QyKgAw7rkJV
+v+f7guqpZlXy6dzAkuU7XULWcgo55MkZlssoiErMvEZJad5aWKvRY3g7qUjaQ6wO
+15wOAUoRBW96eeZZbytgn8kybcBy++Ue49gPtgm1MF/KlAsp0MD5AgMBAAGjgYYw
+gYMwHQYDVR0OBBYEFIH3mVsQuciM3vNSXupOaaBDPqzdMB8GA1UdIwQYMBaAFFNV
+M/JL69BRscF4msEoMXvv6u1JMBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/
+BAQDAgGGMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjANBgkqhkiG9w0B
+AQsFAAOCAgEADxNymiCNr2e37iLReoaxKmZvwox0cTiNAaj7iafRzmwIoY3VXO8Q
+ix5IYcp4FaQ7fV1jyp/AmaSnyHf6Osl0sx8PxsQkO7ALttxKUrjfbvNSVUA2C/vl
+u5m7UVJLIUtFDZBWanzUSmkTsYLHpiANFQKd2c/cU1qXcyzgJVFEFVyyHNkF7Is+
++pjG9M1hwQHOoTnEuU013P7X1mHek+RXEfhJWwe7UsZnBKZaZKbQZu7hEtqKWYp/
+QsHgnjoLYXsh0WD5rz/mBxdTdDLGpFqWDzDqb8rsYnqBzoowvsasV8X8OSkov0Ht
+8Yka0ckFH9yf8j1Cwmbl6ttuonOhky3N/gwLEozuhy7TPcZGVyzevF70kXy7g1CX
+kpFGJyEHXoprlNi8FR4I+NFzbDe6a2cFow1JN19AJ9Z5Rk5m7M0mQPaQ4RcikjB3
+aoLsASCJTm1OpOFHfxEKiBW4Lsp3Uc5/Rb9ZNbfLrwqWZRM7buW1e3ekLqntgbky
+uKKISHqVJuw/vXHl1jNibEo9+JuQ88VNuAcm7WpGUogeCa2iAlPTckPZei+MwZ8w
+tpvxTyYlZEC8DWzY1VC29+W2N5cvh01e2E3Ql08W1zL63dqrgdEZ3VWjzooYi4ep
+BmMXTvouW+Flyvcw/0oTcfN0biDIt0mCkZ5CQVjfGL9DTOYteR5hw+k=
+-----END CERTIFICATE-----
diff --git a/CICD/config/sdnc-web/key.pem b/CICD/config/sdnc-web/key.pem
new file mode 100644 (file)
index 0000000..04bc849
--- /dev/null
@@ -0,0 +1,29 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC0LcK1txIq7tfA
+QA1IgSGxFLK7oi7ut4eow6ay6q8Rree3eTEqcWkaDj6FgzxP2ei7zMExn4kCD7BZ
+2dh92LQ7CP6dXzhQB8QlJeQrOB52IyVJA7NlO3V1GGUfHpzT2UXxWTVDmo8REVIM
+hP9IPsh0tQJcrDCJxQ25ZjRGIEKDYjffehaQWph4rwg18OtdmwZJGUDk3iiWgmdA
+zxiGBgCbX0NZCC+2NQ93NwtFQ9/YaJrcaiG9ta43ut2FSEqa23yl9EcYo5FsNMF4
+2kQ8MTxsB06wx4LKzx/87/R2pOnCZE8OXu1QRx5S9bB2ID0gy3f/DxRpJg+Gvzfy
+Ci8v9eCjAgMBAAECggEAbB+J2MIjhOAPWK8XSYs0TK+/EhohT9+S6RN/1Z4/sLxL
+cS6o8m9cQuaJXlWCu+hoYUpeJQk3jqUbjs/LurlwbnzXTlj10hDXA/PZGJZ0sTAm
+D8rIvNcRhVM+W45jTj30WwDNleQKNpPOSPUGvLPwVxjIchRijEpUEg3jELILOAuW
+ebloKLqc5SDAPKIpepZO7bz4L/dVlQSEBp3OTzyfeecbBNS2Vfw0K+I5BqGJAssZ
+Dq+ixSHRj6mdvm7tf5e14j65W8VKvUoHbktp9z2OBCItySV4g1dqrfM7T9SqsosH
+cbwR5dIieiFnhdg94rfpzH2QTCBt5MGUpCcv+CbQAQKBgQDkenCAImG1jAjzQNxb
+7LXLJeIqJC2E3290hEYge2Bi+1/WmV222AAwNUEPQfKa7qUJRLpX8a4p+9kTaZos
+93szyYEyp4vf93GDHvQPmKMJDCbbxa94txd5dnrtYTN+MCPjpwr+75++JRUUwcNr
+k3mkzM87zhSBkyYcGiCsza4gQQKBgQDJ4enp8Zly3GqufbWJNpKut6e9hC6f4qWi
+4qRUBmjnogm0HiGmn83n9B6SI6OnaRy/dXgPBogZDeETyzGu78nArDK+cy8wSy0H
+aPuApqGJzsuAl6YWudYt4ooBcJL99XgTGxFAb2q04JKxh18V6DRfj7pY2uhZongI
+OdcMSE2H4wKBgQCUzNEcAkhUbmEd264oCB/VsFR9UZZ7pPD3l3X8jZ2WmVQvdS69
+eCuXOfenMjIIiUfeo24g/HuLSER2Ch6pDnykm6WTEd9c+9Bnru8QgT4dFFbyZusC
+2WtmZa1lkBpzInMdPptAsVr+ATSbkh3tn9xnYiPNNUfRo738K2AAauvugQKBgQCd
+dzbqoOXdr4sOm0LzybtTyDBwJB/x2ej0Se9/EpjUw5DqCu6YduE2YTVPK7lEpTol
+JE0G+0NAt5CtzbntB1/Ihwf1gQZ3lsuCkiJJ0K8DPGeC38ZOx5kFpUObp+EfcU29
+KUmlhsImX1xMWJiUD9B6ETN6hxTghVc2o1bXX7YJnQKBgQCGiRnjCEmKd8hefkS8
+ub9F4kdOzXmG4XhK+oZWVGPXIGfnoxm6IbWcjSArA/m8TLfJSHPKujnLOnOkffpi
+7+PWzTHn5BFDGUb8z3mxwJV8e9szoDkljoiUwYU/S8eatAm6lyJv1gp2wmDI9DfT
+86BefCEvGk3EzAo3L6hhHdICzA==
+-----END PRIVATE KEY-----
+
diff --git a/CICD/config/sdnr/devicemanager.properties b/CICD/config/sdnr/devicemanager.properties
new file mode 100644 (file)
index 0000000..346c215
--- /dev/null
@@ -0,0 +1,49 @@
+[aai]
+aaiPropertiesFile=null
+aaiUrl=off
+aaiUserCredentials=
+aaiHeaders=["X-TransactionId: 9999"]
+aaiDeleteOnMountpointRemove=false
+aaiTrustAllCerts=false
+aaiApiVersion=aai/v13
+aaiPcks12ClientCertFile=
+aaiPcks12ClientCertPassphrase=
+aaiClientConnectionTimeout=30000
+aaiApplicationId=SDNR
+aaiReadTimeout=60000
+
+[devicemonitor]
+SeverityconnectionLossOAM=Major
+SeverityconnectionLossMediator=Major
+SeverityconnectionLossNeOAM=Major
+
+[dmonf]
+pollAlarms=false
+
+[toggleAlarmFilter]
+taEnabled=true
+taDelay=3000
+
+[housekeeping]
+hkEnabled=false
+
+[dcae]
+dcaeUrl=off
+dcaeUserCredentials=admin:admin
+dcaeHeartbeatPeriodSeconds=120
+
+[pm]
+pmEnabled=true
+
+[VESCollector]
+VES_COLLECTOR_ENABLED=true
+VES_COLLECTOR_TLS_ENABLED=true
+VES_COLLECTOR_TRUST_ALL_CERTS=true
+VES_COLLECTOR_USERNAME=sample1
+VES_COLLECTOR_PASSWORD=sample1
+VES_COLLECTOR_IP=172.18.0.8
+VES_COLLECTOR_PORT=8443
+VES_COLLECTOR_VERSION=v7
+REPORTING_ENTITY_NAME=ONAP SDN-R
+EVENTLOG_MSG_DETAIL=SHORT
+
diff --git a/CICD/config/sdnr/mountpoint-registrar.properties b/CICD/config/sdnr/mountpoint-registrar.properties
new file mode 100644 (file)
index 0000000..cb73766
--- /dev/null
@@ -0,0 +1,44 @@
+[stndDefinedFault]
+topic=unauthenticated.SEC_3GPP_FAULTSUPERVISION_OUTPUT
+consumerGroup=myG
+consumerID=C1
+timeout=2000
+limit=1000
+fetchPause=5000
+
+[general]
+baseUrl=http://localhost:8181
+sdnrUser=${SDNRUSERNAME}
+sdnrPasswd=${SDNRPASSWORD}
+
+[strimzi-kafka]
+strimziEnabled=true
+bootstrapServers=kafka:9092
+securityProtocol=PLAINTEXT
+saslMechanism=PLAIN
+saslJaasConfig=PLAIN
+
+[fault]
+topic=unauthenticated.SEC_FAULT_OUTPUT
+consumerGroup=myG
+consumerID=C1
+timeout=2000
+limit=1000
+fetchPause=5000
+
+[provisioning]
+topic=unauthenticated.SEC_3GPP_PROVISIONING_OUTPUT
+consumerGroup=myG
+consumerID=C1
+timeout=2000
+limit=1000
+fetchPause=5000
+
+[pnfRegistration]
+topic=unauthenticated.VES_PNFREG_OUTPUT
+consumerGroup=myG
+consumerID=C1
+timeout=2000
+limit=1000
+fetchPause=5000
+
diff --git a/CICD/config/sdnr/mountpoint-registrar.properties.bkup b/CICD/config/sdnr/mountpoint-registrar.properties.bkup
new file mode 100644 (file)
index 0000000..fbe68e4
--- /dev/null
@@ -0,0 +1,99 @@
+[stndDefinedFault]
+username=${DMAAP_STNDDEFINED_FAULT_TOPIC_USERNAME}
+password=${DMAAP_STNDDEFINED_FAULT_TOPIC_PASSWORD}
+topic=unauthenticated.SEC_3GPP_FAULTSUPERVISION_OUTPUT
+TransportType=HTTPNOAUTH
+Protocol=http
+host=onap-dmaap:3904
+contenttype=application/json
+group=myG
+id=C1
+timeout=20000
+limit=10000
+fetchPause=5000
+jersey.config.client.readTimeout=25000
+jersey.config.client.connectTimeout=25000
+jersey.config.client.proxy.username=${HTTP_PROXY_USERNAME}
+jersey.config.client.proxy.password=${HTTP_PROXY_PASSWORD}
+jersey.config.client.proxy.uri=${HTTP_PROXY_URI}
+consumerGroup=myG
+consumerID=C1
+
+[general]
+dmaapEnabled=false
+baseUrl=http://localhost:8181
+sdnrUser=admin
+sdnrPasswd=${ODL_ADMIN_PASSWORD}
+
+[strimzi-kafka]
+strimziEnabled=false
+bootstrapServers=onap-strimzi-kafka-0:9094,onap-strimzi-kafka-1:9094
+securityProtocol=PLAINTEXT
+saslMechanism=PLAIN
+saslJaasConfig=PLAIN
+
+[fault]
+faultConsumerClass=org.onap.ccsdk.features.sdnr.wt.mountpointregistrar.impl.DMaaPFaultVESMsgConsumer
+TransportType=HTTPNOAUTH
+host=onap-dmaap:3904
+topic=unauthenticated.SEC_FAULT_OUTPUT
+contenttype=application/json
+group=myG
+id=C1
+timeout=2000
+limit=1000
+fetchPause=50000
+jersey.config.client.readTimeout=25000
+jersey.config.client.connectTimeout=25000
+jersey.config.client.proxy.username=${HTTP_PROXY_USERNAME}
+jersey.config.client.proxy.password=${HTTP_PROXY_PASSWORD}
+jersey.config.client.proxy.uri=proxy.conexus.srvc.local:3128
+Protocol=http
+username=${DMAAP_FAULT_TOPIC_USERNAME}
+password=${DMAAP_FAULT_TOPIC_PASSWORD}
+consumerGroup=myG
+consumerID=C1
+
+[provisioning]
+username=${DMAAP_CM_TOPIC_USERNAME}
+password=${DMAAP_CM_TOPIC_PASSWORD}
+topic=unauthenticated.SEC_3GPP_PROVISIONING_OUTPUT
+TransportType=HTTPNOAUTH
+Protocol=http
+host=onap-dmaap:3904
+contenttype=application/json
+group=myG
+id=C1
+timeout=20000
+limit=10000
+fetchPause=5000
+jersey.config.client.readTimeout=25000
+jersey.config.client.connectTimeout=25000
+jersey.config.client.proxy.username=${HTTP_PROXY_USERNAME}
+jersey.config.client.proxy.password=${HTTP_PROXY_PASSWORD}
+jersey.config.client.proxy.uri=${HTTP_PROXY_URI}
+consumerGroup=myG
+consumerID=C1
+
+[pnfRegistration]
+pnfRegConsumerClass=org.onap.ccsdk.features.sdnr.wt.mountpointregistrar.impl.DMaaPPNFRegVESMsgConsumer
+TransportType=HTTPNOAUTH
+host=onap-dmaap:3904
+topic=unauthenticated.VES_PNFREG_OUTPUT
+contenttype=application/json
+group=myG
+id=C1
+timeout=2000
+limit=1000
+fetchPause=25000
+jersey.config.client.readTimeout=25000
+jersey.config.client.connectTimeout=25000
+jersey.config.client.proxy.username=${HTTP_PROXY_USERNAME}
+jersey.config.client.proxy.password=${HTTP_PROXY_PASSWORD}
+jersey.config.client.proxy.uri=proxy.conexus.srvc.local:3128
+Protocol=http
+username=${DMAAP_PNFREG_TOPIC_USERNAME}
+password=${DMAAP_PNFREG_TOPIC_PASSWORD}
+consumerGroup=myG
+consumerID=C1
+
diff --git a/CICD/config/sdnr/oauth-aaa-app-config.xml b/CICD/config/sdnr/oauth-aaa-app-config.xml
new file mode 100644 (file)
index 0000000..65e34db
--- /dev/null
@@ -0,0 +1,97 @@
+<?xml version="1.0" ?>
+<!--
+  ~ ============LICENSE_START=======================================================
+  ~ ONAP : ccsdk features
+  ~ ================================================================================
+  ~ Copyright (C) 2021 highstreet technologies GmbH Intellectual Property.
+  ~ All rights reserved.
+  ~ ================================================================================
+  ~ 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.
+  ~ ============LICENSE_END=======================================================
+  ~
+  -->
+
+<shiro-configuration xmlns="urn:opendaylight:aaa:app:config">
+
+
+    <main>
+        <pair-key>tokenAuthRealm</pair-key>
+        <!--<pair-value>org.opendaylight.aaa.shiro.realm.TokenAuthRealm</pair-value>-->
+        <pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.OAuth2Realm</pair-value>
+    </main>
+
+    <main>
+        <pair-key>securityManager.realms</pair-key>
+        <pair-value>$tokenAuthRealm</pair-value>
+    </main>
+    <!-- Used to support OAuth2 use case. -->
+    <main>
+        <pair-key>anyroles</pair-key>
+        <pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters.AnyRoleHttpAuthenticationFilter</pair-value>
+    </main>
+    <main>
+        <pair-key>authcBearer</pair-key>
+<!--        <pair-value>org.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter</pair-value>-->
+        <pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters.BearerAndBasicHttpAuthenticationFilter</pair-value>
+    </main>
+
+    <!-- in order to track AAA challenge attempts -->
+    <main>
+        <pair-key>accountingListener</pair-key>
+        <pair-value>org.opendaylight.aaa.shiro.filters.AuthenticationListener</pair-value>
+    </main>
+    <main>
+        <pair-key>securityManager.authenticator.authenticationListeners</pair-key>
+        <pair-value>$accountingListener</pair-value>
+    </main>
+
+    <!-- Model based authorization scheme supporting RBAC for REST endpoints -->
+    <main>
+        <pair-key>dynamicAuthorization</pair-key>
+        <pair-value>org.onap.ccsdk.features.sdnr.wt.oauthprovider.filters.CustomizedMDSALDynamicAuthorizationFilter</pair-value>
+    </main>
+
+
+    <urls>
+        <pair-key>/**/operations/cluster-admin**</pair-key>
+        <pair-value>authcBearer, roles[admin]</pair-value>
+    </urls>
+    <urls>
+        <pair-key>/**/v1/**</pair-key>
+        <pair-value>authcBasic, roles[admin]</pair-value>
+    </urls>
+    <urls>
+        <pair-key>/**/config/aaa*/**</pair-key>
+        <pair-value>authcBasic, roles[admin]</pair-value>
+    </urls>
+     <urls>
+        <pair-key>/oauth/**</pair-key>
+        <pair-value>anon</pair-value>
+    </urls>
+    <urls>
+        <pair-key>/odlux/**</pair-key>
+        <pair-value>anon</pair-value>
+    </urls>
+    <urls>
+        <pair-key>/apidoc/**</pair-key>
+        <pair-value>authcBasic</pair-value>
+    </urls>
+    <urls>
+        <pair-key>/rests/**</pair-key>
+        <pair-value>authcBearer, anyroles["admin,provision"]</pair-value>
+    </urls>
+    <urls>
+        <pair-key>/**</pair-key>
+        <pair-value>authcBearer, roles[admin]</pair-value>
+    </urls>
+</shiro-configuration>
diff --git a/CICD/config/sdnr/oauth-provider.config.json b/CICD/config/sdnr/oauth-provider.config.json
new file mode 100644 (file)
index 0000000..e7c8d47
--- /dev/null
@@ -0,0 +1,22 @@
+{
+    "tokenSecret": "secret",
+    "tokenIssuer": "onap.sdnc",
+    "publicUrl": "http://172.18.0.4:8080",
+    "redirectUri": "/odlux/index.html#/oauth?token=",
+    "supportOdlUsers": "true",
+    "providers": [
+{
+                       "id": "identity",
+                       "type": "KEYCLOAK",
+                       "url": "https://identity.smo-int-1.t3.lab.osn-lab.com",
+                       "clientId": "odlux.app",
+                       "secret": "2a64fdca-c205-4b52-9f58-195ccc142ddb",
+                       "scope": "openid",
+                       "title": "ONAP-IDENTITY",
+                       "roleMapping": {
+                               "administration": "admin"
+                       },
+                       "realmName": "onap",
+            "trustAll": "true"
+               }]
+}
diff --git a/CICD/config/sdnrconfig.json b/CICD/config/sdnrconfig.json
new file mode 100644 (file)
index 0000000..d4657f6
--- /dev/null
@@ -0,0 +1,9 @@
+{
+    "sdnrHost": "172.18.0.3",
+    "sdnrPort": 8181,
+    "basicAuth": true,
+    "credentials": {
+        "sdnrUsername": "admin",
+        "sdnrPassword": "admin"
+    }
+}
\ No newline at end of file
diff --git a/CICD/config/testsuiteconfig.json b/CICD/config/testsuiteconfig.json
new file mode 100644 (file)
index 0000000..a0d12a6
--- /dev/null
@@ -0,0 +1,4 @@
+{
+    "debugLevel": "DEBUG",
+    "testplanDir": "testplans"
+}
\ No newline at end of file
diff --git a/CICD/log/.gitkeep b/CICD/log/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/CICD/src/ApiRequest.py b/CICD/src/ApiRequest.py
new file mode 100644 (file)
index 0000000..6d16f47
--- /dev/null
@@ -0,0 +1,27 @@
+# Abstract class which defines the common structure for all REST requests
+from abc import ABC, abstractmethod
+
+class ApiRequest():
+    baseurl: str
+    endpoint: str
+    headers: dict[str, str] = {}
+    query_params: dict[str, str]
+    request_body: str
+
+    @abstractmethod
+    def __init__(self, baseurl: str, endpoint: str):
+        self.baseurl = baseurl
+        self.endpoint = endpoint
+
+    @abstractmethod
+    def execute(self):
+        pass
+
+    def add_header(self, key: str, value: str):
+        self.headers[key] = value
+
+    def add_query_param(self, key: str, value: str):
+        self.query_params[key] = value
+
+    def set_request_body(self, request_body: str):
+        self.request_body = request_body
diff --git a/CICD/src/Main.py b/CICD/src/Main.py
new file mode 100644 (file)
index 0000000..55a7cda
--- /dev/null
@@ -0,0 +1,107 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+
+#Entry point of the test suite
+
+from TestPlanModel import TestPlan
+from TestPlanRunner import TestPlanRunner
+from pydantic import BaseModel
+import json
+import os
+import sys
+import argparse
+from pathlib import Path
+from SdnrConfigModel import SdnrConfig
+from TestSuiteConfig import TestSuiteConfig
+import logging
+
+logger = logging.getLogger(__name__)
+parser = argparse.ArgumentParser(description="Test Suite")
+parser.add_argument("--logFile", type=str, required=True, help="log file name including path")
+successCnt: int = 0
+failedCnt: int = 0
+
+class Main:
+    testsuite_config: TestSuiteConfig
+    sdnrconfig: SdnrConfig
+    
+
+    def __init__(self, logFile: str):
+        print("Initializing Test Suite ...")
+        logging.basicConfig (
+        level=logging.DEBUG,
+        format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
+        handlers=[
+            logging.FileHandler(logFile),
+            logging.StreamHandler()
+        ]
+    )
+        self.load_testsuite_config()
+        self.load_sdnr_config()
+
+    def load_sdnr_config(self) -> None:
+        sdnrconfig_path = "config/sdnrconfig.json"
+        self.sdnrconfig = SdnrConfig.model_validate(json.loads(Path(sdnrconfig_path).read_text()))
+
+    def load_testsuite_config(self) -> None:
+        testsuiteconfig_path = "config/testsuiteconfig.json"
+        self.testsuite_config = TestSuiteConfig.model_validate(json.loads(Path(testsuiteconfig_path).read_text()))
+
+    def start(self) -> None:
+        # Iterate through test plans
+        for _, dirs, _ in os.walk(self.testsuite_config.testplan_dir):
+            dirs.sort()
+            for dir in dirs:
+                logger.info("Executing Test Plan : %s", dir)
+                for file in os.listdir(self.testsuite_config.testplan_dir+"/"+dir):
+                    if "plan" in file: 
+                        self.executeTest(dir, file)
+    
+    def executeTest(self, subdir: str, testplan_file: str):
+        global successCnt, failedCnt
+        logger.info("\n======== BEGIN TEST: Test Dir - %s ========", subdir)
+        # 1. Load test plan
+        testplan_path = "testplans/" + subdir + "/" + testplan_file
+        data = json.loads(Path(testplan_path).read_text())
+        test_plan = TestPlan.model_validate(data)
+
+        # 2. Execute Plan
+        testplan_runner = TestPlanRunner(self.testsuite_config, self.sdnrconfig, test_plan)
+        (create_result, validate_result, cleanup_result) = testplan_runner.run()
+        if (create_result and validate_result and cleanup_result):
+            successCnt += 1
+        else:
+            failedCnt += 1
+
+        # 3. Print test result
+        logger.info("---------------------------------------------------------------------------")
+        logger.info("|  Test %s Result - Execution - %s, Validation - %s, Cleanup - %s  |", 
+                    subdir, create_result, validate_result, cleanup_result)
+        logger.info("---------------------------------------------------------------------------")
+        logger.info("======== END TEST: Test Dir - %s.  ========\n", subdir)
+
+#Main
+if __name__ == '__main__':
+    args = parser.parse_args()
+    main = Main(args.logFile)
+    main.start()
+    logger.info ("Test Execution Summary - Success = %d Failed = %d", successCnt, failedCnt)
+    if (failedCnt > 0):
+        logger.info("TEST Suite Execution Failed")
+        sys.exit(1)
+    else:
+        logger.info("TEST Suite Execution Success")
+        sys.exit(0)
diff --git a/CICD/src/SdnrConfigModel.py b/CICD/src/SdnrConfigModel.py
new file mode 100644 (file)
index 0000000..a1e175c
--- /dev/null
@@ -0,0 +1,46 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+
+from pydantic import BaseModel, Field
+import base64
+
+class SdnrCredentials(BaseModel):
+    sdnr_username: str = Field(..., alias="sdnrUsername")
+    sdnr_password: str = Field(..., alias="sdnrPassword")
+    
+class SdnrConfig(BaseModel):
+    sdnr_host: str = Field(..., alias="sdnrHost")
+    sdnr_port: int = Field(..., alias="sdnrPort")
+    basic_auth: bool = Field(..., alias="basicAuth")
+    credentials: SdnrCredentials
+    #TODO: Enhance for Token based Authentication
+
+    def get_auth_token(self) -> str :
+        # Return base64 encoded value if authBasic or token if oauth
+        if self.basic_auth:
+            auth_bytes = (self.credentials.sdnr_username + ":" + self.credentials.sdnr_password).encode("utf-8")
+            auth_encoded = base64.b64encode(auth_bytes)
+            return auth_encoded.decode("utf-8") #return string
+        
+    def get_sdnr_headers(self) -> dict:
+        headers = {
+            'accept': 'application/json',
+            'authorization': 'Basic ' + self.get_auth_token()
+        }
+        return headers
+
+    def get_sdnr_url(self) -> str:
+        return "http://" + self.sdnr_host + ":" + str(self.sdnr_port)
\ No newline at end of file
diff --git a/CICD/src/TestPlanModel.py b/CICD/src/TestPlanModel.py
new file mode 100644 (file)
index 0000000..83cb796
--- /dev/null
@@ -0,0 +1,65 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+
+from typing import Optional
+from pydantic import BaseModel, Field
+
+# Class to validate the Test plan data (Ex: testplan.json)
+class Input (BaseModel):
+   req_type: str = Field(..., alias="reqType")
+   req_endpoint: str = Field(..., alias="reqEndPoint")
+   req_payload_file: str = Field(..., alias="reqPayloadFile")
+
+class Validation (BaseModel):
+   req_type: str = Field(..., alias="reqType")
+   req_endpoint: str = Field(..., alias="reqEndPoint")
+   criteria: object
+
+class Cleanup (BaseModel):
+   req_type: str = Field(..., alias="reqType")
+   req_endpoint: str = Field(..., alias="reqEndPoint")
+
+class TestPlan(BaseModel):
+   description: str
+   plan_id: str = Field(..., alias="plan-id")
+   input: Optional[Input] = None
+   exp_resp_code: Optional[int] = Field(None, alias="expectedRespCode")
+   validation: Validation = Field(..., alias="validation")
+   cleanup: Optional[Cleanup] = None
+
+   def get_validate_url(self):
+      return self.validation.req_endpoint
+   
+   def get_validate_req_type(self):
+      return self.validation.req_type
+
+   def get_cleanup_url(self):
+      return self.cleanup.req_endpoint
+   
+   def get_cleanup_req_type(self):
+      return self.cleanup.req_type
+   
+   def get_input_url(self):
+      return self.cleanup.req_endpoint
+   
+   def get_input_req_type(self):
+      return self.cleanup.req_type
+   
+   def get_input_payload_file(self):
+      return self.input.req_payload_file
+
+   def get_input(self):
+      return self.input
\ No newline at end of file
diff --git a/CICD/src/TestPlanRunner.py b/CICD/src/TestPlanRunner.py
new file mode 100644 (file)
index 0000000..fc93629
--- /dev/null
@@ -0,0 +1,121 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+
+from TestPlanModel import TestPlan
+import json
+import requests
+from pathlib import Path
+from SdnrConfigModel import SdnrConfig
+from TestSuiteConfig import TestSuiteConfig
+import logging
+from typing import Tuple
+import time
+
+logger = logging.getLogger("TestPlanRunner")
+
+class TestPlanRunner():
+    testplan: TestPlan
+    sdnrconfig: SdnrConfig
+    testsuite_cfg: TestSuiteConfig
+    create_success: bool = False
+    validate_success: bool = False
+    cleanup_success: bool = False
+
+    def __init__(self, testsuite_config: TestSuiteConfig, sdnrconfig: SdnrConfig, testplan: TestPlan):
+        self.testplan = testplan
+        self.sdnrconfig = sdnrconfig
+        self.testsuite_cfg = testsuite_config 
+                
+    def run(self) -> Tuple[bool, bool, bool]:
+        self.create_test()
+        if (self.create_success):
+            self.validate_test()
+        self.cleanup()
+        return (self.create_success, self.validate_success, self.cleanup_success)
+
+    def create_test(self):
+        logger.info("Creating Test - START")
+        logger.info("Test Description = %s",self.testplan.description)
+        logger.info("Test Plan Input = %s", self.testplan.get_input())
+        if (self.testplan.get_input() is not None):
+            logger.info("Request Type - %s",self.testplan.get_input_req_type())
+
+            payload_file = "testplans/"+self.testplan.get_input_payload_file()
+            payload = json.loads(Path(payload_file).read_text())
+            logger.info("Payload = %s", payload)
+            logger.info(self.sdnrconfig.get_sdnr_url() + self.testplan.get_input_url())
+            logger.info(self.sdnrconfig.get_sdnr_headers())
+            response = requests.put(self.sdnrconfig.get_sdnr_url() + self.testplan.get_input_url(), 
+                                    verify=True, 
+                                    data=json.dumps(payload), 
+                                    headers=self.sdnrconfig.get_sdnr_headers())
+            
+            if (response.status_code == requests.codes.created):
+                logger.info("Request Successful - Response code = %s", response.status_code)
+                self.create_success = True
+                time.sleep(10)
+            else:
+                logger.info("Creation failed - Reason - %s",response.status_code)
+                logger.info("Error Message - %s", response.json())
+            logger.info("Creating Test - END")
+        else:
+            logger.info("Nothing to CREATE, Proceeding for VALIDATION")
+            self.create_success = True
+
+    def validate_test(self) -> bool:
+        logger.info("Validating test - START")
+        # 1. get validation type and url
+        url = self.testplan.get_validate_url()
+        type = self.testplan.get_validate_req_type()
+        if type == "GET":
+            criteria = self.testplan.validation.criteria
+            for key in criteria.keys():
+                response = requests.get(self.sdnrconfig.get_sdnr_url() + url + key, 
+                                verify=True, 
+                                headers=self.sdnrconfig.get_sdnr_headers())
+                if (response.status_code == requests.codes.OK):
+                    logger.info("Response - %s", response.json())
+                    #TODO: Hard coding for now. 
+                    if response.json()["netconf-node-topology:connection-status"] == criteria[key]:
+                        logger.info("VALIDATION SUCCESS")
+                        self.validate_success = True
+                    else:
+                        logger.info("VALIDATION FAILED")
+              
+        logger.info("Validating test - END")
+
+
+    def cleanup(self):
+        logger.info("**** Cleanup - BEGIN ****")
+        logger.info("self.cleanup = %s", self.testplan.cleanup)
+        if (self.testplan.cleanup is not None):
+            cleanup_obj = self.testplan.cleanup
+            if cleanup_obj.req_type == "DELETE":
+                response = requests.delete(self.sdnrconfig.get_sdnr_url() + self.testplan.get_cleanup_url(),
+                                        verify=True,
+                                        headers=self.sdnrconfig.get_sdnr_headers())
+                if (response.status_code == requests.codes.NO_CONTENT):
+                    print("Cleanup successful")
+                    self.cleanup_success = True
+                else:
+                    logger.info("Cleanup failed")
+
+            logger.info("**** Cleanup - END ****")
+        else:
+            logger.info("Nothing to Clean")
+            self.cleanup_success = True
+
+        
\ No newline at end of file
diff --git a/CICD/src/TestSuiteConfig.py b/CICD/src/TestSuiteConfig.py
new file mode 100644 (file)
index 0000000..137b0b1
--- /dev/null
@@ -0,0 +1,21 @@
+################################################################################
+# Copyright 2025 highstreet technologies GmbH
+#
+# 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.
+#
+
+from pydantic import BaseModel, Field
+
+class TestSuiteConfig(BaseModel):
+    debug_level: str = Field(..., alias="debugLevel")
+    testplan_dir: str = Field(..., alias="testplanDir")
\ No newline at end of file
diff --git a/CICD/testplans/001/CREATE-MOUNTPOINT-TLS.json b/CICD/testplans/001/CREATE-MOUNTPOINT-TLS.json
new file mode 100644 (file)
index 0000000..d97ee22
--- /dev/null
@@ -0,0 +1,24 @@
+{
+    "network-topology:node": [
+        {
+            "node-id": "CICD-NON-CALLHOME-TLS-NODE",
+            "netconf-node-topology:netconf-node": {
+                "port": 65131,
+                "host": "10.20.11.161",
+                "key-based": {
+                   "username": "netconf",
+                   "key-id": "ODL_private_key_1"
+                },
+                "tcp-only": "false",
+                "reconnect-on-changed-schema": "false",
+                "connection-timeout-millis": 20000,
+                "max-connection-attempts": 100,
+                "protocol": {
+                   "name": "TLS",
+                   "key-id": ["ODL_private_key_1", "ODL_private_key_0"]
+                },
+                "keepalive-delay": 120
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/CICD/testplans/001/testplan.json b/CICD/testplans/001/testplan.json
new file mode 100644 (file)
index 0000000..70b7a3d
--- /dev/null
@@ -0,0 +1,21 @@
+{
+    "description": "Test case to mount a device via TLS and validate the ConnectionStatus",
+    "plan-id": "001",
+    "input": { 
+        "reqType": "PUT",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-NON-CALLHOME-TLS-NODE",
+        "reqPayloadFile" : "001/CREATE-MOUNTPOINT-TLS.json"
+    },
+    "expectedRespCode": 201,
+    "validation": {
+        "reqType": "GET",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-NON-CALLHOME-TLS-NODE/netconf-node-topology:netconf-node/",
+        "criteria": {
+            "connection-status": "connected"
+        }
+    },
+    "cleanup": {
+        "reqType": "DELETE",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-NON-CALLHOME-TLS-NODE"
+    }
+}
diff --git a/CICD/testplans/002/CREATE-MOUNTPOINT-SSH.json b/CICD/testplans/002/CREATE-MOUNTPOINT-SSH.json
new file mode 100644 (file)
index 0000000..142f564
--- /dev/null
@@ -0,0 +1,21 @@
+{
+    "network-topology:node": [
+        {
+            "node-id": "CICD-NON-CALLHOME-SSH-NODE",
+            "netconf-node-topology:netconf-node": {
+                "port": 8300,
+                "host": "10.20.11.161",
+                "login-password-unencrypted": {
+                    "username": "netconf",
+                    "password": "netconf!"
+                },
+                "tcp-only": "false",
+                "reconnect-on-changed-schema": "false",
+                "connection-timeout-millis": 20000,
+                "max-connection-attempts": 100,
+               
+                "keepalive-delay": 120
+            }
+        }
+    ]
+}
\ No newline at end of file
diff --git a/CICD/testplans/002/testplan.json b/CICD/testplans/002/testplan.json
new file mode 100644 (file)
index 0000000..51f4881
--- /dev/null
@@ -0,0 +1,21 @@
+{
+    "description": "Test case to mount a device via SSH and validate the ConnectionStatus",
+    "plan-id": "002",
+    "input": { 
+        "reqType": "PUT",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-NON-CALLHOME-SSH-NODE",
+        "reqPayloadFile" : "002/CREATE-MOUNTPOINT-SSH.json"
+    },
+    "expectedRespCode": 201,
+    "validation": {
+        "reqType": "GET",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-NON-CALLHOME-SSH-NODE/netconf-node-topology:netconf-node/",
+        "criteria": {
+            "connection-status": "connected"
+        }
+    },
+    "cleanup": {
+        "reqType": "DELETE",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-NON-CALLHOME-SSH-NODE"
+    }
+}
diff --git a/CICD/testplans/003/testplan.json b/CICD/testplans/003/testplan.json
new file mode 100644 (file)
index 0000000..df0b2b4
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "description": "Test case to validate a device mounted via callhome TLS",
+    "plan-id": "003",
+    "validation": {
+        "reqType": "GET",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-CALLHOME-TLS-NODE/netconf-node-topology:netconf-node/",
+        "criteria": {
+            "connection-status": "connected"
+        }
+    }
+}
diff --git a/CICD/testplans/004/testplan.json b/CICD/testplans/004/testplan.json
new file mode 100644 (file)
index 0000000..ceba7d2
--- /dev/null
@@ -0,0 +1,11 @@
+{
+    "description": "Test case to validate a device mounted via callhome SSH",
+    "plan-id": "004",
+    "validation": {
+        "reqType": "GET",
+        "reqEndPoint": "/rests/data/network-topology:network-topology/topology=topology-netconf/node=CICD-CALLHOME-SSH-NODE/netconf-node-topology:netconf-node/",
+        "criteria": {
+            "connection-status": "connected"
+        }
+    }
+}