diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:40:13 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 06:40:13 +0000 |
commit | e9be59e1502a41bab9891d96d753102a7dafef0b (patch) | |
tree | c3b2da87c414881f4b53d0964f407c83492d813e /hb_report/utillib.sh | |
parent | Initial commit. (diff) | |
download | cluster-glue-a1e0af589373b0dd145b053b2cded45367a92ce0.tar.xz cluster-glue-a1e0af589373b0dd145b053b2cded45367a92ce0.zip |
Adding upstream version 1.0.12.upstream/1.0.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'hb_report/utillib.sh')
-rw-r--r-- | hb_report/utillib.sh | 752 |
1 files changed, 752 insertions, 0 deletions
diff --git a/hb_report/utillib.sh b/hb_report/utillib.sh new file mode 100644 index 0000000..0fcab80 --- /dev/null +++ b/hb_report/utillib.sh @@ -0,0 +1,752 @@ + # Copyright (C) 2007 Dejan Muhamedagic <dmuhamedagic@suse.de> + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public + # License as published by the Free Software Foundation; either + # version 2.1 of the License, or (at your option) any later version. + # + # This software is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + # General Public License for more details. + # + # You should have received a copy of the GNU General Public + # License along with this library; if not, write to the Free Software + # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + # + +# +# figure out the cluster type, depending on the process list +# and existence of configuration files +# +get_cluster_type() { + if ps -ef | egrep -qs '[a]isexec|[c]orosync' || + [ -f /etc/ais/openais.conf -a ! -f "$HA_CF" ] || + [ -f /etc/corosync/corosync.conf -a ! -f "$HA_CF" ] + then + debug "this is OpenAIS cluster stack" + echo "openais" + else + debug "this is Heartbeat cluster stack" + echo "heartbeat" + fi +} +# +# find out which membership tool is installed +# +echo_membership_tool() { + local f membership_tools + membership_tools="ccm_tool crm_node" + for f in $membership_tools; do + which $f 2>/dev/null && break + done +} +# find out if ptest or crm_simulate +# +echo_ptest_tool() { + local f ptest_progs + ptest_progs="crm_simulate ptest" + for f in $ptest_progs; do + which $f 2>/dev/null && break + done +} +# +# find nodes for this cluster +# +getnodes() { + # 1. set by user? + if [ "$USER_NODES" ]; then + echo $USER_NODES + # 2. running crm + elif iscrmrunning; then + debug "querying CRM for nodes" + get_crm_nodes + # 3. hostcache + elif [ -f $HA_VARLIB/hostcache ]; then + debug "reading nodes from $HA_VARLIB/hostcache" + awk '{print $1}' $HA_VARLIB/hostcache + # 4. ha.cf + elif [ "$CLUSTER_TYPE" = heartbeat ]; then + debug "reading nodes from ha.cf" + getcfvar node + # 5. if the cluster's stopped, try the CIB + elif [ -f $CIB_DIR/$CIB_F ]; then + debug "reading nodes from the archived $CIB_DIR/$CIB_F" + (CIB_file=$CIB_DIR/$CIB_F get_crm_nodes) + fi +} + +logd_getcfvar() { + sed 's/#.*//' < $LOGD_CF | + grep -w "^$1" | + sed 's/^[^[:space:]]*[[:space:]]*//' +} +get_logd_logvars() { + # unless logfacility is set to none, heartbeat/ha_logd are + # going to log through syslog + HA_LOGFACILITY=`logd_getcfvar logfacility` + [ "" = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=$DEFAULT_HA_LOGFACILITY + [ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY="" + HA_LOGFILE=`logd_getcfvar logfile` + HA_DEBUGFILE=`logd_getcfvar debugfile` +} +findlogdcf() { + local f + for f in \ + `test -x $HA_BIN/ha_logd && + which strings > /dev/null 2>&1 && + strings $HA_BIN/ha_logd | grep 'logd\.cf'` \ + `for d; do echo $d/logd.cf $d/ha_logd.cf; done` + do + if [ -f "$f" ]; then + echo $f + debug "found logd.cf at $f" + return 0 + fi + done + debug "no logd.cf" + return 1 +} +# +# logging +# +syslogmsg() { + local severity logtag + severity=$1 + shift 1 + logtag="" + [ "$HA_LOGTAG" ] && logtag="-t $HA_LOGTAG" + logger -p ${HA_LOGFACILITY:-$DEFAULT_HA_LOGFACILITY}.$severity $logtag $* +} + +# +# find log destination +# +findmsg() { + local d syslogdirs favourites mark log + # this is tricky, we try a few directories + syslogdirs="/var/log /var/logs /var/syslog /var/adm + /var/log/ha /var/log/cluster /var/log/pacemaker + /var/log/heartbeat /var/log/crm /var/log/corosync /var/log/openais" + favourites="ha-*" + mark=$1 + log="" + for d in $syslogdirs; do + [ -d $d ] || continue + log=`grep -l -e "$mark" $d/$favourites` && break + test "$log" && break + log=`grep -l -e "$mark" $d/*` && break + test "$log" && break + done 2>/dev/null + [ "$log" ] && + ls -t $log | tr '\n' ' ' + [ "$log" ] && + debug "found HA log at `ls -t $log | tr '\n' ' '`" || + debug "no HA log found in $syslogdirs" +} + +# +# print a segment of a log file +# +str2time() { + perl -e "\$time='$*';" -e ' + $unix_tm = 0; + eval "use Date::Parse"; + if (!$@) { + $unix_tm = str2time($time); + } else { + eval "use Date::Manip"; + if (!$@) { + $unit_tm = UnixDate(ParseDateString($time), "%s"); + } + } + if ($unix_tm != "") { + $unix_tm = int($unix_tm); + } + print $unix_tm; + ' +} +getstamp_syslog() { + awk '{print $1,$2,$3}' +} +getstamp_legacy() { + awk '{print $2}' | sed 's/_/ /' +} +getstamp_rfc5424() { + awk '{print $1}' +} +get_ts() { + local l="$1" ts + ts=$(str2time `echo "$l" | $getstampproc`) + if [ -z "$ts" ]; then + local fmt + for fmt in rfc5424 syslog legacy; do + [ "getstamp_$fmt" = "$getstampproc" ] && continue + ts=$(str2time `echo "$l" | getstamp_$fmt`) + [ -n "$ts" ] && break + done + fi + echo $ts +} +linetime() { + get_ts "`tail -n +$2 $1 | head -1`" +} +find_getstampproc() { + local t l func trycnt + t=0 l="" func="" + trycnt=10 + while [ $trycnt -gt 0 ] && read l; do + t=$(str2time `echo $l | getstamp_syslog`) + if [ "$t" ]; then + func="getstamp_syslog" + debug "the log file is in the syslog format" + break + fi + t=$(str2time `echo $l | getstamp_rfc5424`) + if [ "$t" ]; then + func="getstamp_rfc5424" + debug "the log file is in the rfc5424 format" + break + fi + t=$(str2time `echo $l | getstamp_legacy`) + if [ "$t" ]; then + func="getstamp_legacy" + debug "the log file is in the legacy format (please consider switching to syslog format)" + break + fi + trycnt=$(($trycnt-1)) + done + echo $func +} +find_first_ts() { + local l ts + while read l; do + ts=`get_ts "$l"` + [ "$ts" ] && break + warning "cannot extract time: |$l|; will try the next one" + done + echo $ts +} +findln_by_time() { + local logf=$1 + local tm=$2 + local first=1 + local last=`wc -l < $logf` + local tmid mid trycnt + while [ $first -le $last ]; do + mid=$((($last+$first)/2)) + trycnt=10 + while [ $trycnt -gt 0 ]; do + tmid=`linetime $logf $mid` + [ "$tmid" ] && break + warning "cannot extract time: $logf:$mid; will try the next one" + trycnt=$(($trycnt-1)) + # shift the whole first-last segment + first=$(($first-1)) + last=$(($last-1)) + mid=$((($last+$first)/2)) + done + if [ -z "$tmid" ]; then + warning "giving up on log..." + return + fi + if [ $tmid -gt $tm ]; then + last=$(($mid-1)) + elif [ $tmid -lt $tm ]; then + first=$(($mid+1)) + else + break + fi + done + echo $mid +} + +dumplog() { + local logf=$1 + local from_line=$2 + local to_line=$3 + [ "$from_line" ] || + return + tail -n +$from_line $logf | + if [ "$to_line" ]; then + head -$(($to_line-$from_line+1)) + else + cat + fi +} + +# +# find files newer than a and older than b +# +isnumber() { + echo "$*" | grep -qs '^[0-9][0-9]*$' +} +touchfile() { + local t + t=`mktemp` && + perl -e "\$file=\"$t\"; \$tm=$1;" -e 'utime $tm, $tm, $file;' && + echo $t +} +find_files() { + local dirs from_time to_time + local from_stamp to_stamp findexp + dirs=$1 + from_time=$2 + to_time=$3 + isnumber "$from_time" && [ "$from_time" -gt 0 ] || { + warning "sorry, can't find files based on time if you don't supply time" + return + } + if ! from_stamp=`touchfile $from_time`; then + warning "can't create temporary files" + return + fi + add_tmpfiles $from_stamp + findexp="-newer $from_stamp" + if isnumber "$to_time" && [ "$to_time" -gt 0 ]; then + if ! to_stamp=`touchfile $to_time`; then + warning "can't create temporary files" + return + fi + add_tmpfiles $to_stamp + findexp="$findexp ! -newer $to_stamp" + fi + find $dirs -type f $findexp +} + +# +# check permissions of files/dirs +# +pl_checkperms() { +perl -e ' +# check permissions and ownership +# uid and gid are numeric +# everything must match exactly +# no error checking! (file should exist, etc) +($filename, $perms, $in_uid, $in_gid) = @ARGV; +($mode,$uid,$gid) = (stat($filename))[2,4,5]; +$p=sprintf("%04o", $mode & 07777); +$p ne $perms and exit(1); +$uid ne $in_uid and exit(1); +$gid ne $in_gid and exit(1); +' $* +} +num_id() { + getent $1 $2 | awk -F: '{print $3}' +} +chk_id() { + [ "$2" ] && return 0 + echo "$1: id not found" + return 1 +} +check_perms() { + local f p uid gid n_uid n_gid + essential_files | + while read type f p uid gid; do + [ -$type $f ] || { + echo "$f wrong type or doesn't exist" + continue + } + n_uid=`num_id passwd $uid` + chk_id "$uid" "$n_uid" || continue + n_gid=`num_id group $gid` + chk_id "$gid" "$n_gid" || continue + pl_checkperms $f $p $n_uid $n_gid || { + echo "wrong permissions or ownership for $f:" + ls -ld $f + } + done +} + +# +# coredumps +# +pkg_mgr_list() { +# list of: +# regex pkg_mgr +# no spaces allowed in regex + cat<<EOF +zypper.install zypper +EOF +} +listpkg_zypper() { + local bins + local binary=$1 core=$2 + gdb $binary $core </dev/null 2>&1 | + awk ' + # this zypper version dumps all packages on a single line + /Missing separate debuginfos.*zypper.install/ { + sub(".*zypper.install ",""); print + exit} + n>0 && /^Try: zypper install/ {gsub("\"",""); print $NF} + n>0 {n=0} + /Missing separate debuginfo/ {n=1} + ' | sort -u +} +fetchpkg_zypper() { + local pkg + debug "get debuginfo packages using zypper: $@" + zypper -qn ref > /dev/null + for pkg in $@; do + zypper -qn install -C $pkg >/dev/null + done +} +find_pkgmgr() { + local binary=$1 core=$2 + local regex pkg_mgr + pkg_mgr_list | + while read regex pkg_mgr; do + if gdb $binary $core </dev/null 2>&1 | + grep "$regex" > /dev/null; then + echo $pkg_mgr + break + fi + done +} +get_debuginfo() { + local binary=$1 core=$2 + local pkg_mgr pkgs + gdb $binary $core </dev/null 2>/dev/null | + egrep 'Missing.*debuginfo|no debugging symbols found' > /dev/null || + return # no missing debuginfo + pkg_mgr=`find_pkgmgr $binary $core` + if [ -z "$pkg_mgr" ]; then + warning "found core for $binary but there is no debuginfo and we don't know how to get it on this platform" + return + fi + pkgs=`listpkg_$pkg_mgr $binary $core` + [ -n "$pkgs" ] && + fetchpkg_$pkg_mgr $pkgs +} +findbinary() { + local random_binary binary fullpath + random_binary=`which cat 2>/dev/null` # suppose we are lucky + binary=`gdb $random_binary $1 < /dev/null 2>/dev/null | + grep 'Core was generated' | awk '{print $5}' | + sed "s/^.//;s/[.':]*$//"` + if [ x = x"$binary" ]; then + debug "could not detect the program name for core $1 from the gdb output; will try with file(1)" + binary=$(file $1 | awk '/from/{ + for( i=1; i<=NF; i++ ) + if( $i == "from" ) { + print $(i+1) + break + } + }') + binary=`echo $binary | tr -d "'"` + binary=$(echo $binary | tr -d '`') + if [ "$binary" ]; then + binary=`which $binary 2>/dev/null` + fi + fi + if [ x = x"$binary" ]; then + warning "could not find the program path for core $1" + return + fi + fullpath=`which $binary 2>/dev/null` + if [ x = x"$fullpath" ]; then + for d in $HA_BIN $CRM_DAEMON_DIR; do + if [ -x $d/$binary ]; then + echo $d/$binary + debug "found the program at $d/$binary for core $1" + else + warning "could not find the program path for core $1" + fi + done + else + echo $fullpath + debug "found the program at $fullpath for core $1" + fi +} +getbt() { + local corefile absbinpath + which gdb > /dev/null 2>&1 || { + warning "please install gdb to get backtraces" + return + } + for corefile; do + absbinpath=`findbinary $corefile` + [ x = x"$absbinpath" ] && continue + get_debuginfo $absbinpath $corefile + echo "====================== start backtrace ======================" + ls -l $corefile + gdb -batch -n -quiet -ex ${BT_OPTS:-"thread apply all bt full"} -ex quit \ + $absbinpath $corefile 2>/dev/null + echo "======================= end backtrace =======================" + done +} + +# +# heartbeat configuration/status +# +iscrmrunning() { + local pid maxwait + ps -ef | grep -qs [c]rmd || return 1 + crmadmin -D >/dev/null 2>&1 & + pid=$! + maxwait=100 + while kill -0 $pid 2>/dev/null && [ $maxwait -gt 0 ]; do + sleep 0.1 + maxwait=$(($maxwait-1)) + done + if kill -0 $pid 2>/dev/null; then + kill $pid + false + else + wait $pid + fi +} +dumpstate() { + crm_mon -1 | grep -v '^Last upd' > $1/$CRM_MON_F + cibadmin -Ql > $1/$CIB_F + `echo_membership_tool` $MEMBERSHIP_TOOL_OPTS -p > $1/$MEMBERSHIP_F 2>&1 +} +getconfig() { + [ -f "$CONF" ] && + cp -p $CONF $1/ + [ -f "$LOGD_CF" ] && + cp -p $LOGD_CF $1/ + if iscrmrunning; then + dumpstate $1 + touch $1/RUNNING + else + cp -p $CIB_DIR/$CIB_F $1/ 2>/dev/null + touch $1/STOPPED + fi + [ "$HOSTCACHE" ] && + cp -p $HA_VARLIB/hostcache $1/$HOSTCACHE 2>/dev/null + [ "$HB_UUID_F" ] && + crm_uuid -r > $1/$HB_UUID_F 2>&1 + [ -f "$1/$CIB_F" ] && + crm_verify -V -x $1/$CIB_F >$1/$CRM_VERIFY_F 2>&1 +} +crmconfig() { + [ -f "$1/$CIB_F" ] && which crm >/dev/null 2>&1 && + CIB_file=$1/$CIB_F crm configure show >$1/$CIB_TXT_F 2>&1 +} +get_crm_nodes() { + cibadmin -Ql -o nodes | + awk ' + /<node / { + for( i=1; i<=NF; i++ ) + if( $i~/^uname=/ ) { + sub("uname=.","",$i); + sub("\".*","",$i); + print $i; + next; + } + } + ' +} +get_live_nodes() { + if [ `id -u` = 0 ] && which fping >/dev/null 2>&1; then + fping -a $@ 2>/dev/null + else + local h + for h; do ping -c 2 -q $h >/dev/null 2>&1 && echo $h; done + fi +} + +# +# remove values of sensitive attributes +# +# this is not proper xml parsing, but it will work under the +# circumstances +is_sensitive_xml() { + local patt epatt + epatt="" + for patt in $SANITIZE; do + epatt="$epatt|$patt" + done + epatt="`echo $epatt|sed 's/.//'`" + egrep -qs "name=\"$epatt\"" +} +test_sensitive_one() { + local file compress decompress + file=$1 + compress="" + echo $file | grep -qs 'gz$' && compress=gzip + echo $file | grep -qs 'bz2$' && compress=bzip2 + if [ "$compress" ]; then + decompress="$compress -dc" + else + compress=cat + decompress=cat + fi + $decompress < $file | is_sensitive_xml +} +sanitize_xml_attrs() { + local patt + sed $( + for patt in $SANITIZE; do + echo "-e /name=\"$patt\"/s/value=\"[^\"]*\"/value=\"****\"/" + done + ) +} +sanitize_hacf() { + awk ' + $1=="stonith_host"{ for( i=5; i<=NF; i++ ) $i="****"; } + {print} + ' +} +sanitize_one() { + local file compress decompress tmp ref + file=$1 + compress="" + echo $file | grep -qs 'gz$' && compress=gzip + echo $file | grep -qs 'bz2$' && compress=bzip2 + if [ "$compress" ]; then + decompress="$compress -dc" + else + compress=cat + decompress=cat + fi + tmp=`mktemp` + ref=`mktemp` + add_tmpfiles $tmp $ref + if [ -z "$tmp" -o -z "$ref" ]; then + fatal "cannot create temporary files" + fi + touch -r $file $ref # save the mtime + if [ "`basename $file`" = ha.cf ]; then + sanitize_hacf + else + $decompress | sanitize_xml_attrs | $compress + fi < $file > $tmp + mv $tmp $file + touch -r $ref $file +} + +# +# keep the user posted +# +fatal() { + echo "`uname -n`: ERROR: $*" >&2 + exit 1 +} +warning() { + echo "`uname -n`: WARN: $*" >&2 +} +info() { + echo "`uname -n`: INFO: $*" >&2 +} +debug() { + [ "$VERBOSITY" ] && [ $VERBOSITY -gt 0 ] && + echo "`uname -n`: DEBUG: $*" >&2 + return 0 +} +pickfirst() { + for x; do + which $x >/dev/null 2>&1 && { + echo $x + return 0 + } + done + return 1 +} + +# tmp files business +drop_tmpfiles() { + trap 'rm -rf `cat $__TMPFLIST`; rm $__TMPFLIST' EXIT +} +init_tmpfiles() { + if __TMPFLIST=`mktemp`; then + drop_tmpfiles + else + # this is really bad, let's just leave + fatal "eek, mktemp cannot create temporary files" + fi +} +add_tmpfiles() { + test -f "$__TMPFLIST" || return + echo $* >> $__TMPFLIST +} + +# +# get some system info +# +distro() { + local relf f + which lsb_release >/dev/null 2>&1 && { + lsb_release -d + debug "using lsb_release for distribution info" + return + } + relf=`ls /etc/debian_version 2>/dev/null` || + relf=`ls /etc/slackware-version 2>/dev/null` || + relf=`ls -d /etc/*-release 2>/dev/null` && { + for f in $relf; do + test -f $f && { + echo "`ls $f` `cat $f`" + debug "found $relf distribution release file" + return + } + done + } + warning "no lsb_release, no /etc/*-release, no /etc/debian_version: no distro information" +} + +pkg_ver_deb() { + dpkg-query -f '${Name} ${Version}' -W $* 2>/dev/null +} +pkg_ver_rpm() { + rpm -q --qf '%{name} %{version}-%{release} - %{distribution} %{arch}\n' $* 2>&1 | + grep -v 'not installed' +} +pkg_ver_pkg_info() { + for pkg; do + pkg_info | grep $pkg + done +} +pkg_ver_pkginfo() { + for pkg; do + pkginfo $pkg | awk '{print $3}' # format? + done +} +verify_deb() { + debsums -s $* 2>/dev/null +} +verify_rpm() { + rpm --verify $* 2>&1 | grep -v 'not installed' +} +verify_pkg_info() { + : +} +verify_pkginfo() { + : +} + +get_pkg_mgr() { + local pkg_mgr + if which dpkg >/dev/null 2>&1 ; then + pkg_mgr="deb" + elif which rpm >/dev/null 2>&1 ; then + pkg_mgr="rpm" + elif which pkg_info >/dev/null 2>&1 ; then + pkg_mgr="pkg_info" + elif which pkginfo >/dev/null 2>&1 ; then + pkg_mgr="pkginfo" + else + warning "Unknown package manager!" + return + fi + echo $pkg_mgr +} + +pkg_versions() { + local pkg_mgr=`get_pkg_mgr` + [ -z "$pkg_mgr" ] && + return + debug "the package manager is $pkg_mgr" + pkg_ver_$pkg_mgr $* +} +verify_packages() { + local pkg_mgr=`get_pkg_mgr` + [ -z "$pkg_mgr" ] && + return + verify_$pkg_mgr $* +} + +crm_info() { + $CRM_DAEMON_DIR/crmd version 2>&1 +} |