github.com/kata-containers/tests@v0.0.0-20240307153542-772105b56064/metrics/lib/common.bash (about)

     1  #!/bin/bash
     2  #
     3  # Copyright (c) 2017-2021 Intel Corporation
     4  #
     5  # SPDX-License-Identifier: Apache-2.0
     6  
     7  THIS_FILE=$(readlink -f ${BASH_SOURCE[0]})
     8  LIB_DIR=${THIS_FILE%/*}
     9  RESULT_DIR="${LIB_DIR}/../results"
    10  
    11  source ${LIB_DIR}/../../lib/common.bash
    12  source ${LIB_DIR}/json.bash
    13  source /etc/os-release || source /usr/lib/os-release
    14  
    15  # Set variables to reasonable defaults if unset or empty
    16  CTR_EXE="${CTR_EXE:-ctr}"
    17  DOCKER_EXE="${DOCKER_EXE:-docker}"
    18  CTR_RUNTIME="${CTR_RUNTIME:-io.containerd.kata.v2}"
    19  RUNTIME="${RUNTIME:-containerd-shim-kata-v2}"
    20  KATA_HYPERVISOR="${KATA_HYPERVISOR:-qemu}"
    21  TEST_REPO="${TEST_REPO:-github.com/kata-containers/tests}"
    22  JSON_HOST="${JSON_HOST:-}"
    23  
    24  KSM_BASE="/sys/kernel/mm/ksm"
    25  KSM_ENABLE_FILE="${KSM_BASE}/run"
    26  KSM_PAGES_FILE="${KSM_BASE}/pages_to_scan"
    27  KSM_SLEEP_FILE="${KSM_BASE}/sleep_millisecs"
    28  KSM_PAGES_SHARED="${KSM_BASE}/pages_shared"
    29  
    30  http_proxy="${http_proxy:-}"
    31  https_proxy="${https_proxy:-}"
    32  
    33  # The settings we use for an 'aggresive' KSM setup
    34  # Scan 1000 pages every 50ms - 20,000 pages/s
    35  KSM_AGGRESIVE_PAGES=1000
    36  KSM_AGGRESIVE_SLEEP=50
    37  
    38  declare -A registries
    39  registries[ubuntu]=\
    40  "docker.io/library
    41  public.ecr.aws/lts
    42  mirror.gcr.io/library
    43  quay.io/libpod"
    44  
    45  # This function checks existence of commands.
    46  # They can be received standalone or as an array, e.g.
    47  #
    48  # cmds=(“cmd1” “cmd2”)
    49  # check_cmds "${cmds[@]}"
    50  check_cmds()
    51  {
    52  	local cmd req_cmds=( "$@" )
    53  	for cmd in "${req_cmds[@]}"; do
    54  		if ! command -v "$cmd" > /dev/null 2>&1; then
    55  			die "command $cmd not available"
    56  		fi
    57  		echo "command: $cmd: yes"
    58  	done
    59  }
    60  
    61  # This function performs a pull on the image names
    62  # passed in (notionally as 'about to be used'), to ensure
    63  #  - that we have the most upto date images
    64  #  - that any pull/refresh time (for a first pull) does not
    65  #    happen during the test itself.
    66  #
    67  # The image list can be received standalone or as an array, e.g.
    68  #
    69  # images=(“img1” “img2”)
    70  # check_imgs "${images[@]}"
    71  check_images()
    72  {
    73  	local img req_images=( "$@" )
    74  	for img in "${req_images[@]}"; do
    75  		echo "ctr pull'ing: $img"
    76  		if ! sudo "${CTR_EXE}" image pull "$img"; then
    77  			die "Failed to pull image $img"
    78  		fi
    79  		echo "ctr pull'd: $img"
    80  	done
    81  }
    82  
    83  generate_build_dockerfile() {
    84  	local dockerfile="$1"
    85  	local image="$2"
    86  	local map_key="$3"
    87  	local text_to_replace="$4"
    88  	local regs=(${registries["${map_key}"]})
    89  	for r in ${regs[@]}; do
    90  		sed 's|'${text_to_replace}'|'${r}'|g' \
    91  			"${dockerfile}.in" > "${dockerfile}"
    92  		if sudo "${DOCKER_EXE}" build --build-arg http_proxy="${http_proxy}" --build-arg https_proxy="${https_proxy}" --label "$image" --tag "${image}" -f "$dockerfile" "$dockerfile_dir"; then
    93  			return 0
    94  		fi
    95  	done
    96  	return 1
    97  }
    98  
    99  # This function performs a build on the image names
   100  # passed in, to ensure that we have the latest changes from
   101  # the dockerfiles
   102  build_dockerfile_image()
   103  {
   104  	local image="$1"
   105  	local dockerfile_path="$2"
   106  	local dockerfile_dir=${2%/*}
   107  
   108  	if [ -f "$dockerfile_path" ]; then
   109  		echo "docker building $image"
   110  		if ! sudo "${DOCKER_EXE}" build --build-arg http_proxy="${http_proxy}" --build-arg https_proxy="${https_proxy}" --label "$image" --tag "${image}" -f "$dockerfile_path" "$dockerfile_dir"; then
   111  			die "Failed to docker build image $image"
   112  		fi
   113  		return 0
   114  	fi
   115  
   116  	generate_build_dockerfile "${dockerfile_path}" "${image}" "ubuntu" "@UBUNTU_REGISTRY@" \
   117  		|| die "Failed to docker build image $image"
   118  }
   119  
   120  # This function removes the ctr image, builds a new one using a dockerfile
   121  # and imports the image from docker to ctr
   122  check_ctr_images()
   123  {
   124  	local ctr_image="$1"
   125  	local dockerfile_path="$2"
   126  	local docker_image="$(echo ${ctr_image} | cut -d/ -f3 | cut -d: -f1)"
   127  
   128  	if [ -z "$ctr_image" ] || [ -z "$dockerfile_path" ]; then
   129  		die "Missing image or dockerfile path variable"
   130  	fi
   131  
   132  	sudo "${CTR_EXE}" i rm "${ctr_image}"
   133  	build_dockerfile_image "${docker_image}" "${dockerfile_path}"
   134  	sudo "${DOCKER_EXE}" save -o "${docker_image}.tar" "${docker_image}"
   135  	sudo "${CTR_EXE}" i import "${docker_image}.tar"
   136  	rm -rf "${docker_image}".tar
   137  }
   138  
   139  # A one time (per uber test cycle) init that tries to get the
   140  # system to a 'known state' as much as possible
   141  metrics_onetime_init()
   142  {
   143  	# The onetime init must be called once, and only once
   144  	if [ ! -z "$onetime_init_done" ]; then
   145  		die "onetime_init() called more than once"
   146  	fi
   147  
   148  	# Restart services
   149  	restart_docker_service
   150  	restart_containerd_service
   151  
   152  	# We want this to be seen in sub shells as well...
   153  	# otherwise init_env() cannot check us
   154  	export onetime_init_done=1
   155  }
   156  
   157  # Print a banner to the logs noting clearly which test
   158  # we are about to run
   159  test_banner()
   160  {
   161  	echo -e "\n===== starting test [$1] ====="
   162  }
   163  
   164  # Initialization/verification environment. This function makes
   165  # minimal steps for metrics/tests execution.
   166  init_env()
   167  {
   168  	test_banner "${TEST_NAME}"
   169  
   170  	cmd=("docker" "ctr")
   171  
   172  	# check dependencies
   173  	check_cmds "${cmd[@]}"
   174  
   175  	# Remove all stopped containers
   176  	clean_env
   177  	clean_env_ctr
   178  
   179  	# This clean up is more aggressive, this is in order to
   180  	# decrease the factors that could affect the metrics results.
   181  	kill_processes_before_start
   182  }
   183  
   184  # This function checks if there are containers or
   185  # shim/proxy/hypervisor processes up, if found, they are
   186  # killed to start test with clean environment.
   187  kill_processes_before_start() {
   188  	DOCKER_PROCS=$(sudo "${DOCKER_EXE}" ps -q)
   189  	[[ -n "${DOCKER_PROCS}" ]] && clean_env
   190  
   191  	CTR_PROCS=$(sudo "${CTR_EXE}" t list -q)
   192  	[[ -n "${CTR_PROCS}" ]] && clean_env_ctr
   193  
   194  	check_processes
   195  }
   196  
   197  # Generate a random name - generally used when creating containers, but can
   198  # be used for any other appropriate purpose
   199  random_name() {
   200  	mktemp -u kata-XXXXXX
   201  }
   202  
   203  show_system_ctr_state() {
   204  	echo "Showing system state:"
   205  	echo " --Check containers--"
   206  	sudo "${CTR_EXE}" c list
   207  	echo " --Check tasks--"
   208  	sudo "${CTR_EXE}" task list
   209  
   210  	local processes="containerd-shim-kata-v2"
   211  
   212  	for p in ${processes}; do
   213  		echo " --pgrep ${p}--"
   214  		pgrep -a ${p}
   215  	done
   216  }
   217  
   218  common_init(){
   219  	if [ "$CTR_RUNTIME" == "io.containerd.kata.v2" ] || [ "$RUNTIME" == "containerd-shim-kata-v2" ]; then
   220  		extract_kata_env
   221  	else
   222  		# We know we have nothing to do for runc or shimv2
   223  		if [ "$CTR_RUNTIME" != "io.containerd.runc.v2" ] || [ "$RUNTIME" != "runc" ]; then
   224  			warn "Unrecognised runtime"
   225  		fi
   226  	fi
   227  }
   228  
   229  
   230  # Save the current KSM settings so we can restore them later
   231  save_ksm_settings(){
   232  	echo "saving KSM settings"
   233  	ksm_stored_run=$(cat ${KSM_ENABLE_FILE})
   234  	ksm_stored_pages=$(cat ${KSM_ENABLE_FILE})
   235  	ksm_stored_sleep=$(cat ${KSM_ENABLE_FILE})
   236  }
   237  
   238  set_ksm_aggressive(){
   239  	echo "setting KSM to aggressive mode"
   240  	# Flip the run off/on to ensure a restart/rescan
   241  	sudo bash -c "echo 0 > ${KSM_ENABLE_FILE}"
   242  	sudo bash -c "echo ${KSM_AGGRESIVE_PAGES} > ${KSM_PAGES_FILE}"
   243  	sudo bash -c "echo ${KSM_AGGRESIVE_SLEEP} > ${KSM_SLEEP_FILE}"
   244  	sudo bash -c "echo 1 > ${KSM_ENABLE_FILE}"
   245  
   246  	if [ "${KATA_HYPERVISOR}" == "qemu" ]; then
   247  		# Disable virtio-fs and save whether it was enabled previously
   248  		set_virtio_out=$(sudo -E PATH="$PATH" "${LIB_DIR}/../../.ci/set_kata_config.sh" shared_fs virtio-9p)
   249  		echo "${set_virtio_out}"
   250  		grep -q "already" <<< "${set_virtio_out}" || was_virtio_fs=true;
   251  	fi
   252  }
   253  
   254  restore_virtio_fs(){
   255  	# Re-enable virtio-fs if it was enabled previously
   256  	[ -n "${was_virtio_fs}" ] && sudo -E PATH="$PATH" "${LIB_DIR}/../../.ci/set_kata_config.sh" shared_fs virtio-fs || \
   257  		info "Not restoring virtio-fs since it wasn't enabled previously"
   258  }
   259  
   260  restore_ksm_settings(){
   261  	echo "restoring KSM settings"
   262  	# First turn off the run to ensure if we are then re-enabling
   263  	# that any changes take effect
   264  	sudo bash -c "echo 0 > ${KSM_ENABLE_FILE}"
   265  	sudo bash -c "echo ${ksm_stored_pages} > ${KSM_PAGES_FILE}"
   266  	sudo bash -c "echo ${ksm_stored_sleep} > ${KSM_SLEEP_FILE}"
   267  	sudo bash -c "echo ${ksm_stored_run} > ${KSM_ENABLE_FILE}"
   268  	[ "${KATA_HYPERVISOR}" == "qemu" ] && restore_virtio_fs
   269  }
   270  
   271  disable_ksm(){
   272  	echo "disabling KSM"
   273  	sudo bash -c "echo 0 > ${KSM_ENABLE_FILE}"
   274  	[ "${KATA_HYPERVISOR}" == "qemu" ] && restore_virtio_fs
   275  }
   276  
   277  # See if KSM is enabled.
   278  # If so, amend the test name to reflect that
   279  check_for_ksm(){
   280  	if [ ! -f ${KSM_ENABLE_FILE} ]; then
   281  		return
   282  	fi
   283  
   284  	ksm_on=$(< ${KSM_ENABLE_FILE})
   285  
   286  	if [ $ksm_on == "1" ]; then
   287  		TEST_NAME="${TEST_NAME} ksm"
   288  	fi
   289  }
   290  
   291  # Wait for KSM to settle down, or timeout waiting
   292  # The basic algorithm is to look at the pages_shared value
   293  # at the end of every 'full scan', and if the value
   294  # has changed very little, then we are done (because we presume
   295  # a full scan has managed to do few new merges)
   296  #
   297  # arg1 - timeout in seconds
   298  wait_ksm_settle(){
   299  	[[ "$RUNTIME" == "runc" ]] || [[ "$CTR_RUNTIME" == "io.containerd.runc.v2" ]] && return
   300  	local t pcnt
   301  	local oldscan=-1 newscan
   302  	local oldpages=-1 newpages
   303  
   304  	oldscan=$(cat /sys/kernel/mm/ksm/full_scans)
   305  
   306  	# Wait some time for KSM to kick in to avoid early dismissal
   307  	for ((t=0; t<5; t++)); do
   308  		pages=$(cat "${KSM_PAGES_SHARED}")
   309  		[[ "$pages" -ne 0 ]] && echo "Discovered KSM activity" && break
   310  		sleep 1
   311  	done
   312  
   313  	# Go around the loop until either we see a small % change
   314  	# between two full_scans, or we timeout
   315  	for ((t=0; t<$1; t++)); do
   316  
   317  		newscan=$(cat /sys/kernel/mm/ksm/full_scans)
   318  		newpages=$(cat "${KSM_PAGES_SHARED}")
   319  		[[ "$newpages" -eq 0 ]] && echo "No need to wait for KSM to settle" && return
   320  
   321  		if (( newscan != oldscan )); then
   322  			echo -e "\nnew full_scan ($oldscan to $newscan)"
   323  
   324  			# Do we have a previous scan to compare with
   325  			echo "check pages $oldpages to $newpages"
   326  
   327  			if (( oldpages != -1 )); then
   328  				# avoid divide by zero problems
   329  				if (( $oldpages > 0 )); then
   330  					pcnt=$(( 100 - ((newpages * 100) / oldpages) ))
   331  					# abs()
   332  					pcnt=$(( $pcnt * -1 ))
   333  
   334  					echo "$oldpages to $newpages is ${pcnt}%"
   335  
   336  					if (( $pcnt <= 5 )); then
   337  						echo "KSM stabilised at ${t}s"
   338  						return
   339  					fi
   340  				else
   341  					echo "$oldpages KSM pages... waiting"
   342  				fi
   343  			fi
   344  			oldscan=$newscan
   345  			oldpages=$newpages
   346  		else
   347  			echo -n "."
   348  		fi
   349  		sleep 1
   350  	done
   351  	echo "Timed out after ${1}s waiting for KSM to settle"
   352  }
   353  
   354  function start_kubernetes() {
   355  	info "Start k8s"
   356  	pushd "${GOPATH}/src/${TEST_REPO}/integration/kubernetes"
   357  	bash ./init.sh
   358  	popd
   359  }
   360  
   361  function end_kubernetes() {
   362  	info "End k8s"
   363  	pushd "${GOPATH}/src/${TEST_REPO}/integration/kubernetes"
   364  	bash ./cleanup_env.sh
   365  	popd
   366  }
   367  
   368  common_init