#!/bin/bash # SPDX-license-identifier: Apache-2.0 ############################################################################## # Copyright (c) 2018 # All rights reserved. This program and the accompanying materials # are made available under the terms of the Apache License, Version 2.0 # which accompanies this distribution, and is available at # http://www.apache.org/licenses/LICENSE-2.0 ############################################################################## set -o errexit set -o nounset set -o pipefail FUNCTIONS_DIR="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")" # Do not overwrite any user modifications to PATH when sourcing # /etc/environment USER_PATH=$PATH source /etc/environment PATH=$USER_PATH:$PATH source $FUNCTIONS_DIR/_common_test.sh function print_msg { local msg=$1 local RED='\033[0;31m' local NC='\033[0m' echo -e "${RED} $msg ---------------------------------------${NC}" } function control_plane_ip { kubectl get endpoints kubernetes -o jsonpath='{.subsets[].addresses[].ip}' } function ssh_cluster { ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null $(control_plane_ip) -- "$@" } function get_ovn_central_address { #Reuse OVN_CENTRAL_ADDRESS if available (bypassable by --force flag) if [[ "${1:-}" != "--force" ]] && [[ -n "${OVN_CENTRAL_ADDRESS:-}" ]]; then echo "${OVN_CENTRAL_ADDRESS}" return 0 fi local remote_command="ip address show dev $OVN_CENTRAL_INTERFACE primary" declare -a ansible_command=(ansible ovn-central[0] -i \ "${FUNCTIONS_DIR}/../hosting_providers/vagrant/inventory/hosts.ini") declare -a filter=(awk -F '[ \t/]+' \ 'BEGIN {r=1} {for (i=1; i<=NF; i++) if ($i == "inet") {print $(i+1); r=0}} END {exit r}') local result #Determine OVN_CENTRAL_INTERFACE address if ! result="$("${ansible_command[@]}" -a "${remote_command}")"; then echo "Ansible error for remote host ovn-central[0]" >&2 return 1 else if [[ "${result}" != *CHANGED* ]]; then echo "Failed to execute command on remote host ovn-central[0]" >&2 return 2 else if ! result="$("${filter[@]}" <<< "${result}")"; then echo "Failed to retrieve interface address from command output" >&2 return 3 else echo "${result}:6641" fi fi fi } function call_api { #Runs curl with passed flags and provides #additional error handling and debug information #Function outputs server response body #and performs validation of http_code local status local curl_response_file="$(mktemp -p /tmp)" local curl_common_flags=(-s -w "%{http_code}" -o "${curl_response_file}") local command=(curl "${curl_common_flags[@]}" "$@") echo "[INFO] Running '${command[@]}'" >&2 if ! status="$("${command[@]}")"; then echo "[ERROR] Internal curl error! '$status'" >&2 cat "${curl_response_file}" rm "${curl_response_file}" return 2 else echo "[INFO] Server replied with status: ${status}" >&2 cat "${curl_response_file}" rm "${curl_response_file}" if [[ "${status:0:1}" =~ [45] ]]; then return 1 else return 0 fi fi } function call_api_nox { # this version doesn't exit the script if there's # an error. #Runs curl with passed flags and provides #additional error handling and debug information #Function outputs server response body #and performs validation of http_code local status local curl_response_file="$(mktemp -p /tmp)" local curl_common_flags=(-s -w "%{http_code}" -o "${curl_response_file}") local command=(curl "${curl_common_flags[@]}" "$@") echo "[INFO] Running '${command[@]}'" >&2 if ! status="$("${command[@]}")"; then echo "[ERROR] Internal curl error! '$status'" >&2 cat "${curl_response_file}" rm "${curl_response_file}" else echo "[INFO] Server replied with status: ${status}" >&2 if [[ "${status:0:1}" =~ [45] ]]; then cat "${curl_response_file}" else cat "${curl_response_file}" | jq . fi rm "${curl_response_file}" fi } function delete_resource { #Issues DELETE http call to provided endpoint #and further validates by following GET request call_api -X DELETE "$1" ! call_api -X GET "$1" >/dev/null } # init_network() - This function creates the OVN resouces required by the test function init_network { local fname=$1 local router_name="ovn4nfv-master" name=$(cat $fname | yq '.spec.name' | xargs) subnet=$(cat $fname | yq '.spec.subnet' | xargs) gateway=$(cat $fname | yq '.spec.gateway' | xargs) ovn_central_address=$(get_ovn_central_address) router_mac=$(printf '00:00:00:%02X:%02X:%02X' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256))) ovn-nbctl --may-exist --db tcp:$ovn_central_address ls-add $name -- set logical_switch $name other-config:subnet=$subnet external-ids:gateway_ip=$gateway ovn-nbctl --may-exist --db tcp:$ovn_central_address lrp-add $router_name rtos-$name $router_mac $gateway ovn-nbctl --may-exist --db tcp:$ovn_central_address lsp-add $name stor-$name -- set logical_switch_port stor-$name type=router options:router-port=rtos-$name addresses=\"$router_mac\" } # cleanup_network() - This function removes the OVN resources created for the test function cleanup_network { local fname=$1 name=$(cat $fname | yq '.spec.name' | xargs) ovn_central_address=$(get_ovn_central_address) for cmd in "ls-del $name" "lrp-del rtos-$name" "lsp-del stor-$name"; do ovn-nbctl --if-exist --db tcp:$ovn_central_address $cmd done } function _checks_args { if [[ -z $1 ]]; then echo "Missing CSAR ID argument" exit 1 fi if [[ -z $CSAR_DIR ]]; then echo "CSAR_DIR global environment value is empty" exit 1 fi mkdir -p ${CSAR_DIR}/${1} } function _destroy { local type=$1 local name=$2 echo "$(date +%H:%M:%S) - $name : Destroying $type" kubectl delete $type $name --ignore-not-found=true --now while kubectl get $type $name &>/dev/null; do echo "$(date +%H:%M:%S) - $name : Destroying $type" done } # destroy_deployment() - This function ensures that a specific deployment is # destroyed in Kubernetes function destroy_deployment { local deployment_name=$1 _destroy "deployment" $deployment_name } function _recreate { local type=$1 local name=$2 _destroy $type $name kubectl create -f $name.yaml } # wait_deployment() - Wait process to Running status on the Deployment's pods function wait_deployment { local deployment_name=$1 status_phase="" while [[ $status_phase != "Running" ]]; do new_phase=$(kubectl get pods | grep $deployment_name | awk '{print $3}') if [[ $new_phase != $status_phase ]]; then echo "$(date +%H:%M:%S) - $deployment_name : $new_phase" status_phase=$new_phase fi if [[ $new_phase == "Err"* ]]; then exit 1 fi done } # wait_for_pod() - Wait until pods matched by kubectl filters is in running status function wait_for_pod { #Example usage: # wait_for_pods example_pod # wait_for_pods --namespace test different_pod # wait_for_pods -n test -l app=plugin_test kubectl wait --for=condition=Ready --timeout=600s pod "$@" } # wait_for_deployment() - Wait until the deployment is ready function wait_for_deployment { #Example usage: # wait_for_deployment $DEPLOYMENT_NAME $REPLICAS # wait_for_deployment example_deployment 2 status="0/" while [[ "$status" != $2* ]]; do new_status=`kubectl get deployment -A | grep $1 | awk '{print $3}'` if [[ "$new_status" != "$status" ]]; then status="$new_status" fi pod_status=`kubectl get pods -A | grep $1 | awk '{print $4}'` if [[ $pod_status =~ "Err" ]]; then echo "Deployment $1 error" exit 1 fi done } # wait_for_deployment_status() - Wait until the deployment intent group is the specified status function wait_for_deployment_status { #Example usage: # wait_for_deployment_status {base-url-orchestrator}/projects/{project-name}/composite-apps/{composite-app-name}/{composite-app-version}/deployment-intent-groups/{deployment-intent-group-name}/status Instantiated if [ "$#" -ne 2 ]; then echo "Usage: wait_for_deployment_status URL STATUS" exit 1 fi for try in {0..59}; do sleep 1 new_phase="$(call_api $1 | jq -r .status)" echo "$(date +%H:%M:%S) - Filter=[$*] : $new_phase" if [[ "$new_phase" == "$2" ]]; then return 0 fi done exit 1 } function setup_type { local type=$1 shift; if ! $(kubectl version &>/dev/null); then echo "This funtional test requires kubectl client" exit 1 fi for name in $@; do _recreate $type $name done sleep 5 for name in $@; do wait_deployment $name done } function teardown_type { local type=$1 shift; for name in $@; do _destroy $type $name done } # setup() - Base testing setup shared among functional tests function setup { setup_type "deployment" $@ } # teardown() - Base testing teardown function function teardown { teardown_type "deployment" $@ } # check_ip_range() - Verifying IP address in address range function check_ip_range { local IP=$1 local MASK=$2 install_ipcalc if [[ ! -e /usr/bin/ipcalc ]]; then echo -e "Command 'ipcalc' not found" return 0 fi if [[ -z ${IP} ]] || [[ -z ${MASK} ]]; then return 1 fi min=`/usr/bin/ipcalc $MASK|awk '/HostMin:/{print $2}'` max=`/usr/bin/ipcalc $MASK|awk '/HostMax:/{print $2}'` MIN=`echo $min|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'` MAX=`echo $max|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'` IPvalue=`echo $IP|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'` if [[ "$IPvalue" -gt "$MIN" ]] && [[ "$IPvalue" -lt "$MAX" ]]; then echo -e "$IP in ipset $MASK" return 0 fi echo -e "$IP not in ipset $MASK" return 1 } test_folder=${FUNCTIONS_DIR}