states: Break on error, retry states up to 5 times
[fuel.git] / ci / deploy.sh
index dc5f774..908909d 100755 (executable)
@@ -1,6 +1,5 @@
-#!/bin/bash
-# shellcheck disable=SC2034,SC2154,SC1091
-set -ex
+#!/bin/bash -e
+# shellcheck disable=SC2034,SC2154,SC1090,SC1091
 ##############################################################################
 # Copyright (c) 2017 Ericsson AB, Mirantis Inc., Enea AB and others.
 # jonas.bjurel@ericsson.com
@@ -14,8 +13,13 @@ set -ex
 # BEGIN of Exit handlers
 #
 do_exit () {
+    local RC=$?
     clean
-    echo "Exiting ..."
+    if [ ${RC} -eq 0 ]; then
+        notify "\n[OK] MCP: Openstack installation finished succesfully!\n\n" 2
+    else
+        notify "\n[ERROR] MCP: Openstack installation threw a fatal error!\n\n"
+    fi
 }
 #
 # End of Exit handlers
@@ -33,11 +37,17 @@ $(notify "$(basename "$0"): Deploy the Fuel@OPNFV MCP stack" 3)
 $(notify "USAGE:" 2)
   $(basename "$0") -b base-uri -l lab-name -p pod-name -s deploy-scenario \\
     [-B PXE Bridge [-B Mgmt Bridge [-B Internal Bridge [-B Public Bridge]]]] \\
-    [-S storage-dir] [-L /path/to/log/file.tar.gz]
+    [-S storage-dir] [-L /path/to/log/file.tar.gz] \\
+    [-f [-f]] [-F] [-e] [-d] [-D]
 
 $(notify "OPTIONS:" 2)
   -b  Base-uri for the stack-configuration structure
   -B  Bridge(s): 1st usage = PXE, 2nd = Mgmt, 3rd = Internal, 4th = Public
+  -d  Dry-run
+  -D  Debug logging
+  -e  Do not launch environment deployment
+  -f  Deploy on existing Salt master (use twice to also skip config sync)
+  -F  Do only create a Salt master
   -h  Print this message and exit
   -l  Lab-name
   -p  Pod-name
@@ -45,14 +55,6 @@ $(notify "OPTIONS:" 2)
   -S  Storage dir for VM images
   -L  Deployment log path and file name
 
-$(notify "DISABLED OPTIONS (not yet supported with MCP):" 3)
-  -d  (disabled) Dry-run
-  -e  (disabled) Do not launch environment deployment
-  -f  (disabled) Deploy on existing Salt master
-  -F  (disabled) Do only create a Salt master
-  -i  (disabled) iso url
-  -T  (disabled) Timeout, in minutes, for the deploy.
-
 $(notify "Description:" 2)
 Deploys the Fuel@OPNFV stack on the indicated lab resource.
 
@@ -77,6 +79,14 @@ $(notify "Input parameters to the build script are:" 2)
    For baremetal deploys, PXE bridge is used for baremetal node provisioning,
    while "mcpcontrol" is used to provision the infrastructure VMs only.
    The default is 'pxebr'.
+-d Dry-run - Produce deploy config files, but do not execute deploy
+-D Debug logging - Enable extra logging in sh deploy scripts (set -x)
+-e Do not launch environment deployment
+-f Deploy on existing Salt master. It will skip infrastructure VM creation,
+   but it will still sync reclass configuration from current repo to Salt
+   Master node. If specified twice (e.g. -f -f), config sync will also be
+   skipped.
+-F Do only create a Salt master
 -h Print this message and exit
 -L Deployment log path and name, eg. -L /home/jenkins/job.log.tar.gz
 -l Lab name as defined in the configuration directory, e.g. lf
@@ -85,16 +95,6 @@ $(notify "Input parameters to the build script are:" 2)
    has to be defined in config directory (e.g. os-odl-nofeature-ha).
 -S Storage dir for VM images, default is mcp/deploy/images
 
-$(notify "Disabled input parameters (not yet supported with MCP):" 3)
--d (disabled) Dry-run - Produce deploy config files, but do not execute deploy
--f (disabled) Deploy on existing Salt master
--e (disabled) Do not launch environment deployment
--F (disabled) Do only create a Salt master
--T (disabled) Timeout, in minutes, for the deploy.
-   It defaults to using the DEPLOY_TIMEOUT environment variable when defined.
--i (disabled) .iso image to be deployed (needs to be provided in a URI
-   style, it can be a local resource: file:// or a remote resource http(s)://)
-
 $(notify "[NOTE] sudo & virsh priviledges are needed for this script to run" 3)
 
 Example:
@@ -135,6 +135,7 @@ clean() {
 ##############################################################################
 # BEGIN of variables to customize
 #
+CI_DEBUG=${CI_DEBUG:-0}; [[ "${CI_DEBUG}" =~ (false|0) ]] || set -x
 REPO_ROOT_PATH=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")
 DEPLOY_DIR=$(cd "${REPO_ROOT_PATH}/mcp/scripts"; pwd)
 STORAGE_DIR=$(cd "${REPO_ROOT_PATH}/mcp/deploy/images"; pwd)
@@ -144,30 +145,14 @@ OPNFV_BRIDGES=('pxebr' 'mgmt' 'internal' 'public')
 URI_REGEXP='(file|https?|ftp)://.*'
 BASE_CONFIG_URI="file://${REPO_ROOT_PATH}/mcp/config"
 
-export SSH_KEY=${SSH_KEY:-"/var/lib/opnfv/mcp.rsa"}
-export SALT_MASTER=${INSTALLER_IP:-10.20.0.2}
-export SALT_MASTER_USER=${SALT_MASTER_USER:-ubuntu}
-export MAAS_IP=${MAAS_IP:-${SALT_MASTER%.*}.3}
-export MAAS_PXE_NETWORK=${MAAS_PXE_NETWORK:-192.168.11.0}
+# Customize deploy workflow
+DRY_RUN=${DRY_RUN:-0}
+USE_EXISTING_INFRA=${USE_EXISTING_INFRA:-0}
+INFRA_CREATION_ONLY=${INFRA_CREATION_ONLY:-0}
+NO_DEPLOY_ENVIRONMENT=${NO_DEPLOY_ENVIRONMENT:-0}
 
-# Derivated from above global vars
-export MCP_CTRL_NETWORK_ROOTSTR=${SALT_MASTER%.*}
-export MAAS_PXE_NETWORK_ROOTSTR=${MAAS_PXE_NETWORK%.*}
-export SSH_OPTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ${SSH_KEY}"
-export SSH_SALT="${SALT_MASTER_USER}@${SALT_MASTER}"
+source "${DEPLOY_DIR}/globals.sh"
 
-# Variables below are disabled for now, to be re-introduced or removed later
-set +x
-USE_EXISTING_FUEL=''
-FUEL_CREATION_ONLY=''
-NO_DEPLOY_ENVIRONMENT=''
-DRY_RUN=0
-if ! [ -z "${DEPLOY_TIMEOUT}" ]; then
-    DEPLOY_TIMEOUT="-dt ${DEPLOY_TIMEOUT}"
-else
-    DEPLOY_TIMEOUT=""
-fi
-set -x
 #
 # END of variables to customize
 ##############################################################################
@@ -177,7 +162,7 @@ set -x
 #
 set +x
 OPNFV_BRIDGE_IDX=0
-while getopts "b:B:dfFl:L:p:s:S:T:i:he" OPTION
+while getopts "b:B:dDfFl:L:p:s:S:he" OPTION
 do
     case $OPTION in
         b)
@@ -196,25 +181,24 @@ do
                 if [ -n "${bridge}" ]; then
                     OPNFV_BRIDGES[${OPNFV_BRIDGE_IDX}]="${bridge}"
                 fi
-                OPNFV_BRIDGE_IDX=$((OPNFV_BRIDGE_IDX + 1))
+                ((OPNFV_BRIDGE_IDX+=1))
             done
             IFS=${OIFS}
             ;;
         d)
-            notify '' 3 "${OPTION}"; continue
             DRY_RUN=1
             ;;
+        D)
+            CI_DEBUG=1
+            ;;
         f)
-            notify '' 3 "${OPTION}"; continue
-            USE_EXISTING_FUEL='-nf'
+            ((USE_EXISTING_INFRA+=1))
             ;;
         F)
-            notify '' 3 "${OPTION}"; continue
-            FUEL_CREATION_ONLY='-fo'
+            INFRA_CREATION_ONLY=1
             ;;
         e)
-            notify '' 3 "${OPTION}"; continue
-            NO_DEPLOY_ENVIRONMENT='-nde'
+            NO_DEPLOY_ENVIRONMENT=1
             ;;
         l)
             TARGET_LAB=${OPTARG}
@@ -236,19 +220,6 @@ do
                 STORAGE_DIR="${OPTARG}"
             fi
             ;;
-        T)
-            notify '' 3 "${OPTION}"; continue
-            DEPLOY_TIMEOUT="-dt ${OPTARG}"
-            ;;
-        i)
-            notify '' 3 "${OPTION}"; continue
-            ISO=${OPTARG}
-            if [[ ! $ISO =~ ${URI_REGEXP} ]]; then
-                notify "[ERROR] -i $ISO - invalid URI\n"
-                usage
-                exit 1
-            fi
-            ;;
         h)
             usage
             exit 0
@@ -261,12 +232,12 @@ do
 done
 
 if [[ "$(sudo whoami)" != 'root' ]]; then
-    notify "This script requires sudo rights\n" 1>&2
+    notify "[ERROR] This script requires sudo rights\n" 1>&2
     exit 1
 fi
 
 if ! virsh list >/dev/null 2>&1; then
-    notify "This script requires hypervisor access\n" 1>&2
+    notify "[ERROR] This script requires hypervisor access\n" 1>&2
     exit 1
 fi
 
@@ -278,7 +249,7 @@ if [ -z "${TARGET_LAB}" ] || [ -z "${TARGET_POD}" ] || \
     exit 1
 fi
 
-set -x
+[[ "${CI_DEBUG}" =~ (false|0) ]] || set -x
 
 # Enable the automatic exit trap
 trap do_exit SIGINT SIGTERM EXIT
@@ -311,7 +282,9 @@ make -C "${REPO_ROOT_PATH}/mcp/patches" deepclean patches-import
 PHAROS_GEN_CONFIG_SCRIPT="./pharos/config/utils/generate_config.py"
 PHAROS_INSTALLER_ADAPTER="./pharos/config/installers/fuel/pod_config.yml.j2"
 BASE_CONFIG_PDF="${BASE_CONFIG_URI}/labs/${TARGET_LAB}/${TARGET_POD}.yaml"
+BASE_CONFIG_IDF="${BASE_CONFIG_URI}/labs/${TARGET_LAB}/idf-${TARGET_POD}.yaml"
 LOCAL_PDF="${STORAGE_DIR}/$(basename "${BASE_CONFIG_PDF}")"
+LOCAL_IDF="${STORAGE_DIR}/$(basename "${BASE_CONFIG_IDF}")"
 LOCAL_PDF_RECLASS="${STORAGE_DIR}/pod_config.yml"
 if ! curl --create-dirs -o "${LOCAL_PDF}" "${BASE_CONFIG_PDF}"; then
     if [ "${DEPLOY_TYPE}" = 'baremetal' ]; then
@@ -320,6 +293,8 @@ if ! curl --create-dirs -o "${LOCAL_PDF}" "${BASE_CONFIG_PDF}"; then
     else
         notify "[WARN] Could not retrieve PDF (Pod Descriptor File)!\n" 3
     fi
+elif ! curl -o "${LOCAL_IDF}" "${BASE_CONFIG_IDF}"; then
+    notify "[WARN] POD has no IDF (Installer Descriptor File)!\n" 3
 elif ! "${PHAROS_GEN_CONFIG_SCRIPT}" -y "${LOCAL_PDF}" \
     -j "${PHAROS_INSTALLER_ADAPTER}" > "${LOCAL_PDF_RECLASS}"; then
     notify "[ERROR] Could not convert PDF to reclass model input!\n" 1>&2
@@ -329,8 +304,8 @@ fi
 # Check scenario file existence
 SCENARIO_DIR="../config/scenario"
 if [ ! -f  "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml" ]; then
-    notify "[WARN] ${DEPLOY_SCENARIO}.yaml not found! \
-            Setting simplest scenario (os-nosdn-nofeature-noha)\n" 3
+    notify "[WARN] ${DEPLOY_SCENARIO}.yaml not found!\n" 3
+    notify "[WARN] Setting simplest scenario (os-nosdn-nofeature-noha)\n" 3
     DEPLOY_SCENARIO='os-nosdn-nofeature-noha'
     if [ ! -f  "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml" ]; then
         notify "[ERROR] Scenario definition file is missing!\n" 1>&2
@@ -345,9 +320,12 @@ if [ ! -f  "${SCENARIO_DIR}/defaults-$(uname -i).yaml" ]; then
 fi
 
 # Get required infra deployment data
+set +x
 source lib.sh
 eval "$(parse_yaml "${SCENARIO_DIR}/defaults-$(uname -i).yaml")"
 eval "$(parse_yaml "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml")"
+eval "$(parse_yaml "${LOCAL_PDF_RECLASS}")"
+[[ "${CI_DEBUG}" =~ (false|0) ]] || set -x
 
 export CLUSTER_DOMAIN=${cluster_domain}
 
@@ -361,27 +339,69 @@ done
 
 # Expand reclass and virsh network templates
 for tp in "${RECLASS_CLUSTER_DIR}/all-mcp-ocata-common/opnfv/"*.template \
-    net_*.template; do envsubst < "${tp}" > "${tp%.template}"; done
+    net_*.template; do
+        eval "cat <<-EOF
+               $(<"${tp}")
+               EOF" 2> /dev/null > "${tp%.template}"
+done
 
-# Infra setup
-generate_ssh_key
-prepare_vms virtual_nodes "${base_image}" "${STORAGE_DIR}"
-create_networks OPNFV_BRIDGES
-create_vms virtual_nodes virtual_nodes_ram virtual_nodes_vcpus \
-    OPNFV_BRIDGES "${STORAGE_DIR}"
-update_mcpcontrol_network
-start_vms virtual_nodes
-check_connection
+# Map PDF networks 'admin', 'mgmt', 'private' and 'public' to bridge names
+BR_NAMES=('admin' 'mgmt' 'private' 'public')
+BR_NETS=( \
+    "${parameters__param_opnfv_maas_pxe_address}" \
+    "${parameters__param_opnfv_infra_config_address}" \
+    "${parameters__param_opnfv_openstack_compute_node01_tenant_address}" \
+    "${parameters__param_opnfv_openstack_compute_node01_external_address}" \
+)
+for ((i = 0; i < ${#BR_NETS[@]}; i++)); do
+    br_jump=$(eval echo "\$parameters__param_opnfv_jump_bridge_${BR_NAMES[i]}")
+    if [ -n "${br_jump}" ] && [ "${br_jump}" != 'None' ] && \
+       [ -d "/sys/class/net/${br_jump}/bridge" ]; then
+            notify "[OK] Bridge found for '${BR_NAMES[i]}': ${br_jump}\n" 2
+            OPNFV_BRIDGES[${i}]="${br_jump}"
+    elif [ -n "${BR_NETS[i]}" ]; then
+        bridge=$(ip addr | awk "/${BR_NETS[i]%.*}./ {print \$NF; exit}")
+        if [ -n "${bridge}" ] && [ -d "/sys/class/net/${bridge}/bridge" ]; then
+            notify "[OK] Bridge found for net ${BR_NETS[i]%.*}.0: ${bridge}\n" 2
+            OPNFV_BRIDGES[${i}]="${bridge}"
+        fi
+    fi
+done
+notify "[NOTE] Using bridges: ${OPNFV_BRIDGES[*]}\n" 2
 
-./salt.sh "${LOCAL_PDF_RECLASS}"
+# Infra setup
+if [ ${DRY_RUN} -eq 1 ]; then
+    notify "[NOTE] Dry run, skipping all deployment tasks\n" 2 1>&2
+    exit 0
+elif [ ${USE_EXISTING_INFRA} -gt 0 ]; then
+    notify "[NOTE] Use existing infra\n" 2 1>&2
+    check_connection
+else
+    generate_ssh_key
+    prepare_vms virtual_nodes "${base_image}" "${STORAGE_DIR}"
+    create_networks OPNFV_BRIDGES
+    create_vms virtual_nodes virtual_nodes_ram virtual_nodes_vcpus \
+      OPNFV_BRIDGES "${STORAGE_DIR}"
+    update_mcpcontrol_network
+    start_vms virtual_nodes
+    check_connection
+fi
+if [ ${USE_EXISTING_INFRA} -lt 2 ]; then
+    wait_for 5 "./salt.sh ${LOCAL_PDF_RECLASS}"
+fi
 
 # Openstack cluster setup
-for state in "${cluster_states[@]}"; do
-    notify "STATE: ${state}\n" 2
-    # shellcheck disable=SC2086,2029
-    ssh ${SSH_OPTS} "ubuntu@${SALT_MASTER}" \
-        sudo "/root/fuel/mcp/config/states/${state} || true"
-done
+set +x
+if [ ${INFRA_CREATION_ONLY} -eq 1 ] || [ ${NO_DEPLOY_ENVIRONMENT} -eq 1 ]; then
+    notify "[NOTE] Skip openstack cluster setup\n" 2
+else
+    for state in "${cluster_states[@]}"; do
+        notify "[STATE] Applying state: ${state}\n" 2
+        # shellcheck disable=SC2086,2029
+        wait_for 5 "ssh ${SSH_OPTS} ${SSH_SALT} \
+            sudo /root/fuel/mcp/config/states/${state}"
+    done
+fi
 
 ./log.sh "${DEPLOY_LOG}"