Integrate Topology Manager
[multicloud/k8s.git] / kud / tests / _functions.sh
1 #!/bin/bash
2 # SPDX-license-identifier: Apache-2.0
3 ##############################################################################
4 # Copyright (c) 2018
5 # All rights reserved. This program and the accompanying materials
6 # are made available under the terms of the Apache License, Version 2.0
7 # which accompanies this distribution, and is available at
8 # http://www.apache.org/licenses/LICENSE-2.0
9 ##############################################################################
10
11 set -o errexit
12 set -o nounset
13 set -o pipefail
14
15 FUNCTIONS_DIR="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")"
16
17 source /etc/environment
18 source $FUNCTIONS_DIR/_common_test.sh
19
20 function print_msg {
21     local msg=$1
22     local RED='\033[0;31m'
23     local NC='\033[0m'
24
25     echo -e "${RED} $msg ---------------------------------------${NC}"
26 }
27
28 function get_ovn_central_address {
29     #Reuse OVN_CENTRAL_ADDRESS if available (bypassable by --force flag)
30     if [[ "${1:-}" != "--force" ]] && [[ -n "${OVN_CENTRAL_ADDRESS:-}" ]]; then
31         echo "${OVN_CENTRAL_ADDRESS}"
32         return 0
33     fi
34
35     local remote_command="ip address show dev $OVN_CENTRAL_INTERFACE primary"
36     declare -a ansible_command=(ansible ovn-central[0] -i \
37                 "${FUNCTIONS_DIR}/../hosting_providers/vagrant/inventory/hosts.ini")
38     declare -a filter=(awk -F '[ \t/]+' \
39                 'BEGIN {r=1} {for (i=1; i<=NF; i++) if ($i == "inet") {print $(i+1); r=0}} END {exit r}')
40     local result
41
42     #Determine OVN_CENTRAL_INTERFACE address
43     if ! result="$("${ansible_command[@]}" -a "${remote_command}")"; then
44         echo "Ansible error for remote host ovn-central[0]" >&2
45         return 1
46     else
47         if [[ "${result}" != *CHANGED* ]]; then
48             echo "Failed to execute command on remote host ovn-central[0]" >&2
49             return 2
50         else
51             if ! result="$("${filter[@]}" <<< "${result}")"; then
52                 echo "Failed to retrieve interface address from command output" >&2
53                 return 3
54             else
55                 echo "${result}:6641"
56             fi
57         fi
58     fi
59 }
60
61 function call_api {
62     #Runs curl with passed flags and provides
63     #additional error handling and debug information
64
65     #Function outputs server response body
66     #and performs validation of http_code
67
68     local status
69     local curl_response_file="$(mktemp -p /tmp)"
70     local curl_common_flags=(-s -w "%{http_code}" -o "${curl_response_file}")
71     local command=(curl "${curl_common_flags[@]}" "$@")
72
73     echo "[INFO] Running '${command[@]}'" >&2
74     if ! status="$("${command[@]}")"; then
75         echo "[ERROR] Internal curl error! '$status'" >&2
76         cat "${curl_response_file}"
77         rm "${curl_response_file}"
78         return 2
79     else
80         echo "[INFO] Server replied with status: ${status}" >&2
81         cat "${curl_response_file}"
82         rm "${curl_response_file}"
83         if [[ "${status:0:1}" =~ [45] ]]; then
84             return 1
85         else
86             return 0
87         fi
88     fi
89 }
90
91 function call_api_nox {
92     # this version doesn't exit the script if there's
93     # an error.
94
95     #Runs curl with passed flags and provides
96     #additional error handling and debug information
97
98     #Function outputs server response body
99     #and performs validation of http_code
100
101     local status
102     local curl_response_file="$(mktemp -p /tmp)"
103     local curl_common_flags=(-s -w "%{http_code}" -o "${curl_response_file}")
104     local command=(curl "${curl_common_flags[@]}" "$@")
105
106     echo "[INFO] Running '${command[@]}'" >&2
107     if ! status="$("${command[@]}")"; then
108         echo "[ERROR] Internal curl error! '$status'" >&2
109         cat "${curl_response_file}"
110         rm "${curl_response_file}"
111     else
112         echo "[INFO] Server replied with status: ${status}" >&2
113         if [[ "${status:0:1}" =~ [45] ]]; then
114             cat "${curl_response_file}"
115         else
116             cat "${curl_response_file}" | jq .
117         fi
118         rm "${curl_response_file}"
119     fi
120 }
121
122 function delete_resource {
123     #Issues DELETE http call to provided endpoint
124     #and further validates by following GET request
125
126     call_api -X DELETE "$1"
127     ! call_api -X GET "$1" >/dev/null
128 }
129
130 # init_network() - This function creates the OVN resouces required by the test
131 function init_network {
132     local fname=$1
133     local router_name="ovn4nfv-master"
134
135     name=$(cat $fname | yq '.spec.name' | xargs)
136     subnet=$(cat $fname  | yq '.spec.subnet' | xargs)
137     gateway=$(cat $fname  | yq '.spec.gateway' | xargs)
138     ovn_central_address=$(get_ovn_central_address)
139
140     router_mac=$(printf '00:00:00:%02X:%02X:%02X' $((RANDOM%256)) $((RANDOM%256)) $((RANDOM%256)))
141     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
142     ovn-nbctl --may-exist --db tcp:$ovn_central_address lrp-add $router_name rtos-$name $router_mac $gateway
143     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\"
144 }
145
146 # cleanup_network() - This function removes the OVN resources created for the test
147 function cleanup_network {
148     local fname=$1
149
150     name=$(cat $fname | yq '.spec.name' | xargs)
151     ovn_central_address=$(get_ovn_central_address)
152
153     for cmd in "ls-del $name" "lrp-del rtos-$name" "lsp-del stor-$name"; do
154         ovn-nbctl --if-exist --db tcp:$ovn_central_address $cmd
155     done
156 }
157
158 function _checks_args {
159     if [[ -z $1 ]]; then
160         echo "Missing CSAR ID argument"
161         exit 1
162     fi
163     if [[ -z $CSAR_DIR ]]; then
164         echo "CSAR_DIR global environment value is empty"
165         exit 1
166     fi
167     mkdir -p ${CSAR_DIR}/${1}
168 }
169
170 # destroy_deployment() - This function ensures that a specific deployment is
171 # destroyed in Kubernetes
172 function destroy_deployment {
173     local deployment_name=$1
174
175     echo "$(date +%H:%M:%S) - $deployment_name : Destroying deployment"
176     kubectl delete deployment $deployment_name --ignore-not-found=true --now
177     while kubectl get deployment $deployment_name &>/dev/null; do
178         echo "$(date +%H:%M:%S) - $deployment_name : Destroying deployment"
179     done
180 }
181
182 # recreate_deployment() - This function destroys an existing deployment and
183 # creates an new one based on its yaml file
184 function recreate_deployment {
185     local deployment_name=$1
186
187     destroy_deployment $deployment_name
188     kubectl create -f $deployment_name.yaml
189 }
190
191 # wait_deployment() - Wait process to Running status on the Deployment's pods
192 function wait_deployment {
193     local deployment_name=$1
194
195     status_phase=""
196     while [[ $status_phase != "Running" ]]; do
197         new_phase=$(kubectl get pods | grep  $deployment_name | awk '{print $3}')
198         if [[ $new_phase != $status_phase ]]; then
199             echo "$(date +%H:%M:%S) - $deployment_name : $new_phase"
200             status_phase=$new_phase
201         fi
202         if [[ $new_phase == "Err"* ]]; then
203             exit 1
204         fi
205     done
206 }
207
208 # wait_for_pod() - Wait until first pod matched by kubectl filters is in running status
209 function wait_for_pod {
210     #Example usage:
211     # wait_for_pods example_pod
212     # wait_for_pods --namespace test different_pod
213     # wait_for_pods -n test -l app=plugin_test
214
215     status_phase=""
216     while [[ "$status_phase" != "Running" ]]; do
217         new_phase="$(kubectl get pods -o 'go-template={{ index .items 0 "status" "phase" }}' "$@" )"
218         if [[ "$new_phase" != "$status_phase" ]]; then
219             echo "$(date +%H:%M:%S) - Filter=[$*] : $new_phase"
220             status_phase="$new_phase"
221         fi
222         if [[ "$new_phase" == "Err"* ]]; then
223             exit 1
224         fi
225     done
226 }
227
228 # wait_for_deployment() - Wait until the deployment is ready
229 function wait_for_deployment {
230     #Example usage:
231     # wait_for_deployment $DEPLOYMENT_NAME $REPLICAS
232     # wait_for_deployment example_deployment 2
233
234     status="0/"
235
236     while [[ "$status" != $2* ]]; do
237         new_status=`kubectl get deployment -A | grep $1 | awk '{print $3}'`
238         if [[ "$new_status" != "$status" ]]; then
239             status="$new_status"
240         fi
241
242         pod_status=`kubectl get pods -A | grep $1 | awk '{print $4}'`
243         if [[ $pod_status =~ "Err" ]]; then
244             echo "Deployment $1 error"
245             exit 1
246         fi
247     done
248 }
249
250 # setup() - Base testing setup shared among functional tests
251 function setup {
252     if ! $(kubectl version &>/dev/null); then
253         echo "This funtional test requires kubectl client"
254         exit 1
255     fi
256     for deployment_name in $@; do
257         recreate_deployment $deployment_name
258     done
259     sleep 5
260     for deployment_name in $@; do
261         wait_deployment $deployment_name
262     done
263 }
264
265 # teardown() - Base testing teardown function
266 function teardown {
267     for deployment_name in $@; do
268         destroy_deployment $deployment_name
269     done
270 }
271
272 # check_ip_range() - Verifying IP address in address range
273 function check_ip_range {
274     local IP=$1
275     local MASK=$2
276
277     install_ipcalc
278
279     if [[ ! -e /usr/bin/ipcalc ]]; then
280         echo -e "Command 'ipcalc' not found"
281         return 0
282     fi
283
284     if [[ -z ${IP} ]] || [[ -z ${MASK} ]]; then
285             return 1
286     fi
287     min=`/usr/bin/ipcalc $MASK|awk '/HostMin:/{print $2}'`
288     max=`/usr/bin/ipcalc $MASK|awk '/HostMax:/{print $2}'`
289     MIN=`echo $min|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'`
290     MAX=`echo $max|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'`
291     IPvalue=`echo $IP|awk -F"." '{printf"%.0f\n",$1*256*256*256+$2*256*256+$3*256+$4}'`
292     if [[ "$IPvalue" -gt "$MIN" ]] && [[ "$IPvalue" -lt "$MAX" ]]; then
293         echo -e "$IP in ipset $MASK"
294         return 0
295     fi
296     echo -e "$IP not in ipset $MASK"
297     return 1
298 }
299
300 test_folder=${FUNCTIONS_DIR}