mirror of
https://github.com/aljazceru/kata-containers.git
synced 2025-12-26 18:44:47 +01:00
Make the output of `kata-collect-data.sh` slightly more attractive and easier to read by using syntax highlighting where possible. This is achieved by specifying a language for some of the code blocks, specifically for the TOML config files and output of `kata-env` and the YAML osbuilder config file. See: - https://help.github.com/articles/creating-and-highlighting-code-blocks/ - https://github.com/github/linguist Fixes #775. Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
709 lines
13 KiB
Bash
709 lines
13 KiB
Bash
#!/bin/bash
|
|
#
|
|
# Copyright (c) 2017-2018 Intel Corporation
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
|
|
typeset -r script_name=${0##*/}
|
|
typeset -r runtime_name="@RUNTIME_NAME@"
|
|
typeset -r runtime=$(command -v "$runtime_name" 2>/dev/null)
|
|
typeset -r issue_url="@PROJECT_BUG_URL@"
|
|
typeset -r script_version="@VERSION@ (commit @COMMIT@)"
|
|
|
|
typeset -r unknown="unknown"
|
|
|
|
typeset -r osbuilder_file="/var/lib/osbuilder/osbuilder.yaml"
|
|
|
|
# Maximum number of errors to show for a single system component
|
|
# (such as runtime or proxy).
|
|
PROBLEM_LIMIT=${PROBLEM_LIMIT:-50}
|
|
|
|
# List of patterns used to detect problems in logfiles.
|
|
problem_pattern="("
|
|
problem_pattern+="\<abort|"
|
|
problem_pattern+="\<bug\>|"
|
|
problem_pattern+="\<cannot\>|"
|
|
problem_pattern+="\<catastrophic|"
|
|
problem_pattern+="\<could not\>|"
|
|
problem_pattern+="\<couldn\'t\>|"
|
|
problem_pattern+="\<critical|"
|
|
problem_pattern+="\<die\>|"
|
|
problem_pattern+="\<died\>|"
|
|
problem_pattern+="\<does.*not.*exist\>|"
|
|
problem_pattern+="\<dying\>|"
|
|
problem_pattern+="\<empty\>|"
|
|
problem_pattern+="\<erroneous|"
|
|
problem_pattern+="\<error|"
|
|
problem_pattern+="\<expected\>|"
|
|
problem_pattern+="\<fail|"
|
|
problem_pattern+="\<fatal|"
|
|
problem_pattern+="\<impossible\>|"
|
|
problem_pattern+="\<impossibly\>|"
|
|
problem_pattern+="\<incorrect|"
|
|
problem_pattern+="\<invalid\>|"
|
|
problem_pattern+="\<level=\"*error\"* |"
|
|
problem_pattern+="\<level=\"*fatal\"* |"
|
|
problem_pattern+="\<level=\"*panic\"* |"
|
|
problem_pattern+="\<level=\"*warning\"* |"
|
|
problem_pattern+="\<missing\>|"
|
|
problem_pattern+="\<need\>|"
|
|
problem_pattern+="\<no.*such.*file\>|"
|
|
problem_pattern+="\<not.*found\>|"
|
|
problem_pattern+="\<not.*supported\>|"
|
|
problem_pattern+="\<too many\>|"
|
|
problem_pattern+="\<unable\>|"
|
|
problem_pattern+="\<unavailable\>|"
|
|
problem_pattern+="\<unexpected|"
|
|
problem_pattern+="\<unknown\>|"
|
|
problem_pattern+="\<urgent|"
|
|
problem_pattern+="\<warn\>|"
|
|
problem_pattern+="\<warning\>|"
|
|
problem_pattern+="\<wrong\>"
|
|
problem_pattern+=")"
|
|
|
|
# List of patterns used to exclude messages that are not problems
|
|
problem_exclude_pattern="("
|
|
problem_exclude_pattern+="\<launching .* with:"
|
|
problem_exclude_pattern+=")"
|
|
|
|
usage()
|
|
{
|
|
cat <<EOT
|
|
Usage: $script_name [options]
|
|
|
|
Summary: Collect data about an installation of @PROJECT_NAME@.
|
|
|
|
Description: Run this script as root to obtain a markdown-formatted summary
|
|
of the environment of the @PROJECT_NAME@ installation. The output of this script
|
|
can be pasted directly into a github issue at the address below:
|
|
|
|
$issue_url
|
|
|
|
Options:
|
|
|
|
-h | --help : show this usage summary.
|
|
-v | --version : show program version.
|
|
|
|
EOT
|
|
}
|
|
|
|
version()
|
|
{
|
|
cat <<EOT
|
|
$script_name version $script_version
|
|
EOT
|
|
}
|
|
|
|
die()
|
|
{
|
|
local msg="$*"
|
|
echo >&2 "ERROR: $script_name: $msg"
|
|
exit 1
|
|
}
|
|
|
|
msg()
|
|
{
|
|
local msg="$*"
|
|
echo "$msg"
|
|
}
|
|
|
|
heading()
|
|
{
|
|
local name="$*"
|
|
echo -e "\n# $name\n"
|
|
}
|
|
|
|
subheading()
|
|
{
|
|
local name="$*"
|
|
echo -e "\n## $name\n"
|
|
}
|
|
|
|
separator()
|
|
{
|
|
echo -e '\n---\n'
|
|
}
|
|
|
|
have_cmd()
|
|
{
|
|
local cmd="$1"
|
|
|
|
command -v "$cmd" &>/dev/null
|
|
local ret=$?
|
|
|
|
if [ $ret -eq 0 ]; then
|
|
msg "Have \`$cmd\`"
|
|
else
|
|
msg "No \`$cmd\`"
|
|
fi
|
|
|
|
[ $ret -eq 0 ]
|
|
}
|
|
|
|
show_quoted_text()
|
|
{
|
|
local language="$1"
|
|
|
|
shift
|
|
|
|
local text="$*"
|
|
|
|
echo "\`\`\`${language}"
|
|
echo "$text"
|
|
echo "\`\`\`"
|
|
}
|
|
|
|
run_cmd_and_show_quoted_output()
|
|
{
|
|
local language="$1"
|
|
|
|
shift
|
|
|
|
local cmd="$*"
|
|
|
|
local output
|
|
|
|
msg "Output of \"\`$cmd\`\":"
|
|
output=$(eval "$cmd" 2>&1)
|
|
show_quoted_text "${language}" "$output"
|
|
}
|
|
|
|
show_runtime_configs()
|
|
{
|
|
local configs config
|
|
|
|
heading "Runtime config files"
|
|
|
|
configs=$($runtime --@PROJECT_TYPE@-show-default-config-paths)
|
|
if [ $? -ne 0 ]; then
|
|
version=$($runtime --version|tr '\n' ' ')
|
|
die "failed to check config files - runtime is probably too old ($version)"
|
|
fi
|
|
|
|
subheading "Runtime default config files"
|
|
|
|
show_quoted_text "" "$configs"
|
|
|
|
# add in the standard defaults for good measure "just in case"
|
|
configs+=" /etc/@PROJECT_TAG@/configuration.toml"
|
|
configs+=" /usr/share/defaults/@PROJECT_TAG@/configuration.toml"
|
|
configs+=" @CONFIG_PATH@"
|
|
configs+=" @SYSCONFIG@"
|
|
|
|
# create a unique list of config files
|
|
configs=$(echo $configs|tr ' ' '\n'|sort -u)
|
|
|
|
subheading "Runtime config file contents"
|
|
|
|
for config in $configs; do
|
|
if [ -e "$config" ]; then
|
|
run_cmd_and_show_quoted_output "toml" "cat \"$config\""
|
|
else
|
|
msg "Config file \`$config\` not found"
|
|
fi
|
|
done
|
|
|
|
separator
|
|
}
|
|
|
|
show_log_details()
|
|
{
|
|
heading "Logfiles"
|
|
|
|
show_runtime_log_details
|
|
show_proxy_log_details
|
|
show_shim_log_details
|
|
show_throttler_log_details
|
|
|
|
separator
|
|
}
|
|
|
|
show_runtime_log_details()
|
|
{
|
|
subheading "Runtime logs"
|
|
|
|
find_system_journal_problems "runtime" "@RUNTIME_NAME@" ""
|
|
}
|
|
|
|
find_system_journal_problems()
|
|
{
|
|
local name="$1"
|
|
local program="$2"
|
|
local unit="$3"
|
|
|
|
# select by identifier
|
|
local selector="-t"
|
|
|
|
local data_source="system journal"
|
|
|
|
local problems=$(journalctl -q -o cat -a "$selector" "$program" |\
|
|
grep "time=" |\
|
|
egrep -i "$problem_pattern" |\
|
|
egrep -iv "$problem_exclude_pattern" |\
|
|
tail -n ${PROBLEM_LIMIT})
|
|
|
|
if [ -n "$problems" ]; then
|
|
msg "Recent $name problems found in $data_source:"
|
|
show_quoted_text "" "$problems"
|
|
else
|
|
msg "No recent $name problems found in $data_source."
|
|
fi
|
|
}
|
|
|
|
show_proxy_log_details()
|
|
{
|
|
subheading "Proxy logs"
|
|
|
|
find_system_journal_problems "proxy" "@PROJECT_TYPE@-proxy" ""
|
|
}
|
|
|
|
show_shim_log_details()
|
|
{
|
|
subheading "Shim logs"
|
|
|
|
find_system_journal_problems "shim" "@PROJECT_TYPE@-shim" ""
|
|
}
|
|
|
|
show_throttler_log_details()
|
|
{
|
|
subheading "Throttler logs"
|
|
|
|
find_system_journal_problems "throttler" "" "@PROJECT_TYPE@-ksm-throttler"
|
|
}
|
|
|
|
show_package_versions()
|
|
{
|
|
heading "Packages"
|
|
|
|
local pattern="("
|
|
local project
|
|
|
|
# CC 2.x, 3.0 and runv runtimes. They shouldn't be installed but let's
|
|
# check anyway.
|
|
pattern+="cc-oci-runtime"
|
|
pattern+="cc-runtime"
|
|
pattern+="runv"
|
|
|
|
# core components
|
|
for project in @PROJECT_TYPE@
|
|
do
|
|
pattern+="|${project}-proxy"
|
|
pattern+="|${project}-runtime"
|
|
pattern+="|${project}-shim"
|
|
pattern+="|${project}-ksm-throttler"
|
|
pattern+="|${project}-containers-image"
|
|
done
|
|
|
|
# assets
|
|
pattern+="|linux-container"
|
|
|
|
# hypervisor name prefix
|
|
pattern+="|qemu-"
|
|
|
|
pattern+=")"
|
|
|
|
if have_cmd "dpkg"; then
|
|
run_cmd_and_show_quoted_output "" "dpkg -l|egrep \"$pattern\""
|
|
fi
|
|
|
|
if have_cmd "rpm"; then
|
|
run_cmd_and_show_quoted_output "" "rpm -qa|egrep \"$pattern\""
|
|
fi
|
|
|
|
separator
|
|
}
|
|
|
|
show_container_mgr_details()
|
|
{
|
|
heading "Container manager details"
|
|
|
|
if have_cmd "docker"; then
|
|
subheading "Docker"
|
|
run_cmd_and_show_quoted_output "" "docker version"
|
|
run_cmd_and_show_quoted_output "" "docker info"
|
|
run_cmd_and_show_quoted_output "" "systemctl show docker"
|
|
fi
|
|
|
|
if have_cmd "kubectl"; then
|
|
subheading "Kubernetes"
|
|
run_cmd_and_show_quoted_output "" "kubectl version"
|
|
run_cmd_and_show_quoted_output "" "kubectl config view"
|
|
run_cmd_and_show_quoted_output "" "systemctl show kubelet"
|
|
|
|
if have_cmd "crio"; then
|
|
run_cmd_and_show_quoted_output "" "crio --version"
|
|
run_cmd_and_show_quoted_output "" "systemctl show crio"
|
|
fi
|
|
fi
|
|
|
|
separator
|
|
}
|
|
|
|
show_meta()
|
|
{
|
|
local date
|
|
|
|
heading "Meta details"
|
|
|
|
date=$(date '+%Y-%m-%d.%H:%M:%S.%N%z')
|
|
msg "Running \`$script_name\` version \`$script_version\` at \`$date\`."
|
|
|
|
separator
|
|
}
|
|
|
|
show_runtime()
|
|
{
|
|
local cmd
|
|
|
|
msg "Runtime is \`$runtime\`."
|
|
|
|
cmd="@PROJECT_TYPE@-env"
|
|
heading "\`$cmd\`"
|
|
run_cmd_and_show_quoted_output "toml" "$runtime $cmd"
|
|
|
|
separator
|
|
}
|
|
|
|
# Parameter 1: Path to disk image file.
|
|
# Returns: Details of the image, or "$unknown" on error.
|
|
get_image_details()
|
|
{
|
|
local img="$1"
|
|
|
|
[ -z "$img" ] && { echo "$unknown"; return;}
|
|
[ -e "$img" ] || { echo "$unknown"; return;}
|
|
|
|
local loop_device
|
|
local partition_path
|
|
local partitions
|
|
local partition
|
|
local count
|
|
local mountpoint
|
|
local contents
|
|
local expected
|
|
|
|
loop_device=$(loopmount_image "$img")
|
|
if [ -z "$loop_device" ]; then
|
|
echo "$unknown"
|
|
return
|
|
fi
|
|
|
|
partitions=$(get_partitions "$loop_device")
|
|
count=$(echo "$partitions"|wc -l)
|
|
|
|
expected=1
|
|
|
|
if [ "$count" -ne "$expected" ]; then
|
|
release_device "$loop_device"
|
|
echo "$unknown"
|
|
return
|
|
fi
|
|
|
|
partition="$partitions"
|
|
|
|
partition_path="/dev/${partition}"
|
|
if [ ! -e "$partition_path" ]; then
|
|
release_device "$loop_device"
|
|
echo "$unknown"
|
|
return
|
|
fi
|
|
|
|
mountpoint=$(mount_partition "$partition_path")
|
|
|
|
contents=$(read_osbuilder_file "${mountpoint}")
|
|
[ -z "$contents" ] && contents="$unknown"
|
|
|
|
unmount_partition "$mountpoint"
|
|
release_device "$loop_device"
|
|
|
|
echo "$contents"
|
|
}
|
|
|
|
# Parameter 1: Path to the initrd file.
|
|
# Returns: Details of the initrd, or "$unknown" on error.
|
|
get_initrd_details()
|
|
{
|
|
local initrd="$1"
|
|
|
|
[ -z "$initrd" ] && { echo "$unknown"; return;}
|
|
[ -e "$initrd" ] || { echo "$unknown"; return;}
|
|
|
|
local file
|
|
local relative_file=""
|
|
local tmp
|
|
|
|
file="${osbuilder_file}"
|
|
|
|
# All files in the cpio archive are relative so remove leading slash
|
|
relative_file=$(echo "$file"|sed 's!^/!!g')
|
|
|
|
local tmpdir=$(mktemp -d)
|
|
|
|
# Note: 'cpio --directory' seems to be non-portable, so cd(1) instead.
|
|
(cd "$tmpdir" && gzip -dc "$initrd" | cpio \
|
|
--extract \
|
|
--make-directories \
|
|
--no-absolute-filenames \
|
|
$relative_file 2>/dev/null)
|
|
|
|
contents=$(read_osbuilder_file "${tmpdir}")
|
|
[ -z "$contents" ] && contents="$unknown"
|
|
|
|
tmp="${tmpdir}/${file}"
|
|
[ -d "$tmp" ] && rm -rf "$tmp"
|
|
|
|
echo "$contents"
|
|
}
|
|
|
|
# Returns: Full path to the image file.
|
|
get_image_file()
|
|
{
|
|
local cmd="@PROJECT_TYPE@-env"
|
|
local cmdline="$runtime $cmd"
|
|
|
|
local image=$(eval "$cmdline" 2>/dev/null |\
|
|
grep -A 1 '^\[Image\]' |\
|
|
egrep "\<Path\> =" |\
|
|
awk '{print $3}' |\
|
|
tr -d '"')
|
|
|
|
echo "$image"
|
|
}
|
|
|
|
# Returns: Full path to the initrd file.
|
|
get_initrd_file()
|
|
{
|
|
local cmd="@PROJECT_TYPE@-env"
|
|
local cmdline="$runtime $cmd"
|
|
|
|
local initrd=$(eval "$cmdline" 2>/dev/null |\
|
|
grep -A 1 '^\[Initrd\]' |\
|
|
egrep "\<Path\> =" |\
|
|
awk '{print $3}' |\
|
|
tr -d '"')
|
|
|
|
echo "$initrd"
|
|
}
|
|
|
|
# Parameter 1: Path to disk image file.
|
|
# Returns: Path to loop device.
|
|
loopmount_image()
|
|
{
|
|
local img="$1"
|
|
[ -n "$img" ] || die "need image file"
|
|
|
|
local device_path
|
|
|
|
losetup -fP "$img"
|
|
|
|
device_path=$(losetup -j "$img" |\
|
|
cut -d: -f1 |\
|
|
sort -k1,1 |\
|
|
tail -1)
|
|
|
|
echo "$device_path"
|
|
}
|
|
|
|
# Parameter 1: Path to loop device.
|
|
# Returns: Partition names.
|
|
get_partitions()
|
|
{
|
|
local device_path="$1"
|
|
[ -n "$device_path" ] || die "need device path"
|
|
|
|
local device
|
|
local partitions
|
|
|
|
device=${device_path/\/dev\//}
|
|
|
|
partitions=$(lsblk -nli -o NAME "${device_path}" |\
|
|
grep -v "^${device}$")
|
|
|
|
echo "$partitions"
|
|
}
|
|
|
|
# Parameter 1: Path to disk partition device.
|
|
# Returns: Mountpoint.
|
|
mount_partition()
|
|
{
|
|
local partition="$1"
|
|
[ -n "$partition" ] || die "need partition"
|
|
[ -e "$partition" ] || die "partition does not exist: $partition"
|
|
|
|
local mountpoint
|
|
|
|
mountpoint=$(mktemp -d)
|
|
|
|
mount -oro,noload "$partition" "$mountpoint"
|
|
|
|
echo "$mountpoint"
|
|
}
|
|
|
|
# Parameter 1: Mountpoint.
|
|
unmount_partition()
|
|
{
|
|
local mountpoint="$1"
|
|
[ -n "$mountpoint" ] || die "need mountpoint"
|
|
[ -n "$mountpoint" ] || die "mountpoint does not exist: $mountpoint"
|
|
|
|
umount "$mountpoint"
|
|
}
|
|
|
|
# Parameter 1: Loop device path.
|
|
release_device()
|
|
{
|
|
local device="$1"
|
|
[ -n "$device" ] || die "need device"
|
|
[ -e "$device" ] || die "device does not exist: $device"
|
|
|
|
losetup -d "$device"
|
|
}
|
|
|
|
show_throttler_details()
|
|
{
|
|
heading "KSM throttler"
|
|
|
|
subheading "version"
|
|
|
|
local throttlers
|
|
local throttler
|
|
|
|
throttlers=$(find /usr/libexec /usr/lib* -type f |\
|
|
grep -v trigger |\
|
|
egrep "(cc|kata)-ksm-throttler" |\
|
|
sort -u)
|
|
|
|
echo "$throttlers" | while read throttler
|
|
do
|
|
local cmd
|
|
cmd="$throttler --version"
|
|
run_cmd_and_show_quoted_output "" "$cmd"
|
|
done
|
|
|
|
subheading "systemd service"
|
|
|
|
local unit
|
|
|
|
# Note: "vc-throttler" is the old CC service, replaced by
|
|
# "kata-vc-throttler".
|
|
for unit in \
|
|
"cc-ksm-throttler" \
|
|
"kata-ksm-throttler" \
|
|
"kata-vc-throttler" \
|
|
"vc-throttler"
|
|
do
|
|
systemctl status "${unit}" >/dev/null 2>&1 && \
|
|
run_cmd_and_show_quoted_output "" "systemctl show ${unit}"
|
|
done
|
|
}
|
|
|
|
# Retrieve details of the image containing
|
|
# the rootfs used to boot the virtual machine.
|
|
show_image_details()
|
|
{
|
|
local image
|
|
local details
|
|
|
|
image=$(get_image_file)
|
|
|
|
heading "Image details"
|
|
|
|
if [ -n "$image" ]
|
|
then
|
|
details=$(get_image_details "$image")
|
|
show_quoted_text "yaml" "$details"
|
|
else
|
|
msg "No image"
|
|
fi
|
|
|
|
separator
|
|
}
|
|
|
|
# Retrieve details of the initrd containing
|
|
# the rootfs used to boot the virtual machine.
|
|
show_initrd_details()
|
|
{
|
|
local initrd
|
|
local details
|
|
|
|
initrd=$(get_initrd_file)
|
|
|
|
heading "Initrd details"
|
|
|
|
if [ -n "$initrd" ]
|
|
then
|
|
details=$(get_initrd_details "$initrd")
|
|
show_quoted_text "yaml" "$details"
|
|
else
|
|
msg "No initrd"
|
|
fi
|
|
|
|
separator
|
|
}
|
|
|
|
read_osbuilder_file()
|
|
{
|
|
local rootdir="$1"
|
|
|
|
[ -n "$rootdir" ] || die "need root directory"
|
|
|
|
local file="${rootdir}/${osbuilder_file}"
|
|
|
|
[ ! -e "$file" ] && return
|
|
|
|
cat "$file"
|
|
}
|
|
|
|
main()
|
|
{
|
|
args=$(getopt \
|
|
-n "$script_name" \
|
|
-a \
|
|
--options="dhv" \
|
|
--longoptions="debug help version" \
|
|
-- "$@")
|
|
|
|
eval set -- "$args"
|
|
[ $? -ne 0 ] && { usage && exit 1; }
|
|
[ $# -eq 0 ] && { usage && exit 0; }
|
|
|
|
while [ $# -gt 1 ]
|
|
do
|
|
case "$1" in
|
|
-d|--debug)
|
|
set -x
|
|
;;
|
|
|
|
-h|--help)
|
|
usage && exit 0
|
|
;;
|
|
|
|
-v|--version)
|
|
version && exit 0
|
|
;;
|
|
|
|
--)
|
|
shift
|
|
break
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[ $(id -u) -eq 0 ] || die "Need to run as root"
|
|
[ -n "$runtime" ] || die "cannot find runtime '$runtime_name'"
|
|
|
|
show_meta
|
|
show_runtime
|
|
show_runtime_configs
|
|
show_throttler_details
|
|
show_image_details
|
|
show_initrd_details
|
|
show_log_details
|
|
show_container_mgr_details
|
|
show_package_versions
|
|
}
|
|
|
|
main "$@"
|