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