Merge "Add support for different public network netmask"
[fuel.git] / ci / deploy.sh
1 #!/bin/bash -e
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     local RC=$?
17     cleanup_mounts > /dev/null 2>&1
18     if [ ${RC} -eq 0 ]; then
19         notify "\n[OK] MCP: Openstack installation finished succesfully!\n\n" 2
20     else
21         notify "\n[ERROR] MCP: Openstack installation threw a fatal error!\n\n"
22     fi
23 }
24 #
25 # End of Exit handlers
26 ##############################################################################
27
28 ##############################################################################
29 # BEGIN of usage description
30 #
31 usage ()
32 {
33 cat << EOF
34 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
35 $(notify "$(basename "$0"): Deploy the Fuel@OPNFV MCP stack" 3)
36
37 $(notify "USAGE:" 2)
38   $(basename "$0") -l lab-name -p pod-name -s deploy-scenario \\
39     [-b Lab Config Base URI] \\
40     [-B PXE Bridge [-B Mgmt Bridge [-B Internal Bridge [-B Public Bridge]]]] \\
41     [-S storage-dir] [-L /path/to/log/file.tar.gz] \\
42     [-f[f]] [-F] [-e | -E[E]] [-d] [-D]
43
44 $(notify "OPTIONS:" 2)
45   -b  Base-uri for the stack-configuration structure
46   -B  Bridge(s): 1st usage = PXE, 2nd = Mgmt, 3rd = Internal, 4th = Public
47   -d  Dry-run
48   -D  Debug logging
49   -e  Do not launch environment deployment
50   -E  Remove existing VCP VMs (use twice to redeploy baremetal nodes)
51   -f  Deploy on existing Salt master (use twice to also skip config sync)
52   -F  Do only create a Salt master
53   -h  Print this message and exit
54   -l  Lab-name
55   -p  Pod-name
56   -P  Skip installation of package dependencies
57   -s  Deploy-scenario short-name
58   -S  Storage dir for VM images
59   -L  Deployment log path and file name
60
61 $(notify "Description:" 2)
62 Deploys the Fuel@OPNFV stack on the indicated lab resource.
63
64 This script provides the Fuel@OPNFV deployment abstraction.
65 It depends on the OPNFV official configuration directory/file structure
66 and provides a fairly simple mechanism to execute a deployment.
67
68 $(notify "Input parameters to the build script are:" 2)
69 -b Base URI to the configuration directory (needs to be provided in URI style,
70    it can be a local resource: file:// or a remote resource http(s)://).
71    A POD Descriptor File (PDF) and its Installer Descriptor File (IDF)
72    companion should be available at:
73    <base-uri>/labs/<lab-name>/<pod-name>.yaml
74    <base-uri>/labs/<lab-name>/idf-<pod-name>.yaml
75    An example config is provided inside current repo in
76    <./mcp/config>.
77    The default is using the git submodule tracking 'OPNFV Pharos' in
78    <./mcp/scripts/pharos>.
79 -B Bridges to be used by deploy script. It can be specified several times,
80    or as a comma separated list of bridges, or both: -B br1 -B br2,br3
81    First occurence sets PXE Brige, next Mgmt, then Internal and Public.
82    For an empty value, the deploy script will use virsh to create the default
83    expected network (e.g. -B pxe,,,public will use existing "pxe" and "public"
84    bridges, respectively create "mgmt" and "internal").
85    Note that a virtual network "mcpcontrol" is always created. For virtual
86    deploys, "mcpcontrol" is also used for PXE, leaving the PXE bridge unused.
87    For baremetal deploys, PXE bridge is used for baremetal node provisioning,
88    while "mcpcontrol" is used to provision the infrastructure VMs only.
89    The default is 'pxebr'.
90 -d Dry-run - Produce deploy config files, but do not execute deploy
91 -D Debug logging - Enable extra logging in sh deploy scripts (set -x)
92 -e Do not launch environment deployment
93 -E Remove existing VCP VMs. It will destroy and undefine all VCP VMs
94    currently defined on cluster KVM nodes. If specified twice (e.g. -E -E),
95    baremetal nodes (VCP too, implicitly) will be removed, then reprovisioned.
96    Only applicable for baremetal deploys.
97 -f Deploy on existing Salt master. It will skip infrastructure VM creation,
98    but it will still sync reclass configuration from current repo to Salt
99    Master node. If specified twice (e.g. -f -f), config sync will also be
100    skipped.
101 -F Do only create a Salt master
102 -h Print this message and exit
103 -L Deployment log path and name, eg. -L /home/jenkins/job.log.tar.gz
104 -l Lab name as defined in the configuration directory, e.g. lf
105 -p POD name as defined in the configuration directory, e.g. pod2
106 -P Skip installing dependency distro packages on current host
107    This flag should only be used if you have kept back older packages that
108    would be upgraded and that is undesirable on the current system.
109    Note that without the required packages, deploy will fail.
110 -s Deployment-scenario, this points to a short deployment scenario name, which
111    has to be defined in config directory (e.g. os-odl-nofeature-ha).
112 -S Storage dir for VM images, default is mcp/deploy/images
113
114 $(notify "[NOTE] sudo & virsh priviledges are needed for this script to run" 3)
115
116 Example:
117
118 $(notify "sudo $(basename "$0") \\
119   -b file:///home/jenkins/securedlab \\
120   -l lf -p pod2 \\
121   -s os-odl-nofeature-ha" 2)
122 EOF
123 }
124
125 #
126 # END of usage description
127 ##############################################################################
128
129 ##############################################################################
130 # BEGIN of colored notification wrapper
131 #
132 notify() {
133     tput setaf "${2:-1}" || true
134     echo -en "${1:-"[WARN] Unsupported opt arg: $3\\n"}"
135     tput sgr0
136 }
137 #
138 # END of colored notification wrapper
139 ##############################################################################
140
141 ##############################################################################
142 # BEGIN of variables to customize
143 #
144 CI_DEBUG=${CI_DEBUG:-0}; [[ "${CI_DEBUG}" =~ (false|0) ]] || set -x
145 REPO_ROOT_PATH=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")
146 DEPLOY_DIR=$(cd "${REPO_ROOT_PATH}/mcp/scripts"; pwd)
147 STORAGE_DIR=$(cd "${REPO_ROOT_PATH}/mcp/deploy/images"; pwd)
148 RECLASS_CLUSTER_DIR=$(cd "${REPO_ROOT_PATH}/mcp/reclass/classes/cluster"; pwd)
149 DEPLOY_TYPE='baremetal'
150 OPNFV_BRIDGES=('pxebr' 'mgmt' 'internal' 'public')
151 URI_REGEXP='(file|https?|ftp)://.*'
152 BASE_CONFIG_URI="file://${REPO_ROOT_PATH}/mcp/scripts/pharos"
153
154 # Customize deploy workflow
155 DRY_RUN=${DRY_RUN:-0}
156 USE_EXISTING_PKGS=${USE_EXISTING_PKGS:-0}
157 USE_EXISTING_INFRA=${USE_EXISTING_INFRA:-0}
158 INFRA_CREATION_ONLY=${INFRA_CREATION_ONLY:-0}
159 NO_DEPLOY_ENVIRONMENT=${NO_DEPLOY_ENVIRONMENT:-0}
160 ERASE_ENV=${ERASE_ENV:-0}
161
162 source "${DEPLOY_DIR}/globals.sh"
163 source "${DEPLOY_DIR}/lib.sh"
164
165 #
166 # END of variables to customize
167 ##############################################################################
168
169 ##############################################################################
170 # BEGIN of main
171 #
172 set +x
173 OPNFV_BRIDGE_IDX=0
174 while getopts "b:B:dDfEFl:L:p:Ps:S:he" OPTION
175 do
176     case $OPTION in
177         b)
178             BASE_CONFIG_URI=${OPTARG}
179             if [[ ! $BASE_CONFIG_URI =~ ${URI_REGEXP} ]]; then
180                 notify "[ERROR] -b $BASE_CONFIG_URI - invalid URI\n"
181                 usage
182                 exit 1
183             fi
184             ;;
185         B)
186             OIFS=${IFS}
187             IFS=','
188             OPT_BRIDGES=($OPTARG)
189             for bridge in "${OPT_BRIDGES[@]}"; do
190                 if [ -n "${bridge}" ]; then
191                     OPNFV_BRIDGES[${OPNFV_BRIDGE_IDX}]="${bridge}"
192                 fi
193                 ((OPNFV_BRIDGE_IDX+=1))
194             done
195             IFS=${OIFS}
196             ;;
197         d)
198             DRY_RUN=1
199             ;;
200         D)
201             CI_DEBUG=1
202             ;;
203         f)
204             ((USE_EXISTING_INFRA+=1))
205             ;;
206         F)
207             INFRA_CREATION_ONLY=1
208             ;;
209         e)
210             NO_DEPLOY_ENVIRONMENT=1
211             ;;
212         E)
213             ((ERASE_ENV+=1))
214             ;;
215         l)
216             TARGET_LAB=${OPTARG}
217             ;;
218         L)
219             DEPLOY_LOG="${OPTARG}"
220             ;;
221         p)
222             TARGET_POD=${OPTARG}
223             if [[ "${TARGET_POD}" =~ "virtual" ]]; then
224                 DEPLOY_TYPE='virtual'
225             fi
226             ;;
227         P)
228             USE_EXISTING_PKGS=1
229             ;;
230         s)
231             DEPLOY_SCENARIO=${OPTARG}
232             ;;
233         S)
234             if [[ ${OPTARG} ]]; then
235                 STORAGE_DIR="${OPTARG}"
236             fi
237             ;;
238         h)
239             usage
240             exit 0
241             ;;
242         *)
243             notify "[ERROR] Arguments not according to new argument style\n"
244             exit 1
245             ;;
246     esac
247 done
248
249 if [[ "$(sudo whoami)" != 'root' ]]; then
250     notify "[ERROR] This script requires sudo rights\n" 1>&2
251     exit 1
252 fi
253
254 # Validate mandatory arguments are set
255 if [ -z "${TARGET_LAB}" ] || [ -z "${TARGET_POD}" ] || \
256    [ -z "${DEPLOY_SCENARIO}" ]; then
257     notify "[ERROR] At least one of the mandatory args is missing!\n" 1>&2
258     usage
259     exit 1
260 fi
261
262 [[ "${CI_DEBUG}" =~ (false|0) ]] || set -x
263
264 # Enable the automatic exit trap
265 trap do_exit SIGINT SIGTERM EXIT
266
267 # Set no restrictive umask so that Jenkins can remove any residuals
268 umask 0000
269
270 pushd "${DEPLOY_DIR}" > /dev/null
271 # Prepare the deploy config files based on lab/pod information, deployment
272 # scenario, etc.
273
274 # Install required packages on jump server
275 if [ ${USE_EXISTING_PKGS} -eq 1 ]; then
276     notify "[NOTE] Skipping distro pkg installation\n" 2 1>&2
277 else
278     notify "[NOTE] Installing required distro pkgs\n" 2 1>&2
279     if [ -n "$(command -v apt-get)" ]; then
280       pkg_type='deb'; pkg_cmd='sudo apt-get install -y'
281     else
282       pkg_type='rpm'; pkg_cmd='sudo yum install -y --skip-broken'
283     fi
284     eval "$(parse_yaml "./requirements_${pkg_type}.yaml")"
285     for section in 'common' "${DEPLOY_TYPE}" "$(uname -m)"; do
286       section_var="requirements_pkg_${section}[*]"
287       pkg_list+=" ${!section_var}"
288     done
289     # shellcheck disable=SC2086
290     ${pkg_cmd} ${pkg_list}
291 fi
292
293 if ! virsh list >/dev/null 2>&1; then
294     notify "[ERROR] This script requires hypervisor access\n" 1>&2
295     exit 1
296 fi
297
298 # Collect jump server system information for deploy debugging
299 ./sysinfo_print.sh
300
301 # Clone git submodules and apply our patches
302 make -C "${REPO_ROOT_PATH}/mcp/patches" deepclean patches-import
303
304 # Convert Pharos-compatible POD Descriptor File (PDF) to reclass model input
305 PHAROS_GEN_CONFIG_SCRIPT="./pharos/config/utils/generate_config.py"
306 PHAROS_INSTALLER_ADAPTER="./pharos/config/installers/fuel/pod_config.yml.j2"
307 BASE_CONFIG_PDF="${BASE_CONFIG_URI}/labs/${TARGET_LAB}/${TARGET_POD}.yaml"
308 BASE_CONFIG_IDF="${BASE_CONFIG_URI}/labs/${TARGET_LAB}/idf-${TARGET_POD}.yaml"
309 LOCAL_PDF="${STORAGE_DIR}/$(basename "${BASE_CONFIG_PDF}")"
310 LOCAL_IDF="${STORAGE_DIR}/$(basename "${BASE_CONFIG_IDF}")"
311 LOCAL_PDF_RECLASS="${STORAGE_DIR}/pod_config.yml"
312 if ! curl --create-dirs -o "${LOCAL_PDF}" "${BASE_CONFIG_PDF}"; then
313     if [ "${DEPLOY_TYPE}" = 'baremetal' ]; then
314         notify "[ERROR] Could not retrieve PDF (Pod Descriptor File)!\n" 1>&2
315         exit 1
316     else
317         notify "[WARN] Could not retrieve PDF (Pod Descriptor File)!\n" 3
318     fi
319 elif ! curl -o "${LOCAL_IDF}" "${BASE_CONFIG_IDF}"; then
320     notify "[WARN] POD has no IDF (Installer Descriptor File)!\n" 3
321 elif ! "${PHAROS_GEN_CONFIG_SCRIPT}" -y "${LOCAL_PDF}" \
322     -j "${PHAROS_INSTALLER_ADAPTER}" > "${LOCAL_PDF_RECLASS}"; then
323     notify "[ERROR] Could not convert PDF to reclass model input!\n" 1>&2
324     exit 1
325 fi
326
327 # Check scenario file existence
328 SCENARIO_DIR="../config/scenario"
329 if [ ! -f  "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml" ]; then
330     notify "[ERROR] Scenario definition file is missing!\n" 1>&2
331     exit 1
332 fi
333
334 # Check defaults file existence
335 if [ ! -f  "${SCENARIO_DIR}/defaults-$(uname -i).yaml" ]; then
336     notify "[ERROR] Scenario defaults file is missing!\n" 1>&2
337     exit 1
338 fi
339
340 # Get required infra deployment data
341 set +x
342 eval "$(parse_yaml "${SCENARIO_DIR}/defaults-$(uname -i).yaml")"
343 eval "$(parse_yaml "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml")"
344 eval "$(parse_yaml "${LOCAL_PDF_RECLASS}")"
345 [[ "${CI_DEBUG}" =~ (false|0) ]] || set -x
346
347 export CLUSTER_DOMAIN=${cluster_domain}
348
349 # Serialize vnode data as '<name0>,<ram0>,<vcpu0>|<name1>,<ram1>,<vcpu1>[...]'
350 for node in "${virtual_nodes[@]}"; do
351     virtual_custom_ram="virtual_${node}_ram"
352     virtual_custom_vcpus="virtual_${node}_vcpus"
353     virtual_nodes_data+="${node},"
354     virtual_nodes_data+="${!virtual_custom_ram:-$virtual_default_ram},"
355     virtual_nodes_data+="${!virtual_custom_vcpus:-$virtual_default_vcpus}|"
356 done
357 virtual_nodes_data=${virtual_nodes_data%|}
358
359 # Serialize repos, packages to (pre-)install/remove for:
360 # - foundation node VM base image (virtual: all VMs, baremetal: cfg01|mas01)
361 # - virtualized control plane VM base image (only when VCP is used)
362 base_image_flavors=common
363 if [[ "${cluster_states[*]}" =~ virtual_control ]]; then
364   base_image_flavors+=" control"
365 fi
366 for sc in ${base_image_flavors}; do
367   for va in apt_keys apt_repos pkg_install pkg_remove; do
368     key=virtual_${sc}_${va}
369     eval "${key}=\${${key}[@]// /|}"
370     eval "${key}=\${${key}// /,}"
371     virtual_repos_pkgs+="${!key}^"
372   done
373 done
374 virtual_repos_pkgs=${virtual_repos_pkgs%^}
375
376 # Expand reclass and virsh network templates
377 for tp in "${RECLASS_CLUSTER_DIR}/all-mcp-arch-common/opnfv/"*.template \
378     net_*.template; do
379         eval "cat <<-EOF
380                 $(<"${tp}")
381                 EOF" 2> /dev/null > "${tp%.template}"
382 done
383
384 # Convert Pharos-compatible PDF to reclass network definitions
385 if [ "${DEPLOY_TYPE}" = 'baremetal' ]; then
386     find "${RECLASS_CLUSTER_DIR}" -name '*.j2' | while read -r tp
387     do
388         if ! "${PHAROS_GEN_CONFIG_SCRIPT}" -y "${LOCAL_PDF}" \
389           -j "${tp}" > "${tp%.j2}"; then
390              notify "[ERROR] Could not convert PDF to reclass network defs!\n"
391              exit 1
392         fi
393     done
394 fi
395
396 # Map PDF networks 'admin', 'mgmt', 'private' and 'public' to bridge names
397 BR_NAMES=('admin' 'mgmt' 'private' 'public')
398 BR_NETS=( \
399     "${paramaters__param_opnfv_infra_maas_pxe_address}" \
400     "${parameters__param_opnfv_infra_config_address}" \
401     "${parameters__param_opnfv_openstack_compute_node01_tenant_address}" \
402     "${parameters__param_opnfv_openstack_compute_node01_external_address}" \
403 )
404 for ((i = 0; i < ${#BR_NETS[@]}; i++)); do
405     br_jump=$(eval echo "\$parameters__param_opnfv_jump_bridge_${BR_NAMES[i]}")
406     if [ -n "${br_jump}" ] && [ "${br_jump}" != 'None' ] && \
407        [ -d "/sys/class/net/${br_jump}/bridge" ]; then
408             notify "[OK] Bridge found for '${BR_NAMES[i]}': ${br_jump}\n" 2
409             OPNFV_BRIDGES[${i}]="${br_jump}"
410     elif [ -n "${BR_NETS[i]}" ]; then
411         bridge=$(ip addr | awk "/${BR_NETS[i]%.*}./ {print \$NF; exit}")
412         if [ -n "${bridge}" ] && [ -d "/sys/class/net/${bridge}/bridge" ]; then
413             notify "[OK] Bridge found for net ${BR_NETS[i]%.*}.0: ${bridge}\n" 2
414             OPNFV_BRIDGES[${i}]="${bridge}"
415         fi
416     fi
417 done
418 notify "[NOTE] Using bridges: ${OPNFV_BRIDGES[*]}\n" 2
419
420 # Infra setup
421 if [ ${DRY_RUN} -eq 1 ]; then
422     notify "[NOTE] Dry run, skipping all deployment tasks\n" 2 1>&2
423     exit 0
424 elif [ ${USE_EXISTING_INFRA} -gt 0 ]; then
425     notify "[NOTE] Use existing infra\n" 2 1>&2
426     check_connection
427 else
428     generate_ssh_key
429     prepare_vms "${base_image}" "${STORAGE_DIR}" "${virtual_repos_pkgs}" \
430       "${virtual_nodes[@]}"
431     create_networks "${OPNFV_BRIDGES[@]}"
432     create_vms "${STORAGE_DIR}" "${virtual_nodes_data}" "${OPNFV_BRIDGES[@]}"
433     update_mcpcontrol_network
434     start_vms "${virtual_nodes[@]}"
435     check_connection
436 fi
437 if [ ${USE_EXISTING_INFRA} -lt 2 ]; then
438     wait_for 5 "./salt.sh ${LOCAL_PDF_RECLASS}"
439 fi
440
441 # Openstack cluster setup
442 set +x
443 if [ ${INFRA_CREATION_ONLY} -eq 1 ] || [ ${NO_DEPLOY_ENVIRONMENT} -eq 1 ]; then
444     notify "[NOTE] Skip openstack cluster setup\n" 2
445 else
446     for state in "${cluster_states[@]}"; do
447         notify "[STATE] Applying state: ${state}\n" 2
448         # shellcheck disable=SC2086,2029
449         wait_for 5 "ssh ${SSH_OPTS} ${SSH_SALT} sudo \
450             CI_DEBUG=$CI_DEBUG ERASE_ENV=$ERASE_ENV \
451             /root/fuel/mcp/config/states/${state}"
452     done
453 fi
454
455 ./log.sh "${DEPLOY_LOG}"
456
457 popd > /dev/null
458
459 #
460 # END of main
461 ##############################################################################