926d52bdb0f8261d6c9b7ba9f144a0b6b55beef0
[fuel.git] / ci / deploy.sh
1 #!/bin/bash -ex
2 # shellcheck disable=SC2034,SC2154,SC1090,SC1091
3 ##############################################################################
4 # Copyright (c) 2017 Ericsson AB, Mirantis Inc., Enea AB and others.
5 # jonas.bjurel@ericsson.com
6 # All rights reserved. This program and the accompanying materials
7 # are made available under the terms of the Apache License, Version 2.0
8 # which accompanies this distribution, and is available at
9 # http://www.apache.org/licenses/LICENSE-2.0
10 ##############################################################################
11
12 ##############################################################################
13 # BEGIN of Exit handlers
14 #
15 do_exit () {
16     clean
17     echo "Exiting ..."
18 }
19 #
20 # End of Exit handlers
21 ##############################################################################
22
23 ##############################################################################
24 # BEGIN of usage description
25 #
26 usage ()
27 {
28 cat << EOF
29 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
30 $(notify "$(basename "$0"): Deploy the Fuel@OPNFV MCP stack" 3)
31
32 $(notify "USAGE:" 2)
33   $(basename "$0") -b base-uri -l lab-name -p pod-name -s deploy-scenario \\
34     [-B PXE Bridge [-B Mgmt Bridge [-B Internal Bridge [-B Public Bridge]]]] \\
35     [-S storage-dir] [-L /path/to/log/file.tar.gz] [-f] [-F] [-e] [-d]
36
37 $(notify "OPTIONS:" 2)
38   -b  Base-uri for the stack-configuration structure
39   -B  Bridge(s): 1st usage = PXE, 2nd = Mgmt, 3rd = Internal, 4th = Public
40   -d  Dry-run
41   -e  Do not launch environment deployment
42   -f  Deploy on existing Salt master
43   -F  Do only create a Salt master
44   -h  Print this message and exit
45   -l  Lab-name
46   -p  Pod-name
47   -s  Deploy-scenario short-name
48   -S  Storage dir for VM images
49   -L  Deployment log path and file name
50
51 $(notify "Description:" 2)
52 Deploys the Fuel@OPNFV stack on the indicated lab resource.
53
54 This script provides the Fuel@OPNFV deployment abstraction.
55 It depends on the OPNFV official configuration directory/file structure
56 and provides a fairly simple mechanism to execute a deployment.
57
58 $(notify "Input parameters to the build script are:" 2)
59 -b Base URI to the configuration directory (needs to be provided in URI style,
60    it can be a local resource: file:// or a remote resource http(s)://).
61    A POD Descriptor File (PDF) should be available at:
62    <base-uri>/labs/<lab-name>/<pod-name>.yaml
63    The default is './mcp/config'.
64 -B Bridges to be used by deploy script. It can be specified several times,
65    or as a comma separated list of bridges, or both: -B br1 -B br2,br3
66    First occurence sets PXE Brige, next Mgmt, then Internal and Public.
67    For an empty value, the deploy script will use virsh to create the default
68    expected network (e.g. -B pxe,,,public will use existing "pxe" and "public"
69    bridges, respectively create "mgmt" and "internal").
70    Note that a virtual network "mcpcontrol" is always created. For virtual
71    deploys, "mcpcontrol" is also used for PXE, leaving the PXE bridge unused.
72    For baremetal deploys, PXE bridge is used for baremetal node provisioning,
73    while "mcpcontrol" is used to provision the infrastructure VMs only.
74    The default is 'pxebr'.
75 -d Dry-run - Produce deploy config files, but do not execute deploy
76 -e Do not launch environment deployment
77 -f Deploy on existing Salt master
78 -F Do only create a Salt master
79 -h Print this message and exit
80 -L Deployment log path and name, eg. -L /home/jenkins/job.log.tar.gz
81 -l Lab name as defined in the configuration directory, e.g. lf
82 -p POD name as defined in the configuration directory, e.g. pod2
83 -s Deployment-scenario, this points to a short deployment scenario name, which
84    has to be defined in config directory (e.g. os-odl-nofeature-ha).
85 -S Storage dir for VM images, default is mcp/deploy/images
86
87 $(notify "[NOTE] sudo & virsh priviledges are needed for this script to run" 3)
88
89 Example:
90
91 $(notify "sudo $(basename "$0") \\
92   -b file:///home/jenkins/securedlab \\
93   -l lf -p pod2 \\
94   -s os-odl-nofeature-ha" 2)
95 EOF
96 }
97
98 #
99 # END of usage description
100 ##############################################################################
101
102 ##############################################################################
103 # BEGIN of colored notification wrapper
104 #
105 notify() {
106     tput setaf "${2:-1}" || true
107     echo -en "${1:-"[WARN] Unsupported opt arg: $3\\n"}"
108     tput sgr0
109 }
110 #
111 # END of colored notification wrapper
112 ##############################################################################
113
114 ##############################################################################
115 # BEGIN of deployment clean-up
116 #
117 clean() {
118     echo "Cleaning up deploy tmp directories"
119 }
120 #
121 # END of deployment clean-up
122 ##############################################################################
123
124 ##############################################################################
125 # BEGIN of variables to customize
126 #
127 REPO_ROOT_PATH=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")
128 DEPLOY_DIR=$(cd "${REPO_ROOT_PATH}/mcp/scripts"; pwd)
129 STORAGE_DIR=$(cd "${REPO_ROOT_PATH}/mcp/deploy/images"; pwd)
130 RECLASS_CLUSTER_DIR=$(cd "${REPO_ROOT_PATH}/mcp/reclass/classes/cluster"; pwd)
131 DEPLOY_TYPE='baremetal'
132 OPNFV_BRIDGES=('pxebr' 'mgmt' 'internal' 'public')
133 URI_REGEXP='(file|https?|ftp)://.*'
134 BASE_CONFIG_URI="file://${REPO_ROOT_PATH}/mcp/config"
135
136 # Customize deploy workflow
137 DRY_RUN=${DRY_RUN:-0}
138 USE_EXISTING_INFRA=${USE_EXISTING_INFRA:-0}
139 INFRA_CREATION_ONLY=${INFRA_CREATION_ONLY:-0}
140 NO_DEPLOY_ENVIRONMENT=${NO_DEPLOY_ENVIRONMENT:-0}
141
142 source "${DEPLOY_DIR}/globals.sh"
143
144 #
145 # END of variables to customize
146 ##############################################################################
147
148 ##############################################################################
149 # BEGIN of main
150 #
151 set +x
152 OPNFV_BRIDGE_IDX=0
153 while getopts "b:B:dfFl:L:p:s:S:he" OPTION
154 do
155     case $OPTION in
156         b)
157             BASE_CONFIG_URI=${OPTARG}
158             if [[ ! $BASE_CONFIG_URI =~ ${URI_REGEXP} ]]; then
159                 notify "[ERROR] -b $BASE_CONFIG_URI - invalid URI\n"
160                 usage
161                 exit 1
162             fi
163             ;;
164         B)
165             OIFS=${IFS}
166             IFS=','
167             OPT_BRIDGES=($OPTARG)
168             for bridge in "${OPT_BRIDGES[@]}"; do
169                 if [ -n "${bridge}" ]; then
170                     OPNFV_BRIDGES[${OPNFV_BRIDGE_IDX}]="${bridge}"
171                 fi
172                 OPNFV_BRIDGE_IDX=$((OPNFV_BRIDGE_IDX + 1))
173             done
174             IFS=${OIFS}
175             ;;
176         d)
177             DRY_RUN=1
178             ;;
179         f)
180             USE_EXISTING_INFRA=1
181             ;;
182         F)
183             INFRA_CREATION_ONLY=1
184             ;;
185         e)
186             NO_DEPLOY_ENVIRONMENT=1
187             ;;
188         l)
189             TARGET_LAB=${OPTARG}
190             ;;
191         L)
192             DEPLOY_LOG="${OPTARG}"
193             ;;
194         p)
195             TARGET_POD=${OPTARG}
196             if [[ "${TARGET_POD}" =~ "virtual" ]]; then
197                 DEPLOY_TYPE='virtual'
198             fi
199             ;;
200         s)
201             DEPLOY_SCENARIO=${OPTARG}
202             ;;
203         S)
204             if [[ ${OPTARG} ]]; then
205                 STORAGE_DIR="${OPTARG}"
206             fi
207             ;;
208         h)
209             usage
210             exit 0
211             ;;
212         *)
213             notify "[ERROR] Arguments not according to new argument style\n"
214             exit 1
215             ;;
216     esac
217 done
218
219 if [[ "$(sudo whoami)" != 'root' ]]; then
220     notify "[ERROR] This script requires sudo rights\n" 1>&2
221     exit 1
222 fi
223
224 if ! virsh list >/dev/null 2>&1; then
225     notify "[ERROR] This script requires hypervisor access\n" 1>&2
226     exit 1
227 fi
228
229 # Validate mandatory arguments are set
230 if [ -z "${TARGET_LAB}" ] || [ -z "${TARGET_POD}" ] || \
231    [ -z "${DEPLOY_SCENARIO}" ]; then
232     notify "[ERROR] At least one of the mandatory args is missing!\n" 1>&2
233     usage
234     exit 1
235 fi
236
237 set -x
238
239 # Enable the automatic exit trap
240 trap do_exit SIGINT SIGTERM EXIT
241
242 # Set no restrictive umask so that Jenkins can remove any residuals
243 umask 0000
244
245 clean
246
247 pushd "${DEPLOY_DIR}" > /dev/null
248 # Prepare the deploy config files based on lab/pod information, deployment
249 # scenario, etc.
250
251 # Install required packages
252 [ -n "$(command -v apt-get)" ] && sudo apt-get install -y \
253   git make rsync mkisofs curl virtinst cpu-checker qemu-kvm
254 [ -n "$(command -v yum)" ] && sudo yum install -y --skip-broken \
255   git make rsync genisoimage curl virt-install qemu-kvm
256
257 if [ "$(uname -i)" = "aarch64" ]; then
258   [ -n "$(command -v apt-get)" ] && sudo apt-get install -y vgabios && \
259   sudo ln -sf /usr/share/vgabios/vgabios.bin /usr/share/qemu/vgabios-stdvga.bin
260   [ -n "$(command -v yum)" ] && sudo yum install -y --skip-broken vgabios
261 fi
262
263 # Clone git submodules and apply our patches
264 make -C "${REPO_ROOT_PATH}/mcp/patches" deepclean patches-import
265
266 # Convert Pharos-compatible POD Descriptor File (PDF) to reclass model input
267 PHAROS_GEN_CONFIG_SCRIPT="./pharos/config/utils/generate_config.py"
268 PHAROS_INSTALLER_ADAPTER="./pharos/config/installers/fuel/pod_config.yml.j2"
269 BASE_CONFIG_PDF="${BASE_CONFIG_URI}/labs/${TARGET_LAB}/${TARGET_POD}.yaml"
270 BASE_CONFIG_IDF="${BASE_CONFIG_URI}/labs/${TARGET_LAB}/idf-${TARGET_POD}.yaml"
271 LOCAL_PDF="${STORAGE_DIR}/$(basename "${BASE_CONFIG_PDF}")"
272 LOCAL_IDF="${STORAGE_DIR}/$(basename "${BASE_CONFIG_IDF}")"
273 LOCAL_PDF_RECLASS="${STORAGE_DIR}/pod_config.yml"
274 if ! curl --create-dirs -o "${LOCAL_PDF}" "${BASE_CONFIG_PDF}"; then
275     if [ "${DEPLOY_TYPE}" = 'baremetal' ]; then
276         notify "[ERROR] Could not retrieve PDF (Pod Descriptor File)!\n" 1>&2
277         exit 1
278     else
279         notify "[WARN] Could not retrieve PDF (Pod Descriptor File)!\n" 3
280     fi
281 elif ! curl -o "${LOCAL_IDF}" "${BASE_CONFIG_IDF}"; then
282     notify "[WARN] POD has no IDF (Installer Descriptor File)!\n" 3
283 elif ! "${PHAROS_GEN_CONFIG_SCRIPT}" -y "${LOCAL_PDF}" \
284     -j "${PHAROS_INSTALLER_ADAPTER}" > "${LOCAL_PDF_RECLASS}"; then
285     notify "[ERROR] Could not convert PDF to reclass model input!\n" 1>&2
286     exit 1
287 fi
288
289 # Check scenario file existence
290 SCENARIO_DIR="../config/scenario"
291 if [ ! -f  "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml" ]; then
292     notify "[WARN] ${DEPLOY_SCENARIO}.yaml not found! \
293             Setting simplest scenario (os-nosdn-nofeature-noha)\n" 3
294     DEPLOY_SCENARIO='os-nosdn-nofeature-noha'
295     if [ ! -f  "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml" ]; then
296         notify "[ERROR] Scenario definition file is missing!\n" 1>&2
297         exit 1
298     fi
299 fi
300
301 # Check defaults file existence
302 if [ ! -f  "${SCENARIO_DIR}/defaults-$(uname -i).yaml" ]; then
303     notify "[ERROR] Scenario defaults file is missing!\n" 1>&2
304     exit 1
305 fi
306
307 # Get required infra deployment data
308 set +x
309 source lib.sh
310 eval "$(parse_yaml "${SCENARIO_DIR}/defaults-$(uname -i).yaml")"
311 eval "$(parse_yaml "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml")"
312 eval "$(parse_yaml "${LOCAL_PDF_RECLASS}")"
313 set -x
314
315 export CLUSTER_DOMAIN=${cluster_domain}
316
317 declare -A virtual_nodes_ram virtual_nodes_vcpus
318 for node in "${virtual_nodes[@]}"; do
319     virtual_custom_ram="virtual_${node}_ram"
320     virtual_custom_vcpus="virtual_${node}_vcpus"
321     virtual_nodes_ram[$node]=${!virtual_custom_ram:-$virtual_default_ram}
322     virtual_nodes_vcpus[$node]=${!virtual_custom_vcpus:-$virtual_default_vcpus}
323 done
324
325 # Expand reclass and virsh network templates
326 for tp in "${RECLASS_CLUSTER_DIR}/all-mcp-ocata-common/opnfv/"*.template \
327     net_*.template; do
328         eval "cat <<-EOF
329                 $(<"${tp}")
330                 EOF" 2> /dev/null > "${tp%.template}"
331 done
332
333 # Map PDF networks 'admin', 'mgmt', 'private' and 'public' to bridge names
334 BR_NAMES=('admin' 'mgmt' 'private' 'public')
335 BR_NETS=( \
336     "${parameters__param_opnfv_maas_pxe_address}" \
337     "${parameters__param_opnfv_infra_config_address}" \
338     "${parameters__param_opnfv_openstack_compute_node01_tenant_address}" \
339     "${parameters__param_opnfv_openstack_compute_node01_external_address}" \
340 )
341 for ((i = 0; i < ${#BR_NETS[@]}; i++)); do
342     br_jump=$(eval echo "\$parameters__param_opnfv_jump_bridge_${BR_NAMES[i]}")
343     if [ -n "${br_jump}" ] && [ "${br_jump}" != 'None' ] && \
344        [ -d "/sys/class/net/${br_jump}/bridge" ]; then
345             notify "[OK] Bridge found for '${BR_NAMES[i]}': ${br_jump}\n" 2
346             OPNFV_BRIDGES[${i}]="${br_jump}"
347     elif [ -n "${BR_NETS[i]}" ]; then
348         bridge=$(ip addr | awk "/${BR_NETS[i]%.*}./ {print \$NF; exit}")
349         if [ -n "${bridge}" ] && [ -d "/sys/class/net/${bridge}/bridge" ]; then
350             notify "[OK] Bridge found for net ${BR_NETS[i]%.*}.0: ${bridge}\n" 2
351             OPNFV_BRIDGES[${i}]="${bridge}"
352         fi
353     fi
354 done
355 notify "[NOTE] Using bridges: ${OPNFV_BRIDGES[*]}\n" 2
356
357 # Infra setup
358 if [ ${DRY_RUN} -eq 1 ]; then
359     notify "[NOTE] Dry run, skipping all deployment tasks\n" 2 1>&2
360     exit 0
361 elif [ ${USE_EXISTING_INFRA} -eq 1 ]; then
362     notify "[NOTE] Use existing infra\n" 2 1>&2
363     check_connection
364 else
365     generate_ssh_key
366     prepare_vms virtual_nodes "${base_image}" "${STORAGE_DIR}"
367     create_networks OPNFV_BRIDGES
368     create_vms virtual_nodes virtual_nodes_ram virtual_nodes_vcpus \
369       OPNFV_BRIDGES "${STORAGE_DIR}"
370     update_mcpcontrol_network
371     start_vms virtual_nodes
372     check_connection
373     ./salt.sh "${LOCAL_PDF_RECLASS}"
374 fi
375
376 # Openstack cluster setup
377 set +x
378 if [ ${INFRA_CREATION_ONLY} -eq 1 ] || [ ${NO_DEPLOY_ENVIRONMENT} -eq 1 ]; then
379     notify "[NOTE] Skip openstack cluster setup\n" 2
380 else
381     for state in "${cluster_states[@]}"; do
382         notify "[STATE] Applying state: ${state}\n" 2
383         # shellcheck disable=SC2086,2029
384         ssh ${SSH_OPTS} "${SSH_SALT}" \
385             sudo "/root/fuel/mcp/config/states/${state} || true"
386     done
387 fi
388
389 ./log.sh "${DEPLOY_LOG}"
390
391 popd > /dev/null
392
393 notify "\n[DONE] MCP: Openstack installation finished succesfully!\n\n" 2
394
395 #
396 # END of main
397 ##############################################################################