builder: if new ansible dir doesn't exist, use old one.
[ci-management.git] / docker / scripts / lib_csit.sh
1 # lib_csit.sh - Docker build script CSIT library.
2 #               For import only.
3
4 # Copyright (c) 2021 Cisco and/or its affiliates.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at:
8 #
9 #     http://www.apache.org/licenses/LICENSE-2.0
10 #
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16
17 # Don't import more than once.
18 if [ -n "$(alias lib_csit_imported 2> /dev/null)" ] ; then
19     return 0
20 fi
21 alias lib_csit_imported=true
22
23 export CIMAN_DOCKER_SCRIPTS="${CIMAN_DOCKER_SCRIPTS:-$(dirname $BASH_SOURCE)}"
24 . "$CIMAN_DOCKER_SCRIPTS/lib_common.sh"
25 . "$CIMAN_DOCKER_SCRIPTS/lib_apt.sh"
26 . "$CIMAN_DOCKER_SCRIPTS/lib_yum.sh"
27 . "$CIMAN_DOCKER_SCRIPTS/lib_dnf.sh"
28
29 CSIT_SUPPORTED_EXECUTOR_CLASSES="builder csit_dut"
30 csit_supported_executor_class() {
31     if ! grep -q "${1:-}" <<< "$CSIT_SUPPORTED_EXECUTOR_CLASSES" ; then
32         return 1
33     fi
34     return 0
35 }
36
37 csit_supported_os() {
38     case "$1" in
39         # TODO: Remove ubuntu-18.04 once CSIT has completed transition
40         #       to ubuntu-20.04
41         ubuntu-18.04) return 0 ;;
42         ubuntu-20.04) return 0 ;;
43                    *) ;;
44     esac
45     return 1
46 }
47
48 csit_checkout_branch_for_vpp() {
49     local vpp_branch="$1"
50     local csit_dir="$DOCKER_CSIT_DIR"
51     local csit_bash_function_dir="$csit_dir/resources/libraries/bash/function"
52
53     # import checkout_csit_for_vpp() if not defined
54     set +e && [ -z "$(declare -f checkout_csit_for_vpp)" ] \
55         && source "$csit_bash_function_dir/branch.sh"
56     CSIT_DIR="$csit_dir" checkout_csit_for_vpp "$vpp_branch"
57
58     csit_branch="$(git branch | grep -e '^*' | mawk '{print $2}')"
59 }
60
61 csit_install_packages() {
62     local branch="$1"
63     local branchname="$(echo $branch | sed -e 's,/,_,')"
64     local csit_dir="$DOCKER_CSIT_DIR"
65     local csit_ansible_dir="$csit_dir/fdio.infra.ansible"
66     if [ ! -d "$csit_ansible_dir" ] ; then
67         csit_ansible_dir="$csit_dir/resources/tools/testbed-setup/ansible"
68     fi
69     local bld_log="$DOCKER_BUILD_LOG_DIR/$FDIOTOOLS_IMAGENAME"
70     bld_log="${bld_log}-$branchname-csit_install_packages-bld.log"
71
72     git clean -qfdx
73
74     # Install PyYAML required by dbld_csit_find_ansible_packages.py
75     #
76     # Note: Conditional install due to Bug 1696324 -
77     #       Update to python3.6 breaks PyYAML dependencies
78     # Status:       CLOSED CANTFIX
79     # https://bugzilla.redhat.com/show_bug.cgi?id=1696324
80     if [ "$OS_NAME" = "centos-8" ] ; then
81         dnf_install_packages python3-pyyaml
82     else
83         python3 -m pip install pyyaml
84     fi
85
86     local exclude_roles="-e calibration -e kernel -e mellanox -e nomad -e consul"
87     [ "$OS_ARCH" = "aarch64" ] && exclude_roles="$exclude_roles -e iperf"
88
89     # Not in double quotes to let bash remove newline characters
90     local yaml_files="$(grep -r packages_by $csit_ansible_dir | cut -d: -f1 | sort -u | grep -v $exclude_roles)"
91     packages="$(dbld_csit_find_ansible_packages.py --$OS_ID --$OS_ARCH $yaml_files)"
92     packages="${packages/bionic /}"
93     packages="${packages/focal /}"
94
95     # TODO: Fix Ubuntu-18.04 specific package names that fail on Ubuntu-20.04
96     #       (remove when CSIT is updated)
97     if [ "$OS_NAME" = "ubuntu-20.04" ] ; then
98         packages="${packages/libmbedcrypto1/libmbedcrypto3}"
99         packages="${packages/libmbedtls10/libmbedtls12}"
100         packages="$(echo ${packages//python\-/python3\-} | tr ' ' '\n' | sort -u | xargs)"
101     fi
102     if [ -n "$packages" ] ; then
103         case "$OS_NAME" in
104             ubuntu*)
105                 apt_install_packages $packages
106                 ;;
107             debian*)
108                 apt_install_packages $packages
109                 ;;
110             centos-7)
111                 yum_install_packages $packages
112                 ;;
113             centos-8)
114                 dnf_install_packages $packages
115                 ;;
116             *)
117                 echo "Unsupported OS ($OS_ID): CSIT packages NOT INSTALLED!"
118                 ;;
119         esac
120     fi
121 }
122
123 csit_pip_cache() {
124     local branch="$1"
125     local VENV_OPTS=""
126     # ensure PS1 is defined (used by virtualenv activate script)
127     PS1=${PS1:-"#"}
128     CSIT_DIR="$DOCKER_CSIT_DIR"
129
130     if [ -f "$CSIT_DIR/VPP_REPO_URL" ] \
131            && [ -f "$CSIT_DIR/requirements.txt" ]; then
132
133         local csit_bash_function_dir="$CSIT_DIR/resources/libraries/bash/function"
134         local branchname="$(echo $branch | sed -e 's,/,_,')"
135         local bld_log="$DOCKER_BUILD_LOG_DIR"
136         bld_log="${bld_log}/$FDIOTOOLS_IMAGENAME-$branchname-csit_pip_cache-bld.log"
137         local pip_cmd="python3 -m pip --disable-pip-version-check"
138         export PYTHONPATH=$CSIT_DIR
139
140         description="Install CSIT python packages from $branch branch"
141         echo_log "    Starting  $description..."
142         git clean -qfdx
143         rm -rf "$PYTHONPATH/env"
144
145         # TODO: Update CSIT release branches to avoid build breakage
146         #       Fixes https://github.com/pypa/pip/issues/8260
147         $pip_cmd install pip==21.0.1
148         #       rls2009_lts-* branches missing cherry-pick of
149         #       https://gerrit.fd.io/r/c/csit/+/31338
150         sed -i 's/scipy==1.1.0/scipy==1.5.4/' "$PYTHONPATH/requirements.txt"
151
152         # Virtualenv version is pinned in common.sh in newer csit branches.
153         # (note: xargs removes leading/trailing spaces)
154         local common_sh="$csit_bash_function_dir/common.sh"
155         install_virtualenv="$(grep 'virtualenv' $common_sh | grep pip | grep install | cut -d'|' -f1 | xargs)"
156         $install_virtualenv
157         virtualenv --no-download --python=$(which python3) "$CSIT_DIR/env"
158         source "$CSIT_DIR/env/bin/activate"
159
160         if [ "$OS_ARCH" = "aarch64" ] ; then
161             local numpy_ver="$(grep numpy $PYTHONPATH/requirements.txt)"
162             [ -n "$numpy_ver" ] && $pip_cmd install $numpy_ver 2>&1 | \
163                 tee -a $bld_log
164         fi
165
166         # Install csit python requirements
167         $pip_cmd install -r "$CSIT_DIR/requirements.txt" 2>&1 | \
168             tee -a "$bld_log"
169         # Install tox python requirements
170         $pip_cmd install -r "$CSIT_DIR/tox-requirements.txt" 2>&1 | \
171             tee -a "$bld_log"
172         # Run tox which installs pylint requirments
173         pushd $CSIT_DIR >& /dev/null
174         tox || true
175         popd >& /dev/null
176
177         # Clean up virtualenv directories
178         deactivate
179         git checkout -q -- .
180         git clean -qfdx
181         echo_log "    Completed $description!"
182     else
183         echo_log "ERROR: Missing or invalid CSIT_DIR: '$CSIT_DIR'!"
184         return 1
185     fi
186 }
187
188 docker_build_setup_csit() {
189     if csit_supported_executor_class "$EXECUTOR_CLASS" ; then
190         if [ ! -d "$DOCKER_CSIT_DIR" ] ; then
191             echo_log "Cloning CSIT into $DOCKER_CSIT_DIR..."
192             git clone -q https://gerrit.fd.io/r/csit "$DOCKER_CSIT_DIR"
193         fi
194         clean_git_repo "$DOCKER_CSIT_DIR"
195     fi
196 }
197
198 csit_dut_generate_docker_build_files() {
199     local build_files_dir="$DOCKER_BUILD_FILES_DIR"
200
201     mkdir -p "$build_files_dir"
202     cat <<EOF >"$build_files_dir/supervisord.conf"
203 [unix_http_server]
204 file = /tmp/supervisor.sock
205 chmod = 0777
206
207 [rpcinterface:supervisor]
208 supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
209
210 [supervisorctl]
211 serverurl = unix:///tmp/supervisor.sock
212
213 [supervisord]
214 pidfile = /tmp/supervisord.pid
215 identifier = supervisor
216 directory = /tmp
217 logfile = /tmp/supervisord.log
218 loglevel = debug
219 nodaemon = false
220
221 [program:vpp]
222 command = /usr/bin/vpp -c /etc/vpp/startup.conf
223 autostart = false
224 autorestart = true
225 redirect_stderr = true
226 priority = 1
227 EOF
228 }
229
230 csit_builder_generate_docker_build_files() {
231     local build_files_dir="$DOCKER_BUILD_FILES_DIR"
232     local dashes="-----"
233     local dbeg="${dashes}BEGIN"
234     local dend="${dashes}END"
235     local pvt="PRIVATE"
236     local kd="KEY$dashes"
237
238     # TODO: Verify why badkey is required & figure out how to avoid it.
239     mkdir -p "$build_files_dir"
240     cat <<EOF >"$build_files_dir/badkey"
241 $dbeg RSA $pvt $kd
242 MIIEowIBAAKCAQEAslDXf4kZOQI8OGQQdIF8o83nBM0B4fzHLYLxxiY2rKiQ5MGM
243 mQa7p1KKzmd5/NlvFRnXefnjSDQljjPxEY7mh457rX2nXvqHD4GUXZPpBIE73rQ1
244 TViIAXdDzFXJ6ee4yX8ewmVakzYBnlUPDidkWyRnjm/xCgKUCO+CD5AH3ND0onks
245 OYAtHqhDh29/QMIKdMnK87FBxfzhInHwpqPur76zBnpw3u36ylKEymDFrO5dwzsh
246 QvDWjsYRg9ydTXubtwP6+MOpjdR1SNKxcCHKJrPrdAeJW9jg1imYmYpEHZ/P3qsL
247 Jm0hGWbFjdxZLIYIz0vN/nTalcAeqT2OWKrXuwIDAQABAoIBAQCcj1g2FOR9ZlYD
248 WPANqucJVy4/y9OcXHlwnyiyRjj47WOSRdGxRfUa2uEeikHT3ACo8TB8WwfQDGDw
249 8u/075e+az5xvAJo5OQSnD3sz4Hmv6UWSvkFuPZo+xMe5C/M2/QljiQuoBifaeqP
250 3rTCQ5ncYCFAMU7b8BmTot551Ybhu2jCbDMHU7nFHEFOvYinkwfVcaqkrVDUuH+D
251 c3NkAEH9Jz2MEYA2Va4uqFpGt5lfGiED2kMenwPa8eS5LS5HJsxkfMHGlaHXHFUb
252 D+dG/qJtSslVxdzVPgEGvzswo6TgtY1nZTQcB8U63rktFg38B7QGtOkvswAYzxyk
253 HdMIiU3RAoGBAOdIEQRcAThj9eiIFywtBgLBOSg4SoOnvELLr6lgUg2+ICmx06LQ
254 yaai1QRdOWw1VwZ6apNCD00kaUhBu+ou93yLSDnR2uYftkylhcnVuhDyIeNyb81V
255 hV2z0WuNv3aKBFlBxaq391S7WW1XxhpAAagm8fZZur73wV390EVd/hZJAoGBAMVf
256 negT2bg5PVKWvsiEU6eZ00W97tlEDLclkiZawXNnM2/c+2x1Tks6Yf1E/j2FFTB4
257 r0fesbwN346hCejtq5Bup5YEdFA3KtwT5UyeQQLFGYlCtRmBtOd10wkRS93D0tpX
258 iIqkf43Gpx6iFdvBWY5A7N+ZmojCy9zpL5TJ4G3jAoGADOGEoRuGrd9TWMoLkFhJ
259 l2mvhz/rVn3HDGlPtT06FK3cGLZgtRavxGoZNw8CHbayzBeRS/ZH5+H5Qx72GkrX
260 WcZgFWhMqrhlbMtjMiSHIl556LL86xCyRs+3ACh6211AdMAnBCUOz1dH2cEjtV6P
261 ORBCNZg1wGEIEfYK3XIorpECgYBubXfQj8KhUs0fdx3Y3Ehdni/ZdlG7F1qx4YBq
262 mx5e7d+Wd6Hn5Z3fcxO9+yrvypS3YN5YrJzuZSiuCSWdP9RcY7y5r1ZQRv1g0nTZ
263 MDWZUiNea4cddTd8xKxFB3tV4SkIZi8LustuzDVWa0Mlh4EOmP6uf6c5WxtqRsEL
264 UwORFwKBgEjZsfmZGBurjOtSrcsteulOB0D2nOqPVRWXmbSNJT/l73DkEllvVyA/
265 wdW39nyFrA2Qw1K2F+l8DkzMd/WEjmioSWCsvTkXlvrqPfByKg01zCbYy/mhRW7d
266 7sQrPOIl8ygsc3JrxmvzibdWmng1MehvpAM1ogWeTUa1lsDTNJ/6
267 $dend RSA $pvt $kd
268 EOF
269     chmod 600 "$build_files_dir/badkey"
270     cat <<EOF >"$build_files_dir/sshconfig"
271 Host 172.17.0.*
272         StrictHostKeyChecking no
273         UserKnownHostsFile=/dev/null
274 EOF
275 }
276
277 csit_shim_generate_docker_build_files() {
278     local build_files_dir="$DOCKER_BUILD_FILES_DIR"
279     # TODO: Verify why badkey is required & figure out how to avoid it.
280     local badkey='AAAAB3NzaC1yc2EAAAADAQABAAABAQCyUNd/iRk5Ajw4ZBB0gXyjzecEzQHh/MctgvHGJjasqJDkwYyZBrunUorOZ3n82W8VGdd5+eNINCWOM/ERjuaHjnutfade+ocPgZRdk+kEgTvetDVNWIgBd0PMVcnp57jJfx7CZVqTNgGeVQ8OJ2RbJGeOb/EKApQI74IPkAfc0PSieSw5gC0eqEOHb39Awgp0ycrzsUHF/OEicfCmo+6vvrMGenDe7frKUoTKYMWs7l3DOyFC8NaOxhGD3J1Ne5u3A/r4w6mN1HVI0rFwIcoms+t0B4lb2ODWKZiZikQdn8/eqwsmbSEZZsWN3FkshgjPS83+dNqVwB6pPY5Yqte7'
281
282     mkdir -p "$build_files_dir"
283     # TODO: Verify why badkeypub is required & figure out how to avoid it.
284     echo "ssh-rsa $badkey ejk@bhima.local" >"$build_files_dir/badkeypub"
285
286     cat <<EOF >"$build_files_dir/sshconfig"
287 Host 172.17.0.*
288         StrictHostKeyChecking no
289         UserKnownHostsFile=/dev/null
290 EOF
291     cat <<EOF >"$build_files_dir/wrapdocker"
292 #!/bin/bash
293
294 # Ensure that all nodes in /dev/mapper correspond to mapped devices currently loaded by the device-mapper kernel driver
295 dmsetup mknodes
296
297 # First, make sure that cgroups are mounted correctly.
298 CGROUP=/sys/fs/cgroup
299 : {LOG:=stdio}
300
301 [ -d \$CGROUP ] ||
302     mkdir \$CGROUP
303
304 mountpoint -q \$CGROUP ||
305     mount -n -t tmpfs -o uid=0,gid=0,mode=0755 cgroup \$CGROUP || {
306         echo "Could not make a tmpfs mount. Did you use --privileged?"
307         exit 1
308     }
309
310 if [ -d /sys/kernel/security ] && ! mountpoint -q /sys/kernel/security
311 then
312     mount -t securityfs none /sys/kernel/security || {
313         echo "Could not mount /sys/kernel/security."
314         echo "AppArmor detection and --privileged mode might break."
315     }
316 fi
317
318 # Mount the cgroup hierarchies exactly as they are in the parent system.
319 for SUBSYS in \$(cut -d: -f2 /proc/1/cgroup)
320 do
321         [ -d \$CGROUP/\$SUBSYS ] || mkdir \$CGROUP/\$SUBSYS
322         mountpoint -q \$CGROUP/\$SUBSYS ||
323                 mount -n -t cgroup -o \$SUBSYS cgroup \$CGROUP/\$SUBSYS
324
325         # The two following sections address a bug which manifests itself
326         # by a cryptic "lxc-start: no ns_cgroup option specified" when
327         # trying to start containers withina container.
328         # The bug seems to appear when the cgroup hierarchies are not
329         # mounted on the exact same directories in the host, and in the
330         # container.
331
332         # Named, control-less cgroups are mounted with "-o name=foo"
333         # (and appear as such under /proc/<pid>/cgroup) but are usually
334         # mounted on a directory named "foo" (without the "name=" prefix).
335         # Systemd and OpenRC (and possibly others) both create such a
336         # cgroup. To avoid the aforementioned bug, we symlink "foo" to
337         # "name=foo". This shouldn't have any adverse effect.
338         echo \$SUBSYS | grep -q ^name= && {
339                 NAME=\$(echo \$SUBSYS | sed s/^name=//)
340                 ln -s \$SUBSYS \$CGROUP/\$NAME
341         }
342
343         # Likewise, on at least one system, it has been reported that
344         # systemd would mount the CPU and CPU accounting controllers
345         # (respectively "cpu" and "cpuacct") with "-o cpuacct,cpu"
346         # but on a directory called "cpu,cpuacct" (note the inversion
347         # in the order of the groups). This tries to work around it.
348         [ \$SUBSYS = cpuacct,cpu ] && ln -s \$SUBSYS \$CGROUP/cpu,cpuacct
349 done
350
351 # Note: as I write those lines, the LXC userland tools cannot setup
352 # a "sub-container" properly if the "devices" cgroup is not in its
353 # own hierarchy. Let's detect this and issue a warning.
354 grep -q :devices: /proc/1/cgroup ||
355     echo "WARNING: the 'devices' cgroup should be in its own hierarchy."
356 grep -qw devices /proc/1/cgroup ||
357     echo "WARNING: it looks like the 'devices' cgroup is not mounted."
358
359 # Now, close extraneous file descriptors.
360 pushd /proc/self/fd >/dev/null
361 for FD in *
362 do
363     case "\$FD" in
364     # Keep stdin/stdout/stderr
365     [012])
366         ;;
367     # Nuke everything else
368     *)
369         eval exec "\$FD>&-"
370         ;;
371     esac
372 done
373 popd >/dev/null
374
375
376 # If a pidfile is still around (for example after a container restart),
377 # delete it so that docker can start.
378 rm -rf /var/run/docker.pid
379
380 # If we were given a PORT environment variable, start as a simple daemon;
381 # otherwise, spawn a shell as well
382 if [ "\$PORT" ]
383 then
384     exec dockerd -H 0.0.0.0:\$PORT -H unix:///var/run/docker.sock \
385         \$DOCKER_DAEMON_ARGS
386 else
387     if [ "\$LOG" == "file" ]
388     then
389         dockerd \$DOCKER_DAEMON_ARGS &>/var/log/docker.log &
390     else
391         dockerd \$DOCKER_DAEMON_ARGS &
392     fi
393     (( timeout = 60 + SECONDS ))
394     until docker info >/dev/null 2>&1
395     do
396         if (( SECONDS >= timeout )); then
397             echo 'Timed out trying to connect to internal docker host.' >&2
398             break
399         fi
400         sleep 1
401     done
402     [[ \$1 ]] && exec "\$@"
403     exec bash --login
404 fi
405 EOF
406 }