diff options
32 files changed, 2558 insertions, 41 deletions
diff --git a/test/csit/plans/appc/healthcheck/bundle_query.sh b/test/csit/plans/appc/healthcheck/bundle_query.sh index a85bf31c4..3801d0a12 100755 --- a/test/csit/plans/appc/healthcheck/bundle_query.sh +++ b/test/csit/plans/appc/healthcheck/bundle_query.sh @@ -18,10 +18,10 @@ SCRIPTS="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" echo $SCRIPTS -num_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client -u karaf bundle:list | tail -1 | cut -d\| -f1) -#num_failed_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client -u karaf bundle:list | grep Failure | wc -l) -num_failed_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client -u karaf bundle:list | grep Failure | wc -l) -failed_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client -u karaf bundle:list | grep Failure) +num_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client bundle:list | tail -1 | cut -d\| -f1) +#num_failed_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client bundle:list | grep Failure | wc -l) +num_failed_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client bundle:list | grep Failure | wc -l) +failed_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client bundle:list | grep Failure) echo "There are $num_failed_bundles failed bundles out of $num_bundles installed bundles." diff --git a/test/csit/plans/appc/healthcheck/health_check.sh b/test/csit/plans/appc/healthcheck/health_check.sh index 63e0b17aa..e4cfae8f5 100755 --- a/test/csit/plans/appc/healthcheck/health_check.sh +++ b/test/csit/plans/appc/healthcheck/health_check.sh @@ -17,7 +17,7 @@ SCRIPTS="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" echo $SCRIPTS -response=$(curl --write-out '%{http_code}' --silent --output /dev/null -H "Authorization: Basic YWRtaW46S3A4Yko0U1hzek0wV1hsaGFrM2VIbGNzZTJnQXc4NHZhb0dHbUp2VXkyVQ==" -X POST -H "X-FromAppId: csit-appc" -H "X-TransactionId: csit-appc" -H "Accept: application/json" -H "Content-Type: application/json" http://localhost:8282/restconf/operations/SLI-API:healthcheck ) +response=$(curl --write-out '%{http_code}' --silent --output /dev/null -H "Authorization: Basic YWRtaW46YWRtaW4=" -X POST -H "X-FromAppId: csit-appc" -H "X-TransactionId: csit-appc" -H "Accept: application/json" -H "Content-Type: application/json" http://localhost:8282/restconf/operations/SLI-API:healthcheck ) if [ "$response" == "200" ]; then echo "APPC health check passed." diff --git a/test/csit/plans/appc/healthcheck/setup.sh b/test/csit/plans/appc/healthcheck/setup.sh index 406743303..f47685334 100755 --- a/test/csit/plans/appc/healthcheck/setup.sh +++ b/test/csit/plans/appc/healthcheck/setup.sh @@ -19,19 +19,11 @@ # Place the scripts in run order: SCRIPTS="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source ${WORKSPACE}/test/csit/scripts/appc/script1.sh -amsterdam="$(echo ${WORKSPACE} | grep amsterdam | wc -l)" -if [ "$amsterdam" != "1" ]; then - export APPC_DOCKER_IMAGE_VERSION=1.3.0-SNAPSHOT-latest - export CCSDK_DOCKER_IMAGE_VERSION=0.1-STAGING-latest - export BRANCH=master - export SOLUTION_NAME=onap -else - export APPC_DOCKER_IMAGE_VERSION=v1.2.0 - export CCSDK_DOCKER_IMAGE_VERSION=v0.1.0 - export BRANCH=amsterdam - export SOLUTION_NAME=openecomp -fi +export APPC_DOCKER_IMAGE_VERSION=1.3.0-SNAPSHOT-latest +export CCSDK_DOCKER_IMAGE_VERSION=0.2.1-SNAPSHOT +export BRANCH=master +export SOLUTION_NAME=onap export NEXUS_USERNAME=docker export NEXUS_PASSWD=docker @@ -67,8 +59,8 @@ TIME=0 while [ "$TIME" -lt "$TIME_OUT" ]; do startODL_status=$(docker exec appc_controller_container ps -e | grep startODL | wc -l) -waiting_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client -u karaf bundle:list | grep Waiting | wc -l) -run_level=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client -u karaf system:start-level) +waiting_bundles=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client bundle:list | grep Waiting | wc -l) +run_level=$(docker exec appc_controller_container /opt/opendaylight/current/bin/client system:start-level) if [ "$run_level" == "Level 100" ] && [ "$startODL_status" -lt "1" ] && [ "$waiting_bundles" -lt "1" ] ; then echo APPC started in $TIME seconds diff --git a/test/csit/plans/dmaap-buscontroller/with_mr/setup.sh b/test/csit/plans/dmaap-buscontroller/with_mr/setup.sh new file mode 100755 index 000000000..3e6efd4d3 --- /dev/null +++ b/test/csit/plans/dmaap-buscontroller/with_mr/setup.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# ============LICENSE_START======================================================= +# org.onap.dmaap +# ================================================================================ +# Copyright (C) 2018 AT&T 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========================================================= +# +# + + +# Place the scripts in run order: +source ${WORKSPACE}/test/csit/scripts/dmaap-message-router/dmaap-mr-launch.sh +dmaap_mr_launch +MRC_IP=${IP} + +source ${WORKSPACE}/test/csit/scripts/dmaap-buscontroller/start-mock.sh +start_mock "aaf" +AAF_IP=${IP} +start_mock "drps" +DRPS_IP=${IP} + +source ${WORKSPACE}/test/csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh +dmaapbc_launch $AAF_IP $MRC_IP $DRPS_IP +DMAAPBC_IP=${IP} + + +echo "AAF_IP=$AAF_IP MRC_IP=$MRC_IP DRPS_IP=$DRPS_IP DMAAPBC_IP=$DMAAPBC_IP" + +# Pass any variables required by Robot test suites in ROBOT_VARIABLES +ROBOT_VARIABLES="-v AAF_IP:${AAF_IP} -v MRC_IP:${MRC_IP} -v DRPS_IP:${DRPS_IP} -v DMAAPBC_IP:${DMAAPBC_IP}" + diff --git a/test/csit/plans/dmaap-buscontroller/with_mr/teardown.sh b/test/csit/plans/dmaap-buscontroller/with_mr/teardown.sh new file mode 100644 index 000000000..0474dded6 --- /dev/null +++ b/test/csit/plans/dmaap-buscontroller/with_mr/teardown.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# ============LICENSE_START======================================================= +# org.onap.dmaap +# ================================================================================ +# Copyright (C) 2018 AT&T 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========================================================= +# +# +source ${WORKSPACE}/test/csit/scripts/dmaap-message-router/dmaap-mr-teardown.sh + +dmaap_mr_teardown +kill-instance.sh aaf-mock +kill-instance.sh drps-mock +kill-instance.sh dmaapbc + diff --git a/test/csit/plans/dmaap-buscontroller/with_mr/testplan.txt b/test/csit/plans/dmaap-buscontroller/with_mr/testplan.txt new file mode 100644 index 000000000..39a2f4499 --- /dev/null +++ b/test/csit/plans/dmaap-buscontroller/with_mr/testplan.txt @@ -0,0 +1,4 @@ +# Test suites are relative paths under [integration.git]/test/csit/tests/. +# Place the suites in run order. +dmaap-buscontroller/suite1 + diff --git a/test/csit/plans/externalapi-nbi/healthcheck/setup.sh b/test/csit/plans/externalapi-nbi/healthcheck/setup.sh new file mode 100644 index 000000000..50da7ad6a --- /dev/null +++ b/test/csit/plans/externalapi-nbi/healthcheck/setup.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# ======================================================================== +# Copyright (c) 2018 Orange +# 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. +# ======================================================================== + +source ${WORKSPACE}/test/csit/scripts/externalapi-nbi/start_nbi_containers.sh diff --git a/test/csit/plans/externalapi-nbi/healthcheck/teardown.sh b/test/csit/plans/externalapi-nbi/healthcheck/teardown.sh new file mode 100644 index 000000000..83929723b --- /dev/null +++ b/test/csit/plans/externalapi-nbi/healthcheck/teardown.sh @@ -0,0 +1,17 @@ +#!/bin/bash +# ======================================================================== +# Copyright (c) 2018 Orange +# 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. +# ======================================================================== + +source ${WORKSPACE}/test/csit/scripts/externalapi-nbi/delete_nbi_containers.sh
\ No newline at end of file diff --git a/test/csit/plans/externalapi-nbi/healthcheck/testplan.txt b/test/csit/plans/externalapi-nbi/healthcheck/testplan.txt new file mode 100644 index 000000000..06622ae7b --- /dev/null +++ b/test/csit/plans/externalapi-nbi/healthcheck/testplan.txt @@ -0,0 +1,3 @@ +# Test suites are relative paths under [integration.git]/test/csit/tests/. +# Place the suites in run order. +externalapi-nbi/healthcheck diff --git a/test/csit/plans/vnfsdk-refrepo/sanity-check/setup.sh b/test/csit/plans/vnfsdk-refrepo/sanity-check/setup.sh index 1c3e4e83a..d1930cef5 100644 --- a/test/csit/plans/vnfsdk-refrepo/sanity-check/setup.sh +++ b/test/csit/plans/vnfsdk-refrepo/sanity-check/setup.sh @@ -18,12 +18,12 @@ #Start postgres database -docker run -d -i -t --name=postgres -p 5432:5432 nexus3.onap.org:10001/onap/refrepo/postgres:latest +docker run -d -i -t --name=postgres -p 5432:5432 nexus3.onap.org:10001/onap/vnfsdk/refrepo/postgres:latest POSTGRES=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' postgres` #Start market place -docker run -d -i -t --name=refrepo -e POSTGRES_IP=$POSTGRES -p 8702:8702 nexus3.onap.org:10001/onap/refrepo:1.1-STAGING-latest +docker run -d -i -t --name=refrepo -e POSTGRES_IP=$POSTGRES -p 8702:8702 nexus3.onap.org:10001/onap/vnfsdk/refrepo:1.1-STAGING-latest # Wait for Market place initialization echo Wait for VNF Repository initialization diff --git a/test/csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh b/test/csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh index 72c443850..5c349534b 100755 --- a/test/csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh +++ b/test/csit/scripts/dmaap-buscontroller/dmaapbc-launch.sh @@ -4,7 +4,7 @@ # sets global var IP with assigned IP address function dmaapbc_launch() { - TAG=onap/dmaap/buscontroller + TAG=onap/dmaap/buscontroller:latest CONTAINER_NAME=dmaapbc IP="" diff --git a/test/csit/scripts/dmaap-message-router/dmaap-mr-launch.sh b/test/csit/scripts/dmaap-message-router/dmaap-mr-launch.sh new file mode 100755 index 000000000..95fecff39 --- /dev/null +++ b/test/csit/scripts/dmaap-message-router/dmaap-mr-launch.sh @@ -0,0 +1,105 @@ +#!/bin/bash +# +# ============LICENSE_START======================================================= +# ONAP DMAAP MR +# ================================================================================ +# Copyright (C) 2018 AT&T 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============================================ +# =================================================================== +# ECOMP is a trademark and service mark of AT&T Intellectual Property. +# +# This script is a copy of plans/dmaap/mrpubsub/setup.sh, placed in the scripts +# dir, and edited to be a callable function from other plans. e.g. dmaap-buscontroller needs it. +# +source ${SCRIPTS}/common_functions.sh + +# function to launch DMaaP MR docker containers. +# sets global var IP with assigned IP address of MR container. +# (kafka and zk containers are not called externally) + +function dmaap_mr_launch() { + # Clone DMaaP Message Router repo + mkdir -p $WORKSPACE/archives/dmaapmr + cd $WORKSPACE/archives/dmaapmr + #unset http_proxy https_proxy + git clone --depth 1 http://gerrit.onap.org/r/dmaap/messagerouter/messageservice -b master + git pull + cd $WORKSPACE/archives/dmaapmr/messageservice/src/main/resources/docker-compose + cp $WORKSPACE/archives/dmaapmr/messageservice/bundleconfig-local/etc/appprops/MsgRtrApi.properties /var/tmp/ + + + # start DMaaP MR containers with docker compose and configuration from docker-compose.yml + docker login -u docker -p docker nexus3.onap.org:10001 + docker-compose up -d + + # Wait for initialization of Docker contaienr for DMaaP MR, Kafka and Zookeeper + for i in {1..50}; do + if [ $(docker inspect --format '{{ .State.Running }}' dockercompose_dmaap_1) ] && \ + [ $(docker inspect --format '{{ .State.Running }}' dockercompose_zookeeper_1) ] && \ + [ $(docker inspect --format '{{ .State.Running }}' dockercompose_dmaap_1) ] + then + echo "DMaaP Service Running" + break + else + echo sleep $i + sleep $i + fi + done + + + DMAAP_MR_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' dockercompose_dmaap_1) + IP=${DMAAP_MR_IP} + KAFKA_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' dockercompose_kafka_1) + ZOOKEEPER_IP=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' dockercompose_zookeeper_1) + + echo DMAAP_MR_IP=${DMAAP_MR_IP} + echo IP=${IP} + echo KAFKA_IP=${KAFKA_IP} + echo ZOOKEEPER_IP=${ZOOKEEPER_IP} + + # Initial docker-compose up and down is for populating kafka and zookeeper IPs in /var/tmp/MsgRtrApi.properites + docker-compose down + + # Update kafkfa and zookeeper properties in MsgRtrApi.propeties which will be copied to DMaaP Container + sed -i -e 's/<zookeeper_host>/'$ZOOKEEPER_IP'/' /var/tmp/MsgRtrApi.properties + sed -i -e 's/<kafka_host>:<kafka_port>/'$KAFKA_IP':9092/' /var/tmp/MsgRtrApi.properties + + docker-compose build + docker login -u docker -p docker nexus3.onap.org:10001 + docker-compose up -d + + # Wait for initialization of Docker containers + for i in {1..50}; do + if [ $(docker inspect --format '{{ .State.Running }}' dockercompose_dmaap_1) ] && \ + [ $(docker inspect --format '{{ .State.Running }}' dockercompose_zookeeper_1) ] && \ + [ $(docker inspect --format '{{ .State.Running }}' dockercompose_dmaap_1) ] + then + echo "DMaaP Service Running" + break + else + echo sleep $i + sleep $i + fi + done + + # Wait for initialization of docker services + for i in {1..50}; do + curl -sS -m 1 ${DMAAP_MR_IP}:3904/events/TestTopic && break + echo sleep $i + sleep $i + done +} + diff --git a/test/csit/scripts/dmaap-message-router/dmaap-mr-teardown.sh b/test/csit/scripts/dmaap-message-router/dmaap-mr-teardown.sh new file mode 100755 index 000000000..f57385707 --- /dev/null +++ b/test/csit/scripts/dmaap-message-router/dmaap-mr-teardown.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# +# Copyright 2016-2017 Huawei Technologies Co., Ltd. +# +# 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. +# +# Modifications copyright (c) 2018 AT&T Intellectual Property +# + +function dmaap_mr_teardown() { +kill-instance.sh dockercompose_dmaap_1 +kill-instance.sh dockercompose_kafka_1 +kill-instance.sh dockercompose_zookeeper_1 +} diff --git a/test/csit/scripts/externalapi-nbi/delete_nbi_containers.sh b/test/csit/scripts/externalapi-nbi/delete_nbi_containers.sh new file mode 100644 index 000000000..76be3289a --- /dev/null +++ b/test/csit/scripts/externalapi-nbi/delete_nbi_containers.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# ======================================================================== +# Copyright (c) 2018 Orange +# 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. +# ======================================================================== + +echo "This is ${WORKSPACE}/test/csit/scripts/externalapi-nbi/delete_nbi_containers.sh" + +# Check if docker-compose file exists +if [ ! -f "$WORKSPACE/externalapi-nbi/docker-compose.yml" ]; then + echo 'There is nothing to clean. Exiting...' >&2 + exit 0 +fi + +cd $WORKSPACE/externalapi-nbi + +# Remove containers and attached/anonymous volume(s) +docker-compose down -v +# Force stop & remove all containers and volumes +docker-compose rm -f -s -v + +# clean up +rm -rf $WORKSPACE/externalapi-nbi
\ No newline at end of file diff --git a/test/csit/scripts/externalapi-nbi/start_nbi_containers.sh b/test/csit/scripts/externalapi-nbi/start_nbi_containers.sh new file mode 100644 index 000000000..d1a026a67 --- /dev/null +++ b/test/csit/scripts/externalapi-nbi/start_nbi_containers.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# ======================================================================== +# Copyright (c) 2018 Orange +# 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. +# ======================================================================== + +NEXUS_USERNAME=docker +NEXUS_PASSWD=docker +NEXUS_DOCKER_REPO=nexus3.onap.org:10001 +DOCKER_IMAGE_VERSION=latest + +echo "This is ${WORKSPACE}/test/csit/scripts/externalapi-nbi/start_nbi_containers.sh" + +# Create directory +mkdir -p $WORKSPACE/externalapi-nbi +cd $WORKSPACE/externalapi-nbi + +# Fetch the latest docker-compose.yml +wget -O docker-compose.yml 'https://git.onap.org/externalapi/nbi/plain/docker-compose.yml?h=master' + +# Pull the nbi docker image from nexus +# MariaDB and mongoDB will be pulled automatically from docker.io during docker-compose +docker login -u $NEXUS_USERNAME -p $NEXUS_PASSWD $NEXUS_DOCKER_REPO +docker pull $NEXUS_DOCKER_REPO/onap/externalapi/nbi:$DOCKER_IMAGE_VERSION + +# Start nbi, MariaDB and MongoDB containers with docker compose and nbi/docker-compose.yml +docker-compose up -d mariadb mongo && sleep 5 # to ensure that these services are ready for connections +docker-compose up -d nbi diff --git a/test/csit/tests/externalapi-nbi/healthcheck/__init__.robot b/test/csit/tests/externalapi-nbi/healthcheck/__init__.robot new file mode 100644 index 000000000..826350785 --- /dev/null +++ b/test/csit/tests/externalapi-nbi/healthcheck/__init__.robot @@ -0,0 +1,2 @@ +*** Settings *** +Documentation ExternalAPI-NBI - healthcheck diff --git a/test/csit/tests/externalapi-nbi/healthcheck/healthcheck.robot b/test/csit/tests/externalapi-nbi/healthcheck/healthcheck.robot new file mode 100644 index 000000000..eb5a5bb4f --- /dev/null +++ b/test/csit/tests/externalapi-nbi/healthcheck/healthcheck.robot @@ -0,0 +1,29 @@ +*** Settings *** +Documentation The main interface for interacting with External API/NBI +Library RequestsLibrary + +*** Variables *** +${GLOBAL_NBI_SERVER_PROTOCOL} http +${GLOBAL_INJECTED_NBI_IP_ADDR} localhost +${GLOBAL_NBI_SERVER_PORT} 8080 +${NBI_HEALTH_CHECK_PATH} /nbi/api/v1/status +${NBI_ENDPOINT} ${GLOBAL_NBI_SERVER_PROTOCOL}://${GLOBAL_INJECTED_NBI_IP_ADDR}:${GLOBAL_NBI_SERVER_PORT} + + +*** Keywords *** +Run NBI Health Check + [Documentation] Runs NBI Health check + ${resp}= Run NBI Get Request ${NBI_HEALTH_CHECK_PATH} + Should Be Equal As Integers ${resp.status_code} 200 + +Run NBI Get Request + [Documentation] Runs NBI Get request + [Arguments] ${data_path} + ${session}= Create Session session ${NBI_ENDPOINT} + ${resp}= Get Request session ${data_path} + Should Be Equal As Integers ${resp.status_code} 200 + Log Received response from NBI ${resp.text} + ${json}= Set Variable ${resp.json()} + ${status}= Get From Dictionary ${json} status + Should Be Equal ${status} ok + [Return] ${resp}
\ No newline at end of file diff --git a/test/s3p/collector/get_resource_stats.py b/test/s3p/collector/get_resource_stats.py new file mode 100755 index 000000000..8ad22c575 --- /dev/null +++ b/test/s3p/collector/get_resource_stats.py @@ -0,0 +1,87 @@ +#!/usr/bin/python +import subprocess +import sys +import json +import datetime +import collections +import re +import tzlocal +from decimal import Decimal + +sys.path.append('../util') +import docker_util + +AAI1_NAME = "AAI1" +AAI2_NAME = "AAI2" +SO_NAME = "SO" +SDNC_NAME = "SDNC" +AAI1_IP = "10.0.1.1" +AAI2_IP = "10.0.1.2" +SO_IP = "10.0.5.1" +SDNC_IP = "10.0.7.1" + +def aai1(): + containers = docker_util.get_container_list(AAI1_IP) + run(AAI1_NAME, AAI1_IP, containers) + +def aai2(): + containers = docker_util.get_container_list(AAI2_IP) + run(AAI2_NAME, AAI2_IP, containers) + +def so(): + containers = docker_util.get_container_list(SO_IP) + run(SO_NAME, SO_IP, containers) + +def sdnc(): + containers = docker_util.get_container_list(SDNC_IP) + run(SDNC_NAME, SDNC_IP, containers) + +def run(component, ip, containers): + cmd = ["ssh", "-i", "onap_dev"] + cmd.append("ubuntu@" + ip) + cmd.append("sudo docker stats --no-stream") + for c in containers: + cmd.append(c) + ssh = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + result = ssh.stdout.readlines() + if result == []: + error = ssh.stderr.readlines() + print(error) + else: + result.pop(0) + for line in result: + token = line.decode('ascii').strip().split() + data = collections.OrderedDict() + data['datetime'] = datetime.datetime.now(tzlocal.get_localzone()).strftime("%Y-%m-%dT%H:%M:%S%Z") + data['component'] = component + data['container'] = token[0] + data['cpu'] = get_percent_number(token[1]) + data['memory'] = get_memory_number(token[2]) + data['physical'] = get_memory_number(token[4]) + data['mem_percent'] = get_percent_number(token[5]) + size = docker_util.get_container_volume_size(ip, data['container']) + if size is not None: + data['volume'] = size + file.write(json.dumps(data, default = myconverter) + "\n") + file.flush() + +def myconverter(o): + if isinstance(o, datetime.datetime): + return o.__str__() + +def get_percent_number(s): + return float(re.sub('[^0-9\.]', '', s)) + +def get_memory_number(s): + f = float(re.sub('[^0-9\.]', '', s)) + if s.endswith("GiB"): + f = f*1000 + return f + +file = open("resource.log", "w+") +while True: + so() + sdnc() + aai1() + aai2() diff --git a/test/s3p/generator/locustfile.py b/test/s3p/generator/locustfile.py new file mode 100644 index 000000000..63031cd78 --- /dev/null +++ b/test/s3p/generator/locustfile.py @@ -0,0 +1,186 @@ +import random +import string +import time +import datetime +import sys +import collections +import json +import tzlocal +import os +import fcntl +import logging +from locust import HttpLocust, TaskSet, task +from decimal import Decimal + + +class UserBehavior(TaskSet): + base = "/ecomp/mso/infra/e2eServiceInstances/v3" + headers = {"Accept":"application/json","Content-Type":"application/json","Authorization":"Basic SW5mcmFQb3J0YWxDbGllbnQ6cGFzc3dvcmQxJA=="} + service_creation_body = "{\"service\": {\"name\": \"E2E_volte_%s\", \"description\": \"E2E_volte_ONAP_deploy\", \"serviceDefId\": \"a16eb184-4a81-4c8c-89df-c287d390315a\", \"templateId\": \"012c3446-51db-4a2a-9e64-a936f10a5e3c\", \"parameters\": { \"globalSubscriberId\": \"Demonstration\", \"subscriberName\": \"Demonstration\", \"serviceType\": \"vIMS\", \"templateName\": \"VoLTE e2e Service:null\", \"resources\": [ { \"resourceName\": \"VL OVERLAYTUNNEL\", \"resourceDefId\": \"671d4757-b018-47ab-9df3-351c3bda0a98\", \"resourceId\": \"e859b0fd-d928-4cc8-969e-0fee7795d623\", \"nsParameters\": { \"locationConstraints\": [], \"additionalParamForNs\": { \"site2_vni\": \"5010\", \"site1_localNetworkAll\": \"false\", \"site1_vni\": \"5010\", \"site1_exportRT1\": \"11:1\", \"description\": \"overlay\", \"site2_localNetworkAll\": \"false\", \"site1_routerId\": \"9.9.9.9\", \"site1_fireWallEnable\": \"false\", \"site1_networkName\": \"network1\", \"site2_description\": \"overlay\", \"site1_importRT1\": \"11:1\", \"site1_description\": \"overlay\", \"site2_networkName\": \"network3\", \"name\": \"overlay\", \"site2_fireWallEnable\": \"false\", \"site2_id\": \"ZTE-DCI-Controller\", \"site2_routerId\": \"9.9.9.9\", \"site2_importRT1\": \"11:1\", \"site2_exportRT1\": \"11:1\", \"site2_fireWallId\": \"false\", \"site1_id\": \"DCI-Controller-1\", \"tunnelType\": \"L3-DCI\" } } },{\"resourceName\": \"VL UNDERLAYVPN\", \"resourceDefId\": \"4f5d692b-4022-43ab-b878-a93deb5b2061\", \"resourceId\": \"b977ec47-45b2-41f6-aa03-bf6554dc9620\", \"nsParameters\": { \"locationConstraints\": [], \"additionalParamForNs\": { \"topology\": \"full-mesh\", \"site2_name\": \"site2\", \"sna2_name\": \"site2_sna\", \"description\": \"underlay\", \"sna1_name\": \"site1_sna\", \"ac1_route\": \"3.3.3.12/30:dc84ce88-99f7\", \"ac2_peer_ip\": \"3.3.3.20/30\", \"technology\": \"mpls\", \"ac2_route\": \"3.3.3.20/30:98928302-3287\", \"ac2_id\": \"84d937a4-b227-375f-a744-2b778f36e04e\", \"ac1_protocol\": \"STATIC\", \"ac2_svlan\": \"4004\", \"serviceType\": \"l3vpn-ipwan\", \"ac2_ip\": \"3.3.3.21/30\", \"pe2_id\": \"4412d3f0-c296-314d-9284-b72fc5d485e8\", \"ac1_id\": \"b4f01ac0-c1e1-3e58-a8be-325e4372c960\", \"af_type\": \"ipv4\", \"ac1_svlan\": \"4002\", \"ac1_peer_ip\": \"3.3.3.12/30\", \"ac1_ip\": \"3.3.3.13/30\", \"version\": \"1.0\", \"name\": \"testunderlay\", \"id\": \"123124141\", \"pe1_id\": \"2ef788f0-407c-3070-b756-3a5cd71fde18\", \"ac2_protocol\": \"STATIC\", \"site1_name\": \"stie1\" } } } ] } } }" + # following class variables to make them unique across all users + transaction_file= open("transaction.log", "w+") + operation_file = open("operation.log", "w+") + + def on_start(self): + """ on_start is called when a Locust start before any task is scheduled """ + self.init() + + def init(self): + pass + + def myconverter(self, o): + if isinstance(o, datetime.datetime): + return o.__str__() + + @task(1) + def create_service(self): + # Post a E2E service instantiation request to SO + method = "POST" + url = self.base + service_instance_name = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(10)) + data = self.service_creation_body % service_instance_name + + t1 = datetime.datetime.now(tzlocal.get_localzone()) + response = self.client.request(method, url, headers=self.headers, data=data) + t2 = datetime.datetime.now(tzlocal.get_localzone()) + delta = t2 - t1 + data = collections.OrderedDict() + data['datetime'] = datetime.datetime.now(tzlocal.get_localzone()).strftime("%Y-%m-%dT%H:%M:%S%Z") + data['method'] = method + data['url'] = url + data['status_code'] = response.status_code + data['transaction_time'] = (delta.seconds*10^6 + delta.microseconds)/1000 + fcntl.flock(self.transaction_file, fcntl.LOCK_EX) + self.transaction_file.write(json.dumps(data, default = self.myconverter) + "\n") + self.transaction_file.flush() + os.fsync(self.transaction_file) + fcntl.flock(self.transaction_file, fcntl.LOCK_UN) + serviceId = response.json()['service']['serviceId'] + operationId = response.json()['service']['operationId'] + + # Get the request status + method = "GET" + url = self.base + "/" + serviceId + "/operations/" + operationId + url1 = "/ecomp/mso/infra/e2eServiceInstances/v3/{serviceId}/operations/{operationId}" + count = 1 + while count < 50: + tt1 = datetime.datetime.now() + response = self.client.request(method, url, name=url1, headers=self.headers) + tt2 = datetime.datetime.now() + delta = tt2 - tt1 + result = response.json()['operationStatus']['result'] + progress = response.json()['operationStatus']['progress'] + data = collections.OrderedDict() + data['datetime'] = datetime.datetime.now(tzlocal.get_localzone()).strftime("%Y-%m-%dT%H:%M:%S%Z") + data['method'] = method + data['url'] = url1 + data['status_code'] = response.status_code + data['count'] = count + data['result'] = result + data['progress'] = progress + data['transaction_time'] = (delta.seconds*10^6 + delta.microseconds)/1000 + fcntl.flock(self.transaction_file, fcntl.LOCK_EX) + self.transaction_file.write(json.dumps(data, default = self.myconverter) + "\n") + self.transaction_file.flush() + os.fsync(self.transaction_file) + fcntl.flock(self.transaction_file, fcntl.LOCK_UN) + if result == "finished" or result == "error": + break + else: + time.sleep(1) + count = count + 1 + + if result == "finished": + result = "success" + else: + result = "failure" + t3 = datetime.datetime.now(tzlocal.get_localzone()) + delta = t3 - t1 + data = collections.OrderedDict() + data['datetime'] = t1.strftime("%Y-%m-%dT%H:%M:%S%Z") + data['operation'] = "volte_create" + data['result'] = result + data['duration'] = round(delta.seconds + Decimal(delta.microseconds/1000000.0), 3) + fcntl.flock(self.operation_file, fcntl.LOCK_EX) + self.operation_file.write(json.dumps(data, default = self.myconverter) + "\n") + self.operation_file.flush() + os.fsync(self.operation_file) + fcntl.flock(self.operation_file, fcntl.LOCK_UN) + + self.delete_service(serviceId) + + def delete_service(self, serviceId): + method = "DELETE" + url = self.base + "/" + serviceId + data = "{\"globalSubscriberId\":\"Demonstration\", \"serviceType\":\"vIMS\"}" + t1 = datetime.datetime.now(tzlocal.get_localzone()) + response = self.client.request(method, url, name=self.base, headers=self.headers, data=data) + t2 = datetime.datetime.now(tzlocal.get_localzone()) + delta = t2 - t1 + data = collections.OrderedDict() + data['datetime'] = datetime.datetime.now(tzlocal.get_localzone()).strftime("%Y-%m-%dT%H:%M:%S%Z") + data['method'] = method + data['url'] = self.base + data['status_code'] = response.status_code + data['transaction_time'] = (delta.seconds*10^6 + delta.microseconds)/1000 + fcntl.flock(self.transaction_file, fcntl.LOCK_EX) + self.transaction_file.write(json.dumps(data, default = self.myconverter) + "\n") + self.transaction_file.flush() + os.fsync(self.transaction_file) + fcntl.flock(self.transaction_file, fcntl.LOCK_UN) + operationId = response.json()['operationId'] + + # Get the request status + method = "GET" + url = self.base + "/" + serviceId + "/operations/" + operationId + url1 = "/ecomp/mso/infra/e2eServiceInstances/v3/{serviceId}/operations/{operationId}" + count = 1 + while count < 50: + tt1 = datetime.datetime.now(tzlocal.get_localzone()) + response = self.client.request(method, url, name=url1, headers=self.headers) + tt2 = datetime.datetime.now(tzlocal.get_localzone()) + delta = tt2 - tt1 + result = response.json()['operationStatus']['result'] + progress = response.json()['operationStatus']['progress'] + data = collections.OrderedDict() + data['datetime'] = datetime.datetime.now(tzlocal.get_localzone()).strftime("%Y-%m-%dT%H:%M:%S%Z") + data['method'] = method + data['url'] = url1 + data['status_code'] = response.status_code + data['count'] = count + data['result'] = result + data['progress'] = progress + data['transaction_time'] = (delta.seconds*10^6 + delta.microseconds)/1000 + fcntl.flock(self.transaction_file, fcntl.LOCK_EX) + self.transaction_file.write(json.dumps(data, default = self.myconverter) + "\n") + self.transaction_file.flush() + os.fsync(self.transaction_file) + fcntl.flock(self.transaction_file, fcntl.LOCK_UN) + if result == "finished" or result == "error": + break + else: + time.sleep(1) + count = count + 1 + + if result == "finished": + result = "success" + else: + result = "failure" + t3 = datetime.datetime.now(tzlocal.get_localzone()) + delta = t3 - t1 + data = collections.OrderedDict() + data['datetime'] = t1.strftime("%Y-%m-%dT%H:%M:%S%Z") + data['operation'] = "volte_delete" + data['result'] = result + data['duration'] = round(delta.seconds + Decimal(delta.microseconds/1000000.0), 3) + fcntl.flock(self.operation_file, fcntl.LOCK_EX) + self.operation_file.write(json.dumps(data, default = self.myconverter) + "\n") + self.operation_file.flush() + os.fsync(self.operation_file) + fcntl.flock(self.operation_file, fcntl.LOCK_UN) + + +class WebsiteUser(HttpLocust): + task_set = UserBehavior + min_wait = 1000 + max_wait = 3000 diff --git a/test/s3p/mock/set_expectation.sh b/test/s3p/mock/set_expectation.sh new file mode 100755 index 000000000..e8c2c24b4 --- /dev/null +++ b/test/s3p/mock/set_expectation.sh @@ -0,0 +1,249 @@ +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "GET", + "path": "/api/huaweivnfmdriver/v1/swagger.json" + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "content-type": ["application/json"] + }, + "body": { + "not": false, + "type": "JSON", + "json": "{\"errcode\":\"0\",\"errmsg\":\"get token successfully.\",\"data\":{\"expiredDate\":\"2018-11-10 10:03:33\"}}" + } + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "POST", + "path": "/controller/v2/tokens" + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "content-type": ["application/json"] + }, + "body": { + "not": false, + "type": "JSON", + "json": "{\"errcode\":\"0\",\"errmsg\":\"get token successfully.\",\"data\":{\"expiredDate\":\"2018-11-10 10:03:33\",\"token_id\":\"7F06BFDDAC33A989:77DAD6058B1BB81EF1A557745E4D9C78399B31C4DB509704ED8A7DF05A362A59\"}}" + } + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "POST", + "path": "/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/vpn-services" + }, + "httpResponse": { + "statusCode": 201 + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "PUT", + "path": "/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/huawei-ac-net-l3vpn-svc-vfi:vrf-attributes" + }, + "httpResponse": { + "statusCode": 204 + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "POST", + "path": "/restconf/data/huawei-ac-net-l3vpn-svc:l3vpn-svc-cfg/sites" + }, + "httpResponse": { + "statusCode": 201 + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +# ZTE DCI +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "POST", + "path": "/v2.0/l3-dci-connects" + }, + "httpResponse": { + "statusCode": 201 + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +# huaweivnfmdriver +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "POST", + "path": "/api/huaweivnfmdriver/v1/a0400010-11d7-4875-b4ae-5f42ed5d3a85/vnfs" + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "content-type": ["application/json"] + }, + "body": { + "not": false, + "type": "JSON", + "json": "{\"vnfInstanceId\":\"fa3dca847b054f4eb9d3bc8bb9e5eec9\",\"jobId\":\"fa3dca847b054f4eb9d3bc8bb9e5eec9_post\"}" + } + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +# huaweivnfmdriver +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "GET", + "path": "/api/huaweivnfmdriver/v1/a0400010-11d7-4875-b4ae-5f42ed5d3a85/jobs/fa3dca847b054f4eb9d3bc8bb9e5eec9_post", + "queryStringParameters": { + "responseId": ["0"] + } + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "content-type": ["application/json"] + }, + "body": { + "not": false, + "type": "JSON", + "json": "{\"jobId\":\"fa3dca847b054f4eb9d3bc8bb9e5eec9\",\"responsedescriptor\":{\"progress\":\"50\",\"status\":\"processing\",\"errorCode\":null,\"responseId\":\"0\"}}" + } + }, + "times" : { + "unlimited" : false + }, + "timeToLive" : { + "unlimited" : true + } +}' + +# huaweivnfmdriver +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "GET", + "path": "/api/huaweivnfmdriver/v1/a0400010-11d7-4875-b4ae-5f42ed5d3a85/jobs/fa3dca847b054f4eb9d3bc8bb9e5eec9_post", + "queryStringParameters": { + "responseId": ["0"] + } + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "content-type": ["application/json"] + }, + "body": { + "not": false, + "type": "JSON", + "json": "{\"jobId\":\"fa3dca847b054f4eb9d3bc8bb9e5eec9\",\"responsedescriptor\":{\"progress\":\"100\",\"status\":\"processing\",\"errorCode\":null,\"responseId\":\"0\"}}" + } + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "GET", + "path": "/api/huaweivnfmdriver/v1/a0400010-11d7-4875-b4ae-5f42ed5d3a85/jobs/fa3dca847b054f4eb9d3bc8bb9e5eec9_post", + "queryStringParameters": { + "responseId": ["50"] + } + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "content-type": ["application/json"] + }, + "body": { + "not": false, + "type": "JSON", + "json": "{\"jobId\":\"fa3dca847b054f4eb9d3bc8bb9e5eec9\",\"responsedescriptor\":{\"progress\":\"100\",\"status\":\"processing\",\"errorCode\":null,\"responseId\":\"50\"}}" + } + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + + +curl -v -X PUT "http://localhost:1080/expectation" -d '{ + "httpRequest": { + "method": "GET", + "path": "/api/huaweivnfmdriver/v1/a0400010-11d7-4875-b4ae-5f42ed5d3a85/jobs/fa3dca847b054f4eb9d3bc8bb9e5eec9_post", + "queryStringParameters": { + "responseId": ["2"] + } + }, + "httpResponse": { + "statusCode": 200, + "headers": { + "content-type": ["application/json"] + }, + "body": { + "not": false, + "type": "JSON", + "json": "{\"jobId\":\"fa3dca847b054f4eb9d3bc8bb9e5eec9\",\"responsedescriptor\":{\"progress\":\"100\",\"status\":\"processing\",\"errorCode\":null,\"responseId\":\"2\"}}" + } + }, + "times" : { + "unlimited" : true + }, + "timeToLive" : { + "unlimited" : true + } +}' + diff --git a/test/vcpe/config_sdnc_so.py b/test/vcpe/config_sdnc_so.py new file mode 100755 index 000000000..660c70eb4 --- /dev/null +++ b/test/vcpe/config_sdnc_so.py @@ -0,0 +1,89 @@ +#! /usr/bin/python + +import logging +from vcpecommon import * +import csar_parser + + +def insert_customer_service_to_sdnc(vcpecommon): + """ + INSERT INTO SERVICE_MODEL (`service_uuid`, `model_yaml`,`invariant_uuid`,`version`,`name`,`description`,`type`,`category`,`ecomp_naming`,`service_instance_name_prefix`,`filename`,`naming_policy`) values ('7e319b6f-e710-440e-bbd2-63c1004949ef', null, 'a99ace8a-6e3b-447d-b2ff-4614e4234eea',null,'vCPEService', 'vCPEService', 'Service','Network L1-3', 'N', 'vCPEService', 'vCpeResCust110701/service-Vcperescust110701-template.yml',null); + INSERT INTO ALLOTTED_RESOURCE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`naming_policy`,`ecomp_generated_naming`,`depending_service`,`role`,`type`,`service_dependency`,`allotted_resource_type`) VALUES ( '7e40b664-d7bf-47ad-8f7c-615631d53cd7', NULL, 'f51b0aae-e24a-4cff-b190-fe3daf3d15ee', 'f3137496-1605-40e9-b6df-64aa0f8e91a0', '1.0', NULL,'Y',NULL,NULL,'TunnelXConnect',NULL, 'TunnelXConnect'); + INSERT INTO ALLOTTED_RESOURCE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`naming_policy`,`ecomp_generated_naming`,`depending_service`,`role`,`type`,`service_dependency`,`allotted_resource_type`) VALUES ( 'e46097e1-6a0c-4cf3-a7e5-c39ed407e65e', NULL, 'aa60f6ba-541b-48d6-a5ff-3b0e1f0ad9bf', '0e157d52-b945-422f-b3a8-ab685c2be079', '1.0', NULL,'Y',NULL,NULL,'BRG',NULL, 'TunnelXConnect'); + INSERT INTO VF_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`name`,`naming_policy`,`ecomp_generated_naming`,`avail_zone_max_count`,`nf_function`,`nf_code`,`nf_type`,`nf_role`,`vendor`,`vendor_version`) VALUES ( '3768afa5-cf9e-4071-bb25-3a2e2628dd87', NULL, '5f56893b-d026-4672-b785-7f5ffeb498c6', '7cf28b23-5719-485b-9ab4-dae1a2fa0e07', '1.0', 'vspvgw111601',NULL,'Y',1,NULL,NULL,NULL,NULL,'vCPE','1.0'); + INSERT INTO VF_MODULE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`vf_module_type`,`availability_zone_count`,`ecomp_generated_vm_assignments`) VALUES ( '17a9c7d1-6f8e-4930-aa83-0d323585184f', NULL, 'd772ddd1-7623-40b4-a2a5-ec287916cb51', '6e1a0652-f5e9-4caa-bff8-39bf0c8589a3', '1.0', 'Base',NULL,NULL); + + :return: + """ + logger = logging.getLogger('__name__') + logger.info('Inserting customer service data to SDNC DB') + csar_file = vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(csar_file) + cmds = [] + cmds.append("INSERT INTO SERVICE_MODEL (`service_uuid`, `model_yaml`,`invariant_uuid`,`version`,`name`," \ + "`description`,`type`,`category`,`ecomp_naming`,`service_instance_name_prefix`,`filename`," \ + "`naming_policy`) values ('{0}', null, '{1}',null,'{2}', 'vCPEService', 'Service','Network L1-3'," \ + "'N', 'vCPEService', '{3}/{4}',null);".format(parser.svc_model['modelVersionId'], + parser.svc_model['modelInvariantId'], + parser.svc_model['modelName'], + parser.svc_model['modelName'], + parser.svc_model['modelName'])) + + for model in parser.vnf_models: + if 'tunnel' in model['modelCustomizationName'].lower() or 'brg' in model['modelCustomizationName'].lower(): + cmds.append("INSERT INTO ALLOTTED_RESOURCE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`," \ + "`uuid`,`version`,`naming_policy`,`ecomp_generated_naming`,`depending_service`,`role`,`type`," \ + "`service_dependency`,`allotted_resource_type`) VALUES ('{0}',NULL,'{1}','{2}','1.0'," \ + "NULL,'Y', NULL,NULL,'TunnelXConnect'," \ + "NULL, 'TunnelXConnect');".format(model['modelCustomizationId'], model['modelInvariantId'], + model['modelVersionId'])) + else: + cmds.append("INSERT INTO VF_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`," \ + "`name`,`naming_policy`,`ecomp_generated_naming`,`avail_zone_max_count`,`nf_function`," \ + "`nf_code`,`nf_type`,`nf_role`,`vendor`,`vendor_version`) VALUES ('{0}',NULL,'{1}','{2}'," \ + "'1.0', '{3}',NULL,'Y',1,NULL,NULL,NULL,NULL,'vCPE'," \ + "'1.0');".format(model['modelCustomizationId'], model['modelInvariantId'], + model['modelVersionId'], model['modelCustomizationName'].split()[0])) + + model = parser.vfmodule_models[0] + cmds.append("INSERT INTO VF_MODULE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`," \ + "`vf_module_type`,`availability_zone_count`,`ecomp_generated_vm_assignments`) VALUES ('{0}', NULL," \ + "'{1}', '{2}', '1.0', 'Base',NULL,NULL)" \ + ";".format(model['modelCustomizationId'], model['modelInvariantId'], model['modelVersionId'])) + print('\n'.join(cmds)) + vcpecommon.insert_into_sdnc_db(cmds) + + +def insert_customer_service_to_so(vcpecommon): + logger = logging.getLogger('__name__') + logger.info('Inserting neutron HEAT template to SO DB and creating a recipe for customer service') + csar_file = vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(csar_file) + cmds = [] + cmds.append("INSERT INTO `service_recipe` (`ACTION`, `VERSION_STR`, `DESCRIPTION`, `ORCHESTRATION_URI`, " \ + "`SERVICE_PARAM_XSD`, `RECIPE_TIMEOUT`, `SERVICE_TIMEOUT_INTERIM`, `CREATION_TIMESTAMP`, " \ + "`SERVICE_MODEL_UUID`) VALUES ('createInstance','1','{0}'," \ + "'/mso/async/services/CreateVcpeResCustService',NULL,181,NULL, NOW()," \ + "'{1}');".format(parser.svc_model['modelName'], parser.svc_model['modelVersionId'])) + + cmds.append("delete from `heat_template_params` where" + "`HEAT_TEMPLATE_ARTIFACT_UUID`='efee1d84-b8ec-11e7-abc4-cec278b6b50a';") + cmds.append("delete from `heat_template` where ARTIFACT_UUID='efee1d84-b8ec-11e7-abc4-cec278b6b50a';") + network_tempalte_file = vcpecommon.find_file('neutron', 'yaml', 'preload_templates') + with open(network_tempalte_file, 'r') as fin: + lines = fin.readlines() + longtext = '\n'.join(lines) + cmds.append("INSERT INTO `heat_template`(`ARTIFACT_UUID`, `NAME`, `VERSION`, `BODY`, `TIMEOUT_MINUTES`, " \ + "`DESCRIPTION`, `CREATION_TIMESTAMP`, `ARTIFACT_CHECKSUM`) VALUES(" \ + "'efee1d84-b8ec-11e7-abc4-cec278b6b50a', 'Generic NeutronNet', '1', '{0}', 10, " \ + "'Generic Neutron Template', NOW(), 'MANUAL RECORD');".format(longtext)) + + cmds.append("INSERT INTO `heat_template_params`(`HEAT_TEMPLATE_ARTIFACT_UUID`, `PARAM_NAME`, `IS_REQUIRED`, " \ + "`PARAM_TYPE`, `PARAM_ALIAS`) VALUES('efee1d84-b8ec-11e7-abc4-cec278b6b50a', 'shared', 0, " \ + "'string', NULL);") + + print('\n'.join(cmds)) + vcpecommon.insert_into_so_db(cmds) + diff --git a/test/vcpe/csar_parser.py b/test/vcpe/csar_parser.py new file mode 100755 index 000000000..f101364d5 --- /dev/null +++ b/test/vcpe/csar_parser.py @@ -0,0 +1,231 @@ +#! /usr/bin/python +import os +import zipfile +import shutil +import yaml +import json +import logging + + +class CsarParser: + def __init__(self): + self.logger = logging.getLogger(__name__) + self.svc_model = {} + self.net_models = [] # there could be multiple networks + self.vnf_models = [] # this version only support a single VNF in the service template + self.vfmodule_models = [] # this version only support a single VF module in the service template + + def get_service_yaml_from_csar(self, csar_file): + """ + :param csar_file: csar file path name, e.g. 'csar/vgmux.csar' + :return: + """ + tmpdir = './__tmp' + if os.path.isdir(tmpdir): + shutil.rmtree(tmpdir) + os.mkdir(tmpdir) + + with zipfile.ZipFile(csar_file, "r") as zip_ref: + zip_ref.extractall(tmpdir) + + yamldir = tmpdir + '/Definitions' + if os.path.isdir(yamldir): + for filename in os.listdir(yamldir): + # look for service template like this: service-Vcpesvcbrgemu111601-template.yml + if filename.startswith('service-') and filename.endswith('-template.yml'): + return os.path.join(yamldir, filename) + + self.logger.error('Invalid file: ' + csar_file) + return '' + + def get_service_model_info(self, svc_template): + """ extract service model info from yaml and convert to what to be used in SO request + Sample from yaml: + { + "UUID": "aed4fc5e-b871-4e26-8531-ceabd46df85e", + "category": "Network L1-3", + "description": "Infra service", + "ecompGeneratedNaming": true, + "invariantUUID": "c806682a-5b3a-44d8-9e88-0708be151296", + "name": "vcpesvcinfra111601", + "namingPolicy": "", + "serviceEcompNaming": true, + "serviceRole": "", + "serviceType": "", + "type": "Service" + }, + + Convert to + { + "modelType": "service", + "modelInvariantId": "ca4c7a70-06fd-45d8-8b9e-c9745d25bf2b", + "modelVersionId": "5d8911b4-e50c-4096-a81e-727a8157193c", + "modelName": "vcpesvcbrgemu111601", + "modelVersion": "1.0" + }, + + """ + if svc_template['metadata']['type'] != 'Service': + self.logger.error('csar error: metadata->type is not Service') + return + + metadata = svc_template['metadata'] + self.svc_model = { + 'modelType': 'service', + 'modelInvariantId': metadata['invariantUUID'], + 'modelVersionId': metadata['UUID'], + 'modelName': metadata['name'] + } + if 'version' in metadata: + self.svc_model['modelVersion'] = metadata['version'] + else: + self.svc_model['modelVersion'] = '1.0' + + def get_vnf_and_network_model_info(self, svc_template): + """ extract vnf and network model info from yaml and convert to what to be used in SO request + Sample from yaml: + "topology_template": { + "node_templates": { + "CPE_PUBLIC": { + "metadata": { + "UUID": "33b2c367-a165-4bb3-81c3-0150cd06ceff", + "category": "Generic", + "customizationUUID": "db1d4ac2-62cd-4e5d-b2dc-300dbd1a5da1", + "description": "Generic NeutronNet", + "invariantUUID": "3d4c0e47-4794-4e98-a794-baaced668930", + "name": "Generic NeutronNet", + "resourceVendor": "ATT (Tosca)", + "resourceVendorModelNumber": "", + "resourceVendorRelease": "1.0.0.wd03", + "subcategory": "Network Elements", + "type": "VL", + "version": "1.0" + }, + "type": "org.openecomp.resource.vl.GenericNeutronNet" + }, + Convert to + { + "modelType": "network", + "modelInvariantId": "3d4c0e47-4794-4e98-a794-baaced668930", + "modelVersionId": "33b2c367-a165-4bb3-81c3-0150cd06ceff", + "modelName": "Generic NeutronNet", + "modelVersion": "1.0", + "modelCustomizationId": "db1d4ac2-62cd-4e5d-b2dc-300dbd1a5da1", + "modelCustomizationName": "CPE_PUBLIC" + }, + """ + node_dic = svc_template['topology_template']['node_templates'] + for node_name, v in node_dic.items(): + model = { + 'modelInvariantId': v['metadata']['invariantUUID'], + 'modelVersionId': v['metadata']['UUID'], + 'modelName': v['metadata']['name'], + 'modelVersion': v['metadata']['version'], + 'modelCustomizationId': v['metadata']['customizationUUID'], + 'modelCustomizationName': node_name + } + + if v['type'].startswith('org.openecomp.resource.vl.GenericNeutronNet'): + # a neutron network is found + self.logger.info('Parser found a network: ' + node_name) + model['modelType'] = 'network' + self.net_models.append(model) + elif v['type'].startswith('org.openecomp.resource.vf.'): + # a VNF is found + self.logger.info('Parser found a VNF: ' + node_name) + model['modelType'] = 'vnf' + self.vnf_models.append(model) + else: + self.logger.warning('Parser found a node that is neither a network nor a VNF: ' + node_name) + + def get_vfmodule_model_info(self, svc_template): + """ extract network model info from yaml and convert to what to be used in SO request + Sample from yaml: + "topology_template": { + "groups": { + "vspinfra1116010..Vspinfra111601..base_vcpe_infra..module-0": { + "metadata": { + "vfModuleModelCustomizationUUID": "11ddac51-30e3-4a3f-92eb-2eb99c2cb288", + "vfModuleModelInvariantUUID": "02f70416-581e-4f00-bde1-d65e69af95c5", + "vfModuleModelName": "Vspinfra111601..base_vcpe_infra..module-0", + "vfModuleModelUUID": "88c78078-f1fd-4f73-bdd9-10420b0f6353", + "vfModuleModelVersion": "1" + }, + "properties": { + "availability_zone_count": null, + "initial_count": 1, + "max_vf_module_instances": 1, + "min_vf_module_instances": 1, + "vf_module_description": null, + "vf_module_label": "base_vcpe_infra", + "vf_module_type": "Base", + "vfc_list": null, + "volume_group": false + }, + "type": "org.openecomp.groups.VfModule" + } + }, + Convert to + { + "modelType": "vfModule", + "modelInvariantId": "02f70416-581e-4f00-bde1-d65e69af95c5", + "modelVersionId": "88c78078-f1fd-4f73-bdd9-10420b0f6353", + "modelName": "Vspinfra111601..base_vcpe_infra..module-0", + "modelVersion": "1", + "modelCustomizationId": "11ddac51-30e3-4a3f-92eb-2eb99c2cb288", + "modelCustomizationName": "Vspinfra111601..base_vcpe_infra..module-0" + }, + """ + node_dic = svc_template['topology_template']['groups'] + for node_name, v in node_dic.items(): + if v['type'].startswith('org.openecomp.groups.VfModule'): + model = { + 'modelType': 'vfModule', + 'modelInvariantId': v['metadata']['vfModuleModelInvariantUUID'], + 'modelVersionId': v['metadata']['vfModuleModelUUID'], + 'modelName': v['metadata']['vfModuleModelName'], + 'modelVersion': v['metadata']['vfModuleModelVersion'], + 'modelCustomizationId': v['metadata']['vfModuleModelCustomizationUUID'], + 'modelCustomizationName': v['metadata']['vfModuleModelName'] + } + self.vfmodule_models.append(model) + self.logger.info('Parser found a VF module: ' + model['modelCustomizationName']) + + def parse_service_yaml(self, filename): + # clean up + self.svc_model = {} + self.net_models = [] # there could be multiple networks + self.vnf_models = [] # this version only support a single VNF in the service template + self.vfmodule_models = [] # this version only support a single VF module in the service template + + svc_template = yaml.load(file(filename, 'r')) + self.get_service_model_info(svc_template) + self.get_vnf_and_network_model_info(svc_template) + self.get_vfmodule_model_info(svc_template) + + return True + + def parse_csar(self, csar_file): + yaml_file = self.get_service_yaml_from_csar(csar_file) + if yaml_file != '': + return self.parse_service_yaml(yaml_file) + + def print_models(self): + print('---------Service Model----------') + print(json.dumps(self.svc_model, indent=2, sort_keys=True)) + + print('---------Network Model(s)----------') + for model in self.net_models: + print(json.dumps(model, indent=2, sort_keys=True)) + + print('---------VNF Model(s)----------') + for model in self.vnf_models: + print(json.dumps(model, indent=2, sort_keys=True)) + + print('---------VF Module Model(s)----------') + for model in self.vfmodule_models: + print(json.dumps(model, indent=2, sort_keys=True)) + + def test(self): + self.parse_csar('csar/service-Vcpesvcinfra111601-csar.csar') + self.print_models() diff --git a/test/vcpe/get_info.py b/test/vcpe/get_info.py new file mode 100755 index 000000000..5b0c6879b --- /dev/null +++ b/test/vcpe/get_info.py @@ -0,0 +1,26 @@ +#! /usr/bin/python + +import time +import logging +import json +import mysql.connector +import ipaddress +import re +import sys +import base64 +from vcpecommon import * +import preload +import vcpe_custom_service + + +logging.basicConfig(level=logging.INFO, format='%(message)s') + +vcpecommon = VcpeCommon() +nodes=['brg', 'bng', 'mux', 'dhcp'] +hosts = vcpecommon.get_vm_ip(nodes) +print(json.dumps(hosts, indent=4, sort_keys=True)) + + + + + diff --git a/test/vcpe/healthcheck.py b/test/vcpe/healthcheck.py new file mode 100755 index 000000000..b94848e13 --- /dev/null +++ b/test/vcpe/healthcheck.py @@ -0,0 +1,30 @@ +#! /usr/bin/python + +import logging +import json +from vcpecommon import * +import commands + + +logging.basicConfig(level=logging.INFO, format='%(message)s') +common = VcpeCommon() + +print('Checking vGMUX REST API from SDNC') +cmd = 'curl -u admin:admin -X GET http://10.0.101.21:8183/restconf/config/ietf-interfaces:interfaces' +ret = commands.getstatusoutput("ssh -i onap_dev root@sdnc '{0}'".format(cmd)) +sz = ret[-1].split('\n')[-1] +print('\n') +print(sz) + +print('Checking vBRG REST API from SDNC') +cmd = 'curl -u admin:admin -X GET http://10.3.0.2:8183/restconf/config/ietf-interfaces:interfaces' +ret = commands.getstatusoutput("ssh -i onap_dev root@sdnc '{0}'".format(cmd)) +sz = ret[-1].split('\n')[-1] +print('\n') +print(sz) + +print('Checking SDNC DB for vBRG MAC address') +mac = common.get_brg_mac_from_sdnc() +print(mac) + + diff --git a/test/vcpe/loop.py b/test/vcpe/loop.py new file mode 100755 index 000000000..ad5879715 --- /dev/null +++ b/test/vcpe/loop.py @@ -0,0 +1,37 @@ +#! /usr/bin/python + +import time +import logging +import json +import mysql.connector +import ipaddress +import re +import sys +import base64 +from vcpecommon import * +import preload +import commands +import vcpe_custom_service + + +logging.basicConfig(level=logging.INFO, format='%(message)s') + +cpecommon = VcpeCommon() +custom = vcpe_custom_service.CustomService(cpecommon) + +nodes=['mux'] +hosts = cpecommon.get_vm_ip(nodes) + +custom.del_vgmux_ves_mode(hosts['mux']) +time.sleep(2) +custom.del_vgmux_ves_collector(hosts['mux']) +exit() + +time.sleep(2) +logging.info('Setting vGMUX DCAE collector IP address') +custom.set_vgmux_ves_collector(hosts['mux']) +time.sleep(2) +vgmux_vnf_name = cpecommon.load_object('vgmux_vnf_name') +logging.info('vGMUX VNF instance name is %s', vgmux_vnf_name) +logging.info('Letting vGMUX report packet loss to DCAE') +custom.set_vgmux_packet_loss_rate(hosts['mux'], 55, vgmux_vnf_name) diff --git a/test/vcpe/preload.py b/test/vcpe/preload.py new file mode 100755 index 000000000..c4efafde6 --- /dev/null +++ b/test/vcpe/preload.py @@ -0,0 +1,216 @@ +#! /usr/bin/python + +import requests +import json +import sys +from datetime import datetime +from vcpecommon import * +import csar_parser +import logging +import base64 + + +class Preload: + def __init__(self, vcpecommon): + self.logger = logging.getLogger(__name__) + self.vcpecommon = vcpecommon + + def replace(self, sz, replace_dict): + for old_string, new_string in replace_dict.items(): + sz = sz.replace(old_string, new_string) + if self.vcpecommon.template_variable_symbol in sz: + self.logger.error('Error! Cannot find a value to replace ' + sz) + return sz + + def generate_json(self, template_file, replace_dict): + with open(template_file) as json_input: + json_data = json.load(json_input) + stk = [json_data] + while len(stk) > 0: + data = stk.pop() + for k, v in data.items(): + if type(v) is dict: + stk.append(v) + elif type(v) is list: + stk.extend(v) + elif type(v) is str or type(v) is unicode: + if self.vcpecommon.template_variable_symbol in v: + data[k] = self.replace(v, replace_dict) + else: + self.logger.warning('Unexpected line in template: %s. Look for value %s', template_file, v) + return json_data + + def reset_sniro(self): + self.logger.debug('Clearing SNIRO data') + r = requests.post(self.vcpecommon.sniro_url + '/reset', headers=self.vcpecommon.sniro_headers) + if 2 != r.status_code / 100: + self.logger.debug(r.content) + self.logger.error('Clearing SNIRO date failed.') + sys.exit() + + def preload_sniro(self, template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, vbrg_ar_name, + vgmux_svc_instance_uuid, vbrg_svc_instance_uuid): + self.reset_sniro() + self.logger.info('Preloading SNIRO for homing service') + replace_dict = {'${tunnelxconn_ar_name}': tunnelxconn_ar_name, + '${vgw_name}': vgw_name, + '${brg_ar_name}': vbrg_ar_name, + '${vgmux_svc_instance_uuid}': vgmux_svc_instance_uuid, + '${vbrg_svc_instance_uuid}': vbrg_svc_instance_uuid + } + sniro_data = self.generate_json(template_sniro_data, replace_dict) + self.logger.debug('SNIRO data:') + self.logger.debug(json.dumps(sniro_data, indent=4, sort_keys=True)) + + base64_sniro_data = base64.b64encode(json.dumps(sniro_data)) + self.logger.debug('SNIRO data: 64') + self.logger.debug(base64_sniro_data) + replace_dict = {'${base64_sniro_data}': base64_sniro_data, '${sniro_ip}': self.vcpecommon.hosts['robot']} + sniro_request = self.generate_json(template_sniro_request, replace_dict) + self.logger.debug('SNIRO request:') + self.logger.debug(json.dumps(sniro_request, indent=4, sort_keys=True)) + + r = requests.post(self.vcpecommon.sniro_url, headers=self.vcpecommon.sniro_headers, json=sniro_request) + if 2 != r.status_code / 100: + response = r.json() + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.error('SNIRO preloading failed.') + sys.exit() + + return True + + def preload_network(self, template_file, network_role, subnet_start_ip, subnet_gateway, common_dict, name_suffix): + """ + :param template_file: + :param network_role: cpe_signal, cpe_public, brg_bng, bng_mux, mux_gw + :param subnet_start_ip: + :param subnet_gateway: + :param name_suffix: e.g. '201711201311' + :return: + """ + network_name = '_'.join([self.vcpecommon.instance_name_prefix['network'], network_role.lower(), name_suffix]) + subnet_name = self.vcpecommon.network_name_to_subnet_name(network_name) + common_dict['${' + network_role+'_net}'] = network_name + common_dict['${' + network_role+'_subnet}'] = subnet_name + replace_dict = {'${network_role}': network_role, + '${service_type}': 'vCPE', + '${network_type}': 'Generic NeutronNet', + '${network_name}': network_name, + '${subnet_start_ip}': subnet_start_ip, + '${subnet_gateway}': subnet_gateway + } + self.logger.info('Preloading network ' + network_role) + return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_network_url) + + def preload(self, template_file, replace_dict, url): + json_data = self.generate_json(template_file, replace_dict) + self.logger.debug(json.dumps(json_data, indent=4, sort_keys=True)) + r = requests.post(url, headers=self.vcpecommon.sdnc_headers, auth=self.vcpecommon.sdnc_userpass, json=json_data) + response = r.json() + if int(response.get('output', {}).get('response-code', 0)) != 200: + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.error('Preloading failed.') + return False + return True + + def preload_vgw(self, template_file, brg_mac, commont_dict, name_suffix): + replace_dict = {'${brg_mac}': brg_mac, + '${suffix}': name_suffix + } + replace_dict.update(commont_dict) + self.logger.info('Preloading vGW') + return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url) + + def preload_vfmodule(self, template_file, service_instance_id, vnf_model, vfmodule_model, common_dict, name_suffix): + """ + :param template_file: + :param service_instance_id: + :param vnf_model: parsing results from csar_parser + :param vfmodule_model: parsing results from csar_parser + :param common_dict: + :param name_suffix: + :return: + """ + + # examples: + # vfmodule_model['modelCustomizationName']: "Vspinfra111601..base_vcpe_infra..module-0", + # vnf_model['modelCustomizationName']: "vspinfra111601 0", + + vfmodule_name = '_'.join([self.vcpecommon.instance_name_prefix['vfmodule'], + vfmodule_model['modelCustomizationName'].split('..')[0].lower(), name_suffix]) + + # vnf_type and generic_vnf_type are identical + replace_dict = {'${vnf_type}': vfmodule_model['modelCustomizationName'], + '${generic_vnf_type}': vfmodule_model['modelCustomizationName'], + '${service_type}': service_instance_id, + '${generic_vnf_name}': vnf_model['modelCustomizationName'], + '${vnf_name}': vfmodule_name, + '${suffix}': name_suffix} + replace_dict.update(common_dict) + self.logger.info('Preloading VF Module ' + vfmodule_name) + return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url) + + def preload_all_networks(self, template_file, name_suffix): + common_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()} + for network, v in self.vcpecommon.preload_network_config.items(): + subnet_start_ip, subnet_gateway_ip = v + if not self.preload_network(template_file, network, subnet_start_ip, subnet_gateway_ip, + common_dict, name_suffix): + return None + return common_dict + + def test(self): + # this is for testing purpose + name_suffix = datetime.now().strftime('%Y%m%d%H%M') + vcpecommon = VcpeCommon() + preloader = Preload(vcpecommon) + + network_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()} + template_file = 'preload_templates/template.network.json' + for k, v in self.vcpecommon.preload_network_config.items(): + if not preloader.preload_network(template_file, k, v[0], v[1], network_dict, name_suffix): + break + + print('---------------------------------------------------------------') + print('Network related replacement dictionary:') + print(json.dumps(network_dict, indent=4, sort_keys=True)) + print('---------------------------------------------------------------') + + keys = ['infra', 'bng', 'gmux', 'brg'] + for key in keys: + csar_file = self.vcpecommon.find_file(key, 'csar', 'csar') + template_file = self.vcpecommon.find_file(key, 'json', 'preload_templates') + if csar_file and template_file: + parser = csar_parser.CsarParser() + parser.parse_csar(csar_file) + service_instance_id = 'test112233' + preloader.preload_vfmodule(template_file, service_instance_id, parser.vnf_models[0], + parser.vfmodule_models[0], network_dict, name_suffix) + + def test_sniro(self): + template_sniro_data = self.vcpecommon.find_file('sniro_data', 'json', 'preload_templates') + template_sniro_request = self.vcpecommon.find_file('sniro_request', 'json', 'preload_templates') + + vcperescust_csar = self.vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(vcperescust_csar) + tunnelxconn_ar_name = None + brg_ar_name = None + vgw_name = None + for model in parser.vnf_models: + if 'tunnel' in model['modelCustomizationName']: + tunnelxconn_ar_name = model['modelCustomizationName'] + elif 'brg' in model['modelCustomizationName']: + brg_ar_name = model['modelCustomizationName'] + elif 'vgw' in model['modelCustomizationName']: + vgw_name = model['modelCustomizationName'] + + if not (tunnelxconn_ar_name and brg_ar_name and vgw_name): + self.logger.error('Cannot find all names from %s.', vcperescust_csar) + sys.exit() + + vgmux_svc_instance_uuid = '88888888888888' + vbrg_svc_instance_uuid = '999999999999999' + + self.preload_sniro(template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, brg_ar_name, + vgmux_svc_instance_uuid, vbrg_svc_instance_uuid) diff --git a/test/vcpe/soutils.py b/test/vcpe/soutils.py new file mode 100755 index 000000000..cc82068a6 --- /dev/null +++ b/test/vcpe/soutils.py @@ -0,0 +1,318 @@ +#! /usr/bin/python + +import sys +import logging +import requests +import json +from datetime import datetime +import progressbar +import time +import csar_parser +import preload +from vcpecommon import * + + +class SoUtils: + def __init__(self, vcpecommon, api_version): + """ + :param vcpecommon: + :param api_version: must be 'v4' or 'v5' + """ + self.logger = logging.getLogger(__name__) + self.vcpecommon = vcpecommon + if api_version not in self.vcpecommon.so_req_api_url: + self.logger.error('Incorrect SO API version: %s', api_version) + sys.exit() + self.service_req_api_url = self.vcpecommon.so_req_api_url[api_version] + + def submit_create_req(self, req_json, req_type, service_instance_id=None, vnf_instance_id=None): + """ + POST {serverRoot}/serviceInstances/v4 + POST {serverRoot}/serviceInstances/v4/{serviceInstanceId}/vnfs + POST {serverRoot}/serviceInstances/v4/{serviceInstanceId}/networks + POST {serverRoot}/serviceInstances/v4/{serviceInstanceId}/vnfs/{vnfInstanceId}/vfModules + :param req_json: + :param service_instance_id: this is required only for networks, vnfs, and vf modules + :param req_type: + :param vnf_instance_id: + :return: req_id, instance_id + """ + if req_type == 'service': + url = self.service_req_api_url + elif req_type == 'vnf': + url = '/'.join([self.service_req_api_url, service_instance_id, 'vnfs']) + elif req_type == 'network': + url = '/'.join([self.service_req_api_url, service_instance_id, 'networks']) + elif req_type == 'vfmodule': + url = '/'.join([self.service_req_api_url, service_instance_id, 'vnfs', vnf_instance_id, 'vfModules']) + else: + self.logger.error('Invalid request type: {0}. Can only be service/vnf/network/vfmodule'.format(req_type)) + return None, None + + r = requests.post(url, headers=self.vcpecommon.so_headers, auth=self.vcpecommon.so_userpass, json=req_json) + response = r.json() + + self.logger.debug('---------------------------------------------------------------') + self.logger.debug('------- Creation request submitted to SO, got response --------') + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.debug('---------------------------------------------------------------') + req_id = response.get('requestReferences', {}).get('requestId', '') + instance_id = response.get('requestReferences', {}).get('instanceId', '') + return req_id, instance_id + + def check_progress(self, req_id, eta=0, interval=5): + if not req_id: + self.logger.error('Error when checking SO request progress, invalid request ID: ' + req_id) + return False + duration = 0.0 + bar = progressbar.ProgressBar(redirect_stdout=True) + url = self.vcpecommon.so_check_progress_api_url + '/' + req_id + + while True: + time.sleep(interval) + r = requests.get(url, headers=self.vcpecommon.so_headers, auth=self.vcpecommon.so_userpass) + response = r.json() + + duration += interval + if eta > 0: + percentage = min(95, 100 * duration / eta) + else: + percentage = int(response['request']['requestStatus']['percentProgress']) + + if response['request']['requestStatus']['requestState'] == 'IN_PROGRESS': + self.logger.debug('------------------Request Status-------------------------------') + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + bar.update(percentage) + else: + self.logger.debug('---------------------------------------------------------------') + self.logger.debug('----------------- Creation Request Results --------------------') + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.debug('---------------------------------------------------------------') + flag = response['request']['requestStatus']['requestState'] == 'COMPLETE' + if not flag: + self.logger.error('Request failed.') + self.logger.error(json.dumps(response, indent=4, sort_keys=True)) + bar.update(100) + bar.finish() + return flag + + def add_req_info(self, req_details, instance_name, product_family_id=None): + req_details['requestInfo'] = { + 'instanceName': instance_name, + 'source': 'VID', + 'suppressRollback': 'true', + 'requestorId': 'vCPE-Robot' + } + if product_family_id: + req_details['requestInfo']['productFamilyId'] = product_family_id + + def add_related_instance(self, req_details, instance_id, instance_model): + instance = {"instanceId": instance_id, "modelInfo": instance_model} + if 'relatedInstanceList' not in req_details: + req_details['relatedInstanceList'] = [{"relatedInstance": instance}] + else: + req_details['relatedInstanceList'].append({"relatedInstance": instance}) + + def generate_vnf_or_network_request(self, req_type, instance_name, vnf_or_network_model, service_instance_id, + service_model): + req_details = { + 'modelInfo': vnf_or_network_model, + 'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name, + "tenantId": self.vcpecommon.os_tenant_id}, + 'requestParameters': {"userParams": []} + } + self.add_req_info(req_details, instance_name, self.vcpecommon.product_family_id) + self.add_related_instance(req_details, service_instance_id, service_model) + return {'requestDetails': req_details} + + def generate_vfmodule_request(self, instance_name, vfmodule_model, service_instance_id, + service_model, vnf_instance_id, vnf_model): + req_details = { + 'modelInfo': vfmodule_model, + 'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name, + "tenantId": self.vcpecommon.os_tenant_id}, + 'requestParameters': {"usePreload": 'true'} + } + self.add_req_info(req_details, instance_name, self.vcpecommon.product_family_id) + self.add_related_instance(req_details, service_instance_id, service_model) + self.add_related_instance(req_details, vnf_instance_id, vnf_model) + return {'requestDetails': req_details} + + def generate_service_request(self, instance_name, model): + req_details = { + 'modelInfo': model, + 'subscriberInfo': {'globalSubscriberId': self.vcpecommon.global_subscriber_id}, + 'requestParameters': { + "userParams": [], + "subscriptionServiceType": "vCPE", + "aLaCarte": 'true' + } + } + self.add_req_info(req_details, instance_name) + return {'requestDetails': req_details} + + def generate_custom_service_request(self, instance_name, model, brg_mac): + req_details = { + 'modelInfo': model, + 'subscriberInfo': {'subscriberName': 'Kaneohe', + 'globalSubscriberId': self.vcpecommon.global_subscriber_id}, + 'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name, + "tenantId": self.vcpecommon.os_tenant_id}, + 'requestParameters': { + "userParams": [ + { + 'name': 'BRG_WAN_MAC_Address', + 'value': brg_mac + } + ], + "subscriptionServiceType": "vCPE", + 'aLaCarte': 'false' + } + } + self.add_req_info(req_details, instance_name, self.vcpecommon.custom_product_family_id) + return {'requestDetails': req_details} + + def create_custom_service(self, csar_file, brg_mac, name_suffix=None): + parser = csar_parser.CsarParser() + if not parser.parse_csar(csar_file): + return False + + # yyyymmdd_hhmm + if not name_suffix: + name_suffix = '_' + datetime.now().strftime('%Y%m%d%H%M') + + # create service + instance_name = '_'.join([self.vcpecommon.instance_name_prefix['service'], + parser.svc_model['modelName'], name_suffix]) + instance_name = instance_name.lower() + req = self.generate_custom_service_request(instance_name, parser.svc_model, brg_mac) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + self.logger.info('Creating custom service {0}.'.format(instance_name)) + req_id, svc_instance_id = self.submit_create_req(req, 'service') + if not self.check_progress(req_id, 140): + return False + return True + + def wait_for_aai(self, node_type, uuid): + self.logger.info('Waiting for AAI traversal to complete...') + bar = progressbar.ProgressBar() + for i in range(30): + time.sleep(1) + bar.update(i*100.0/30) + if self.vcpecommon.is_node_in_aai(node_type, uuid): + bar.update(100) + bar.finish() + return + + self.logger.error("AAI traversal didn't finish in 30 seconds. Something is wrong. Type {0}, UUID {1}".format( + node_type, uuid)) + sys.exit() + + def create_entire_service(self, csar_file, vnf_template_file, preload_dict, name_suffix, heatbridge=False): + """ + :param csar_file: + :param vnf_template_file: + :param preload_dict: + :param name_suffix: + :return: service instance UUID + """ + self.logger.info('\n----------------------------------------------------------------------------------') + self.logger.info('Start to create entire service defined in csar: {0}'.format(csar_file)) + parser = csar_parser.CsarParser() + self.logger.info('Parsing csar ...') + if not parser.parse_csar(csar_file): + self.logger.error('Cannot parse csar: {0}'.format(csar_file)) + return None + + # create service + instance_name = '_'.join([self.vcpecommon.instance_name_prefix['service'], + parser.svc_model['modelName'], name_suffix]) + instance_name = instance_name.lower() + self.logger.info('Creating service instance: {0}.'.format(instance_name)) + req = self.generate_service_request(instance_name, parser.svc_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, svc_instance_id = self.submit_create_req(req, 'service') + if not self.check_progress(req_id, eta=2, interval=1): + return None + + # wait for AAI to complete traversal + self.wait_for_aai('service', svc_instance_id) + + # create networks + for model in parser.net_models: + base_name = model['modelCustomizationName'].lower().replace('mux_vg', 'mux_gw') + network_name = '_'.join([self.vcpecommon.instance_name_prefix['network'], base_name, name_suffix]) + network_name = network_name.lower() + self.logger.info('Creating network: ' + network_name) + req = self.generate_vnf_or_network_request('network', network_name, model, svc_instance_id, + parser.svc_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, net_instance_id = self.submit_create_req(req, 'network', svc_instance_id) + if not self.check_progress(req_id, eta=20): + return None + + self.logger.info('Changing subnet name to ' + self.vcpecommon.network_name_to_subnet_name(network_name)) + self.vcpecommon.set_network_name(network_name) + subnet_name_changed = False + for i in range(20): + time.sleep(3) + if self.vcpecommon.set_subnet_name(network_name): + subnet_name_changed = True + break + + if not subnet_name_changed: + self.logger.error('Failed to change subnet name for ' + network_name) + return None + + + vnf_model = None + vnf_instance_id = None + # create VNF + if len(parser.vnf_models) == 1: + vnf_model = parser.vnf_models[0] + vnf_instance_name = '_'.join([self.vcpecommon.instance_name_prefix['vnf'], + vnf_model['modelCustomizationName'].split(' ')[0], name_suffix]) + vnf_instance_name = vnf_instance_name.lower() + self.logger.info('Creating VNF: ' + vnf_instance_name) + req = self.generate_vnf_or_network_request('vnf', vnf_instance_name, vnf_model, svc_instance_id, + parser.svc_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, vnf_instance_id = self.submit_create_req(req, 'vnf', svc_instance_id) + if not self.check_progress(req_id, eta=2, interval=1): + self.logger.error('Failed to create VNF {0}.'.format(vnf_instance_name)) + return False + + # wait for AAI to complete traversal + if not vnf_instance_id: + self.logger.error('No VNF instance ID returned!') + sys.exit() + self.wait_for_aai('vnf', vnf_instance_id) + + preloader = preload.Preload(self.vcpecommon) + preloader.preload_vfmodule(vnf_template_file, svc_instance_id, parser.vnf_models[0], parser.vfmodule_models[0], + preload_dict, name_suffix) + # create VF Module + if len(parser.vfmodule_models) == 1: + if not vnf_instance_id or not vnf_model: + self.logger.error('Invalid VNF instance ID or VNF model!') + sys.exit() + + model = parser.vfmodule_models[0] + vfmodule_instance_name = '_'.join([self.vcpecommon.instance_name_prefix['vfmodule'], + model['modelCustomizationName'].split('..')[0], name_suffix]) + vfmodule_instance_name = vfmodule_instance_name.lower() + self.logger.info('Creating VF Module: ' + vfmodule_instance_name) + req = self.generate_vfmodule_request(vfmodule_instance_name, model, svc_instance_id, parser.svc_model, + vnf_instance_id, vnf_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, vfmodule_instance_id = self.submit_create_req(req, 'vfmodule', svc_instance_id, vnf_instance_id) + if not self.check_progress(req_id, eta=70, interval=5): + self.logger.error('Failed to create VF Module {0}.'.format(vfmodule_instance_name)) + return None + + # run heatbridge + if heatbridge: + self.vcpecommon.headbridge(vfmodule_instance_name, svc_instance_id) + self.vcpecommon.save_vgmux_vnf_name(vnf_instance_name) + + return svc_instance_id diff --git a/test/vcpe/vcpe.py b/test/vcpe/vcpe.py new file mode 100755 index 000000000..7de86ae8d --- /dev/null +++ b/test/vcpe/vcpe.py @@ -0,0 +1,207 @@ +#! /usr/bin/python +import sys +import logging +from vcpecommon import * +import soutils +from datetime import datetime +import preload +import vcpe_custom_service +import csar_parser +import config_sdnc_so + + +def config_sniro(vcpecommon, vgmux_svc_instance_uuid, vbrg_svc_instance_uuid): + logger = logging.getLogger(__name__) + + logger.info('\n----------------------------------------------------------------------------------') + logger.info('Start to config SNIRO homing emulator') + + preloader = preload.Preload(vcpecommon) + template_sniro_data = vcpecommon.find_file('sniro_data', 'json', 'preload_templates') + template_sniro_request = vcpecommon.find_file('sniro_request', 'json', 'preload_templates') + + vcperescust_csar = vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(vcperescust_csar) + tunnelxconn_ar_name = None + brg_ar_name = None + vgw_name = None + for model in parser.vnf_models: + if 'tunnel' in model['modelCustomizationName']: + tunnelxconn_ar_name = model['modelCustomizationName'] + elif 'brg' in model['modelCustomizationName']: + brg_ar_name = model['modelCustomizationName'] + elif 'vgw' in model['modelCustomizationName']: + vgw_name = model['modelCustomizationName'] + + if not (tunnelxconn_ar_name and brg_ar_name and vgw_name): + logger.error('Cannot find all names from %s.', vcperescust_csar) + sys.exit() + + preloader.preload_sniro(template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, brg_ar_name, + vgmux_svc_instance_uuid, vbrg_svc_instance_uuid) + + +def create_one_service(vcpecommon, csar_file, vnf_template_file, preload_dict, suffix, heatbridge=False): + """ + :return: service instance UUID + """ + so = soutils.SoUtils(vcpecommon, 'v4') + return so.create_entire_service(csar_file, vnf_template_file, preload_dict, suffix, heatbridge) + +def deploy_brg_only(): + logging.basicConfig(level=logging.INFO, format='%(message)s') + logger = logging.getLogger(__name__) + + vcpecommon = VcpeCommon() + preload_dict = vcpecommon.load_preload_data() + name_suffix = preload_dict['${brg_bng_net}'].split('_')[-1] + + # create multiple services based on the pre-determined order + svc_instance_uuid = vcpecommon.load_object(vcpecommon.svc_instance_uuid_file) + for keyword in ['brg']: + heatbridge = 'gmux' == keyword + csar_file = vcpecommon.find_file(keyword, 'csar', 'csar') + vnf_template_file = vcpecommon.find_file(keyword, 'json', 'preload_templates') + svc_instance_uuid[keyword] = create_one_service(vcpecommon, csar_file, vnf_template_file, preload_dict, + name_suffix, heatbridge) + if not svc_instance_uuid[keyword]: + sys.exit() + + # Setting up SNIRO + config_sniro(vcpecommon, svc_instance_uuid['gmux'], svc_instance_uuid['brg']) + +def deploy_infra(): + logging.basicConfig(level=logging.INFO, format='%(message)s') + logger = logging.getLogger(__name__) + + vcpecommon = VcpeCommon() + + # preload all networks + network_template = vcpecommon.find_file('network', 'json', 'preload_templates') + name_suffix = datetime.now().strftime('%Y%m%d%H%M') + preloader = preload.Preload(vcpecommon) + preload_dict = preloader.preload_all_networks(network_template, name_suffix) + logger.debug('Initial preload dictionary:') + logger.debug(json.dumps(preload_dict, indent=4, sort_keys=True)) + if not preload_dict: + logger.error("Failed to preload networks.") + sys.exit() + vcpecommon.save_preload_data(preload_dict) + + # create multiple services based on the pre-determined order + svc_instance_uuid = {} + for keyword in ['infra', 'bng', 'gmux', 'brg']: + heatbridge = 'gmux' == keyword + csar_file = vcpecommon.find_file(keyword, 'csar', 'csar') + vnf_template_file = vcpecommon.find_file(keyword, 'json', 'preload_templates') + svc_instance_uuid[keyword] = create_one_service(vcpecommon, csar_file, vnf_template_file, preload_dict, + name_suffix, heatbridge) + if not svc_instance_uuid[keyword]: + sys.exit() + + vcpecommon.save_object(svc_instance_uuid, vcpecommon.svc_instance_uuid_file) + # Setting up SNIRO + config_sniro(vcpecommon, svc_instance_uuid['gmux'], svc_instance_uuid['brg']) + + print('----------------------------------------------------------------------------------------------------') + print('Congratulations! The following have been completed correctly:') + print(' - Infrastructure Service Instantiation: ') + print(' * 4 VMs: DHCP, AAA, DNS, Web Server') + print(' * 2 Networks: CPE_PUBLIC, CPE_SIGNAL') + print(' - vBNG Service Instantiation: ') + print(' * 1 VM: vBNG') + print(' * 2 Networks: BRG_BNG, BNG_MUX') + print(' - vGMUX Service Instantiation: ') + print(' * 1 VM: vGMUX') + print(' * 1 Network: MUX_GW') + print(' - vBRG Service Instantiation: ') + print(' * 1 VM: vBRG') + print(' - Adding vGMUX vServer information to AAI.') + print(' - SNIRO Homing Emulator configuration.') + + +def deploy_custom_service(): + nodes = ['brg', 'mux'] + vcpecommon = VcpeCommon(nodes) + custom_service = vcpe_custom_service.CustomService(vcpecommon) + + # clean up + host_dic = {k: vcpecommon.hosts[k] for k in nodes} + if not vcpecommon.delete_vxlan_interfaces(host_dic): + sys.exit() + + custom_service.clean_up_sdnc() + custom_service.del_all_vgw_stacks(vcpecommon.vgw_name_keyword) + + # create new service + csar_file = vcpecommon.find_file('rescust', 'csar', 'csar') + vgw_template_file = vcpecommon.find_file('vgw', 'json', 'preload_templates') + preload_dict = vcpecommon.load_preload_data() + custom_service.create_custom_service(csar_file, vgw_template_file, preload_dict) + + +def closed_loop(lossrate=0): + if lossrate > 0: + while 'y' != raw_input('Please enter docker container "drools" in Policy VM and type "policy stop". Then enter y here: ').lower(): + continue + nodes = ['brg', 'mux'] + logger = logging.getLogger('__name__') + vcpecommon = VcpeCommon(nodes) + logger.info('Cleaning up vGMUX data reporting settings') + vcpecommon.del_vgmux_ves_mode() + time.sleep(2) + vcpecommon.del_vgmux_ves_collector() + + logger.info('Staring vGMUX data reporting to DCAE') + time.sleep(2) + vcpecommon.set_vgmux_ves_collector() + + logger.info('Setting vGMUX to report packet loss rate: %s', lossrate) + time.sleep(2) + vcpecommon.set_vgmux_packet_loss_rate(lossrate, vcpecommon.load_vgmux_vnf_name()) + if lossrate > 0: + print('Please enter docker container "drools" in Policy VM and type "policy start". Then observe vGMUX being restarted.') + + +def init_so_sdnc(): + logger = logging.getLogger('__name__') + vcpecommon = VcpeCommon() + config_sdnc_so.insert_customer_service_to_so(vcpecommon) + config_sdnc_so.insert_customer_service_to_sdnc(vcpecommon) + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO, format='%(message)s') + + print('----------------------------------------------------------------------------------------------------') + print(' vcpe.py: Brief info about this program') +# print(' vcpe.py sdc: Onboard VNFs, design and distribute vCPE services (under development)') + print(' vcpe.py init: Add customer service data to SDNC and SO DBs.') + print(' vcpe.py infra: Deploy infrastructure, including DHCP, AAA, DNS, Web Server, vBNG, vGMUX, vBRG.') + print(' vcpe.py customer: Deploy customer service, including vGW and VxLANs') + print(' vcpe.py loop: Test closed loop control') + print('----------------------------------------------------------------------------------------------------') + + if len(sys.argv) != 2: + sys.exit() + + if sys.argv[1] == 'sdc': + print('Under development') + elif sys.argv[1] == 'init': + if 'y' == raw_input('Ready to add customer service data to SDNC and SO DBs? This is needed only once.' + 'y/n: ').lower(): + init_so_sdnc() + elif sys.argv[1] == 'infra': + if 'y' == raw_input('Ready to deploy infrastructure? y/n: ').lower(): + deploy_infra() + elif sys.argv[1] == 'customer': + if 'y' == raw_input('Ready to deploy customer service? y/n: ').lower(): + deploy_custom_service() + elif sys.argv[1] == 'loop': + closed_loop(22) + elif sys.argv[1] == 'noloss': + closed_loop(0) + elif sys.argv[1] == 'brg': + deploy_brg_only() + diff --git a/test/vcpe/vcpe_custom_service.py b/test/vcpe/vcpe_custom_service.py new file mode 100755 index 000000000..d89129eef --- /dev/null +++ b/test/vcpe/vcpe_custom_service.py @@ -0,0 +1,80 @@ +#! /usr/bin/python + +import os +import requests +import time +from vcpecommon import * +from datetime import datetime +import soutils +import logging +import preload +import json + + +class CustomService: + def __init__(self, vcpecommon): + self.logger = logging.getLogger(__name__) + self.vcpecommon = vcpecommon + + # delete all vgw stacks + def del_all_vgw_stacks(self, keyword): + param = ' '.join([k + ' ' + v for k, v in self.vcpecommon.cloud.items()]) + openstackcmd = 'openstack ' + param + ' ' + + stacks = os.popen(openstackcmd + 'stack list').read() + found = False + for stack_description in stacks.split('\n'): + if keyword in stack_description: + found = True + stack_name = stack_description.split('|')[2].strip() + cmd = openstackcmd + 'stack delete -y ' + stack_name + self.logger.info('Deleting ' + stack_name) + os.popen(cmd) + + if not found: + self.logger.info('No vGW stack to delete') + + # clean up SDNC + def clean_up_sdnc(self): + items = ['tunnelxconn-allotted-resources', 'brg-allotted-resources'] + for res in items: + self.logger.info('Cleaning up ' + res + ' from SDNC') + requests.delete(self.vcpecommon.sdnc_ar_cleanup_url + res, auth=self.vcpecommon.sdnc_userpass) + + def print_success_info(self, print_instructions=True, nodes=None): + if not nodes: + nodes = ['brg', 'mux', 'gw', 'web'] + ip_dict = self.vcpecommon.get_vm_ip(nodes, self.vcpecommon.external_net_addr, + self.vcpecommon.external_net_prefix_len) + + print(json.dumps(ip_dict, indent=4, sort_keys=True)) + for node in ['brg', 'mux']: + print('VxLAN config in {0}:'.format(node)) + self.vcpecommon.get_vxlan_interfaces(ip_dict[node], print_info=True) + + print(json.dumps(ip_dict, indent=4, sort_keys=True)) + + if print_instructions: + print('----------------------------------------------------------------------------') + print('Custom service created successfully. See above for VxLAN configuration info.') + print('To test data plane connectivity, following the steps below.') + print(' 1. ssh to vGW at {0}'.format(ip_dict['gw'])) + print(' 2. Restart DHCP: systemctl restart isc-dhcp-server') + print(' 3. ssh to vBRG at {0}'.format(ip_dict['brg'])) + print(' 4. Get IP from vGW: dhclient lstack') + print(' 5. Add route to Internet: ip route add 10.2.0.0/24 via 192.168.1.254 dev lstack') + print(' 6. ping the web server: ping {0}'.format('10.2.0.10')) + print(' 7. wget http://{0}'.format('10.2.0.10')) + + def create_custom_service(self, csar_file, vgw_template_file, preload_dict=None): + name_suffix = datetime.now().strftime('%Y%m%d%H%M') + brg_mac = self.vcpecommon.get_brg_mac_from_sdnc() + # preload vGW + if preload_dict: + preloader = preload.Preload(self.vcpecommon) + preloader.preload_vgw(vgw_template_file, brg_mac, preload_dict, name_suffix) + + # create service + so = soutils.SoUtils(self.vcpecommon, 'v5') + if so.create_custom_service(csar_file, brg_mac, name_suffix): + self.print_success_info() diff --git a/test/vcpe/vcpecommon.py b/test/vcpe/vcpecommon.py new file mode 100755 index 000000000..5b3e009a3 --- /dev/null +++ b/test/vcpe/vcpecommon.py @@ -0,0 +1,414 @@ +import json +import logging +import os +import pickle +import re +import sys + +import ipaddress +import mysql.connector +import requests +import commands +import time + + +class VcpeCommon: + ############################################################################################# + # Start: configurations that you must change for a new ONAP installation + external_net_addr = '10.12.0.0' + external_net_prefix_len = 16 + ############################################################################################# + # set the openstack cloud access credentials here + cloud = { + '--os-auth-url': 'http://10.12.25.2:5000', + '--os-username': 'YOUR ID', + '--os-user-domain-id': 'default', + '--os-project-domain-id': 'default', + '--os-tenant-id': '087050388b204c73a3e418dd2c1fe30b', + '--os-region-name': 'RegionOne', + '--os-password': 'YOUR PASSWD', + '--os-project-domain-name': 'Integration-SB-01', + '--os-identity-api-version': '3' + } + + common_preload_config = { + 'oam_onap_net': 'oam_onap_c4Uw', + 'oam_onap_subnet': 'oam_onap_c4Uw', + 'public_net': 'external', + 'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4' + } + # End: configurations that you must change for a new ONAP installation + ############################################################################################# + + template_variable_symbol = '${' + ############################################################################################# + # preloading network config + # key=network role + # value = [subnet_start_ip, subnet_gateway_ip] + preload_network_config = { + 'cpe_public': ['10.2.0.2', '10.2.0.1'], + 'cpe_signal': ['10.4.0.2', '10.4.0.1'], + 'brg_bng': ['10.3.0.2', '10.3.0.1'], + 'bng_mux': ['10.1.0.10', '10.1.0.1'], + 'mux_gw': ['10.5.0.10', '10.5.0.1'] + } + + global_subscriber_id = 'SDN-ETHERNET-INTERNET' + + def __init__(self, extra_host_names=None): + self.logger = logging.getLogger(__name__) + self.logger.info('Initializing configuration') + + self.host_names = ['so', 'sdnc', 'robot', 'aai-inst1', 'dcaedoks00'] + if extra_host_names: + self.host_names.extend(extra_host_names) + # get IP addresses + self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len) + # this is the keyword used to name vgw stack, must not be used in other stacks + self.vgw_name_keyword = 'base_vcpe_vgw' + self.svc_instance_uuid_file = '__var/svc_instance_uuid' + self.preload_dict_file = '__var/preload_dict' + self.vgmux_vnf_name_file = '__var/vgmux_vnf_name' + self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116' + self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb' + self.instance_name_prefix = { + 'service': 'vcpe_svc', + 'network': 'vcpe_net', + 'vnf': 'vcpe_vnf', + 'vfmodule': 'vcpe_vfmodule' + } + self.aai_userpass = 'AAI', 'AAI' + self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh' + self.os_tenant_id = self.cloud['--os-tenant-id'] + self.os_region_name = self.cloud['--os-region-name'] + self.common_preload_config['pub_key'] = self.pub_key + self.sniro_url = 'http://' + self.hosts['robot'] + ':8080/__admin/mappings' + self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + + ############################################################################################# + # SDNC urls + self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U' + self.sdnc_db_name = 'sdnctl' + self.sdnc_db_user = 'sdnctl' + self.sdnc_db_pass = 'gamma' + self.sdnc_db_port = '32768' + self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \ + ':8282/restconf/operations/VNF-API:preload-network-topology-operation' + self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \ + ':8282/restconf/operations/VNF-API:preload-vnf-topology-operation' + self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':8282/restconf/config/GENERIC-RESOURCE-API:' + + ############################################################################################# + # SO urls, note: do NOT add a '/' at the end of the url + self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':8080/ecomp/mso/infra/serviceInstances/v4', + 'v5': 'http://' + self.hosts['so'] + ':8080/ecomp/mso/infra/serviceInstances/v5'} + self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':8080/ecomp/mso/infra/orchestrationRequests/v2' + self.so_userpass = 'InfraPortalClient', 'password1$' + self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.so_db_name = 'mso_catalog' + self.so_db_user = 'root' + self.so_db_pass = 'password' + self.so_db_port = '32768' + + self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces' + self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.vpp_api_userpass = ('admin', 'admin') + self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent' + + def headbridge(self, openstack_stack_name, svc_instance_uuid): + """ + Add vserver information to AAI + """ + self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name)) + cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid) + ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd)) + self.logger.debug('%s', ret) + + def get_brg_mac_from_sdnc(self): + """ + :return: BRG MAC address. Currently we only support one BRG instance. + """ + cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name, + host=self.hosts['sdnc'], port=self.sdnc_db_port) + cursor = cnx.cursor() + query = "SELECT * from DHCP_MAP" + cursor.execute(query) + + self.logger.debug('DHCP_MAP table in SDNC') + counter = 0 + mac = None + for mac, ip in cursor: + counter += 1 + self.logger.debug(mac + ':' + ip) + + cnx.close() + + if counter != 1: + self.logger.error('Found %s MAC addresses in DHCP_MAP', counter) + sys.exit() + else: + self.logger.debug('Found MAC addresses in DHCP_MAP: %s', mac) + return mac + + def insert_into_sdnc_db(self, cmds): + cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name, + host=self.hosts['sdnc'], port=self.sdnc_db_port) + cursor = cnx.cursor() + for cmd in cmds: + self.logger.debug(cmd) + cursor.execute(cmd) + self.logger.debug('%s', cursor) + cnx.commit() + cursor.close() + cnx.close() + + def insert_into_so_db(self, cmds): + cnx = mysql.connector.connect(user=self.so_db_user, password=self.so_db_pass, database=self.so_db_name, + host=self.hosts['so'], port=self.so_db_port) + cursor = cnx.cursor() + for cmd in cmds: + self.logger.debug(cmd) + cursor.execute(cmd) + self.logger.debug('%s', cursor) + cnx.commit() + cursor.close() + cnx.close() + + def find_file(self, file_name_keyword, file_ext, search_dir): + """ + :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra + :param file_ext: e.g., csar, json + :param search_dir path to search + :return: path name of the file + """ + file_name_keyword = file_name_keyword.lower() + file_ext = file_ext.lower() + if not file_ext.startswith('.'): + file_ext = '.' + file_ext + + filenamepath = None + for file_name in os.listdir(search_dir): + file_name_lower = file_name.lower() + if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext): + if filenamepath: + self.logger.error('Multiple files found for *{0}*.{1} in ' + 'directory {2}'.format(file_name_keyword, file_ext, search_dir)) + sys.exit() + filenamepath = os.path.abspath(os.path.join(search_dir, file_name)) + + if filenamepath: + return filenamepath + else: + self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir)) + sys.exit() + + @staticmethod + def network_name_to_subnet_name(network_name): + """ + :param network_name: example: vcpe_net_cpe_signal_201711281221 + :return: vcpe_net_cpe_signal_subnet_201711281221 + """ + fields = network_name.split('_') + fields.insert(-1, 'subnet') + return '_'.join(fields) + + def set_network_name(self, network_name): + param = ' '.join([k + ' ' + v for k, v in self.cloud.items()]) + openstackcmd = 'openstack ' + param + cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1']) + os.popen(cmd) + + def set_subnet_name(self, network_name): + """ + Example: network_name = vcpe_net_cpe_signal_201711281221 + set subnet name to vcpe_net_cpe_signal_subnet_201711281221 + :return: + """ + param = ' '.join([k + ' ' + v for k, v in self.cloud.items()]) + openstackcmd = 'openstack ' + param + + # expected results: | subnets | subnet_id | + subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|') + if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets': + subnet_id = subnet_info[2].strip() + subnet_name = self.network_name_to_subnet_name(network_name) + cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id]) + os.popen(cmd) + self.logger.info("Subnet name set to: " + subnet_name) + return True + else: + self.logger.error("Can't get subnet info from network name: " + network_name) + return False + + def is_node_in_aai(self, node_type, node_uuid): + key = None + search_node_type = None + if node_type == 'service': + search_node_type = 'service-instance' + key = 'service-instance-id' + elif node_type == 'vnf': + search_node_type = 'generic-vnf' + key = 'vnf-id' + else: + logging.error('Invalid node_type: ' + node_type) + sys.exit() + + url = 'https://{0}:8443/aai/v11/search/nodes-query?search-node-type={1}&filter={2}:EQUALS:{3}'.format( + self.hosts['aai-inst1'], search_node_type, key, node_uuid) + + headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot'} + requests.packages.urllib3.disable_warnings() + r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False) + response = r.json() + self.logger.debug('aai query: ' + url) + self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True)) + return 'result-data' in response + + @staticmethod + def extract_ip_from_str(net_addr, net_addr_len, sz): + """ + :param net_addr: e.g. 10.5.12.0 + :param net_addr_len: e.g. 24 + :param sz: a string + :return: the first IP address matching the network, e.g. 10.5.12.3 + """ + network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False) + ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz) + for ip in ip_list: + this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False) + if this_net == network: + return str(ip) + return None + + def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None): + """ + :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg'] + :param net_addr: e.g. 10.12.5.0 + :param net_addr_len: e.g. 24 + :return: dictionary {keyword: ip} + """ + if not net_addr: + net_addr = self.external_net_addr + + if not net_addr_len: + net_addr_len = self.external_net_prefix_len + + param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k]) + openstackcmd = 'nova ' + param + ' list' + self.logger.debug(openstackcmd) + + ip_dict = {} + results = os.popen(openstackcmd).read() + for line in results.split('\n'): + fields = line.split('|') + if len(fields) == 8: + vm_name = fields[2] + ip_info = fields[-2] + for keyword in keywords: + if keyword in vm_name: + ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info) + if ip: + ip_dict[keyword] = ip + if len(ip_dict) != len(keywords): + self.logger.error('Cannot find all desired IP addresses for %s.', keywords) + self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True)) + sys.exit() + return ip_dict + + def del_vgmux_ves_mode(self): + url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode' + r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + self.logger.debug('%s', r) + + def del_vgmux_ves_collector(self): + url = self.vpp_ves_url.format(self.hosts['mux']) + '/config' + r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + self.logger.debug('%s', r) + + def set_vgmux_ves_collector(self ): + url = self.vpp_ves_url.format(self.hosts['mux']) + data = {'config': + {'server-addr': self.hosts['dcaedoks00'], + 'server-port': '8080', + 'read-interval': '10', + 'is-add':'1' + } + } + r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data) + self.logger.debug('%s', r) + + def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name): + url = self.vpp_ves_url.format(self.hosts['mux']) + data = {"mode": + {"working-mode": "demo", + "base-packet-loss": str(lossrate), + "source-name": vg_vnf_instance_name + } + } + r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data) + self.logger.debug('%s', r) + + # return all the VxLAN interface names of BRG or vGMUX based on the IP address + def get_vxlan_interfaces(self, ip, print_info=False): + url = self.vpp_inf_url.format(ip) + self.logger.debug('url is this: %s', url) + r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + data = r.json()['interfaces']['interface'] + if print_info: + for inf in data: + if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel': + print(json.dumps(inf, indent=4, sort_keys=True)) + + return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel'] + + # delete all VxLAN interfaces of each hosts + def delete_vxlan_interfaces(self, host_dic): + for host, ip in host_dic.items(): + deleted = False + self.logger.info('{0}: Getting VxLAN interfaces'.format(host)) + inf_list = self.get_vxlan_interfaces(ip) + for inf in inf_list: + deleted = True + time.sleep(2) + self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf)) + url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2' + requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + + for inf in inf_list: + deleted = True + time.sleep(2) + self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf)) + url = self.vpp_inf_url.format(ip) + '/interface/' + inf + requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + + if len(self.get_vxlan_interfaces(ip)) > 0: + self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip)) + return False + + if not deleted: + self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host)) + return True + + @staticmethod + def save_object(obj, filepathname): + with open(filepathname, 'wb') as fout: + pickle.dump(obj, fout) + + @staticmethod + def load_object(filepathname): + with open(filepathname, 'rb') as fin: + return pickle.load(fin) + + def save_preload_data(self, preload_data): + self.save_object(preload_data, self.preload_dict_file) + + def load_preload_data(self): + return self.load_object(self.preload_dict_file) + + def save_vgmux_vnf_name(self, vgmux_vnf_name): + self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file) + + def load_vgmux_vnf_name(self): + return self.load_object(self.vgmux_vnf_name_file) + diff --git a/version-manifest/src/main/resources/docker-manifest.csv b/version-manifest/src/main/resources/docker-manifest.csv index 4d9a19b61..f0dc07849 100644 --- a/version-manifest/src/main/resources/docker-manifest.csv +++ b/version-manifest/src/main/resources/docker-manifest.csv @@ -29,14 +29,21 @@ onap/multicloud/vio-vesagent,1.0.0 onap/music,latest onap/oof,latest onap/oom/kube2msb,1.0.0 -onap/org.onap.dcaegen2.collectors.ves.vescollector,v1.1.0 -onap/org.onap.dcaegen2.deployments.bootstrap,v1.1.1 +onap/org.onap.dcaegen2.collectors.snmptrap,latest +onap/org.onap.dcaegen2.collectors.ves.vescollector,latest +onap/org.onap.dcaegen2.deployments.bootstrap,1.1.2 +onap/org.onap.dcaegen2.deployments.cm-container,latest +onap/org.onap.dcaegen2.deployments.k8s-bootstrap-container,latest +onap/org.onap.dcaegen2.deployments.redis-cluster-container,latest +onap/org.onap.dcaegen2.deployments.tca-cdap-container,latest onap/org.onap.dcaegen2.platform.cdapbroker,v4.0.0 -onap/org.onap.dcaegen2.platform.configbinding,v1.2.0 -onap/org.onap.dcaegen2.platform.deployment-handler,v1.1.0 -onap/org.onap.dcaegen2.platform.inventory-api,v1.2.0 -onap/org.onap.dcaegen2.platform.policy-handler,v1.1.0 -onap/org.onap.dcaegen2.platform.servicechange-handler,v1.0.0 +onap/org.onap.dcaegen2.platform.configbinding,latest +onap/org.onap.dcaegen2.platform.deployment-handler,latest +onap/org.onap.dcaegen2.platform.inventory-api,latest +onap/org.onap.dcaegen2.platform.policy-handler,latest +onap/org.onap.dcaegen2.platform.servicechange-handler,latest +onap/org.onap.dcaegen2.services.heartbeat,latest +onap/org.onap.dcaegen2.services.prh.prh-app-server,latest onap/policy-drools,1.2-STAGING-latest onap/policy-pe,1.2-STAGING-latest onap/portal-app,2.1-STAGING-latest diff --git a/version-manifest/src/main/resources/java-manifest.csv b/version-manifest/src/main/resources/java-manifest.csv index 4d3eda7cf..4bed11cda 100644 --- a/version-manifest/src/main/resources/java-manifest.csv +++ b/version-manifest/src/main/resources/java-manifest.csv @@ -90,20 +90,24 @@ org.onap.cli,cli-sample-yaml-generator,1.1.0 org.onap.cli,cli-validation,1.1.0 org.onap.cli,cli-zip,1.1.0 org.onap.dcaegen2,dcaegen2,1.1.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-aai,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-cdap-common,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-cdap-plugins,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-cdap-tca,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-common,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-dmaap,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-it,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-model,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-tca,2.0.0 -org.onap.dcaegen2.analytics.tca,dcae-analytics-test,2.0.0 -org.onap.dcaegen2.collectors.ves,VESCollector,1.1.4 +org.onap.dcaegen2.analytics.tca,dcae-analytics,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-aai,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-cdap-common,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-cdap-plugins,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-cdap-tca,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-common,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-dmaap,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-it,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-model,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-tca,2.2.0 +org.onap.dcaegen2.analytics.tca,dcae-analytics-test,2.2.0 +org.onap.dcaegen2.collectors.ves,VESCollector,1.2.4 org.onap.dcaegen2.platform,inventory-api,1.0.0 org.onap.dcaegen2.platform,servicechange-handler,1.0.0 +org.onap.dcaegen2.services,prh,1.0.0 +org.onap.dcaegen2.services.prh,prh-aai-client,1.0.0 +org.onap.dcaegen2.services.prh,prh-app-server,1.0.0 +org.onap.dcaegen2.services.prh,prh-dmaap-client,1.0.0 org.onap.dmaap.messagerouter.dmaapclient,dmaapClient,1.0.0 org.onap.dmaap.messagerouter.messageservice,dmaapMR1,1.0.1 org.onap.dmaap.messagerouter.mirroragent,dmaapMMAgent,1.0.0 |