Files
s390-tools/scripts/dbginfo.sh
Julian Wiedmann 852fb9dfb8 dbginfo: collect extended network interface statistics
The basic statistics are already collected via ifconfig. But that tool
is deprecated, and provides no way of collecting the extended stats.
So also gather data from 'ip link'.

Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2019-10-17 16:35:01 +02:00

1216 lines
32 KiB
Bash
Executable File

#!/bin/sh
#
# dbginfo.sh - Tool to collect runtime, configuration, and trace information
#
# Copyright IBM Corp. 2002, 2018
#
# s390-tools is free software; you can redistribute it and/or modify
# it under the terms of the MIT license. See LICENSE for details.
#
# Switching to neutral locale
LC_ALL=C
export LC_ALL
# The general name of this script
readonly SCRIPTNAME="${0##*/}"
########################################
# print version info
print_version() {
cat <<EOF
${SCRIPTNAME}: Debug information script version %S390_TOOLS_VERSION%
Copyright IBM Corp. 2002, 2018
EOF
}
########################################
# print how to use this script
print_usage()
{
print_version
cat <<EOF
Usage: ${SCRIPTNAME} [OPTIONS]
This script collects runtime, configuration and trace information about
your Linux on System z installation for debugging purposes.
It also traces information about z/VM if the Linux runs under z/VM.
The collected information is written to a TAR archive named
/tmp/DBGINFO-[date]-[time]-[hostname]-[processorid].tgz
where [date] and [time] are the date and time when debug data is collected.
[hostname] indicates the hostname of the system the data was collected from.
The [processorid] is taken from the processor 0 and indicates the processor
identification.
Options:
-d|--directory specify the directory where the data collection
stores the temporary data and the final archive.
-h|--help print this help
-v|--version print version information
Please report bugs to: linux390@de.ibm.com
EOF
}
######################################
# Verification to run as root
#
if test "$(/usr/bin/id -u 2>/dev/null)" -ne 0; then
echo "${SCRIPTNAME}: Error: You must be user root to run \"${SCRIPTNAME}\"!"
exit 1
fi
#######################################
# Parsing the command line
#
paramWORKDIR_BASE="/tmp/"
while [ ${#} -gt 0 ]; do
case ${1} in
--help|-h)
print_usage
exit 0
;;
--version|-v)
print_version
exit 0
;;
--directory|-d)
paramWORKDIR_BASE=${2}
shift
;;
-*|--*|*)
echo
echo "${SCRIPTNAME}: invalid option \"${1}\""
echo "Try '${SCRIPTNAME} --help' for more information"
echo
exit 1
;;
esac
shift
done
if test -z "${paramWORKDIR_BASE}"; then
echo "${SCRIPTNAME}: Error: No directory specified for data collection!"
echo
exit 1
fi
if test ! -d "${paramWORKDIR_BASE}"; then
echo "${SCRIPTNAME}: Error: The specified directory \"${paramWORKDIR_BASE}\" does not exist!"
echo
exit 1
fi
########################################
# Global used variables
#
# The base working directory
readonly WORKDIR_BASE="$(echo "${paramWORKDIR_BASE}" | sed -e 's#/$##')/"
# The terminal
readonly TERMINAL="$(tty 2>/dev/null)"
# The hostname of the system
readonly SYSTEMHOSTNAME="$(hostname -s 2>/dev/null)"
# The kernel release version as delivered from uname -r
readonly KERNEL_RELEASE_VERSION="$(uname -r 2>/dev/null)"
# The processor ID for the first processor
readonly PROCESSORID="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \
sed 's/.*identification[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')"
# The processor version for the first processor
readonly PROCESSORVERSION="$(grep -E ".*processor 0:.*" /proc/cpuinfo | \
sed 's/.*version[[:space:]]*\=[[:space:]]*\([[:alnum:]]*\).*/\1/g')"
# The current date
readonly DATETIME="$(date +%Y-%m-%d-%H-%M-%S 2>/dev/null)"
# The current working directory for the actual script execution
if test -z "${PROCESSORID}"; then
readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}"
else
readonly WORKDIR_CURRENT="DBGINFO-${DATETIME}-${SYSTEMHOSTNAME:-localhost}-${PROCESSORID}"
fi
# The current path where the collected information is put together
readonly WORKPATH="${WORKDIR_BASE}${WORKDIR_CURRENT}/"
# The current TAR archive that finally includes all collected information
readonly WORKARCHIVE="${WORKDIR_BASE}${WORKDIR_CURRENT}.tgz"
# The log file of activities from this script execution
readonly LOGFILE="${WORKPATH}dbginfo.log"
# The file to indicate that another instance of the script is already running
readonly LOCKFILE="/tmp/${SCRIPTNAME}.lock"
# File that includes output of Linux commands
readonly OUTPUT_FILE_CMD="${WORKPATH}runtime.out"
# File that includes output of z/VM commands (if running in z/VM)
readonly OUTPUT_FILE_VMCMD="${WORKPATH}zvm_runtime.out"
# File that includes content of files from sysfs
readonly OUTPUT_FILE_SYSFS="${WORKPATH}sysfsfiles.out"
# File that includes the output of lsof
readonly OUTPUT_FILE_LSOF="${WORKPATH}open_files.out"
# File that includes content of OSA OAT
readonly OUTPUT_FILE_OSAOAT="${WORKPATH}osa_oat"
# File that includes content of Ethtool commands
readonly OUTPUT_FILE_ETHTOOL="${WORKPATH}ethtool.out"
# File that includes the output of journalctl
readonly OUTPUT_FILE_JOURNALCTL="${WORKPATH}journalctl.out"
# File that includes the output of OpenVSwitch
readonly OUTPUT_FILE_OVS="${WORKPATH}openvswitch"
# File that includes the KVM domain xml file
readonly OUTPUT_FILE_XML="${WORKPATH}domain_xml"
# File that includes the docker inspect output
readonly OUTPUT_FILE_DOCKER="${WORKPATH}docker_inspect.out"
# File that includes nvme related information
readonly OUTPUT_FILE_NVME="${WORKPATH}nvme.out"
# Mount point of the debug file system
readonly MOUNT_POINT_DEBUGFS="/sys/kernel/debug"
# The amount of steps running the whole collections
readonly COLLECTION_COUNT=13
# The kernel version (e.g. '2' from 2.6.32 or '3' from 3.2.1)
readonly KERNEL_VERSION=$(uname -r 2>/dev/null | cut -d'.' -f1)
# The kernel major revision number (e.g. '6' from 2.6.32 or '2' from 3.2.1)
readonly KERNEL_MAJOR_REVISION=$(uname -r 2>/dev/null | cut -d'.' -f2)
# The kernel mainor revision number (e.g. '32' from 2.6.32 or '1' from 3.2.1)
readonly KERNEL_MINOR_REVISION=$(uname -r 2>/dev/null | cut -d'.' -f3 | sed 's/[^0-9].*//g')
# Is this kernel supporting sysfs - since 2.4 (0=yes, 1=no)
if test "${KERNEL_VERSION}" -lt 2 ||
( test "${KERNEL_VERSION}" -eq 2 && test "${KERNEL_MAJOR_REVISION}" -le 4 ); then
readonly LINUX_SUPPORT_SYSFS=1
else
readonly LINUX_SUPPORT_SYSFS=0
fi
# Is this kernel potentially using the /sys/kernel/debug feature - since 2.6.13 (0=yes, 1=no)
if test "${KERNEL_VERSION}" -lt 2 ||
( test "${KERNEL_VERSION}" -eq 2 &&
( test "${KERNEL_MAJOR_REVISION}" -lt 6 ||
( test "${KERNEL_MAJOR_REVISION}" -eq 6 && test "${KERNEL_MINOR_REVISION}" -lt 13 ))); then
readonly LINUX_SUPPORT_SYSFSDBF=1
else
readonly LINUX_SUPPORT_SYSFSDBF=0
fi
if test "x${PROCESSORVERSION}" = "xFF" || test "x${PROCESSORVERSION}" = "xff"; then
readonly RUNTIME_ENVIRONMENT=$(grep -E "VM00.*Control Program.*" /proc/sysinfo| sed 's/.*:[[:space:]]*\([[:graph:]]*\).*/\1/g')
else
readonly RUNTIME_ENVIRONMENT="LPAR"
fi
########################################
# Collection of proc fs entries
PROCFILES="\
/proc/buddyinfo\
/proc/cio_ignore\
/proc/cmdline\
/proc/cpuinfo\
/proc/crypto\
/proc/dasd/devices\
/proc/dasd/statistics\
/proc/devices\
/proc/diskstats\
/proc/driver/z90crypt\
/proc/interrupts\
/proc/iomem\
/proc/mdstat\
/proc/meminfo\
/proc/misc\
/proc/modules\
/proc/mounts\
/proc/net/vlan\
/proc/net/bonding\
/proc/partitions\
/proc/qeth\
/proc/qeth_perf\
/proc/qeth_ipa_takeover\
/proc/sched_debug\
/proc/schedstat\
/proc/service_levels\
/proc/slabinfo\
/proc/stat\
/proc/swaps\
/proc/sys/kernel\
/proc/sys/vm\
/proc/sysinfo\
/proc/version\
/proc/zoneinfo\
"
# Adding files to PROCFILES in case scsi devices are available
if test -e /proc/scsi; then
PROCFILES="${PROCFILES}\
$(find /proc/scsi -type f -perm /444 2>/dev/null)\
"
fi
# Adding files to PROCFILES in case we run on Kernel 2.4 or older
if test "${LINUX_SUPPORT_SYSFS}" -eq 1; then
PROCFILES="${PROCFILES}\
/proc/chpids\
/proc/chandev\
/proc/ksyms\
/proc/lvm/global\
/proc/subchannels\
"
fi
# Adding s390dbf files to PROCFILE in case we run on Kernel lower than 2.6.13
if test "${LINUX_SUPPORT_SYSFSDBF}" -eq 1; then
if test -e /proc/s390dbf; then
PROCFILES="${PROCFILES}\
$(find /proc/s390dbf -type f -not -path "*/raw" -not -path "*/flush" 2>/dev/null)\
"
fi
fi
########################################
LOGFILES="\
/var/log/anaconda.*\
/var/log/audit\
/var/log/boot*\
/var/log/cron*\
/var/log/dmesg*\
/var/log/dracut.log*\
/var/log/IBMtape.trace\
/var/log/IBMtape.errorlog\
/var/log/libvirt\
/var/log/lin_tape.trace\
/var/log/lin_tape.errorlog\
/var/log/messages*\
/var/log/opencryptoki\
/var/log/syslog*\
/var/log/sa\
/var/log/yum.log\
/var/log/openvswitch/ovs-vswitchd.log\
/var/log/openvswitch/ovsdb-server.log\
/run/docker/libcontainerd/containerd/events.log\
/run/containerd/events.log\
"
########################################
CONFIGFILES="\
/boot/grub2/grub.cfg\
/boot/zipl/active_devices.txt\
/boot/zipl/config\
/etc/*.conf\
/etc/anacrontab\
/etc/auto.*\
/etc/cron.*\
/etc/crontab\
/etc/crypttab\
/etc/default\
/etc/depmod.d\
/etc/dracut.conf.d\
/etc/exports\
/etc/fstab\
/etc/groups\
/etc/grub.d\
/etc/hosts*\
/etc/iscsi\
/etc/inittab\
/etc/libvirt\
/etc/logrotate.d\
/etc/lvm\
/etc/modprobe.conf*\
/etc/modprobe.d\
/etc/mtab\
/etc/multipath\
/etc/network\
/etc/networks\
/etc/pam.d\
/etc/profile\
/etc/profile.d\
/etc/pki/tls/openssl.cnf\
/etc/rc.d\
/etc/rc.local\
/etc/resolv.*\
/etc/rsyslog.d\
/etc/ssl/openssl.conf\
/etc/ssl/openssl.cnf\
/etc/sysconfig\
/etc/sysctl.d\
/etc/syslog*\
/etc/systemd\
/etc/udev*\
/etc/xinet.d\
/etc/*release\
$(find /lib/modules -name modules.dep 2>/dev/null)\
/etc/docker\
/lib/systemd/system/docker.service\
/usr/lib/systemd/system/docker.service\
/etc/apparmor.d\
"
########################################
CMDS="uname -a\
:uptime\
:runlevel\
:iptables -L\
:ulimit -a\
:ps -emo pid,tid,nlwp,policy,user,tname,ni,pri,psr,sgi_p,stat,wchan,start_time,time,pcpu,pmem,vsize,size,rss,share,command\
:ps -eHo pid,tid,nlwp,policy,user,tname,ni,pri,psr,sgi_p,stat,wchan,start_time,time,pcpu,pmem,vsize,size,rss,share,command\
:ps axX\
:dmesg -s 1048576\
:last\
:lsshut\
:ifconfig -a\
:nm-tool\
:route -n\
:ip route list\
:ip route list table all\
:ip rule list\
:ip neigh list\
:ip link show\
:ip ntable\
:ip a sh\
:ip -s -s link\
:firewall-cmd --list-all\
:ipcs -a\
:netstat -pantu\
:netstat -s\
:dmsetup ls\
:dmsetup ls --tree\
:dmsetup table\
:dmsetup table --target multipath\
:dmsetup status\
:multipathd -k'show config'\
:multipathd -k'show maps'\
:multipathd -k'show topo'\
:multipathd -k'show paths'\
:multipathd -k'show maps stats'\
:multipathd -k'show maps status'\
:multipathd -k'show status'\
:multipathd -k'show daemon'\
:multipathd -k'show blacklist'\
:multipathd -k'show devices'\
:multipath -v6 -ll\
:multipath -d\
:multipath -t\
:lsqeth\
:lschp\
:lscss\
:lscpu -ae\
:lsmem\
:lsdasd\
:lsdasd -u\
:ziorep_config -ADM\
:lsmod\
:lsdev\
:lsscsi\
:lstape\
:lszfcp\
:lszfcp -D\
:lszfcp -V\
:icainfo\
:icastats\
:lszcrypt -VV\
:ivp.e\
:pkcsconf -mlist\
:cat /var/lib/opencryptoki/pk_config_data\
:ls -al /usr/lib64/opencryptoki/stdll\
:SPident\
:rpm -qa | sort\
:sysctl -a\
:lsof \
> '${OUTPUT_FILE_LSOF}'\
:mount\
:df -h\
:df -i\
:pvpath -qa\
:find /boot -print0 | sort -z | xargs -0 -n 10 ls -ld\
:find /dev -print0 | sort -z | xargs -0 -n 10 ls -ld\
:java -version\
:cat /root/.bash_history\
:env\
:journalctl --all --no-pager --since=$(date -d '5 day ago' +%Y-%m-%d) --until=now --lines=50000 \
> '${OUTPUT_FILE_JOURNALCTL}'\
:openssl engine\
:systemd-delta\
:systemctl --all --no-pager show\
:systemctl --all --no-pager list-units\
:systemctl --all --no-pager list-unit-files\
:docker info\
:docker images\
:docker network ls\
:docker ps -a\
:docker version\
:docker stats --no-stream\
:systemctl status docker.service\
:blockdev --report\
:lvdisplay\
:lspci -vv\
:smc_dbg\
"
########################################
VM_CMDS="q userid\
:q users\
:q privclass\
:q cplevel\
:q cpservice\
:q cpprot user\
:q specex\
:q ssi\
:q cpus\
:q srm\
:q vtod\
:q time full\
:q timezone\
:q loaddev\
:q v osa\
:q v dasd\
:q v crypto\
:q v fcp\
:q v pav\
:q v sw\
:q v st\
:q v nic\
:q st\
:q xstore\
:q xstore user system\
:q sxspages\
:q vmlan\
:q vswitch\
:q vswitch details\
:q vswitch access\
:q vswitch active\
:q vswitch accesslist\
:q vswitch promiscuous\
:q vswitch controller\
:q port group all active details\
:q set\
:q comm\
:q controller all\
:q fcp\
:q frames\
:q lan\
:q lan all details\
:q lan all access\
:q cache\
:q nic\
:q pav\
:q proc\
:q proc topology\
:q mt\
:q qioass\
:q spaces\
:q swch all\
:q trace\
:q mdcache\
:q alloc page\
:q alloc spool\
:q dump\
:q dumpdev\
:q reorder VMUSERID\
:q quickdsp VMUSERID\
:ind load\
:ind sp\
:ind user\
"
###############################################################################
########################################
collect_cmdsout() {
local cmd
local ifs_orig
ifs_orig="${IFS}"
pr_syslog_stdout "1 of ${COLLECTION_COUNT}: Collecting command output"
IFS=:
for cmd in ${CMDS}; do
IFS=${ifs_orig} call_run_command "${cmd}" "${OUTPUT_FILE_CMD}"
done
IFS="${ifs_orig}"
if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then
call_run_command "hyptop -b -d 1 -n 5 -f \#,c,m,C:s,M:s,o -S c" "${OUTPUT_FILE_CMD}"
else call_run_command "hyptop -b -d 1 -n 5 -f \#,T,c,e,m,C:s,E:s,M:s,o -S c" "${OUTPUT_FILE_CMD}"
fi
pr_log_stdout " "
}
########################################
collect_vmcmdsout() {
local vm_command
local cp_command
local vm_cmds
local vm_userid
local module_loaded
local ifs_orig
local cp_buffer_size
local rc_buffer_size
module_loaded=1
ifs_orig="${IFS}"
if echo "${RUNTIME_ENVIRONMENT}" | grep -qi "z/VM" >/dev/null 2>&1; then
pr_syslog_stdout "2 of ${COLLECTION_COUNT}: Collecting z/VM command output"
if which vmcp >/dev/null 2>&1; then
cp_command="vmcp"
if ! lsmod 2>/dev/null | grep -q vmcp && modinfo vmcp >/dev/null 2>&1; then
modprobe vmcp && module_loaded=0 && sleep 2
fi
elif which hcp >/dev/null 2>&1; then
cp_command="hcp"
if ! lsmod 2>/dev/null | grep -q cpint; then
modprobe cpint && module_loaded=0 && sleep 2
fi
else
pr_log_stdout " "
pr_log_stdout "${SCRIPTNAME}: Warning: No program found to communicate to z/VM CP"
pr_log_stdout " Skipping collection of z/VM command output"
pr_log_stdout " "
return 1
fi
vm_userid=$(${cp_command} q userid 2>/dev/null | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
vm_cmds=$(echo "${VM_CMDS}" | sed "s/VMUSERID/${vm_userid}/g")
IFS=:
for vm_command in ${vm_cmds}; do
IFS="${ifs_orig}"
cp_buffer_size=2
rc_buffer_size=2
while test ${rc_buffer_size} -eq 2 && test ${cp_buffer_size} -lt 1024; do
cp_buffer_size=$(( cp_buffer_size * 2 ))
eval ${cp_command} -b ${cp_buffer_size}k "${vm_command}" >/dev/null 2>&1
rc_buffer_size=$?
done
call_run_command "${cp_command} -b ${cp_buffer_size}k ${vm_command}" "${OUTPUT_FILE_VMCMD}"
IFS=:
done
IFS=${ifs_orig}
if test ${module_loaded} -eq 0 && test "x${cp_command}" = "xhcp"; then
rmmod cpint
elif test ${module_loaded} -eq 0 && test "x${cp_command}" = "xvmcp"; then
rmmod vmcp
fi
else
pr_syslog_stdout "2 of ${COLLECTION_COUNT}: Collecting z/VM command output skipped - no z/VM environment"
fi
pr_log_stdout " "
}
########################################
collect_procfs() {
local file_name
pr_syslog_stdout "3 of ${COLLECTION_COUNT}: Collecting procfs"
for file_name in ${PROCFILES}; do
call_collect_file "${file_name}"
done
pr_log_stdout " "
}
########################################
collect_sysfs() {
local debugfs_mounted
local dir_name
local file_name
debugfs_mounted=0
# Requires kernel version newer then 2.4
if test "${LINUX_SUPPORT_SYSFS}" -eq 0; then
pr_syslog_stdout "4 of ${COLLECTION_COUNT}: Collecting sysfs"
# Requires kernel version of 2.6.13 or newer
if test "${LINUX_SUPPORT_SYSFSDBF}" -eq 0; then
if ! grep -qE "${MOUNT_POINT_DEBUGFS}.*debugfs" /proc/mounts 2>/dev/null; then
if mount -t debugfs debugfs "${MOUNT_POINT_DEBUGFS}" >/dev/null 2>&1; then
sleep 2
debugfs_mounted=1
else
pr_log_stdout "${SCRIPTNAME}: Warning: Unable to mount debugfs at \"${MOUNT_POINT_DEBUGFS}\""
fi
fi
fi
# Collect sysfs files using multiple threads (-J 1) while excluding
# files known to block on read (-x). Stop reading a file that takes
# more than 5 seconds (-T 5) such as an active ftrace buffer.
dump2tar /sys -z -o "${WORKPATH}/sysfs.tgz" -x '*/tracing/trace_pipe*' \
-x '*/tracing/per_cpu/*' --ignore-failed-read -J 1 -T 5
if [ $? -ne 0 ] ; then
echo "${SCRIPTNAME}: Warning: dump2tar failed or is unavailable - falling back to slow path"
call_run_command "find /sys -print0 | sort -z | xargs -0 -n 10 ls -ld" "${OUTPUT_FILE_SYSFS}"
find /sys -noleaf -type d 2>/dev/null | while IFS= read -r dir_name; do
mkdir -p "${WORKPATH}${dir_name}"
done
find /sys -noleaf -type f -perm /444\
-a -not -name "*trace_pipe*"\
2>/dev/null | while IFS= read -r file_name; do
echo " ${file_name}"
if ! dd if="${file_name}" status=noxfer iflag=nonblock of="${WORKPATH}${file_name}" >/dev/null 2>&1; then
echo "${SCRIPTNAME}: Warning: failed to copy \"${file_name}\""
fi
done
fi
if test ${debugfs_mounted} -eq 1; then
umount "${MOUNT_POINT_DEBUGFS}"
fi
else
pr_syslog_stdout "4 of ${COLLECTION_COUNT}: Collecting sysfs skipped. Kernel $(uname -r) must be newer than 2.4"
fi
pr_log_stdout " "
}
########################################
collect_logfiles() {
local file_name
pr_syslog_stdout "5 of ${COLLECTION_COUNT}: Collecting log files"
for file_name in ${LOGFILES}; do
call_collect_file "${file_name}"
done
pr_log_stdout " "
}
########################################
collect_configfiles() {
local file_name
pr_syslog_stdout "6 of ${COLLECTION_COUNT}: Collecting config files"
for file_name in ${CONFIGFILES}; do
call_collect_file "${file_name}"
done
pr_log_stdout " "
}
########################################
collect_osaoat() {
local network_devices
local network_device
network_devices=$(lsqeth 2>/dev/null | grep "Device name" \
| sed 's/.*:[[:space:]]\+\([^[:space:]]*\)[[:space:]]\+/\1/g')
if which qethqoat >/dev/null 2>&1; then
if test -n "${network_devices}"; then
pr_syslog_stdout "7 of ${COLLECTION_COUNT}: Collecting osa oat output"
for network_device in ${network_devices}; do
call_run_command "qethqoat ${network_device}" "${OUTPUT_FILE_OSAOAT}.out" &&
call_run_command "qethqoat -r ${network_device}" "${OUTPUT_FILE_OSAOAT}_${network_device}.raw"
done
else
pr_syslog_stdout "7 of ${COLLECTION_COUNT}: Collecting osa oat output skipped - no devices"
fi
else
pr_syslog_stdout "7 of ${COLLECTION_COUNT}: Collecting osa oat output skipped - not available"
fi
pr_log_stdout " "
}
########################################
collect_ethtool() {
local network_devices
local network_device
network_devices=$(ls /sys/class/net 2>/dev/null)
if which ethtool >/dev/null 2>&1; then
if test -n "${network_devices}"; then
pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting ethtool output"
for network_device in ${network_devices}; do
call_run_command "ethtool ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -k ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -a ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -c ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -g ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -i ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -l ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -P ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -S ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
call_run_command "ethtool -T ${network_device}" "${OUTPUT_FILE_ETHTOOL}"
done
else
pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting ethtool output skipped - no devices"
fi
else
pr_syslog_stdout "8 of ${COLLECTION_COUNT}: Collecting ethtool output skipped - not available"
fi
pr_log_stdout " "
}
########################################
# OpenVSwitch
collect_ovs() {
local br_list
local ovscmd
local bridge
local ovsbrcmd
local ovscmds
local ovsbrcmds
br_list=$(ovs-vsctl list-br)
ovscmds="ovs-dpctl -s show\
:ovs-vsctl -t 5 show\
:ovsdb-client dump\
"
if test -n "${br_list}"; then
pr_syslog_stdout "9 of ${COLLECTION_COUNT}: Collecting OpenVSwitch output"
IFS=:
for ovscmd in ${ovscmds}; do
IFS=${ifs_orig} call_run_command "${ovscmd}" "${OUTPUT_FILE_OVS}.out"
done
IFS="${ifs_orig}"
for bridge in ${br_list}; do
ovsbrcmds="ovs-ofctl show ${bridge}\
:ovs-ofctl dump-flows ${bridge}\
:ovs-appctl fdb/show ${bridge}\
"
IFS=:
for ovsbrcmd in ${ovsbrcmds}; do
IFS=${ifs_orig} call_run_command "${ovsbrcmd}" "${OUTPUT_FILE_OVS}.out"
done
IFS="${ifs_orig}"
done
else
pr_syslog_stdout "9 of ${COLLECTION_COUNT}: Collecting OpenVSwitch output skipped"
fi
pr_log_stdout " "
}
########################################
collect_domain_xml() {
local domain_list
local domain
domain_list=$(virsh list --all --name)
if test -n "${domain_list}"; then
pr_syslog_stdout "10 of ${COLLECTION_COUNT}: Collecting domain xml files"
for domain in ${domain_list}; do
call_run_command "virsh dumpxml ${domain}" "${OUTPUT_FILE_XML}_${domain}.xml"
done
else
pr_syslog_stdout "10 of ${COLLECTION_COUNT}: Collecting domain xml files skipped"
fi
pr_log_stdout " "
}
########################################
collect_docker() {
local item_list
local item
# call docker inspect for all containers
item_list=$(docker ps -qa)
if test -n "${item_list}"; then
pr_syslog_stdout "11a of ${COLLECTION_COUNT}: Collecting docker container output"
for item in ${item_list}; do
call_run_command "docker inspect ${item}" "${OUTPUT_FILE_DOCKER}"
done
else
pr_syslog_stdout "11a of ${COLLECTION_COUNT}: Collecting docker container output skipped"
fi
# call docker inspect for all networks
item_list=$(docker network ls -q)
if test -n "${item_list}"; then
pr_syslog_stdout "11b of ${COLLECTION_COUNT}: Collecting docker network output"
for item in ${item_list}; do
call_run_command "docker network inspect ${item}" "${OUTPUT_FILE_DOCKER}"
done
else
pr_syslog_stdout "11b of ${COLLECTION_COUNT}: Collecting docker network output skipped"
fi
pr_log_stdout " "
}
########################################
collect_nvme() {
local NVME
pr_syslog_stdout "12 of ${COLLECTION_COUNT}: Collecting nvme output"
call_run_command "nvme list" "${OUTPUT_FILE_NVME}"
for NVME in /dev/nvme[0-9]*; do
if [ -c $NVME ]; then
call_run_command "smartctl -x $NVME" "${OUTPUT_FILE_NVME}"
call_run_command "nvme fw-log $NVME" "${OUTPUT_FILE_NVME}"
call_run_command "nvme smart-log $NVME" "${OUTPUT_FILE_NVME}"
call_run_command "nvme error-log $NVME" "${OUTPUT_FILE_NVME}"
fi
done
pr_log_stdout " "
}
########################################
post_processing() {
local file_mtime
local file_mtime_epoche
local tmp_file
local file_name
pr_syslog_stdout "${COLLECTION_COUNT} of ${COLLECTION_COUNT}: Postprocessing"
find "${WORKPATH}etc/libvirt/qemu/" -maxdepth 1 -name "*.xml" 2>/dev/null | while IFS= read -r file_name; do
file_mtime_epoche=$(stat --format=%Y "${file_name}")
file_mtime=$(date +%Y%m%d%H%M.%S --date="@${file_mtime_epoche}")
tmp_file=${file_name}.$$
echo " ${file_name}"
if ! sed "s/\( \+passwd='\).*\('\)/\1********\2/g" "${file_name}" > "${tmp_file}"; then
echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}"
echo
fi
mv "${tmp_file}" "${file_name}"
touch --time=mtime -t "${file_mtime}" "${file_name}"
done
find "${WORKPATH}etc/libvirt/" -name "auth.conf" 2>/dev/null | while IFS= read -r file_name; do
file_mtime_epoche=$(stat --format=%Y "${file_name}")
file_mtime=$(date +%Y%m%d%H%M.%S --date="@${file_mtime_epoche}")
tmp_file=${file_name}.$$
echo " ${file_name}"
if ! sed "s/\(password=\).*/\1********/g" "${file_name}" > "${tmp_file}"; then
echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}"
echo
fi
mv "${tmp_file}" "${file_name}"
touch --time=mtime -t "${file_mtime}" "${file_name}"
done
find "${WORKPATH}" -maxdepth 1 -name "*.xml" 2>/dev/null | while IFS= read -r file_name; do
file_mtime_epoche=$(stat --format=%Y "${file_name}")
file_mtime=$(date +%Y%m%d%H%M.%S --date="@${file_mtime_epoche}")
tmp_file=${file_name}.$$
echo " ${file_name}"
if ! sed "s/\( \+passwd='\).*\('\)/\1********\2/g" "${file_name}" > "${tmp_file}"; then
echo "${SCRIPTNAME}: Warning: Postprocessing failed on ${file_name}"
echo
fi
mv "${tmp_file}" "${file_name}"
touch --time=mtime -t "${file_mtime}" "${file_name}"
done
pr_log_stdout " "
}
########################################
# Be aware that this output must be
# redirected into a separate logfile
call_run_command() {
local cmd
local logfile
local raw_cmd
cmd="${1}"
logfile="${2}"
raw_cmd=$(echo "${cmd}" | sed -ne 's/^\([^[:space:]]*\).*$/\1/p')
echo "#######################################################" >> "${logfile}"
echo "${USER}@${SYSTEMHOSTNAME:-localhost}> ${cmd}" >> "${logfile}"
# check if command exists
if ! which "${raw_cmd}" >/dev/null 2>&1; then
# check if command is a builtin
if ! command -v "${raw_cmd}" >/dev/null 2>&1; then
echo "${SCRIPTNAME}: Warning: Command \"${raw_cmd}\" not available" >> "${logfile}"
echo >> "${logfile}"
return 1
fi
fi
if ! eval "${cmd}" >> "${logfile}" 2>&1; then
echo "${SCRIPTNAME}: Warning: Command \"${cmd}\" failed" >> "${logfile}"
echo >> "${logfile}"
return 1
else
echo >> "${logfile}"
return 0
fi
}
########################################
call_collect_file() {
local directory_name
local file_name
file_name="${1}"
echo " ${file_name}"
directory_name=$(dirname "${file_name}" 2>/dev/null)
if test ! -e "${WORKPATH}${directory_name}"; then
mkdir -p "${WORKPATH}${directory_name}" 2>&1
fi
if ! cp -r --preserve=mode,timestamps -d -L --parents "${file_name}" "${WORKPATH}" 2>&1; then
return 1
else
return 0
fi
}
###############################################################################
########################################
# print that an instance is already running
print_alreadyrunning() {
print_version
cat <<EOF
Please check the system if another instance of '${SCRIPTNAME}' is already
running. If this is not the case, please remove the lock file
'${LOCKFILE}'.
EOF
}
########################################
# Setup the environment
environment_setup()
{
if test ! -e "${WORKDIR_BASE}"; then
mkdir -p "${WORKDIR_BASE}"
elif test ! -d "${WORKDIR_BASE}"; then
echo "${SCRIPTNAME}: Error: \"${WORKDIR_BASE}\" exists but this is a file!"
echo " Please make sure \"${WORKDIR_BASE}\" is a directory."
exit 1
fi
if test -e "${LOCKFILE}"; then
print_alreadyrunning
exit 1
else
touch "${LOCKFILE}"
echo "${DATETIME}" > "${LOCKFILE}"
fi
if ! mkdir "${WORKPATH}" 2>/dev/null; then
echo "${SCRIPTNAME}: Error: Target directory \"${WORKPATH}\" already exists or"
echo " \"${WORKDIR_BASE}\" does not exist!"
exit 1
fi
chmod 0700 "${WORKPATH}"
}
########################################
# create gzip-ped tar file
create_package()
{
local rc_tar
pr_stdout "Finalizing: Creating archive with collected data"
cd "${WORKDIR_BASE}"
tar -czf "${WORKARCHIVE}" "${WORKDIR_CURRENT}"
rc_tar=$?
if [ $rc_tar -eq 0 ]; then
chmod 0600 "${WORKARCHIVE}"
pr_stdout " "
pr_stdout "Collected data was saved to:"
pr_stdout " >> ${WORKARCHIVE} <<"
pr_stdout " "
pr_stdout "Review the collected data before sending to your service organization. "
pr_stdout " "
elif [ $rc_tar -eq 127 ]; then
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Error: tar command is not available!"
pr_stdout " Please install the corresponding package!"
else
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Error: Collection of data failed!"
pr_stdout " The creation of \"${WORKARCHIVE}\" was not successful."
pr_stdout " Please check the directory \"${WORKDIR_BASE}\""
pr_stdout " to provide enough free available space."
fi
}
########################################
# Cleaning up the prepared/collected information
environment_cleanup()
{
if ! rm -rf "${WORKPATH}" 2>/dev/null; then
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${WORKPATH}\" failed"
pr_stdout " Please remove the directory manually"
pr_stdout " "
fi
if ! rm -f "${LOCKFILE}" 2>/dev/null; then
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Warning: Deletion of \"${WORKDIR_BASE}${SCRIPTNAME}\" failed"
pr_stdout " Please remove the file manually"
pr_stdout " "
fi
}
########################################
# Function to perform a cleanup in case of a received signal
emergency_exit()
{
pr_stdout " "
pr_stdout "${SCRIPTNAME}: Info: Data collection has been interrupted"
pr_stdout " Cleanup of temporary collected data"
environment_cleanup
pr_stdout "${SCRIPTNAME}: Info: Emergency exit processed"
pr_stdout " "
logger -t "${SCRIPTNAME}" "Data collection interrupted"
exit
}
########################################
# Function to print to stdout when rediretion is active
pr_stdout()
{
echo "${@}" >&8
}
########################################
# Function to print to stdout and into log file when rediretion is active
pr_log_stdout()
{
echo "$@"
echo "$@" >&8
}
########################################
# Function to print to stdout and into log file when rediretion is active
pr_syslog_stdout()
{
echo "$@"
echo "$@" >&8
logger -t "${SCRIPTNAME}" "$@"
}
###############################################################################
# Running the script
environment_setup
print_version
# saving stdout/stderr and redirecting stdout/stderr into log file
exec 8>&1 9>&2 >"${LOGFILE}" 2>&1
# trap on SIGHUP=1 SIGINT=2 SIGTERM=15
trap emergency_exit SIGHUP SIGINT SIGTERM
pr_log_stdout ""
pr_log_stdout "Hardware platform = $(uname -i)"
pr_log_stdout "Kernel version = ${KERNEL_VERSION}.${KERNEL_MAJOR_REVISION}.${KERNEL_MINOR_REVISION} ($(uname -r 2>/dev/null))"
pr_log_stdout "Runtime environment = ${RUNTIME_ENVIRONMENT}"
pr_log_stdout ""
logger -t "${SCRIPTNAME}" "Starting data collection"
collect_cmdsout
collect_vmcmdsout
# Collecting the proc file system (content is specific based on kernel version)
collect_procfs
# Collecting sysfs in case we run on Kernel 2.4 or newer
collect_sysfs
collect_logfiles
collect_configfiles
collect_osaoat
collect_ethtool
collect_ovs
collect_domain_xml
collect_docker
collect_nvme
post_processing
create_package
environment_cleanup
logger -t "${SCRIPTNAME}" "Data collection completed"
exec 1>&8 2>&9 8>&- 9>&-
#EOF