diff options
author | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
---|---|---|
committer | Igor Pashev <pashev.igor@gmail.com> | 2014-10-26 12:33:50 +0400 |
commit | 47e6e7c84f008a53061e661f31ae96629bc694ef (patch) | |
tree | 648a07f3b5b9d67ce19b0fd72e8caa1175c98f1a /qa/common.check | |
download | pcp-debian.tar.gz |
Debian 3.9.10debian/3.9.10debian
Diffstat (limited to 'qa/common.check')
-rw-r--r-- | qa/common.check | 1620 |
1 files changed, 1620 insertions, 0 deletions
diff --git a/qa/common.check b/qa/common.check new file mode 100644 index 0000000..8b66507 --- /dev/null +++ b/qa/common.check @@ -0,0 +1,1620 @@ +# +# common preliminary check procedures for QA scripts +# +# Copyright (c) 2014 Red Hat. +# Copyright (c) 1997-2002 Silicon Graphics, Inc. All Rights Reserved. +# + +if [ ! -f localconfig ] +then + ./mk.localconfig +fi + +# need to know what version we're running +. ./localconfig + +# common routine for preventing a test from running on some platforms +# use: _notrun "Reason for not running this test is ..." +# +_notrun() +{ + echo $@ >$here/$seq.notrun + echo "$seq: [not run] `cat $here/$seq.notrun`" + exit 0 +} + +# common routine for failing a test in a standard way +# use: _fail "Reason for failing this test is ..." +# +_fail() +{ + echo FAIL: $@ 1>&2 + status=1 + exit $status +} + +# calculate the size of a given fail, echo it on standard output +# +_filesize() +{ + filename=$1 + + if [ $PCP_PLATFORM = darwin ] + then + # Mac OS X format + # stat(1) format + # 234881026 5304024 -rwxr-xr-x 1 kenj kenj 0 2016 "May 4 14:00:42 2011" "Apr 27 20:14:16 2011" "Apr 27 20:14:16 2011" "Apr 27 20:14:16 2011" 4096 8 0 441 + stat $filename 2>&1 | $PCP_AWK_PROG '{ print $8 }' + else + # stat(1) format + # File: `441' + # Size: 2016 Blocks: 8 IO Block: 4096 regular file + # Device: 816h/2070d Inode: 758237 Links: 1 + # Access: (0755/-rwxr-xr-x) Uid: ( 1000/ kenj) Gid: ( 1000/ kenj) + # Access: 2011-05-09 06:52:58.000000000 +1000 + # Modify: 2011-02-27 14:42:30.000000000 +1100 + # Change: 2011-02-28 19:21:27.000000000 +1100 + stat $filename 2>&1 | $PCP_AWK_PROG '$1 == "Size:" { print $2 }' + fi +} + +# determine whether sufficient free space exists to run a test; +# passed in parameter is the size needed, in megabytes. +# +_check_freespace() +{ + needspace=$1 + + # Filesystem 1M-blocks Used Available Use% Mounted on + # /dev/sda5 57349 24838 29621 46% / + free=`df -mP . | $PCP_AWK_PROG 'NR == 2 { print $4 }'` + + if [ -z "$free" ] + then + echo "Cannot determine free space (df -mP fails)" + elif [ "$free" -lt "$needspace" ] + then + echo "Insufficient free space ($free MB, need $needspace MB)" + fi +} + +# check that a given agent (argument 1) is up and running; +# indicated by return status and agent warnings to stdout. +# +_check_agent() +{ + agent=$1 + quiet=$2 + sts=0 + + [ X"$quiet" = "X" ] && quiet=false + + pminfo -f $agent 2>&1 | \ + $PCP_AWK_PROG ' +BEGIN { value=0; metric=0; warn=0 } +NF==0 { next } +/ value / { value++; next } +/^'$agent'/ { metric++ + if ($0 !~ /:/) next + } + { warn++ } +END { if (warn) printf "%d warnings, ",warn + printf "%d metrics and %d values\n",metric,value + }' > $tmp.fetch 2>&1 + + pminfo -v $agent 2>&1 | LC_COLLATE=POSIX sort >$tmp.verify + $quiet || cat $tmp.fetch + if grep warnings $tmp.fetch > /dev/null 2>&1 + then + # X warnings, Y metrics and Z values + num_warn=`sed -n -e '/warnings/s/ .*//p' < $tmp.fetch` + if [ "$num_warn" -gt 0 ] + then + echo "Warnings when fetching $agent metrics:" + cat $tmp.verify + sts=1 + fi + else + num_warn=0 + fi + + return $sts +} + +# +# prepare and cleanup pmda routines work together to ensure +# pmcd+pmda state is left as it was at the start of a test. +# +_prepare_pmda() +{ + agent=$1 + + iam=$agent + done_clean=false + install_on_cleanup=false + pminfo $agent >/dev/null 2>&1 && install_on_cleanup=true + # copy the pmcd config file to restore state later. + cp $PCP_PMCDCONF_PATH $tmp.pmcd.conf + unset ROOT MAKEFLAGS +} + +_cleanup_pmda() +{ + agent=$1 + + if $done_clean + then + : + else + [ -f $tmp.pmcd.conf ] && $sudo mv $tmp.pmcd.conf $PCP_PMCDCONF_PATH + rm -f $tmp.* + $sudo $PCP_RC_DIR/pcp restart | _filter_pcp_start + _wait_for_pmcd + _wait_for_pmlogger + if $install_on_cleanup + then + ( cd $PCP_PMDAS_DIR/$agent; $sudo ./Install </dev/null >/dev/null 2>&1 ) + else + ( cd $PCP_PMDAS_DIR/$agent; $sudo ./Remove >/dev/null 2>&1 ) + fi + done_clean=true + fi +} + +_host_to_ipaddr() +{ + perl -MSocket -e ' + my $packed_ip = gethostbyname("'$1'"); + if (defined $packed_ip) { + my $ip_address = inet_ntoa($packed_ip); + print $ip_address; + }' +} + +_ipaddr_to_host() +{ + perl -MSocket -e ' + my $iaddr = inet_aton("'$1'"); + if (defined $iaddr) { + my $name = gethostbyaddr($iaddr, AF_INET); + print $name; + }' +} + +_host_to_fqdn() +{ + ans=`hostname -f 2>/dev/null | grep '\.'` + if [ -z "$ans" ] + then + if which nslookup >/dev/null 2>&1 + then + ans=`nslookup $1 2>&1 | sed -n -e '/^Name:[ ][ ]*/s///p'` + fi + fi + if [ -z "$ans" ] + then + if which domainname >/dev/null 2>&1 + then + ans=`domainname` + [ -z "$ans" ] || ans="$1.$ans" + fi + fi + if [ -z "$ans" ] + then + # fake out localhost.localdomain if no working DNS + ans=localhost.localdomain + fi + echo "$ans" +} + +_check_metric() +{ + if pminfo -h ${2-localhost} -f $1 2>&1 | grep " value " >/dev/null + then + : + else + echo "Check failed for metric \"$1\" at host \"${2-localhost}\" ... is PMDA installed?" + exit 1 + fi +} + +# wait_for_pmcd [maxdelay [host]] +# +_wait_for_pmcd() +{ + # 20 seconds default seems like a reasonble max time to get going + #debug# set -x + _can_wait=${1-20} + _host=${2-localhost} + _i=0 + _dead=true + #debug# ping -c 2 $_host + #debug# pcp -h $_host + #debug# pcp -h `hostname` + while [ $_i -lt $_can_wait ] + do + #debug# pmprobe -n $PCP_VAR_DIR/pmns/root_pmcd -h $_host pmcd.numclients + _sts=`pmprobe -n $PCP_VAR_DIR/pmns/root_pmcd -h $_host pmcd.numclients 2>/dev/null | $PCP_AWK_PROG '{print $2}'` + if [ "${_sts:-0}" -gt 0 ] + then + # numval really > 0, we're done + # + _dead=false + break + fi + sleep 1 + _i=`expr $_i + 1` + done + if $_dead + then + echo "Arrgghhh ... pmcd at $_host failed to start after $_can_wait seconds" + echo "=== failing pmprobe ===" + echo "+ pmprobe -n $PCP_VAR_DIR/pmns/root_pmcd -h $_host pmcd.numclients" + pmprobe -n $PCP_VAR_DIR/pmns/root_pmcd -h $_host pmcd.numclients + case $_host + in + localhost) + echo "=== pmcd.log ===" + cat $PCP_LOG_DIR/pmcd/pmcd.log + + echo "likely looking processes ..." + ps "$PCP_PS_ALL_FLAGS" | egrep "[p]m|[P]ID" + ;; + *) + ssh pcpqa@$_host "sh -c '. \$PCP_DIR/etc/pcp.env; echo; echo "=== pmcd.log ==="; [ -f \$PCP_LOG_DIR/pmcd/pmcd.log ] && cat \$PCP_LOG_DIR/pmcd/pmcd.log; [ -f \$PCP_LOG_DIR/pmcd.log ] && cat \$PCP_LOG_DIR/pmcd.log; echo; echo likely looking processes ...; ( ps \$PCP_PS_ALL_FLAGS | egrep \"[p]m|[P]ID\" )'" </dev/null + ;; + esac + status=2 + exit $status + fi +} + +# wait_for_pmlogger [pid logfile [maxdelay]] +# +_wait_for_pmlogger() +{ + # 20 seconds default seems like a reasonable max time to get going + _maxdelay=20 + + case $# in + 0) + _pid="-P" + dir_hostname=`hostname || echo localhost` + _logfile="$PCP_LOG_DIR/pmlogger/$dir_hostname/pmlogger.log" + _iam="primary" + ;; + 2) + _pid=$1 + _logfile=$2 + _iam="pid=$_pid" + ;; + 3) + _pid=$1 + _logfile=$2 + _maxdelay=$3 + _iam="pid=$_pid" + ;; + *) + echo "_wait_for_pmlogger(): wrong number of arguments" + status=1 + exit $status + ;; + esac + + #debug# set -x + _i=0 + _dead=true + rm -f $tmp._wait_for_pmlogger.pmlc + while [ $_i -lt $_maxdelay ] + do + #debug# ps -ef | grep "[^0-9]$_pid[^0-9]" + echo "pmlc $_pid" >> $tmp._wait_for_pmlogger.pmlc + if pmlc $_pid </dev/null 2>&1 \ + | tee -a $tmp._wait_for_pmlogger.pmlc \ + | egrep "Connection refused|Transport endpoint is not connected" >/dev/null + then + sleep 1 + _i=`expr $_i + 1` + else + # pmlogger socket has been set up ... + _dead=false + # give pmlogger a chance to detect that pmlc has gone away + # so the port is free + sleep 1 + break + fi + done + if $_dead + then + echo "Arrgghhh ... pmlogger ($_iam) failed to start after $_maxdelay seconds" + echo "at `date`." + echo "pmlogger log ($_logfile) ..." + cat $_logfile + echo + + # If pmlc could not connect to pmlogger, report details + if [ -s $tmp._wait_for_pmlogger.pmlc ] + then + echo "pmlc output ..." + cat $tmp._wait_for_pmlogger.pmlc + echo + fi + + # If pmlogger could not connect to PMCD, find which host it was + # connecting to, and get the pmcd.log file from that host. + cat $_logfile | $PCP_AWK_PROG '/pmlogger: Cannot connect to PMCD on host/' \ + | sed -e ' s/pmlogger: Cannot connect to PMCD on host "//g' \ + -e ' s/": .*//g' >$tmp._wait_for_pmlogger.host + if [ -s $tmp._wait_for_pmlogger.host ] + then + _pmcdhost=`cat $tmp._wait_for_pmlogger.host` + echo "pmcd log ($_pmcdhost:$PCP_LOG_DIR/pmcd/pmcd.log) ..." + if [ -r /hosts/$_pmcdhost$PCP_LOG_DIR/pmcd/pmcd.log ] + then + cat /hosts/$_pmcdhost$PCP_LOG_DIR/pmcd/pmcd.log + elif [ -r /hosts/$_pmcdhost$PCP_LOG_DIR/pmcd.log ] + then + cat /hosts/$_pmcdhost$PCP_LOG_DIR/pmcd.log + else + if [ "`hostname | sed -e 's/\..*//'`" != $_pmcdhost ] + then + if scp -q $_pmcdhost:$PCP_LOG_DIR/pmcd/pmcd.log \ + $tmp._wait_for_pmlogger.pmcdlog + then + cat $tmp._wait_for_pmlogger.pmcdlog + elif scp -q $_pmcdhost:$PCP_LOG_DIR/pmcd.log \ + $tmp._wait_for_pmlogger.pmcdlog + then + cat $tmp._wait_for_pmlogger.pmcdlog + fi + else + cat $PCP_LOG_DIR/pmcd/pmcd.log + fi + fi + fi + base=`cat $_logfile | sed -n '/^Archive basename:[ ]*/s///p'` + if [ -n "$base" -a -f $base.0 ] + then + echo "archive created ..." + pmdumplog -l $base.0 + nres=`pmdumplog $base.0 | grep '^[0-9]' | wc -l | sed -e 's/ //g'` + echo "archive contains $nres records" + else + echo "archive not created" + fi + echo + echo "local pmlogger map ..." + for map in $PCP_TMP_DIR/pmlogger/* + do + if [ "`echo $map`" = "$PCP_TMP_DIR"'/pmlogger/*' ] + then + echo "No files in $PCP_TMP_DIR/pmlogger !?" + else + ls -l $map + cat $map + fi + done + echo + echo "Likely looking processes ..." + ps $PCP_PS_ALL_FLAGS | egrep '[p]m|[P]ID' + status=2 + exit $status + fi + rm -f $tmp._wait_for_pmlogger.* +} + +# Start a pmlogger with appropriate privs that it can create +# portmap files in PCP_TMP_DIR (requires pcp or root account) +# +# Couple of side-effects to be aware of: +# Returns with $pid containing the backgrounded pmlogger PID; +# Creates the pmlogger archive, logfile, etc as root/pcp, not +# as the user running QA (so, tmp cleanup needs to use sudo). +# +_start_up_pmlogger() +{ + cat >$tmp.cmd <<End-of-File +#!/bin/sh +pmlogger $@ & +echo pid=\$! +End-of-File + + __user=root + id pcp >/dev/null 2>&1 && __user=pcp + + $sudo -u $__user sh $tmp.cmd $@ >$tmp.pid + eval `cat $tmp.pid` +} + +# wait for a pmlogger process to end that is not our child +# (else we could just use shell wait command). Instead we +# must keep tabs on the PID file and bail when its gone. +# +_wait_pmlogger_end() +{ + pid=$1 + + while true + do + test -f "$PCP_TMP_DIR/pmlogger/$pid" || return + pmsleep 0.1 + done +} + +# ensure primary logger allows pmlc state changes +# +_writable_primary_logger() +{ + configfile="$PCP_SYSCONF_DIR/pmlogger/config.default" + + echo "_open_primary_logger checking config file: $configfile" >>$seq.full + + # first, move aside the default configuration file + $sudo mv "$configfile" "$configfile.$seq" + + # next, create a new default configuration file + PMLOGCONF="$PCP_BINADM_DIR/pmlogconf" + test -f "$configfile" || \ + $sudo $PMLOGCONF -q -h localhost "$configfile" >>$seq.full 2>&1 + + # finally, open it up to local access from pmlc + $sudo sed -i -e 's/^allow localhost.*$/allow localhost : all;/g' \ + -e 's/^allow local:.*$/allow local:* : all;/g' "$configfile" +} + +# restore primary logger pmlc access restrictions +# +_restore_primary_logger() +{ + configfile="$PCP_SYSCONF_DIR/pmlogger/config.default" + + # close out access from local pmlc once more + test -f "$configfile" && \ + $sudo sed -i -e 's/^allow localhost.*$/allow localhost : enquire;/g' \ + -e 's/^allow local:.*$/allow local:* : enquire;/g' \ + "$configfile" + + # if there was an initial configuration, put it back + test -f "$configfile.$seq" && $sudo mv "$configfile.$seq" "$configfile" + ( id pcp && $sudo chown pcp:pcp "$configfile" ) >/dev/null 2>&1 +} + +# purify support +# +# typical usage: +# +# At the top before outputting $seq.out but after setting $seq ... +# _check_purify prog +# +# Main code... +# _setup_purify prog +# _run_purify [arg1] [arg2] +# + +# initial setup for running purify +# creates purified binary in $_purify_dir +# +_setup_purify() +{ + # Need to change these to match Purify setup locally, if you + # have Purify! + # + LM_LICENSE_FILE=27000@snort.melbourne.sgi.com + RSU_LICENSE_MAP=/usr/Rational/config/LICENSE_MAP + export LM_LICENSE_FILE RSU_LICENSE_MAP + + rm -f $seq.full + _path=$1 + _prog=`echo $_path | sed -e 's#.*/##'` + _pure_prog="$_prog.pure" + _purify_dir=$tmp.purify + + rm -rf $_purify_dir + mkdir $_purify_dir + cp $_path $_purify_dir + _here=`pwd` + cd $_purify_dir + + cat >.purify<<EOF +suppress umr _write +suppress miu +EOF + unset PURIFYOPTIONS PUREOPTIONS + purify -log-file=stderr $_prog >out 2>&1 + if [ ! -x $_prog.pure ] + then + cat out + echo "Hmm ... purify failed to create $_prog.pure" + exit + fi + cd $_here +} + +_run_purify() +{ + args=$* + + _here=`pwd` + cd $_purify_dir + $_purify_dir/$_pure_prog $args > $tmp._purify.out 2>&1 + cat $tmp._purify.out >>$_here/$seq.full + if grep -i expired $tmp._purify.out >/dev/null; then + cat $tmp._purify.out + else + _filter_purify < $tmp._purify.out + fi + cd $_here +} + +_filter_purify() +{ + $PCP_AWK_PROG ' +state == 0 && /License successfully checked out/ { state = 1; next } +state == 0 && /Purify checking enabled/ { state = 1; next } +state == 1 { print }' \ + | sed \ + -e 's/pid [0-9][0-9]*/pid PID/g' \ + -e "s;$_purify_dir;TMP;g" \ + -e '/reserved for Purify internal use/d' \ + -e 's/suppressed chunks/suppressed blocks/g' \ + | $PCP_AWK_PROG -v extra="$PURIFY_FILTER_EXTRA" ' +/Purify instrumented/ { skip = 0 } +/bytes leaked\./ { print "..."; skip = 1; next } +skip == 1 { next } + { print } +/Purify Heap Analysis/ { print "..."; skip = 1 } +/Basic memory usage \(including Purify/ { print "..."; skip = 1 } +extra != "" && /Current file descriptors/ { print "..."; skip = 1 }' \ + | (if [ "$PURIFY_FILTER_EXTRA" ] + then sed -e 's/in use: [0-9][0-9]*/in use: N/' + else cat - + fi) +} + +_check_purify() +{ + [ $# -eq 1 ] || _notrun "_check_purify needs executable as argument" + _path=$1 + which purify >/dev/null 2>&1 + [ $? -eq 0 ] || _notrun "No purify binary found" +} + +_check_display() +{ + # note: non-X systems (MacOSX, Windows) must pass here unchallenged + which xdpyinfo >/dev/null 2>&1 + if test $? -eq 0 + then + DISPLAY=$PCPQA_CLOSE_X_SERVER xdpyinfo >/dev/null 2>&1 || \ + _notrun "Failed sanity check on DISPLAY $PCPQA_CLOSE_X_SERVER" + fi +} + +# valgrind support +# +# typical usage: +# +# At the top before outputting $seq.out but after setting $seq ... +# _check_valgrind +# +# Main code... +# _run_valgrind app [arg1 [arg2] ... ] +# + +_check_valgrind() +{ + which valgrind >/dev/null 2>&1 + [ $? -eq 0 ] || _notrun "No valgrind binary found" +} + +# Note: because we may be avoiding leaks in system libraries +# via the suppressions, there is potential indeterminism for +# the following case: +# -All heap blocks were freed -- no leaks are possible +# +LEAK SUMMARY: +# +definitely lost: 0 bytes in 0 blocks +# +indirectly lost: 0 bytes in 0 blocks +# +_filter_valgrind() +{ + noleak="LEAK SUMMARY:\ndefinitely lost: 0 bytes in 0 blocks\nindirectly lost: 0 bytes in 0 blocks" + sed \ + -e 's/^==*[1-9][0-9]*==* *//' \ + -e '/^$/d' \ + -e '/^Copyright (/d' \ + -e '/^Using Valgrind-/d' \ + -e '/^Parent PID:/d' \ + -e '/^HEAP SUMMARY:/d' \ + -e '/^in use at exit:/d' \ + -e '/^total heap usage:/d' \ + -e '/^possibly lost:/d' \ + -e '/^still reachable:/d' \ + -e '/^suppressed:/d' \ + -e '/^Reachable blocks (those to which a pointer was found)/d' \ + -e '/^To see them, rerun with:/d' \ + -e '/^For counts of detected and suppressed errors,/d' \ + -e '/^ERROR SUMMARY:/s/ (suppressed: [^)]*)/ .../' \ + -e "s/^All heap blocks were freed -- .*/$noleak/g" +} + +_run_valgrind() +{ + __version=`valgrind --version | sed -e 's/valgrind-//'` + if [ -f $here/valgrind-suppress-$__version ] + then + __extra="--suppressions=$here/valgrind-suppress-$__version" + echo "Warning: using extra $__extra" >>$seq.full + else + __extra='' + fi + valgrind \ + --leak-check=full --read-var-info=yes \ + --suppressions=$here/valgrind-suppress $__extra \ + --log-file=$tmp._valgrind \ + $* 2>$tmp._valgrind.err >$tmp._valgrind.out + echo "=== std out ===" + cat $tmp._valgrind.out + echo "=== std err ===" + cat $tmp._valgrind.err + echo "=== valgrind report ===" >>$seq.full + cat $tmp._valgrind >>$seq.full + echo "=== filtered valgrind report ===" + _filter_valgrind <$tmp._valgrind +} + +# +# Checks that given_value is in range of correct_value +/- tolerance. +# Tolerance can be an absolute value or a percentage of the correct value +# (see examples with tolerances below). +# Outputs suitable message to stdout if it's not in range. +# +# A verbose option, -v, may be used as the LAST argument +# +# e.g. +# foo: 0.0298 = 0.03 +/- 5% +# _within_tolerance "foo" 0.0298 0.03 5% +# +# foo: 0.0298 = 0.03 +/- 0.01 +# _within_tolerance "foo" 0.0298 0.03 0.01 +# +# foo: 0.0298 = 0.03 -0.01 +0.002 +# _within_tolerance "foo" 0.0298 0.03 0.01 0.002 +# +# foo: verbose output of 0.0298 = 0.03 +/- 5% +# _within_tolerance "foo" 0.0298 0.03 5% -v + +_within_tolerance() +{ + _name=$1 + _given_val=$2 + _correct_val=$3 + _mintol=$4 + _maxtol=$_mintol + _verbose=0 + _debug=false + + # maxtol arg is optional + # verbose arg is optional + if [ $# -ge 5 ] + then + if [ "$5" = "-v" ] + then + _verbose=1 + else + _maxtol=$5 + fi + fi + if [ $# -ge 6 ] + then + [ "$6" = "-v" ] && _verbose=1 + fi + + # find min with or without % + _mintolerance=`echo $_mintol | sed -e 's/%//'` + if [ $_mintol = $_mintolerance ] + then + _min=`echo "scale=5; $_correct_val-$_mintolerance" | bc` + else + _min=`echo "scale=5; $_correct_val-$_mintolerance*0.01*$_correct_val" | bc` + fi + + # find max with or without % + _maxtolerance=`echo $_maxtol | sed -e 's/%//'` + if [ $_maxtol = $_maxtolerance ] + then + _max=`echo "scale=5; $_correct_val+$_maxtolerance" | bc` + else + _max=`echo "scale=5; $_correct_val+$_maxtolerance*0.01*$_correct_val" | bc` + fi + + $_debug && echo "min = $_min" + $_debug && echo "max = $_max" + + cat <<EOF > $tmp._bc.1 +scale=5; +if ($_min <= $_given_val) 1; +if ($_min > $_given_val) 0; +EOF + + cat <<EOF > $tmp._bc.2 +scale=5; +if ($_given_val <= $_max) 1; +if ($_given_val > $_max) 0; +EOF + + _above_min=`bc < $tmp._bc.1` + _below_max=`bc < $tmp._bc.2` + + _in_range=`expr $_above_min \& $_below_max` + + # fix up min, max precision for output + # can vary for 5.3, 6.2 + _min=`echo $_min | sed -e 's/0*$//'` # get rid of trailling zeroes + _max=`echo $_max | sed -e 's/0*$//'` # get rid of trailling zeroes + + if [ $_in_range -eq 1 ] + then + [ $_verbose -eq 1 ] && echo $_name is in range + return 0 + else + [ $_verbose -eq 1 ] && echo $_name has value of $_given_val + [ $_verbose -eq 1 ] && echo $_name is NOT in range $_min .. $_max + return 1 + fi +} + +# comment pmlogger_check and pmsnap entries in the crontab file +# (also cron.pmcheck and cron.pmsnap entries for backwards compatibility) +# Usage: _remove_cron backup sudo +# +# backup - where to keep the old crontab file +# sudo - location of sudo +# +_remove_cron() +{ + rc_backup=${1:-crontab} + rc_sudo=${2:-sudo} + + $rc_sudo rm -f $rc_backup + if $rc_sudo crontab -l 2>/dev/null >$rc_backup + then + : + else + # error, make sure the backup is empty so no changes are made + echo >$rc_backup + fi + + if [ -s $rc_backup ] + then + $rc_sudo cat $rc_backup \ + | sed \ + -e 's/^[^#].*pmlogger_check/#&/' \ + -e 's/^[^#].*pmsnap/#&/' \ + -e 's/^[^#].*cron.pmcheck/#&/' \ + -e 's/^[^#].*cron.pmsnap/#&/' \ + | $rc_sudo crontab > /dev/null 2>&1 + fi +} + +# restore crontab back to original state +# Usage: _restore_cron backup sudo +# +# backup - where to keep the old crontab file +# sudo - location of sudo +# +_restore_cron() +{ + rc_backup=${1:-crontab} + rc_sudo=${2:-sudo} + + if [ -s $rc_backup ] + then + $rc_sudo rm -f rc_cron_out rc_cron_check rc_cron_diff + if $rc_sudo crontab $rc_backup >rc_cron_out 2>&1 + then + # check everything is OK + # + $rc_sudo crontab -l >rc_cron_check + sed -e '/^#/d' $rc_backup >$rc_backup.clean + sed -e '/^#/d' rc_cron_check >rc_cron_check.clean + if diff -u $rc_backup.clean rc_cron_check.clean >rc_cron_diff 2>&1 + then + : + else + echo "_restore_cron: Warning: could not restore crontab to original state" + echo " Unexpected differences ..." + diff -u $rc_backup rc_cron_check + fi + $rc_sudo rm -f rc_cron_check rc_cron_diff + else + echo "_restore_cron: Warning: could not restore crontab to original state" + echo " crontab(1) failed ..." + cat rc_cron_out + fi + $rc_sudo rm -f rc_cron_out rc_cron_check rc_cron_diff + fi +} + +# get offset into an archive relative to the first pmResult +# past the preamble +# +# Usage: _arch_start archive [offset] +# +_arch_start() +{ + pmdumplog -z $1 \ + | $PCP_AWK_PROG ' +/^[0-9][0-9]:[0-9][0-9]:/ { if ($3 ~ /pmcd.pmlogger.host/) next + split($1, t, ":") + t[3] += '"${2-0}"' + while (t[3] < 0) { + t[3] += 60 + t[2]-- + } + while (t[3] > 60) { + t[3] -= 60 + t[2]++ + } + while (t[2] < 0) { + t[2] += 60 + t[1]-- + } + while (t[2] > 60) { + t[2] -= 60 + t[1]++ + } + while (t[1] < 0) + t[1] += 24 + while (t[1] > 23) + t[1] -= 24 + printf "@%02d:%02d:%06.3f",t[1],t[2],t[3] + exit + }' +} + +# get an unused ipc port ... returned on std out, empty for failure +# Usage: _get_port tcp|udp low_port high_port +# +_get_port() +{ + [ $# -ne 3 ] && return + __proto=$1 + __port=$2 + while [ $__port -le "$3" ] + do + if fuser $__port/$__proto >/dev/null 2>&1 + then + : + else + echo $__port + return + fi + __port=`expr $__port + 1` + done +} + +# get host endianness ("le" or "be") +_get_endian() +{ + check=`echo a | od -x 2>&1 | sed -e 's/^0[^ ]* *//' -e 's/ //g' -e '/^$/d'` + case "$check" + in + 0a61) echo le ;; + 610a) echo be ;; + *) + echo "Arrgh ... od -x returned something odd ($check)" + echo a | od -x + status=1 + exit + ;; + esac +} + +# get host word size ("32" or "64") +_get_word_size() +{ + machine=`uname -m` + case "$machine" + in + i?86|athlon|ppc) + size=32 + # but, apps are 64-bit on my Mac OS X + [ $PCP_PLATFORM = darwin ] && size=64 + ;; + x86_64|ia64|ppc64|s390x|aarch64) + size=64 + ;; + *) + echo "uname gave machine type with unknown word size ($machine)" + status=1 + exit + ;; + esac + echo $size +} + +# _all_hostnames host - generate all hostnames (or IP addresses) for this host, +# that map to some network interface, excluding loopback +# +_all_hostnames() +{ + touch $tmp._addr + ssh pcpqa@$1 </dev/null netstat -in 2>/dev/null >$tmp._tmp + if grep 'Network.*Address' $tmp._tmp >/dev/null + then + # This is the IRIX version of netstat -in, get IP addr from the + # Address field + # + # Name Mtu Network Address Ipkts Ierrs ... + # ef0 1500 134.14.55.128 134.14.55.149 712168207 10 ... + # 134.14.55.159 + # 134.14.55.147 + # ef2* 1500 none none 0 0 ... + # lo0 32992 127 127.0.0.1 23628402 0 ... + # + $PCP_AWK_PROG <$tmp._tmp >$tmp._addr ' +/^lo/ { next } +NF >= 4 && $4 ~ /^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$/ { print $4 } +NF == 1 && $1 ~ /^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$/ { print $1 } +END { print "End-of-List" }' + else + ssh pcpqa@$1 </dev/null /sbin/ifconfig 2>/dev/null >$tmp._tmp + if grep 'UP.*RUNNING' $tmp._tmp >/dev/null + then + # This is the Linux version of ifconfig, get IP addr from the + # inet addr: line + # + # eth0 Link encap:Ethernet HWaddr 00:90:27:98:EE:A8 + # inet addr:134.14.55.176 Bcast:134.14.55.255 ... + # UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 + # ... + # + # lo Link encap:Local Loopback + # inet addr:127.0.0.1 Mask:255.0.0.0 + # UP LOOPBACK RUNNING MTU:16436 Metric:1 + # ... + # + # Note addr: tag is not present in some ifconfig output + # + $PCP_AWK_PROG <$tmp._tmp ' +/^lo/ { skip=1; next } +skip == 1 && NF > 0 { next } +skip == 1 { skip = 0 } +$1 == "inet" && $2 ~ /addr:/ { print $2 } +$1 == "inet" && $2 ~ /^[0-9]/ { print $2 } +END { print "End-of-List" }' \ + | sed -e 's/addr://' >$tmp._addr + else + # Nothing we can do really, as there is no way of passing + # an error back from here, other than returning an empty + # list + return + fi + fi + cat $tmp._addr \ + | while read __ip + do + if [ "$__ip" = "End-of-List" ] + then + echo + break + fi + # check that ip addr is reachable + if ping -c 1 $__ip >/dev/null 2>&1 + then + __host=`_ipaddr_to_host $__ip` + if [ ! -z "$__host" ] + then + $PCP_ECHO_PROG $PCP_ECHO_N ",$__host""$PCP_ECHO_C" + else + $PCP_ECHO_PROG $PCP_ECHO_N ",$__ip""$PCP_ECHO_C" + fi + fi + done \ + | sed -e 's/^,//' +} + +# _all_ipaddrs - generate all IP addresses for this host, +# that map to some network interface, excluding +# loopback, slip, ppp +# +# See _all_hostnames() above for comments on the method used. +# +_all_ipaddrs() +{ + touch $tmp._addr + if [ "$1" = "localhost" ] + then + netstat -in 2>/dev/null >$tmp._tmp + else + ssh pcpqa@$1 </dev/null netstat -in 2>/dev/null >$tmp._tmp + fi + if grep 'Network.*Address' $tmp._tmp >/dev/null + then + # this is the IRIX version of netstat -in, get IP addr from the + # Address field + # + $PCP_AWK_PROG <$tmp._tmp >$tmp._addr ' +/^lo/ || /^sl/ || /^pp/ { next } +NF >= 4 && $4 ~ /^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$/ { print $4 } +NF == 1 && $1 ~ /^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$/ { print $1 } +END { print "End-of-List" }' + else + if [ "$1" = "localhost" ] + then + /sbin/ifconfig 2>/dev/null >$tmp._tmp + else + ssh pcpqa@$1 </dev/null /sbin/ifconfig 2>/dev/null >$tmp._tmp + fi + if grep 'UP.*RUNNING' $tmp._tmp >/dev/null + then + # This is the Linux version of ifconfig, get IP addr from the + # inet addr: line + # +# ppp0 Link encap:Point-to-Point Protocol +# inet addr:134.14.52.219 P-t-P:134.14.52.189 Mask:255.255.255.255 +# UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1522 Metric:1 +# RX packets:50119 errors:0 dropped:0 overruns:0 frame:0 +# TX packets:47474 errors:0 dropped:0 overruns:0 carrier:0 +# collisions:0 txqueuelen:3 +# RX bytes:7017171 (6.6 Mb) TX bytes:3952015 (3.7 Mb) + # + # Note addr: tag is not present in some ifconfig output, + # AND UP comes first! + # + $PCP_AWK_PROG <$tmp._tmp ' +/^[a-z]/ { loopback = 0 } +/^lo/ { loopback = 1; next } +$1 == "inet" && $2 ~ /addr:/ { save = $2; next } +$1 == "inet" && $2 ~ /^[0-9]/ { save = $2; next } +$1 == "TX" { if (loopback == 0 && save != 0) + print save + save = 0 + } +END { print "End-of-List" }' \ + | sed -e 's/addr://' >$tmp._addr + else + # Nothing we can do really, as there is no way of passing + # an error back from here, other than returning an empty + # list + return + fi + fi + cat $tmp._addr \ + | while read __ip + do + if [ "$__ip" = "End-of-List" ] + then + echo + break + fi + $PCP_ECHO_PROG $PCP_ECHO_N ",$__ip""$PCP_ECHO_C" + done \ + | sed -e 's/^,//' +} + +# fqdn for localhost +# +_get_fqdn() +{ + _host_to_fqdn `hostname` +} + +# Distro-specific filtering for init, rc scripts, chkconfig, et al +# +_filter_init_distro() +{ + if [ -f /etc/mandriva-release ] + then + # looks like this is a Mandriva bug ... see + # http://mandriva.598463.n5.nabble.com/Bug-24409-initscripts-New-netfs-provides-local-fs-scripts-can-t-be-turned-off-td869820.html + # + sed \ + -e '/Warning: netfs is needed by pcp in runlevel/d' + else + cat + fi +} + +# deal with chkconfig et al +# assumes $sudo is set correctly +# try very hard to _not_ emit messages unless serious errors encountered +# +_change_config() +{ + if [ $PCP_PLATFORM = linux ] + then + pat=$1 + [ "$1" = "verbose" ] && pat="" + + if which systemctl >/dev/null 2>&1 + then + # Run with systemd whenever it is available now + # + case "$2" + in + on) act=enable ;; + off) act=disable ;; + *) + echo "_change_config: Error: \$2=$2 not \"on\" or \"off\" as expected" + exit 1 + ;; + esac + if [ -n "$pat" ] + then + $sudo systemctl $act $pat.service >$tmp._tmp 2>$tmp._err + [ $? -eq 0 ] || cat $tmp._tmp + fi + elif which chkconfig >/dev/null 2>&1 + then + # Try the older RedHat and SuSE way .. + # + [ -n "$pat" ] && $sudo chkconfig $pat $2 + elif [ -x /usr/sbin/sysv-rc-conf ] + then + # Try the Debian and Ubuntu way .. + # + [ -n "$pat" ] && $sudo /usr/sbin/sysv-rc-conf $pat $2 + elif which rc-update >/dev/null 2>&1 + then + # Try the Gentoo way .. + # + if [ -n "$pat" ] + then + case $2 + in + on) act=add ;; + off) act=delete ;; + *) + echo "_change_config: Error: \$2=$2 not \"on\" or \"off\" as expected" + exit 1 + ;; + esac + if [ -z "`$sudo rc-update show default | grep $pat`" ] + then + $sudo rc-update $act $pat >$tmp._tmp 2>&1 + [ $? -eq 0 ] || cat $tmp._tmp + fi + fi + else + # I have no clue! + # + echo "_change_config: Error: cannot change config \"$1 $2\"" + exit 1 + fi + elif [ $PCP_PLATFORM = solaris ] + then + # Try the Solaris way .. + # + if which svcadm >/dev/null 2>&1 + then + case $1 + in + pmcd) pat=pmcd ;; + pmlogger) pat=pmlogger ;; + verbose) pat="" ;; + *) pat=$1 ;; + esac + if [ -n "$pat" ] + then + if [ "$2" = on ] + then + state=`svcs -l svc:/application/pcp/$pat | sed -n '/^state[ ]/s///p'` + [ -n "$state" -a "$state" != "online" ] \ + && $sudo svcadm clear svc:/application/pcp/$pat + $sudo svcadm enable svc:/application/pcp/$pat + elif [ "$2" = off ] + then + $sudo svcadm disable svc:/application/pcp/$pat + else + echo "_change_config: Error: \$2=$2 not \"on\" or \"off\" as expected" + exit 1 + fi + fi + else + echo "_change_config: Error: cannot find svcs for Solaris" + exit 1 + fi + elif [ $PCP_PLATFORM = darwin ] + then + case $1 + in + pmcd) pat=PMCD ;; + pmlogger) pat=PMLOGGER ;; + pmie) pat=PMIE ;; + verbose) pat="" ;; + *) pat=$1 ;; + esac + if [ -n "$pat" ] + then + state=`sed -n -e "/^$pat=/{"' +s/.*=// +s/-//g +p +}' /etc/hostconfig` + if [ -z "$state" ] + then + echo "_change_config: Error: No $pat control line in /etc/hostconfig" + echo "You need to add a $pat=-YES- line to this file" + exit 1 + fi + if [ "$2" = "on" ] + then + req_state=YES + elif [ "$2" = "off" ] + then + req_state=NO + else + echo "_change_config: Error: bad state ($2) should be on or off" + exit 1 + fi + if [ "$state" != "$req_state" ] + then + sed </etc/hostconfig >$tmp._state \ + -e "/^$pat=/s/-.*/-$req_state-/" + sudo cp $tmp._state /etc/hostconfig + fi + fi + else + # I have no idea what to do for this platform! + # + echo "_change_config: Error: cannot \"$1 $2\" for $PCP_PLATFORM" + exit 1 + fi 2>&1 \ + | _filter_init_distro +} + +_get_config() +{ + if [ "$PCP_PLATFORM" = linux ] + then + case $1 + in + pmlogger|pmcd) pat=$1 ;; + verbose) pat="" ;; + *) pat=$1 ;; + esac + + if which systemctl >/dev/null 2>&1 + then + # Run with systemd whenever it is available now + # + if [ -z "$pat" ] + then + # unconditionally "on", or no such option + # + echo on + else + if systemctl -q is-enabled $pat.service + then + echo on + else + echo off + fi + fi + elif which chkconfig >/dev/null 2>&1 + then + # Try the older RedHat and SuSE way .. + # + if [ -z "$pat" ] + then + # unconditionally "on", or no such option + # + echo on + else + if chkconfig $pat >$tmp.__tmp 2>&1 + then + # success from chkconfig is only useful if no output + # was generated ... in the latter case, grep the output + # for hints (this is for SuSE SLES9 in particular) + # + if [ -s $tmp.__tmp ] + then + if grep ' on$' $tmp.__tmp >/dev/null + then + echo on + elif grep ' off$' $tmp.__tmp >/dev/null + then + echo off + else + echo off + fi + else + echo on + fi + else + echo off + fi + fi + elif [ -x /usr/sbin/sysv-rc-conf ] + then + # Try the Debian and Ubuntu way .. + # + if [ -z "$pat" ] + then + # unconditionally "on", or no such option + # + echo on + else + if $sudo /usr/sbin/sysv-rc-conf $pat + then + echo on + else + echo off + fi + fi + elif which rc-update >/dev/null 2>&1 + then + # Try the Gentoo way .. + # + if [ -z "$pat" ] + then + # unconditionally "on", or no such option + # + echo on + else + if rc-update show default | grep $pat >/dev/null + then + echo on + else + echo off + fi + fi + else + echo "_change_config: Error: don't know how to change config for Linux" + exit 1 + fi + elif [ $PCP_PLATFORM = solaris ] + then + if which svcs >/dev/null 2>&1 + then + case $1 + in + pmlogger|pmcd) pat=$1 ;; + verbose) pat="" ;; + *) pat=$1 ;; + esac + if [ -z "$pat" ] + then + # unconditionally "on", or no such option + # + echo on + else + state=`svcs -H svc:/application/pcp/$pat | sed -e 's/[ ].*//'` + case "$state" + in + online|maintenance) + echo on + ;; + offline|disabled) + echo off + ;; + *) + echo "_change_config: Error: smf ($state) from svcs not expected" + exit 1 + ;; + esac + fi + else + echo "_change_config: Error: cannot find svcs for Solaris" + exit 1 + fi + elif [ $PCP_PLATFORM = darwin ] + then + case $1 + in + pmcd) pat=PMCD ;; + pmlogger) pat=PMLOGGER ;; + pmie) pat=PMIE ;; + verbose) pat="" ;; + *) pat=$1 ;; + esac + if [ -n "$pat" ] + then + state=`sed -n -e "/^$pat=/{"' +s/.*=// +s/-//g +p +}' /etc/hostconfig` + if [ -z "$state" ] + then + echo "_change_config: Error: No $pat control line in /etc/hostconfig" >&2 + echo "You need to add a $pat=-YES- line to this file" >&2 + exit 1 + fi + if [ "$state" = "YES" ] + then + echo on + elif [ "$state" = "NO" ] + then + echo off + else + echo "_change_config: Error: bad state ($state) should be YES or NO" >&2 + exit 1 + fi + fi + else + echo "_change_config: Error: cannot \"$1 $2\" for $PCP_PLATFORM" + fi +} + +# disable all system pmloggers running via the control file +# +_disable_loggers() +{ + [ -z "$PCP_PMLOGGERCONTROL_PATH" ] && \ + PCP_PMLOGGERCONTROL_PATH="$PCP_VAR_DIR/config/pmlogger/control" + if [ -f $PCP_PMLOGGERCONTROL_PATH ] + then + [ ! -f $tmp.control ] && \ + $sudo cp $PCP_PMLOGGERCONTROL_PATH $tmp.control + fi + cat <<End-of-File >$tmp._tmp +# dummy file created by qa/$seq on `date` +# the goal here is to have a controlled primary logger that does +# not make requests to pmcd! +\$version=1.1 +LOCALHOSTNAME y n PCP_LOG_DIR/pmlogger/LOCALHOSTNAME -c /dev/null +End-of-File + $sudo cp $tmp._tmp $PCP_PMLOGGERCONTROL_PATH +} + +_restore_loggers() +{ + [ -z "$PCP_PMLOGGERCONTROL_PATH" ] && \ + PCP_PMLOGGERCONTROL_PATH="$PCP_VAR_DIR/config/pmlogger/control" + [ -f $tmp.control ] && \ + $sudo cp $tmp.control $PCP_PMLOGGERCONTROL_PATH +} + +# _check_core [dir] +# checks for core files in dir (defaults to .) +# +_check_core() +{ + if [ -z "$1" ] + then + dir="" + else + if [ -d $1 ] + then + dir=$1/ + else + echo "_check_core: aaargh $1 is not a directory!" + return + fi + fi + if [ "`echo ${dir}core*`" != "${dir}core*" ] + then + [ -z "$here" ] && here=/tmp + [ -z "$seq" ] && seq=9999 + $PCP_ECHO_PROG $PCP_ECHO_N "Dumped core! (saved in $here as""$PCP_ECHO_C" + for c in ${dir}core* + do + d=`basename $c` + $sudo mv $c $here/$seq.$d + $PCP_ECHO_PROG $PCP_ECHO_N " $seq.$d""$PCP_ECHO_C" + done + echo ")" + status=1 + fi +} + +# prepare for a save-able pmcd and pmda configuration. +# +_prepare_pmda_install() +{ + [ -z "$1" ] && echo "Error: bad _prepare_pmda_install call" + iam=$1 + + # copy the pmcd config file to restore state later. + cp $PCP_PMCDCONF_PATH $tmp.pmcd.conf + + cd $PCP_PMDAS_DIR/$iam + if [ -f Makefile -o -f GNUmakefile ] ; then + if $sudo ${MAKE:-make} clobber >$tmp._tmp 2>&1 ; then + : + else + cat $tmp._tmp + echo "Arrgh, ${MAKE:-make} clobber failed" + exit + fi + fi + + # start from a known starting point + $sudo ./Remove >/dev/null 2>&1 + [ -f $PCP_VAR_DIR/config/$iam/$iam.conf ] && \ + $sudo mv $PCP_VAR_DIR/config/$iam/$iam.conf $tmp.$iam.conf +} + +# restore a saved pmcd configuration and ensure pmda back in place. +# +_restore_pmda_install() +{ + [ -z "$1" ] && echo "Error: bad _restore_pmda_install call" + iam=$1 + signal=$PCP_BINADM_DIR/pmsignal + + [ -f $tmp.$iam.conf ] && \ + $sudo mv $tmp.$iam.conf $PCP_VAR_DIR/config/$iam/$iam.conf + + if diff $tmp.pmcd.conf $PCP_PMCDCONF_PATH > /dev/null 2>&1 + then + : + else +# do a default install which ensures the pmns and any views are installed + + cd $PCP_PMDAS_DIR/$iam + $sudo ./Install < /dev/null > /dev/null 2>&1 + +# PMDA may have been installed differently to default. As everything is +# installed we can use the old pmcd.conf file to restore state. + + if diff $tmp.pmcd.conf $PCP_PMCDCONF_PATH > /dev/null 2>&1 + then + : + else + [ -f $tmp.pmcd.conf ] && $sudo mv $tmp.pmcd.conf $PCP_PMCDCONF_PATH + $sudo $signal -a -s HUP pmcd + fi + fi +} + +# find a local port that is not in use, optionally starting from a suggested port +# +_find_free_port() +{ + base=$1 + [ -z "$base" ] && base=54321 + + while $PCP_BINADM_DIR/telnet-probe -c localhost $base + do + base=`expr $base + 1` + done + echo $base +} + +_get_ino() +{ + # get inode number for $1 + # throw away stderr (and return '') in case $1 has been removed by now + # + stat "$1" 2>/dev/null \ + | sed -n '/Device:[ ].*[ ]Inode:/{ +s/Device:[ ].*[ ]Inode:[ ]*// +s/[ ].*// +p +}' +} + +_get_primary_logger_pid() +{ + # get pid for primary pmlogger + # + _primary_inode=`_get_ino $PCP_TMP_DIR/pmlogger/primary` + _pid='' + for _file in $PCP_TMP_DIR/pmlogger/* + do + case "$_file" + in + */primary|*\*) + ;; + */[0-9]*) + _inode=`_get_ino "$_file"` + if [ "$_primary_inode" = "$_inode" ] + then + _pid="`echo $_file | sed -e 's/.*\/\([^/]*\)$/\1/'`" + break + fi + ;; + esac + done + echo $_pid +} + +_get_libpcp_config() +{ + pmconfig -L -s > $tmp.config + . $tmp.config + rm $tmp.config +} |