[lab-config] Use Pharos submodule by default
[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 "[WARN] ${DEPLOY_SCENARIO}.yaml not found!\n" 3
331     notify "[WARN] Setting simplest scenario (os-nosdn-nofeature-noha)\n" 3
332     DEPLOY_SCENARIO='os-nosdn-nofeature-noha'
333     if [ ! -f  "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml" ]; then
334         notify "[ERROR] Scenario definition file is missing!\n" 1>&2
335         exit 1
336     fi
337 fi
338
339 # Check defaults file existence
340 if [ ! -f  "${SCENARIO_DIR}/defaults-$(uname -i).yaml" ]; then
341     notify "[ERROR] Scenario defaults file is missing!\n" 1>&2
342     exit 1
343 fi
344
345 # Get required infra deployment data
346 set +x
347 eval "$(parse_yaml "${SCENARIO_DIR}/defaults-$(uname -i).yaml")"
348 eval "$(parse_yaml "${SCENARIO_DIR}/${DEPLOY_TYPE}/${DEPLOY_SCENARIO}.yaml")"
349 eval "$(parse_yaml "${LOCAL_PDF_RECLASS}")"
350 [[ "${CI_DEBUG}" =~ (false|0) ]] || set -x
351
352 export CLUSTER_DOMAIN=${cluster_domain}
353
354 # Serialize vnode data as '<name0>,<ram0>,<vcpu0>|<name1>,<ram1>,<vcpu1>[...]'
355 for node in "${virtual_nodes[@]}"; do
356     virtual_custom_ram="virtual_${node}_ram"
357     virtual_custom_vcpus="virtual_${node}_vcpus"
358     virtual_nodes_data+="${node},"
359     virtual_nodes_data+="${!virtual_custom_ram:-$virtual_default_ram},"
360     virtual_nodes_data+="${!virtual_custom_vcpus:-$virtual_default_vcpus}|"
361 done
362 virtual_nodes_data=${virtual_nodes_data%|}
363
364 # Serialize repos, packages to (pre-)install/remove for:
365 # - foundation node VM base image (virtual: all VMs, baremetal: cfg01|mas01)
366 # - virtualized control plane VM base image (only when VCP is used)
367 base_image_flavors=common
368 if [[ "${cluster_states[*]}" =~ virtual_control ]]; then
369   base_image_flavors+=" control"
370 fi
371 for sc in ${base_image_flavors}; do
372   for va in apt_keys apt_repos pkg_install pkg_remove; do
373     key=virtual_${sc}_${va}
374     eval "${key}=\${${key}[@]// /|}"
375     eval "${key}=\${${key}// /,}"
376     virtual_repos_pkgs+="${!key}^"
377   done
378 done
379 virtual_repos_pkgs=${virtual_repos_pkgs%^}
380
381 # Expand reclass and virsh network templates
382 for tp in "${RECLASS_CLUSTER_DIR}/all-mcp-arch-common/opnfv/"*.template \
383     net_*.template; do
384         eval "cat <<-EOF
385                 $(<"${tp}")
386                 EOF" 2> /dev/null > "${tp%.template}"
387 done
388
389 # Convert Pharos-compatible PDF to reclass network definitions
390 if [ "${DEPLOY_TYPE}" = 'baremetal' ]; then
391     find "${RECLASS_CLUSTER_DIR}" -name '*.j2' | while read -r tp
392     do
393         if ! "${PHAROS_GEN_CONFIG_SCRIPT}" -y "${LOCAL_PDF}" \
394           -j "${tp}" > "${tp%.j2}"; then
395              notify "[ERROR] Could not convert PDF to reclass network defs!\n"
396              exit 1
397         fi
398     done
399 fi
400
401 # Map PDF networks 'admin', 'mgmt', 'private' and 'public' to bridge names
402 BR_NAMES=('admin' 'mgmt' 'private' 'public')
403 BR_NETS=( \
404     "${paramaters__param_opnfv_infra_maas_pxe_address}" \
405     "${parameters__param_opnfv_infra_config_address}" \
406     "${parameters__param_opnfv_openstack_compute_node01_tenant_address}" \
407     "${parameters__param_opnfv_openstack_compute_node01_external_address}" \
408 )
409 for ((i = 0; i < ${#BR_NETS[@]}; i++)); do
410     br_jump=$(eval echo "\$parameters__param_opnfv_jump_bridge_${BR_NAMES[i]}")
411     if [ -n "${br_jump}" ] && [ "${br_jump}" != 'None' ] && \
412        [ -d "/sys/class/net/${br_jump}/bridge" ]; then
413             notify "[OK] Bridge found for '${BR_NAMES[i]}': ${br_jump}\n" 2
414             OPNFV_BRIDGES[${i}]="${br_jump}"
415     elif [ -n "${BR_NETS[i]}" ]; then
416         bridge=$(ip addr | awk "/${BR_NETS[i]%.*}./ {print \$NF; exit}")
417         if [ -n "${bridge}" ] && [ -d "/sys/class/net/${bridge}/bridge" ]; then
418             notify "[OK] Bridge found for net ${BR_NETS[i]%.*}.0: ${bridge}\n" 2
419             OPNFV_BRIDGES[${i}]="${bridge}"
420         fi
421     fi
422 done
423 notify "[NOTE] Using bridges: ${OPNFV_BRIDGES[*]}\n" 2
424
425 # Infra setup
426 if [ ${DRY_RUN} -eq 1 ]; then
427     notify "[NOTE] Dry run, skipping all deployment tasks\n" 2 1>&2
428     exit 0
429 elif [ ${USE_EXISTING_INFRA} -gt 0 ]; then
430     notify "[NOTE] Use existing infra\n" 2 1>&2
431     check_connection
432 else
433     generate_ssh_key
434     prepare_vms "${base_image}" "${STORAGE_DIR}" "${virtual_repos_pkgs}" \
435       "${virtual_nodes[@]}"
436     create_networks "${OPNFV_BRIDGES[@]}"
437     create_vms "${STORAGE_DIR}" "${virtual_nodes_data}" "${OPNFV_BRIDGES[@]}"
438     update_mcpcontrol_network
439     start_vms "${virtual_nodes[@]}"
440     check_connection
441 fi
442 if [ ${USE_EXISTING_INFRA} -lt 2 ]; then
443     wait_for 5 "./salt.sh ${LOCAL_PDF_RECLASS}"
444 fi
445
446 # Openstack cluster setup
447 set +x
448 if [ ${INFRA_CREATION_ONLY} -eq 1 ] || [ ${NO_DEPLOY_ENVIRONMENT} -eq 1 ]; then
449     notify "[NOTE] Skip openstack cluster setup\n" 2
450 else
451     for state in "${cluster_states[@]}"; do
452         notify "[STATE] Applying state: ${state}\n" 2
453         # shellcheck disable=SC2086,2029
454         wait_for 5 "ssh ${SSH_OPTS} ${SSH_SALT} sudo \
455             CI_DEBUG=$CI_DEBUG ERASE_ENV=$ERASE_ENV \
456             /root/fuel/mcp/config/states/${state}"
457     done
458 fi
459
460 ./log.sh "${DEPLOY_LOG}"
461
462 popd > /dev/null
463
464 #
465 # END of main
466 ##############################################################################