Add ipmicap.sh: ipmitool wrapper to capture output to file
This commit adds the script /usr/local/bin/ipmicap.sh to the system controller. ipmicap.sh is a wrapper around ipmitool to capture serial console logs to file. It is used by dcmanager code to capture IPMI serial console logs when the rvmc_debug_level install property is 1 or higher. The script has a capture-only mode (--redirect) and an interactive mode. - The capture-only mode is invoked during subcloud install/upgrade operations when the configured via the capture_serial_console install value. - The interactive mode is intended to be used for normal lab or field troubleshooting operations. It can be used as a direct replacement of ipmitool, where the session output is automatically captured to file. Note on log file: The log file contains raw control characters. It is best viewed using 'less' with the '-R/--RAW-CONTROL-CHARS' option. Test Plan: PASS: - Test invocations of ipmicap.sh for both --redirect and interactive modes. Ensure output is properly captured in file. - Test various ipmicap.sh failure modes: - Invalid arguments - Improper credentials - Non-existent output directory for --file param - Ensure --kill option works as intended. ipmitool sessions are cleaned up properly. TODO: - Build package and ISO plus installation to ensure proper packaging Story: 2010144 Task: 49147 Signed-off-by: Kyle MacLeod <kyle.macleod@windriver.com> Change-Id: Ib2f61b47fae007b7c2f2eaabb61ad38dff7487e3
This commit is contained in:
parent
2ce860fb1a
commit
1916fba1f7
|
@ -1,5 +1,6 @@
|
|||
scripts/gen-bootloader-iso.sh usr/local/bin
|
||||
scripts/gen-bootloader-iso-centos.sh usr/local/bin
|
||||
scripts/ipmicap.sh usr/local/bin
|
||||
scripts/show-certs.sh usr/local/bin
|
||||
scripts/stx-iso-utils.sh usr/local/bin
|
||||
scripts/stx-iso-utils-centos.sh usr/local/bin
|
||||
|
|
|
@ -3,12 +3,12 @@ Upstream-Name: platform-util
|
|||
Source: https://opendev.org/starlingx/utilities
|
||||
|
||||
Files: *
|
||||
Copyright: (c) 2013-2021 Wind River Systems, Inc
|
||||
Copyright: (c) 2013-2023 Wind River Systems, Inc
|
||||
Others (See individual files for more details)
|
||||
License: Apache-2
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2021 Wind River Systems, Inc
|
||||
Copyright: 2021-2023 Wind River Systems, Inc
|
||||
License: Apache-2
|
||||
|
||||
License: Apache-2
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/usr/local/bin/gen-bootloader-iso.sh
|
||||
/usr/local/bin/gen-bootloader-iso-centos.sh
|
||||
/usr/local/bin/ipmicap.sh
|
||||
/usr/local/bin/show-certs.sh
|
||||
/usr/local/bin/stx-iso-utils.sh
|
||||
/usr/local/bin/stx-iso-utils-centos.sh
|
||||
|
|
|
@ -33,6 +33,7 @@ override_dh_auto_install:
|
|||
install -m 555 scripts/update-dm.sh $(DEBIAN_BUILDDIR)/usr/local/bin/
|
||||
install -m 555 scripts/gen-bootloader-iso.sh $(DEBIAN_BUILDDIR)/usr/local/bin/
|
||||
install -m 555 scripts/gen-bootloader-iso-centos.sh $(DEBIAN_BUILDDIR)/usr/local/bin/
|
||||
install -m 555 scripts/ipmicap.sh $(DEBIAN_BUILDDIR)/usr/local/bin/
|
||||
install -m 555 scripts/stx-iso-utils.sh $(DEBIAN_BUILDDIR)/usr/local/bin/
|
||||
install -m 555 scripts/stx-iso-utils-centos.sh $(DEBIAN_BUILDDIR)/usr/local/bin/
|
||||
install -m 555 scripts/show-certs.sh $(DEBIAN_BUILDDIR)/usr/local/bin/
|
||||
|
|
|
@ -0,0 +1,319 @@
|
|||
#!/bin/bash
|
||||
# vim: filetype=sh shiftwidth=4 softtabstop=4 expandtab
|
||||
#
|
||||
# Copyright (c) 2023 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# IPMI serial console capture script.
|
||||
# Captures the serial console output via underlying 'ipmitool' tool.
|
||||
# Also acts as an interactive ipmitool wrapper, capturing all console input/output.
|
||||
#
|
||||
set -o nounset; # Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR. Same as 'set -u'
|
||||
set -o pipefail; # Catch the error in case a piped command fails
|
||||
set +o posix # Ensure posix mode is off. Required for process substitution
|
||||
# set -o xtrace; # Turn on traces, useful while debugging (short form: on: 'set -x' off: 'set +x')
|
||||
|
||||
################################################################################
|
||||
# Helpers
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
readonly SCRIPTNAME=$(basename "$0")
|
||||
# shellcheck disable=SC2155,SC2034
|
||||
readonly SCRIPTDIR=$(readlink -m "$(dirname "$0")")
|
||||
|
||||
BMC_USER=${BMC_USER:-}
|
||||
BMC_ADDRESS=${BMC_ADDRESS:-}
|
||||
BMC_PASSWORD=${BMC_PASSWORD:-}
|
||||
LOG_FILE=${LOG_FILE:-$(pwd)/${SCRIPTNAME%.*}-$(date '+%Y%m%d-%H%M').log}
|
||||
SOL_ACTIVE=
|
||||
|
||||
DEBUG=
|
||||
|
||||
help() {
|
||||
cat<<EOF
|
||||
Wrapper script to capture the output of 'ipmitool' in a log file.
|
||||
|
||||
This script starts up ipmitool for the given BMC session parameters, capturing all output into a log file.
|
||||
|
||||
Once connected, you can interact with the session normally.
|
||||
|
||||
To stop the capture you either exit the ipmitool session normally (via the
|
||||
hotkey: '<Enter>~.'), or you can invoke the script again using the --kill
|
||||
option to cleanly shut everything down.
|
||||
|
||||
There are two intended use-cases for this script:
|
||||
1. Automatically capturing the serial console output during install via ipmitool
|
||||
In this case, the --redirect flag is provided, which simply redirects all output
|
||||
to the given log file. The console is not meant to be interactive in this case
|
||||
2. Provide an interactive wrapper around ipmitool which captures all input and output
|
||||
into the provided log file.
|
||||
|
||||
Note on log file: The log file is captured including raw control characters. It
|
||||
is best viewed using 'less' with the '-R/--RAW-CONTROL-CHARS' option.
|
||||
|
||||
USAGE:
|
||||
$SCRIPTNAME [-H <bmc-address] [-U <bmc-user>] [ -P <bmc-password> ] [--force-deactivate]
|
||||
|
||||
ARGUMENTS:
|
||||
IPMI connection arguments: If these are not given you will be prompted.
|
||||
You can also provide any of these as environment variables if preferred.
|
||||
|
||||
-H|--host <bmc-address> : BMC host address [env: BMC_ADDRESS]
|
||||
-U|--user <bmc-user> : BMC user [env: BMC_USER]
|
||||
-P|--password <bmc-address> : BMC password [env: BMC_PASSWORD]
|
||||
|
||||
-l|--log <file path> : Path to the console capture log file (directory must exist)
|
||||
Default: ./${SCRIPTNAME}-YYYY-MM-DD-HHMM.log
|
||||
-k|--kill : Stop the capture, cleanup the ipmitool session for this BMC address
|
||||
-f|--force-deactivate : Issue a 'sol deactivate' command before connecting
|
||||
-r|--redirect : Redirect all ouput to file (do not show on local console)
|
||||
This option is only useful for automated/background capture.
|
||||
|
||||
-h|--help: print this help
|
||||
|
||||
EXAMPLES:
|
||||
|
||||
Capture:
|
||||
$SCRIPTNAME -H 2620:10a:a001:df0::2 -U sysadmin -P sysadmin
|
||||
$SCRIPTNAME # you will be prompted for BMC details
|
||||
|
||||
# You can provide any of BMC_ADDRESS, BMC_USER, BMC_PASSWORD via environment variables:
|
||||
export BMC_PASSWORD=SecretPassword
|
||||
$SCRIPTNAME -H 2620:10a:a001:df0::2 -U sysadmin
|
||||
|
||||
# Override log file
|
||||
$SCRIPTNAME -l ./subcloud1-console-\$(date '+%Y%m%d-%H%M').log
|
||||
|
||||
Shutdown:
|
||||
# Inside the ipmitool session: <Enter>~.
|
||||
|
||||
# Or force sol deactivate and kill ipmitool for this configuration:
|
||||
$SCRIPTNAME -H 2620:10a:a001:df0::2 -U sysadmin -P sysadmin --kill
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Logging: these all log to stderr
|
||||
die() { >&2 colorecho red "FATAL: $*"; exit 1; }
|
||||
die_with_rc() { local rc=$1; shift; >&2 colorecho red "FATAL: $*, rc=$rc"; exit "$rc"; }
|
||||
check_rc_die() { local rc=$1; shift; [ "$rc" != "0" ] && die_with_rc "$rc" "$@"; return 0; }
|
||||
check_rc_err() { local rc=$1; shift; [ "$rc" != "0" ] && log_error "$*, rc=$rc"; return 0; }
|
||||
log_error() { >&2 colorecho red "ERROR: $*"; }
|
||||
log_warn() { >&2 colorecho orange "WARN: $*"; }
|
||||
log_info() { >&2 echo "$*"; }
|
||||
log_debug() { if [ -n "$DEBUG" ]; then >&2 echo "DEBUG: $*"; fi; }
|
||||
log_progress() { >&2 colorecho green "$*"; }
|
||||
get_logdate() { date '+%Y-%m-%d %H:%M:%S'; } # eg: log_info "$(get_logdate) My log message"
|
||||
|
||||
_init_log() {
|
||||
LOG_FILE="${LOG_FILE:-$(pwd)/${SCRIPTNAME%.*}.log}"
|
||||
log_debug "$(get_logdate) Logging output to $LOG_FILE"
|
||||
}
|
||||
|
||||
redirect_output_to_file() {
|
||||
# output to file only:
|
||||
_init_log
|
||||
if [ -f "${LOG_FILE}" ]; then
|
||||
# append
|
||||
exec &>> "${LOG_FILE}"
|
||||
else
|
||||
exec &> "${LOG_FILE}"
|
||||
fi
|
||||
}
|
||||
|
||||
tee_output_to_file() {
|
||||
# output to console and file:
|
||||
_init_log
|
||||
exec &> >(exec tee --append "${LOG_FILE}")
|
||||
}
|
||||
|
||||
colorecho() { # usage: colorecho <colour> <text> or colorecho -n <colour> <text>
|
||||
local echo_arg=
|
||||
if [ "$1" = "-n" ]; then
|
||||
echo_arg="-n"; shift;
|
||||
fi
|
||||
local colour="$1"; shift
|
||||
case "${colour}" in
|
||||
red) echo ${echo_arg} -e "$(tput setaf 1)$*$(tput sgr0)"; ;;
|
||||
green) echo ${echo_arg} -e "$(tput setaf 2)$*$(tput sgr0)"; ;;
|
||||
green-bold) echo ${echo_arg} -e "$(tput setaf 2; tput bold)$*$(tput sgr0)"; ;;
|
||||
yellow) echo ${echo_arg} -e "$(tput setaf 3; tput bold)$*$(tput sgr0)"; ;;
|
||||
orange) echo ${echo_arg} -e "$(tput setaf 3)$*$(tput sgr0)"; ;;
|
||||
blue) echo ${echo_arg} -e "$(tput setaf 4)$*$(tput sgr0)"; ;;
|
||||
purple) echo ${echo_arg} -e "$(tput setaf 5)$*$(tput sgr0)"; ;;
|
||||
cyan) echo ${echo_arg} -e "$(tput setaf 6)$*$(tput sgr0)"; ;;
|
||||
bold) echo ${echo_arg} -e "$(tput bold)$*$(tput sgr0)"; ;;
|
||||
normal|*) echo ${echo_arg} -e "$*"; ;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
# Script Functions
|
||||
|
||||
ipmitool_deactivate() {
|
||||
# Forcefully deactivate via sol deactivate
|
||||
# Use -E to supply password via IPMI_PASSWORD (security)
|
||||
export IPMI_PASSWORD=${BMC_PASSWORD}
|
||||
log_info "Disconnecting: ipmitool -I lanplus -H ${BMC_ADDRESS} -U ${BMC_USER} -E sol deactivate"
|
||||
ipmitool -I lanplus -H "${BMC_ADDRESS}" -U "${BMC_USER}" -E sol deactivate
|
||||
rc=$?
|
||||
log_info "Exit code from sol deactivate: ${rc}"
|
||||
export SOL_ACTIVE=
|
||||
}
|
||||
|
||||
ipmitool_activate() {
|
||||
# Establish remote console access via ipmi sol
|
||||
# Use -E to supply password via IPMI_PASSWORD
|
||||
export IPMI_PASSWORD=${BMC_PASSWORD}
|
||||
export SOL_ACTIVE=1
|
||||
log_info "Connecting: ipmitool -I lanplus -H ${BMC_ADDRESS} -U ${BMC_USER} -E sol activate"
|
||||
ipmitool -I lanplus -H "${BMC_ADDRESS}" -U "${BMC_USER}" -E sol activate
|
||||
local -i rc=$?
|
||||
# We see exit code of 143 when killed
|
||||
if [ ${rc} -eq 0 ] || [ ${rc} -eq 143 ]; then
|
||||
log_progress "ipmitool sol activate normal exit"
|
||||
else
|
||||
log_error "ipmitool sol activate abnormal exit [rc: ${rc}]"
|
||||
exit ${rc}
|
||||
fi
|
||||
export SOL_ACTIVE=
|
||||
}
|
||||
|
||||
ipmitool_kill() {
|
||||
log_progress "Shutting down IPMI capture"
|
||||
ipmitool_deactivate
|
||||
local pid pid2
|
||||
# Now kill ipmitool for this BMC address
|
||||
pid=$(pgrep -a ipmitool | awk '/'"${BMC_ADDRESS}"'/ { print $1; }')
|
||||
if [ -n "${pid}" ]; then
|
||||
log_info "Killing ipmitool pid: ${pid}"
|
||||
kill "${pid}"
|
||||
sleep 1
|
||||
pid2=$(pgrep -a ipmitool | awk '/'"${BMC_ADDRESS}"'/ { print $1; }')
|
||||
if [ -n "${pid2}" ]; then
|
||||
log_error "FAILED to kill ipmitool pid: ${pid}, pid2: ${pid2} (retrying with -9)"
|
||||
kill -9 "${pid2}"
|
||||
return 1
|
||||
fi
|
||||
log_info "Finished. You may need to run 'reset' in the original terminal."
|
||||
else
|
||||
log_info "No ipmitool session found for ${BMC_ADDRESS}"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
do_cleanup() {
|
||||
if [ -n "${SOL_ACTIVE}" ]; then
|
||||
ipmitool_deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
# Main
|
||||
#
|
||||
main() {
|
||||
local arg_force_deactivate=
|
||||
local arg_redirect_to_file=
|
||||
local arg_rvmc_config=
|
||||
local arg_kill=
|
||||
while [ $# -gt 0 ] ; do
|
||||
case "${1:-""}" in
|
||||
-h|--help)
|
||||
help
|
||||
;;
|
||||
-D|--debug)
|
||||
DEBUG=1
|
||||
;;
|
||||
-f|--force-deactivate)
|
||||
arg_force_deactivate=1
|
||||
;;
|
||||
-k|--kill)
|
||||
arg_kill=1
|
||||
;;
|
||||
-r|--redirect)
|
||||
arg_redirect_to_file=1
|
||||
;;
|
||||
-H|--host)
|
||||
shift
|
||||
BMC_ADDRESS=$1
|
||||
;;
|
||||
-U|--user)
|
||||
shift
|
||||
BMC_USER=$1
|
||||
;;
|
||||
-P|--password)
|
||||
shift
|
||||
BMC_PASSWORD=$1
|
||||
;;
|
||||
-l|--log*)
|
||||
shift
|
||||
LOG_FILE=$1
|
||||
export LOG_FILE
|
||||
;;
|
||||
--rvmc-config)
|
||||
shift
|
||||
arg_rvmc_config=$1
|
||||
;;
|
||||
*)
|
||||
die "Invalid argument '$1' [use -h/--help for help]"
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
if ! hash ipmitool 2>&-; then
|
||||
die "Cannot find 'ipmitool' in path. Is it installed?"
|
||||
fi
|
||||
if [ -n "${arg_rvmc_config}" ]; then
|
||||
if [ ! -r "${arg_rvmc_config}" ]; then
|
||||
die "RVMC config file does not exist or is not accessible: ${arg_rvmc_config}"
|
||||
fi
|
||||
BMC_ADDRESS=$(awk '/bmc_address:/ { print $2; }' "${arg_rvmc_config}")
|
||||
[ -n "${BMC_ADDRESS}" ] || die "Could not set BMC_ADDRESS from ${arg_rvmc_config}"
|
||||
BMC_USER=$(awk '/bmc_username:/ { print $2; }' "${arg_rvmc_config}")
|
||||
[ -n "${BMC_USER}" ] || die "Could not set BMC_USER from ${arg_rvmc_config}"
|
||||
BMC_PASSWORD=$(awk '/bmc_password:/ { print $2; }' "${arg_rvmc_config}" | base64 -d)
|
||||
[ -n "${BMC_PASSWORD}" ] || die "Could not set BMC_PASSWORD from ${arg_rvmc_config}"
|
||||
else
|
||||
# Interactive. Prompt for BMC info:
|
||||
[ -n "${BMC_ADDRESS}" ] || read -r -p "BMC address: " BMC_ADDRESS
|
||||
[ -n "${BMC_USER}" ] || read -r -p "BMC user: " BMC_USER
|
||||
[ -n "${BMC_PASSWORD}" ] || read -r -s -p "BMC password: " BMC_PASSWORD
|
||||
fi
|
||||
# Final check
|
||||
[ -n "${BMC_ADDRESS}" ] || die "BMC_ADDRESS is empty"
|
||||
[ -n "${BMC_USER}" ] || die "BMC_USER is empty"
|
||||
[ -n "${BMC_PASSWORD}" ] || die "BMC_PASSWORD is empty"
|
||||
export BMC_ADDRESS BMC_USER BMC_PASSWORD
|
||||
|
||||
if [ -n "${arg_kill}" ]; then
|
||||
ipmitool_kill
|
||||
exit $?
|
||||
fi
|
||||
|
||||
log_progress "Capturing console output at: ${LOG_FILE}"
|
||||
log_progress "To disconnect, use '<Enter>~.' or reinvoke this script with the --kill option."
|
||||
|
||||
# captures output in $LOG_FILE
|
||||
if [ -n "${arg_redirect_to_file}" ]; then
|
||||
redirect_output_to_file
|
||||
else
|
||||
tee_output_to_file
|
||||
fi
|
||||
|
||||
if [ -n "${arg_force_deactivate}" ]; then
|
||||
log_progress "Forcing deactivate"
|
||||
ipmitool_deactivate
|
||||
fi
|
||||
|
||||
# Ensure we disconnect on normal and abnormal termination signals:
|
||||
trap do_cleanup INT QUIT TERM EXIT
|
||||
|
||||
ipmitool_activate
|
||||
}
|
||||
|
||||
if [[ "${BASH_SOURCE[0]}" = "$0" ]]; then
|
||||
main "$@"
|
||||
fi
|
Loading…
Reference in New Issue