mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-11 13:40:07 +00:00
Merge lp:~percona-toolkit-dev/percona-toolkit/pt-stalk-2.0 and, previously merged into that, lp:~percona-toolkit-dev/percona-toolkit/use-mktemp-871438.
This commit is contained in:
450
bin/pt-collect
450
bin/pt-collect
@@ -1,450 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# This program is part of Percona Toolkit: http://www.percona.com/software/
|
||||
# See "COPYRIGHT, LICENSE, AND WARRANTY" at the end of this file for legal
|
||||
# notices and disclaimers.
|
||||
|
||||
usage() {
|
||||
if [ "${OPT_ERR}" ]; then
|
||||
echo "${OPT_ERR}" >&2
|
||||
fi
|
||||
echo "Usage: pt-collect -d -g -i -o -s [OPTIONS] [-- MYSQL-OPTIONS]" >&2
|
||||
echo "For more information, 'man pt-collect' or 'perldoc $0'." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Make sure the disk isn't getting too full. Exit if the disk is more than $1
|
||||
# percent full, or there is less than $2 megabytes of free space on $3 drive.
|
||||
check_disk_space() {
|
||||
PCT=${1:-"100"}
|
||||
MB=${2:-"0"}
|
||||
DEST="$3"
|
||||
avail=$(df -m -P "${DEST}" | awk '/^\//{print $4}');
|
||||
full=$(df -m -P "${DEST}" | awk '/^\//{print $5}' | sed -e 's/%//g');
|
||||
if [ "${avail}" -le "${MB}" -o "${full}" -ge "${PCT}" ]; then
|
||||
echo "Not enough free space (${full}% full, ${avail}MB free)"
|
||||
echo "Wanted less than ${PCT}% full and more than ${MB}MB"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
for o; do
|
||||
case "${o}" in
|
||||
--)
|
||||
shift; break;
|
||||
;;
|
||||
--help)
|
||||
usage;
|
||||
;;
|
||||
-d)
|
||||
shift; OPT_d="${1}"; shift;
|
||||
;;
|
||||
-f)
|
||||
shift; OPT_f="${1}"; shift;
|
||||
;;
|
||||
-i)
|
||||
shift; OPT_i="${1}"; shift;
|
||||
;;
|
||||
-g)
|
||||
shift; OPT_g="${1}"; shift;
|
||||
;;
|
||||
-m)
|
||||
shift; OPT_m="${1}"; shift;
|
||||
;;
|
||||
-o)
|
||||
shift; OPT_o="${1}"; shift;
|
||||
;;
|
||||
-p)
|
||||
shift; OPT_p="${1}"; shift;
|
||||
;;
|
||||
-s)
|
||||
shift; OPT_s="${1}"; shift;
|
||||
;;
|
||||
-t)
|
||||
shift; OPT_t="${1}"; shift;
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
|
||||
if [ -z "${OPT_d}" -o -z "${OPT_i}" -o -z "${OPT_o}" -o -z "${OPT_g}" -o -z "${OPT_s}" ]; then
|
||||
OPT_ERR="Missing command-line argument."
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ "${OPT_p}" ]; then
|
||||
d="${OPT_p}"
|
||||
else
|
||||
d=$(date +%F-%T | tr :- _);
|
||||
fi
|
||||
|
||||
# Check disk space up-front.
|
||||
check_disk_space "${OPT_f}" "${OPT_m}" "${OPT_d}" || exit 1
|
||||
|
||||
echo "Gathering info for $d"
|
||||
|
||||
# Make sure there's only one of me.
|
||||
(
|
||||
flock 200
|
||||
|
||||
# Get pidof mysqld; pidof doesn't exist on some systems. We try our best...
|
||||
p=$(pidof -s mysqld);
|
||||
if [ -z "${p}" ]; then
|
||||
p=$(pgrep -o -x mysqld);
|
||||
fi
|
||||
if [ -z "${p}" ]; then
|
||||
p=$(ps -eaf | grep 'mysql[d]' | grep -v mysqld_safe | awk '{print $2}' | head -n1);
|
||||
fi
|
||||
|
||||
# Get memory allocation info before anything else.
|
||||
if [ "${p}" ]; then
|
||||
if pmap --help 2>&1 | grep -- -x >/dev/null 2>&1 ; then
|
||||
pmap -x $p > "$OPT_d/$d-pmap"
|
||||
else
|
||||
# Some pmap's apparently don't support -x (issue 116).
|
||||
pmap $p > "$OPT_d/$d-pmap"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Getting a GDB stacktrace can be an intensive operation, so do this only if
|
||||
# necessary.
|
||||
if [ "${OPT_g}" = "yes" -a "${p}" ]; then
|
||||
gdb -ex "set pagination 0" -ex "thread apply all bt" --batch -p $p >> "$OPT_d/$d-stacktrace"
|
||||
else
|
||||
echo "GDB (-g) was not enabled" >> "$OPT_d/$d-stacktrace"
|
||||
fi
|
||||
|
||||
# Get MySQL's variables if possible. Then sleep long enough that we probably
|
||||
# complete SHOW VARIABLES if all's well. (We don't want to run mysql in the
|
||||
# foreground, because it could hang.)
|
||||
mysql "$@" -e 'SHOW GLOBAL VARIABLES' >> "$OPT_d/$d-variables" 2>&1 &
|
||||
sleep .2
|
||||
|
||||
# Get the major.minor version number. Version 3.23 doesn't matter for our
|
||||
# purposes, and other releases have x.x.x* version conventions so far.
|
||||
VER="$(awk '/^version[^_]/{print substr($2,1,3)}' "$OPT_d/$d-variables")"
|
||||
|
||||
# Is MySQL logging its errors to a file? If so, tail that file.
|
||||
errfile="$(awk '/log_error/{print $2}' "$OPT_d/$d-variables")"
|
||||
if [ -z "${errfile}" -a "${p}" ]; then
|
||||
# Try getting it from the open filehandle...
|
||||
errfile="$(ls -l /proc/${p}/fd | awk '/ 2 ->/{print $NF}')"
|
||||
fi
|
||||
|
||||
if [ "${errfile}" ]; then
|
||||
echo "The error file seems to be ${errfile}"
|
||||
tail -f "${errfile}" >"$OPT_d/$d-log_error" 2>&1 &
|
||||
error_pid=$!
|
||||
# Send a mysqladmin debug to the server so we can potentially learn about
|
||||
# locking etc.
|
||||
mysqladmin debug "$@"
|
||||
else
|
||||
echo "Could not detect error file; will not tail MySQL's log file"
|
||||
fi
|
||||
|
||||
# Get a sample of these right away, so we can get these without interaction
|
||||
# with the other commands we're about to run.
|
||||
INNOSTAT="SHOW /*!40100 ENGINE*/ INNODB STATUS\G"
|
||||
mysql "$@" -e "${INNOSTAT}" >> "$OPT_d/$d-innodbstatus1" 2>&1 &
|
||||
mysql "$@" -e 'SHOW FULL PROCESSLIST\G' >> "$OPT_d/$d-processlist1" 2>&1 &
|
||||
mysql "$@" -e 'SHOW OPEN TABLES' >> "$OPT_d/$d-opentables1" 2>&1 &
|
||||
if [ "${VER}" '>' "5.1" ]; then
|
||||
mysql "$@" -e 'SHOW ENGINE INNODB MUTEX' >> "$OPT_d/$d-mutex-status1" 2>&1 &
|
||||
else
|
||||
mysql "$@" -e 'SHOW MUTEX STATUS' >> "$OPT_d/$d-mutex-status1" 2>&1 &
|
||||
fi
|
||||
|
||||
# If TCP dumping is specified, start that on the server's port.
|
||||
if [ "${OPT_t}" = "yes" ]; then
|
||||
port=$(awk '/^port/{print $2}' "$OPT_d/$d-variables")
|
||||
if [ "${port}" ]; then
|
||||
tcpdump -i any -s 4096 -w "$OPT_d/$d-tcpdump" port ${port} &
|
||||
tcpdump_pid=$!
|
||||
fi
|
||||
fi
|
||||
|
||||
# Next, start oprofile gathering data during the whole rest of this process.
|
||||
# The --init should be a no-op if it has already been init-ed.
|
||||
if [ "${OPT_o}" = "yes" ]; then
|
||||
if opcontrol --init; then
|
||||
opcontrol --start --no-vmlinux
|
||||
else
|
||||
OPT_o="no"
|
||||
fi
|
||||
elif [ "${OPT_s}" = "yes" ]; then
|
||||
# Don't run oprofile and strace at the same time.
|
||||
strace -T -s 0 -f -p $p > "${DEST}/$d-strace" 2>&1 &
|
||||
strace_pid=$!
|
||||
fi
|
||||
|
||||
# Grab a few general things first. Background all of these so we can start
|
||||
# them all up as quickly as possible. We use mysqladmin -c even though it is
|
||||
# buggy and won't stop on its own in 5.1 and newer, because there is a chance
|
||||
# that we will get and keep a connection to the database; in troubled times
|
||||
# the database tends to exceed max_connections, so reconnecting in the loop
|
||||
# tends not to work very well.
|
||||
ps -eaf >> "$OPT_d/$d-ps" 2>&1 &
|
||||
sysctl -a >> "$OPT_d/$d-sysctl" 2>&1 &
|
||||
top -bn1 >> "$OPT_d/$d-top" 2>&1 &
|
||||
vmstat 1 $OPT_i >> "$OPT_d/$d-vmstat" 2>&1 &
|
||||
vmstat $OPT_i 2 >> "$OPT_d/$d-vmstat-overall" 2>&1 &
|
||||
iostat -dx 1 $OPT_i >> "$OPT_d/$d-iostat" 2>&1 &
|
||||
iostat -dx $OPT_i 2 >> "$OPT_d/$d-iostat-overall" 2>&1 &
|
||||
mpstat -P ALL 1 $OPT_i >> "$OPT_d/$d-mpstat" 2>&1 &
|
||||
mpstat -P ALL $OPT_i 1 >> "$OPT_d/$d-mpstat-overall" 2>&1 &
|
||||
lsof -nP -p $p -bw >> "$OPT_d/$d-lsof" 2>&1 &
|
||||
mysqladmin "$@" ext -i1 -c$OPT_i >> "$OPT_d/$d-mysqladmin" 2>&1 &
|
||||
mysqladmin_pid=$!
|
||||
|
||||
# This loop gathers data for the rest of the duration, and defines the time
|
||||
# of the whole job.
|
||||
echo "Loop start: $(date +'TS %s.%N %F %T')"
|
||||
for a in `seq 1 $OPT_i`; do
|
||||
# We check the disk, but don't exit, because we need to stop jobs if we
|
||||
# need to exit.
|
||||
check_disk_space "${OPT_f}" "${OPT_m}" "${OPT_d}" || break
|
||||
|
||||
# Synchronize ourselves onto the clock tick, so the sleeps are 1-second
|
||||
sleep $(date +%s.%N | awk '{print 1 - ($1 % 1)}')
|
||||
ts="$(date +"TS %s.%N %F %T")"
|
||||
|
||||
# Collect the stuff for this cycle
|
||||
(cat /proc/diskstats 2>&1; echo $ts) >> "$OPT_d/$d-diskstats" &
|
||||
(cat /proc/stat 2>&1; echo $ts) >> "$OPT_d/$d-procstat" &
|
||||
(cat /proc/vmstat 2>&1; echo $ts) >> "$OPT_d/$d-procvmstat" &
|
||||
(cat /proc/meminfo 2>&1; echo $ts) >> "$OPT_d/$d-meminfo" &
|
||||
(cat /proc/slabinfo 2>&1; echo $ts) >> "$OPT_d/$d-slabinfo" &
|
||||
(cat /proc/interrupts 2>&1; echo $ts) >> "$OPT_d/$d-interrupts" &
|
||||
(df -h 2>&1; echo $ts) >> "$OPT_d/$d-df" &
|
||||
(netstat -antp 2>&1; echo $ts) >> "$OPT_d/$d-netstat" &
|
||||
(netstat -s 2>&1; echo $ts) >> "$OPT_d/$d-netstat_s" &
|
||||
done
|
||||
echo "Loop end: $(date +'TS %s.%N %F %T')"
|
||||
|
||||
if [ "${OPT_o}" = "yes" ]; then
|
||||
opcontrol --stop
|
||||
opcontrol --dump
|
||||
kill $(pidof oprofiled);
|
||||
opcontrol --save=pt_collect_$d
|
||||
|
||||
# Attempt to generate a report; if this fails, then just tell the user how
|
||||
# to generate the report.
|
||||
path_to_binary=$(which mysqld);
|
||||
if [ "${path_to_binary}" -a -f "${path_to_binary}" ]; then
|
||||
opreport --demangle=smart --symbols --merge tgid session:pt_collect_$d "${path_to_binary}" > "$OPT_d/$d-opreport"
|
||||
else
|
||||
echo "oprofile data saved to pt_collect_$d; you should now be able to get a report" > "$OPT_d/$d-opreport"
|
||||
echo "by running something like" >> "$OPT_d/$d-opreport"
|
||||
echo "opreport --demangle=smart --symbols --merge tgid session:pt_collect_$d /path/to/mysqld" >> "$OPT_d/$d-opreport"
|
||||
fi
|
||||
elif [ "${OPT_s}" = "yes" ]; then
|
||||
kill -s 2 ${strace_pid}
|
||||
sleep 1
|
||||
kill -s 15 ${strace_pid}
|
||||
# Sometimes strace leaves threads/processes in T status.
|
||||
kill -s 18 $p
|
||||
fi
|
||||
|
||||
mysql "$@" -e "${INNOSTAT}" >> "$OPT_d/$d-innodbstatus2" 2>&1 &
|
||||
mysql "$@" -e 'SHOW FULL PROCESSLIST\G' >> "$OPT_d/$d-processlist2" 2>&1 &
|
||||
mysql "$@" -e 'SHOW OPEN TABLES' >> "$OPT_d/$d-opentables2" 2>&1 &
|
||||
if [ "${VER}" '>' "5.1" ]; then
|
||||
mysql "$@" -e 'SHOW ENGINE INNODB MUTEX' >> "$OPT_d/$d-mutex-status2" 2>&1 &
|
||||
else
|
||||
mysql "$@" -e 'SHOW MUTEX STATUS' >> "$OPT_d/$d-mutex-status2" 2>&1 &
|
||||
fi
|
||||
|
||||
# Kill backgrounded tasks.
|
||||
kill $mysqladmin_pid
|
||||
[ "$error_pid" ] && kill $error_pid
|
||||
[ "$tcpdump_pid" ] && kill $tcpdump_pid
|
||||
|
||||
# Finally, record what system we collected this data from.
|
||||
hostname > "$OPT_d/$d-hostname"
|
||||
)200>/tmp/percona-toolkit-collect-lockfile >> "$OPT_d/$d-output" 2>&1
|
||||
|
||||
# ############################################################################
|
||||
# Documentation
|
||||
# ############################################################################
|
||||
:<<'DOCUMENTATION'
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
pt-collect - Collect information from a server for some period of time.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
Usage: pt-collect -d -g -i -o -s [OPTIONS] [-- MYSQL-OPTIONS]
|
||||
|
||||
pt-collect tool gathers a variety of information about a system for a period
|
||||
of time. It is typically executed when the stalk tool detects a condition
|
||||
and wants to collect information to assist in diagnosis. Four options
|
||||
must be specified on the command line: -dgios.
|
||||
|
||||
=head1 RISKS
|
||||
|
||||
The following section is included to inform users about the potential risks,
|
||||
whether known or unknown, of using this tool. The two main categories of risks
|
||||
are those created by the nature of the tool (e.g. read-only tools vs. read-write
|
||||
tools) and those created by bugs.
|
||||
|
||||
pt-collect is a read-only tool. It should be very low-risk.
|
||||
|
||||
At the time of this release, we know of no bugs that could cause serious harm
|
||||
to users.
|
||||
|
||||
The authoritative source for updated information is always the online issue
|
||||
tracking system. Issues that affect this tool will be marked as such. You can
|
||||
see a list of such issues at the following URL:
|
||||
L<http://www.percona.com/bugs/pt-collect>.
|
||||
|
||||
See also L<"BUGS"> for more information on filing bugs and getting help.
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
pt-collect creates a lock to ensure that only one instance runs at a time,
|
||||
and then saves a variety of performance and status data into files in the
|
||||
configured directory. Files are named with a timestamp so they can be
|
||||
grouped together. The tool is MySQL-centric by default, and gathers quite
|
||||
a bit of diagnostic data that's useful for understanding the behavior of
|
||||
a MySQL database server.
|
||||
|
||||
Options after C<--> are passed to C<mysql> and C<mysqladmin>.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item -d (required)
|
||||
|
||||
DESTINATION Where to store the resulting data; must already exist.
|
||||
|
||||
=item -g <yes/no> (required)
|
||||
|
||||
Collect GDB stack traces.
|
||||
|
||||
=item -i INTERVAL (required)
|
||||
|
||||
How many seconds to collect data.
|
||||
|
||||
=item -o <yes/no> (required)
|
||||
|
||||
Collect oprofile data; disables -s.
|
||||
|
||||
=item -s <yes/no> (required)
|
||||
|
||||
Collect strace data.
|
||||
|
||||
=item -f PERCENT
|
||||
|
||||
Exit if the disk is more than this percent full (default 100).
|
||||
|
||||
=item -m MEGABYTES
|
||||
|
||||
Exit if there are less than this many megabytes free disk space (default 0).
|
||||
|
||||
=item -p PREFIX
|
||||
|
||||
Store the data into files with this prefix (optional).
|
||||
|
||||
=item -t <yes/no>
|
||||
|
||||
Collect tcpdump data.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ENVIRONMENT
|
||||
|
||||
This tool does not use any environment variables.
|
||||
|
||||
=head1 SYSTEM REQUIREMENTS
|
||||
|
||||
This tool requires Bash v3 or newer and assumes that these programs
|
||||
are installed, in the PATH, and executable: sysctl, top, vmstat, iostat,
|
||||
mpstat, lsof, mysql, mysqladmin, df, netstat, pidof, flock, and others
|
||||
depending on what command-line options are specified. If some of those
|
||||
programs are not available, the tool will still run but may print warnings.
|
||||
|
||||
=head1 BUGS
|
||||
|
||||
For a list of known bugs, see L<http://www.percona.com/bugs/pt-collect>.
|
||||
|
||||
Please report bugs at L<https://bugs.launchpad.net/percona-toolkit>.
|
||||
Include the following information in your bug report:
|
||||
|
||||
=over
|
||||
|
||||
=item * Complete command-line used to run the tool
|
||||
|
||||
=item * Tool L<"--version">
|
||||
|
||||
=item * MySQL version of all servers involved
|
||||
|
||||
=item * Output from the tool including STDERR
|
||||
|
||||
=item * Input files (log/dump/config files, etc.)
|
||||
|
||||
=back
|
||||
|
||||
If possible, include debugging output by running the tool with C<PTDEBUG>;
|
||||
see L<"ENVIRONMENT">.
|
||||
|
||||
=head1 DOWNLOADING
|
||||
|
||||
Visit L<http://www.percona.com/software/percona-toolkit/> to download the
|
||||
latest release of Percona Toolkit. Or, get the latest release from the
|
||||
command line:
|
||||
|
||||
wget percona.com/get/percona-toolkit.tar.gz
|
||||
|
||||
wget percona.com/get/percona-toolkit.rpm
|
||||
|
||||
wget percona.com/get/percona-toolkit.deb
|
||||
|
||||
You can also get individual tools from the latest release:
|
||||
|
||||
wget percona.com/get/TOOL
|
||||
|
||||
Replace C<TOOL> with the name of any tool.
|
||||
|
||||
=head1 AUTHORS
|
||||
|
||||
Baron Schwartz
|
||||
|
||||
=head1 ABOUT PERCONA TOOLKIT
|
||||
|
||||
This tool is part of Percona Toolkit, a collection of advanced command-line
|
||||
tools developed by Percona for MySQL support and consulting. Percona Toolkit
|
||||
was forked from two projects in June, 2011: Maatkit and Aspersa. Those
|
||||
projects were created by Baron Schwartz and developed primarily by him and
|
||||
Daniel Nichter, both of whom are employed by Percona. Visit
|
||||
L<http://www.percona.com/software/> for more software developed by Percona.
|
||||
|
||||
=head1 COPYRIGHT, LICENSE, AND WARRANTY
|
||||
|
||||
This program is copyright 2010-2011 Baron Schwartz, 2011-2012 Percona Inc.
|
||||
Feedback and improvements are welcome.
|
||||
|
||||
THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
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, version 2; OR the Perl Artistic License. On UNIX and similar
|
||||
systems, you can issue `man perlgpl' or `man perlartistic' to read these
|
||||
licenses.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
|
||||
Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
|
||||
=head1 VERSION
|
||||
|
||||
pt-collect 2.0.2
|
||||
|
||||
=cut
|
||||
|
||||
DOCUMENTATION
|
71
bin/pt-mext
71
bin/pt-mext
@@ -17,10 +17,49 @@ if [ -z "$1" ]; then
|
||||
usage;
|
||||
fi
|
||||
|
||||
FILE=/tmp/mext_temp_file;
|
||||
# ###########################################################################
|
||||
# tmpdir package
|
||||
# This package is a copy without comments from the original. The original
|
||||
# with comments and its test file can be found in the Bazaar repository at,
|
||||
# lib/bash/tmpdir.sh
|
||||
# t/lib/bash/tmpdir.sh
|
||||
# See https://launchpad.net/percona-toolkit for more information.
|
||||
# ###########################################################################
|
||||
|
||||
TMPDIR=""
|
||||
|
||||
mk_tmpdir() {
|
||||
local dir=${1:-""}
|
||||
|
||||
if [ -n "$dir" ]; then
|
||||
if [ ! -d "$dir" ]; then
|
||||
mkdir $dir || die "Cannot make tmpdir $dir"
|
||||
fi
|
||||
TMPDIR="$dir"
|
||||
else
|
||||
local tool=`basename $0`
|
||||
local pid="$$"
|
||||
TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
|
||||
|| die "Cannot make secure tmpdir"
|
||||
fi
|
||||
}
|
||||
|
||||
rm_tmpdir() {
|
||||
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||
rm -rf $TMPDIR
|
||||
fi
|
||||
TMPDIR=""
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# End tmpdir package
|
||||
# ###########################################################################
|
||||
|
||||
mk_tmpdir
|
||||
|
||||
FILE="$TMPDIR/mext_temp_file";
|
||||
NUM=0;
|
||||
REL=0;
|
||||
rm -f $FILE*;
|
||||
|
||||
# Command-line parsing.
|
||||
args=`getopt -u -n mext r "$@"`;
|
||||
@@ -45,15 +84,15 @@ $@ | grep -v '+' | grep -v Variable_name | sed 's/|//g' \
|
||||
| while read line; do
|
||||
if [ "$line" = "" ]; then
|
||||
NUM=`expr $NUM + 1`;
|
||||
echo "" > $FILE$NUM;
|
||||
echo "" > "$FILE$NUM"
|
||||
fi
|
||||
echo "$line" >> $FILE$NUM;
|
||||
echo "$line" >> "$FILE$NUM"
|
||||
done
|
||||
|
||||
# Count how many files there are and prepare to format the output
|
||||
SPEC="%-33s %13d"
|
||||
AWKS=""
|
||||
NUM=`ls $FILE* | wc -l`;
|
||||
NUM=`ls "$FILE"* | wc -l`;
|
||||
# The last file will be empty...
|
||||
NUM=`expr $NUM - 3`;
|
||||
|
||||
@@ -63,19 +102,19 @@ for i in `seq 0 $NUM`; do
|
||||
NEXTFILE=`expr $i + 1`;
|
||||
|
||||
# Sort each file and eliminate empty lines, so 'join' doesn't complain.
|
||||
sort $FILE$i | grep . > $FILE$i.tmp;
|
||||
mv $FILE$i.tmp $FILE$i;
|
||||
sort $FILE${NEXTFILE} | grep . > $FILE${NEXTFILE}.tmp;
|
||||
mv $FILE${NEXTFILE}.tmp $FILE${NEXTFILE};
|
||||
sort "$FILE$i" | grep . > "$FILE$i.tmp"
|
||||
mv "$FILE$i.tmp" "$FILE$i"
|
||||
sort "$FILE${NEXTFILE}" | grep . > "$FILE${NEXTFILE}.tmp"
|
||||
mv "$FILE${NEXTFILE}.tmp" "$FILE${NEXTFILE}"
|
||||
|
||||
# Join the files together. This gets slow O(n^2) as we add more files, but
|
||||
# this really shouldn't be performance critical.
|
||||
join $FILE$i $FILE${NEXTFILE} | grep . > $FILE;
|
||||
join "$FILE$i" "$FILE${NEXTFILE}" | grep . > "$FILE"
|
||||
|
||||
# Find the max length of the [numeric only] values in the file so we know how
|
||||
# wide to make the columns
|
||||
MAXLEN=`awk '{print $2}' $FILE${NEXTFILE} | grep -v '[^0-9]' | awk '{print length($1)}' | sort -rn | head -n1`
|
||||
mv $FILE $FILE${NEXTFILE};
|
||||
MAXLEN=`awk '{print $2}' "$FILE${NEXTFILE}" | grep -v '[^0-9]' | awk '{print length($1)}' | sort -rn | head -n1`
|
||||
mv "$FILE" "$FILE${NEXTFILE}"
|
||||
SPEC="$SPEC %${MAXLEN}d";
|
||||
if [ "$REL" = "1" ]; then
|
||||
AWKS="$AWKS, \$`expr $i + 3` - \$`expr $i + 2`";
|
||||
@@ -86,10 +125,12 @@ done
|
||||
|
||||
# Print output
|
||||
AWKCMD="printf(\"$SPEC\n\", \$1, \$2$AWKS);";
|
||||
awk "{$AWKCMD}" $FILE`expr $NUM + 1`;
|
||||
awk "{$AWKCMD}" "$FILE`expr $NUM + 1`"
|
||||
|
||||
# Remove all temporary files.
|
||||
rm -f $FILE*;
|
||||
# Remove all temporary files and the tmp dir.
|
||||
rm_tmpdir
|
||||
|
||||
exit 0
|
||||
|
||||
# ############################################################################
|
||||
# Documentation
|
||||
|
@@ -13,6 +13,44 @@ usage() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# tmpdir package
|
||||
# This package is a copy without comments from the original. The original
|
||||
# with comments and its test file can be found in the Bazaar repository at,
|
||||
# lib/bash/tmpdir.sh
|
||||
# t/lib/bash/tmpdir.sh
|
||||
# See https://launchpad.net/percona-toolkit for more information.
|
||||
# ###########################################################################
|
||||
|
||||
TMPDIR=""
|
||||
|
||||
mk_tmpdir() {
|
||||
local dir=${1:-""}
|
||||
|
||||
if [ -n "$dir" ]; then
|
||||
if [ ! -d "$dir" ]; then
|
||||
mkdir $dir || die "Cannot make tmpdir $dir"
|
||||
fi
|
||||
TMPDIR="$dir"
|
||||
else
|
||||
local tool=`basename $0`
|
||||
local pid="$$"
|
||||
TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
|
||||
|| die "Cannot make secure tmpdir"
|
||||
fi
|
||||
}
|
||||
|
||||
rm_tmpdir() {
|
||||
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||
rm -rf $TMPDIR
|
||||
fi
|
||||
TMPDIR=""
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# End tmpdir package
|
||||
# ###########################################################################
|
||||
|
||||
# ########################################################################
|
||||
# Some global setup is necessary for cross-platform compatibility, even
|
||||
# when sourcing this script for testing purposes.
|
||||
@@ -62,9 +100,9 @@ fuzzy_formula='
|
||||
# symlink them to /etc/passwd and then run this program as root. Call this
|
||||
# function with "rm" or "touch" as an argument.
|
||||
temp_files() {
|
||||
for file in /tmp/percona-toolkit{,-mysql-variables,-mysql-status,-innodb-status} \
|
||||
/tmp/percona-toolkit{2,-mysql-databases,-mysql-processlist,-noncounters} \
|
||||
/tmp/percona-toolkit-mysql{dump,-slave};
|
||||
for file in $TMPDIR/percona-toolkit{,-mysql-variables,-mysql-status,-innodb-status} \
|
||||
$TMPDIR/percona-toolkit{2,-mysql-databases,-mysql-processlist,-noncounters} \
|
||||
$TMPDIR/percona-toolkit-mysql{dump,-slave};
|
||||
do
|
||||
case "$1" in
|
||||
touch)
|
||||
@@ -127,16 +165,16 @@ secs_to_time () {
|
||||
}'
|
||||
}
|
||||
|
||||
# gets a value from /tmp/percona-toolkit-mysql-variables. Returns zero if it doesn't
|
||||
# gets a value from $TMPDIR/percona-toolkit-mysql-variables. Returns zero if it doesn't
|
||||
# exist.
|
||||
get_var () {
|
||||
v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" /tmp/percona-toolkit-mysql-variables)"
|
||||
v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-variables)"
|
||||
echo "${v:-0}"
|
||||
}
|
||||
|
||||
# Returns true if a variable exists
|
||||
var_exists () {
|
||||
$AP_GREP "$1" /tmp/percona-toolkit-mysql-variables >/dev/null 2>&1;
|
||||
$AP_GREP "$1" $TMPDIR/percona-toolkit-mysql-variables >/dev/null 2>&1;
|
||||
}
|
||||
|
||||
# Returns "Enabled", "Disabled", or "Not Supported" depending on whether the
|
||||
@@ -145,7 +183,7 @@ var_exists () {
|
||||
# (string equal) to some value.
|
||||
feat_on() {
|
||||
if var_exists $1 ; then
|
||||
var="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" /tmp/percona-toolkit-mysql-variables)"
|
||||
var="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-variables)"
|
||||
if [ "${var}" = "ON" ]; then
|
||||
echo "Enabled"
|
||||
elif [ "${var}" = "OFF" -o "${var}" = "0" -o -z "${var}" ]; then
|
||||
@@ -172,10 +210,10 @@ feat_on() {
|
||||
fi
|
||||
}
|
||||
|
||||
# gets a value from /tmp/percona-toolkit-mysql-status. Returns zero if it doesn't
|
||||
# gets a value from $TMPDIR/percona-toolkit-mysql-status. Returns zero if it doesn't
|
||||
# exist.
|
||||
get_stat () {
|
||||
v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" /tmp/percona-toolkit-mysql-status)"
|
||||
v="$($AP_AWK "\$1 ~ /^$1$/ { print \$2 }" $TMPDIR/percona-toolkit-mysql-status)"
|
||||
echo "${v:-0}"
|
||||
}
|
||||
|
||||
@@ -195,14 +233,17 @@ fuzzy_pct () {
|
||||
# Functions for parsing specific files and getting desired info from them.
|
||||
# These are called from within main() and are separated so they can be tested
|
||||
# easily. The calling convention is that the data they need to run is prepared
|
||||
# first by putting it into /tmp/percona-toolkit. Then code that's testing just needs to
|
||||
# put sample data into /tmp/percona-toolkit and call it.
|
||||
# first by putting it into $TMPDIR/percona-toolkit. Then code that's testing
|
||||
# just needs to put sample data into $TMPDIR/percona-toolkit and call it.
|
||||
# ##############################################################################
|
||||
|
||||
# Parses the output of 'ps -e -o args | $AP_GREP mysqld' or 'ps auxww...'
|
||||
# which should be in /tmp/percona-toolkit.
|
||||
# which should be in $TMPDIR/percona-toolkit.
|
||||
parse_mysqld_instances () {
|
||||
local file=$1
|
||||
local socket=${socket:-""}
|
||||
local port=${port:-""}
|
||||
local datadir=${datadir:-""}
|
||||
echo " Port Data Directory Socket"
|
||||
echo " ===== ========================== ======"
|
||||
$AP_GREP '/mysqld ' $file | while read line; do
|
||||
@@ -224,11 +265,11 @@ parse_mysqld_instances () {
|
||||
}
|
||||
|
||||
# Tries to find the my.cnf file by examining 'ps' output, which should be in
|
||||
# /tmp/percona-toolkit. You have to specify the port for the instance you are
|
||||
# $TMPDIR/percona-toolkit. You have to specify the port for the instance you are
|
||||
# interested in, in case there are multiple instances.
|
||||
find_my_cnf_file() {
|
||||
local file=$1
|
||||
local port=$2
|
||||
local port=${2:-""}
|
||||
if test -n "$port" && $AP_GREP -- "/mysqld.*--port=$port" $file >/dev/null 2>&1 ; then
|
||||
$AP_GREP -- "/mysqld.*--port=$port" $file \
|
||||
| $AP_AWK 'BEGIN{RS=" "; FS="=";} $1 ~ /--defaults-file/ { print $2; }' \
|
||||
@@ -240,7 +281,7 @@ find_my_cnf_file() {
|
||||
fi
|
||||
}
|
||||
|
||||
# Gets the MySQL system time. Uses input from /tmp/percona-toolkit-mysql-variables.
|
||||
# Gets the MySQL system time. Uses input from $TMPDIR/percona-toolkit-mysql-variables.
|
||||
get_mysql_timezone () {
|
||||
tz="$(get_var time_zone)"
|
||||
if [ "${tz}" = "SYSTEM" ]; then
|
||||
@@ -249,14 +290,14 @@ get_mysql_timezone () {
|
||||
echo "${tz}"
|
||||
}
|
||||
|
||||
# Gets the MySQL system version. Uses input from /tmp/percona-toolkit-mysql-variables.
|
||||
# Gets the MySQL system version. Uses input from $TMPDIR/percona-toolkit-mysql-variables.
|
||||
get_mysql_version () {
|
||||
name_val Version "$(get_var version) $(get_var version_comment)"
|
||||
name_val "Built On" "$(get_var version_compile_os) $(get_var version_compile_machine)"
|
||||
}
|
||||
|
||||
# Gets the system start and uptime in human readable format. Last restart date
|
||||
# should be in /tmp/percona-toolkit.
|
||||
# should be in $TMPDIR/percona-toolkit.
|
||||
get_mysql_uptime () {
|
||||
local file=$1
|
||||
restart="$(cat $file)"
|
||||
@@ -265,7 +306,7 @@ get_mysql_uptime () {
|
||||
echo "${restart} (up ${uptime})"
|
||||
}
|
||||
|
||||
# Summarizes the output of SHOW MASTER LOGS, which is in /tmp/percona-toolkit
|
||||
# Summarizes the output of SHOW MASTER LOGS, which is in $TMPDIR/percona-toolkit
|
||||
summarize_binlogs () {
|
||||
local file=$1
|
||||
name_val "Binlogs" $(wc -l $file)
|
||||
@@ -282,7 +323,7 @@ format_binlog_filters () {
|
||||
}
|
||||
|
||||
# Takes as input a file that has two samples of SHOW STATUS, columnized next to
|
||||
# each other. These should be in /tmp/percona-toolkit. Outputs fuzzy-ed numbers:
|
||||
# each other. These should be in $TMPDIR/percona-toolkit. Outputs fuzzy-ed numbers:
|
||||
# absolute, all-time per second, and per-second over the interval between the
|
||||
# samples. Omits any rows that are all zeroes.
|
||||
format_status_variables () {
|
||||
@@ -387,7 +428,7 @@ summarize_processlist () {
|
||||
echo
|
||||
}
|
||||
|
||||
# Pretty-prints the my.cnf file, which should be in /tmp/percona-toolkit. It's super
|
||||
# Pretty-prints the my.cnf file, which should be in $TMPDIR/percona-toolkit. It's super
|
||||
# annoying, but some *modern* versions of awk don't support POSIX character
|
||||
# sets in regular expressions, like [[:space:]] (looking at you, Debian). So
|
||||
# the below patterns contain [<space><tab>] and must remain that way.
|
||||
@@ -545,8 +586,8 @@ format_innodb_status () {
|
||||
name_val "Pending I/O Writes" "$(find_pending_io_writes "${file}")"
|
||||
name_val "Pending I/O Flushes" "$(find_pending_io_flushes "${file}")"
|
||||
$AP_AWK -F, '/^---TRANSACTION/{print $2}' "${file}" \
|
||||
| $AP_SED -e 's/ [0-9]* sec.*//' | sort | uniq -c > /tmp/percona-toolkit2
|
||||
name_val "Transaction States" "$(group_concat /tmp/percona-toolkit2)"
|
||||
| $AP_SED -e 's/ [0-9]* sec.*//' | sort | uniq -c > $TMPDIR/percona-toolkit2
|
||||
name_val "Transaction States" "$(group_concat $TMPDIR/percona-toolkit2)"
|
||||
if $AP_GREP 'TABLE LOCK table' "${file}" >/dev/null ; then
|
||||
echo "Tables Locked"
|
||||
$AP_AWK '/^TABLE LOCK table/{print $4}' "${file}" \
|
||||
@@ -633,9 +674,9 @@ format_overall_db_stats () {
|
||||
printf fmt, db, counts[db ",tables"], counts[db ",views"], counts[db ",sps"], counts[db ",trg"], counts[db ",func"], counts[db ",fk"], counts[db ",partn"];
|
||||
}
|
||||
}
|
||||
' $file > /tmp/percona-toolkit
|
||||
head -n2 /tmp/percona-toolkit
|
||||
tail -n +3 /tmp/percona-toolkit | sort
|
||||
' $file > $TMPDIR/percona-toolkit
|
||||
head -n2 $TMPDIR/percona-toolkit
|
||||
tail -n +3 $TMPDIR/percona-toolkit | sort
|
||||
|
||||
echo
|
||||
# Now do the summary of engines per DB
|
||||
@@ -693,9 +734,9 @@ format_overall_db_stats () {
|
||||
print "";
|
||||
}
|
||||
}
|
||||
' $file > /tmp/percona-toolkit
|
||||
head -n1 /tmp/percona-toolkit
|
||||
tail -n +2 /tmp/percona-toolkit | sort
|
||||
' $file > $TMPDIR/percona-toolkit
|
||||
head -n1 $TMPDIR/percona-toolkit
|
||||
tail -n +2 $TMPDIR/percona-toolkit | sort
|
||||
|
||||
echo
|
||||
# Now do the summary of index types per DB. Careful -- index is a reserved
|
||||
@@ -766,9 +807,9 @@ format_overall_db_stats () {
|
||||
print "";
|
||||
}
|
||||
}
|
||||
' $file > /tmp/percona-toolkit
|
||||
head -n1 /tmp/percona-toolkit
|
||||
tail -n +2 /tmp/percona-toolkit | sort
|
||||
' $file > $TMPDIR/percona-toolkit
|
||||
head -n1 $TMPDIR/percona-toolkit
|
||||
tail -n +2 $TMPDIR/percona-toolkit | sort
|
||||
|
||||
echo
|
||||
# Now do the summary of datatypes per DB
|
||||
@@ -857,10 +898,10 @@ format_overall_db_stats () {
|
||||
print "";
|
||||
}
|
||||
}
|
||||
' $file > /tmp/percona-toolkit
|
||||
hdr=$($AP_GREP -n Database /tmp/percona-toolkit | cut -d: -f1);
|
||||
head -n${hdr} /tmp/percona-toolkit
|
||||
tail -n +$((${hdr} + 1)) /tmp/percona-toolkit | sort
|
||||
' $file > $TMPDIR/percona-toolkit
|
||||
hdr=$($AP_GREP -n Database $TMPDIR/percona-toolkit | cut -d: -f1);
|
||||
head -n${hdr} $TMPDIR/percona-toolkit
|
||||
tail -n +$((${hdr} + 1)) $TMPDIR/percona-toolkit | sort
|
||||
echo
|
||||
}
|
||||
|
||||
@@ -878,6 +919,7 @@ main() {
|
||||
export PATH="/usr/gnu/bin/:/usr/xpg4/bin/:${PATH}"
|
||||
|
||||
# Set up temporary files.
|
||||
mk_tmpdir
|
||||
temp_files "rm"
|
||||
temp_files "touch"
|
||||
|
||||
@@ -887,25 +929,26 @@ main() {
|
||||
section Percona_Toolkit_MySQL_Summary_Report
|
||||
name_val "System time" "`date -u +'%F %T UTC'` (local TZ: `date +'%Z %z'`)"
|
||||
section Instances
|
||||
ps auxww 2>/dev/null | $AP_GREP mysqld > /tmp/percona-toolkit
|
||||
parse_mysqld_instances /tmp/percona-toolkit
|
||||
ps auxww 2>/dev/null | $AP_GREP mysqld > $TMPDIR/percona-toolkit
|
||||
parse_mysqld_instances $TMPDIR/percona-toolkit
|
||||
|
||||
# ########################################################################
|
||||
# Fetch some basic info so we can start
|
||||
# ########################################################################
|
||||
mysql "$@" -ss -e 'SELECT CURRENT_USER()' > /tmp/percona-toolkit
|
||||
mysql "$@" -ss -e 'SELECT CURRENT_USER()' > $TMPDIR/percona-toolkit
|
||||
if [ "$?" != "0" ]; then
|
||||
echo "Cannot connect to mysql, please specify command-line options."
|
||||
temp_files "rm"
|
||||
rm_tmpdir
|
||||
exit 1
|
||||
fi
|
||||
user="$(cat /tmp/percona-toolkit)";
|
||||
mysql "$@" -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > /tmp/percona-toolkit-mysql-variables
|
||||
mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > /tmp/percona-toolkit-mysql-status
|
||||
mysql "$@" -ss -e 'SHOW DATABASES' > /tmp/percona-toolkit-mysql-databases 2>/dev/null
|
||||
mysql "$@" -ssE -e 'SHOW SLAVE STATUS' > /tmp/percona-toolkit-mysql-slave 2>/dev/null
|
||||
mysql "$@" -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > /tmp/percona-toolkit-innodb-status 2>/dev/null
|
||||
mysql "$@" -ssE -e 'SHOW FULL PROCESSLIST' > /tmp/percona-toolkit-mysql-processlist 2>/dev/null
|
||||
user="$(cat $TMPDIR/percona-toolkit)";
|
||||
mysql "$@" -ss -e 'SHOW /*!40100 GLOBAL*/ VARIABLES' > $TMPDIR/percona-toolkit-mysql-variables
|
||||
mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' > $TMPDIR/percona-toolkit-mysql-status
|
||||
mysql "$@" -ss -e 'SHOW DATABASES' > $TMPDIR/percona-toolkit-mysql-databases 2>/dev/null
|
||||
mysql "$@" -ssE -e 'SHOW SLAVE STATUS' > $TMPDIR/percona-toolkit-mysql-slave 2>/dev/null
|
||||
mysql "$@" -ssE -e 'SHOW /*!50000 ENGINE*/ INNODB STATUS' > $TMPDIR/percona-toolkit-innodb-status 2>/dev/null
|
||||
mysql "$@" -ssE -e 'SHOW FULL PROCESSLIST' > $TMPDIR/percona-toolkit-mysql-processlist 2>/dev/null
|
||||
now="$(mysql "$@" -ss -e 'SELECT NOW()')"
|
||||
port="$(get_var port)"
|
||||
|
||||
@@ -920,16 +963,16 @@ main() {
|
||||
|
||||
uptime="$(get_stat Uptime)"
|
||||
mysql "$@" -ss -e "SELECT LEFT(NOW() - INTERVAL ${uptime} SECOND, 16)" \
|
||||
> /tmp/percona-toolkit
|
||||
name_val Started "$(get_mysql_uptime /tmp/percona-toolkit)"
|
||||
> $TMPDIR/percona-toolkit
|
||||
name_val Started "$(get_mysql_uptime $TMPDIR/percona-toolkit)"
|
||||
|
||||
name_val Databases "$($AP_GREP -c . /tmp/percona-toolkit-mysql-databases)"
|
||||
name_val Databases "$($AP_GREP -c . $TMPDIR/percona-toolkit-mysql-databases)"
|
||||
name_val Datadir "$(get_var datadir)"
|
||||
procs="$(get_stat Threads_connected)"
|
||||
procr="$(get_stat Threads_running)"
|
||||
name_val Processes "$(fuzz ${procs}) connected, $(fuzz ${procr}) running"
|
||||
if [ -s /tmp/percona-toolkit-mysql-slave ]; then slave=""; else slave="not "; fi
|
||||
slavecount=$($AP_GREP -c 'Binlog Dump' /tmp/percona-toolkit-mysql-processlist)
|
||||
if [ -s $TMPDIR/percona-toolkit-mysql-slave ]; then slave=""; else slave="not "; fi
|
||||
slavecount=$($AP_GREP -c 'Binlog Dump' $TMPDIR/percona-toolkit-mysql-processlist)
|
||||
name_val Replication "Is ${slave}a slave, has ${slavecount} slaves connected"
|
||||
|
||||
# TODO move this into a section with other files: error log, slow log and
|
||||
@@ -942,7 +985,7 @@ main() {
|
||||
# Processlist, sliced several different ways
|
||||
# ########################################################################
|
||||
section Processlist
|
||||
summarize_processlist /tmp/percona-toolkit-mysql-processlist
|
||||
summarize_processlist $TMPDIR/percona-toolkit-mysql-processlist
|
||||
|
||||
# ########################################################################
|
||||
# Queries and query plans
|
||||
@@ -951,7 +994,7 @@ main() {
|
||||
sleep 10
|
||||
# TODO: gather this data in the same format as normal: stats, TS line
|
||||
mysql "$@" -ss -e 'SHOW /*!50000 GLOBAL*/ STATUS' \
|
||||
| join /tmp/percona-toolkit-mysql-status - > /tmp/percona-toolkit
|
||||
| join $TMPDIR/percona-toolkit-mysql-status - > $TMPDIR/percona-toolkit
|
||||
# Make a file with a list of things we want to omit because they aren't
|
||||
# counters, they are gauges (in RRDTool terminology). Gauges are shown
|
||||
# elsewhere in the output.
|
||||
@@ -975,9 +1018,9 @@ main() {
|
||||
Threads_cached Threads_connected Threads_running \
|
||||
Uptime_since_flush_status;
|
||||
do
|
||||
echo "${var}" >> /tmp/percona-toolkit-noncounters
|
||||
echo "${var}" >> $TMPDIR/percona-toolkit-noncounters
|
||||
done
|
||||
format_status_variables /tmp/percona-toolkit | $AP_GREP -v -f /tmp/percona-toolkit-noncounters
|
||||
format_status_variables $TMPDIR/percona-toolkit | $AP_GREP -v -f $TMPDIR/percona-toolkit-noncounters
|
||||
|
||||
# ########################################################################
|
||||
# Table cache
|
||||
@@ -1054,22 +1097,22 @@ main() {
|
||||
trg_arg="${trg_arg} ${triggers}";
|
||||
fi
|
||||
# Find out which databases to dump
|
||||
num_dbs="$($AP_GREP -c . /tmp/percona-toolkit-mysql-databases)"
|
||||
num_dbs="$($AP_GREP -c . $TMPDIR/percona-toolkit-mysql-databases)"
|
||||
echo "There are ${num_dbs} databases. Would you like to dump all, or just one?"
|
||||
echo -n "Type the name of the database, or press Enter to dump all of them. "
|
||||
read dbtodump
|
||||
mysqldump "$@" --no-data --skip-comments \
|
||||
--skip-add-locks --skip-add-drop-table --compact \
|
||||
--skip-lock-all-tables --skip-lock-tables --skip-set-charset \
|
||||
${trg_arg} ${dbtodump:---all-databases} > /tmp/percona-toolkit-mysqldump
|
||||
${trg_arg} ${dbtodump:---all-databases} > $TMPDIR/percona-toolkit-mysqldump
|
||||
# Test the result by checking the file, not by the exit status, because we
|
||||
# might get partway through and then die, and the info is worth analyzing
|
||||
# anyway.
|
||||
if $AP_GREP 'CREATE TABLE' /tmp/percona-toolkit-mysqldump >/dev/null 2>&1; then
|
||||
format_overall_db_stats /tmp/percona-toolkit-mysqldump
|
||||
if $AP_GREP 'CREATE TABLE' $TMPDIR/percona-toolkit-mysqldump >/dev/null 2>&1; then
|
||||
format_overall_db_stats $TMPDIR/percona-toolkit-mysqldump
|
||||
else
|
||||
echo "Skipping schema analysis due to apparent error in dump file"
|
||||
rm -f /tmp/percona-toolkit-mysqldump
|
||||
rm -f $TMPDIR/percona-toolkit-mysqldump
|
||||
fi
|
||||
else
|
||||
echo "Skipping schema analysis"
|
||||
@@ -1079,23 +1122,23 @@ main() {
|
||||
# Noteworthy Technologies
|
||||
# ########################################################################
|
||||
section Noteworthy_Technologies
|
||||
if [ -e /tmp/percona-toolkit-mysqldump ]; then
|
||||
if $AP_GREP FULLTEXT /tmp/percona-toolkit-mysqldump > /dev/null; then
|
||||
if [ -e $TMPDIR/percona-toolkit-mysqldump ]; then
|
||||
if $AP_GREP FULLTEXT $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
|
||||
name_val "Full Text Indexing" Yes
|
||||
else
|
||||
name_val "Full Text Indexing" No
|
||||
fi
|
||||
if $AP_GREP 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' /tmp/percona-toolkit-mysqldump > /dev/null; then
|
||||
if $AP_GREP 'GEOMETRY\|POINT\|LINESTRING\|POLYGON' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
|
||||
name_val "Geospatial Types" Yes
|
||||
else
|
||||
name_val "Geospatial Types" No
|
||||
fi
|
||||
if $AP_GREP 'FOREIGN KEY' /tmp/percona-toolkit-mysqldump > /dev/null; then
|
||||
if $AP_GREP 'FOREIGN KEY' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
|
||||
name_val "Foreign Keys" Yes
|
||||
else
|
||||
name_val "Foreign Keys" No
|
||||
fi
|
||||
if $AP_GREP 'PARTITION BY' /tmp/percona-toolkit-mysqldump > /dev/null; then
|
||||
if $AP_GREP 'PARTITION BY' $TMPDIR/percona-toolkit-mysqldump > /dev/null; then
|
||||
name_val "Partitioning" Yes
|
||||
else
|
||||
name_val "Partitioning" No
|
||||
@@ -1175,8 +1218,8 @@ main() {
|
||||
name_val "Adaptive Flushing" $(get_var innodb_adaptive_flushing)
|
||||
name_val "Adaptive Checkpoint" $(get_var innodb_adaptive_checkpoint)
|
||||
|
||||
if [ -s /tmp/percona-toolkit-innodb-status ]; then
|
||||
format_innodb_status /tmp/percona-toolkit-innodb-status
|
||||
if [ -s $TMPDIR/percona-toolkit-innodb-status ]; then
|
||||
format_innodb_status $TMPDIR/percona-toolkit-innodb-status
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1211,15 +1254,15 @@ main() {
|
||||
section Binary_Logging
|
||||
binlog=$(get_var log_bin)
|
||||
if [ "${binlog}" ]; then
|
||||
mysql "$@" -ss -e 'SHOW MASTER LOGS' > /tmp/percona-toolkit 2>/dev/null
|
||||
summarize_binlogs /tmp/percona-toolkit
|
||||
mysql "$@" -ss -e 'SHOW MASTER LOGS' > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
summarize_binlogs $TMPDIR/percona-toolkit
|
||||
format="$(get_var binlog_format)"
|
||||
name_val binlog_format "${format:-STATEMENT}"
|
||||
name_val expire_logs_days $(get_var expire_logs_days)
|
||||
name_val sync_binlog $(get_var sync_binlog)
|
||||
name_val server_id $(get_var server_id)
|
||||
mysql "$@" -ss -e 'SHOW MASTER STATUS' > /tmp/percona-toolkit 2>/dev/null
|
||||
format_binlog_filters /tmp/percona-toolkit
|
||||
mysql "$@" -ss -e 'SHOW MASTER STATUS' > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
format_binlog_filters $TMPDIR/percona-toolkit
|
||||
fi
|
||||
|
||||
# Replication: seconds behind, running, filters, skip_slave_start, skip_errors,
|
||||
@@ -1252,8 +1295,8 @@ main() {
|
||||
# If there is a my.cnf in a standard location, see if we can pretty-print it.
|
||||
# ########################################################################
|
||||
section Configuration_File
|
||||
ps auxww 2>/dev/null | $AP_GREP mysqld > /tmp/percona-toolkit
|
||||
cnf_file=$(find_my_cnf_file /tmp/percona-toolkit ${port});
|
||||
ps auxww 2>/dev/null | $AP_GREP mysqld > $TMPDIR/percona-toolkit
|
||||
cnf_file=$(find_my_cnf_file $TMPDIR/percona-toolkit ${port});
|
||||
if [ ! -e "${cnf_file}" ]; then
|
||||
name_val "Config File" "Cannot autodetect, trying common locations"
|
||||
cnf_file="/etc/my.cnf";
|
||||
@@ -1266,8 +1309,8 @@ main() {
|
||||
fi
|
||||
if [ -e "${cnf_file}" ]; then
|
||||
name_val "Config File" "${cnf_file}"
|
||||
cat "${cnf_file}" > /tmp/percona-toolkit
|
||||
pretty_print_cnf_file /tmp/percona-toolkit
|
||||
cat "${cnf_file}" > $TMPDIR/percona-toolkit
|
||||
pretty_print_cnf_file $TMPDIR/percona-toolkit
|
||||
else
|
||||
name_val "Config File" "Cannot autodetect or find, giving up"
|
||||
fi
|
||||
@@ -1276,6 +1319,8 @@ main() {
|
||||
|
||||
# Make sure that we signal the end of the tool's output.
|
||||
section The_End
|
||||
|
||||
rm_tmpdir
|
||||
}
|
||||
|
||||
# Execute the program if it was not included from another file. This makes it
|
||||
@@ -1325,8 +1370,8 @@ See also L<"BUGS"> for more information on filing bugs and getting help.
|
||||
|
||||
pt-mysql-summary works by connecting to a MySQL database server and querying
|
||||
it for status and configuration information. It saves these bits of data
|
||||
into files in /tmp, and then formats them neatly with awk and other scripting
|
||||
languages.
|
||||
into files in a temporary directory, and then formats them neatly with awk
|
||||
and other scripting languages.
|
||||
|
||||
To use, simply execute it. Optionally add the same command-line options
|
||||
you would use to connect to MySQL, like C<pt-mysql-summary --user=foo>.
|
||||
|
67
bin/pt-sift
67
bin/pt-sift
@@ -13,6 +13,47 @@ usage() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# tmpdir package
|
||||
# This package is a copy without comments from the original. The original
|
||||
# with comments and its test file can be found in the Bazaar repository at,
|
||||
# lib/bash/tmpdir.sh
|
||||
# t/lib/bash/tmpdir.sh
|
||||
# See https://launchpad.net/percona-toolkit for more information.
|
||||
# ###########################################################################
|
||||
|
||||
# pt-sift isn't ready for this yet.
|
||||
#set -u
|
||||
|
||||
TMPDIR=""
|
||||
|
||||
mk_tmpdir() {
|
||||
local dir=${1:-""}
|
||||
|
||||
if [ -n "$dir" ]; then
|
||||
if [ ! -d "$dir" ]; then
|
||||
mkdir $dir || die "Cannot make tmpdir $dir"
|
||||
fi
|
||||
TMPDIR="$dir"
|
||||
else
|
||||
local tool=`basename $0`
|
||||
local pid="$$"
|
||||
TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
|
||||
|| die "Cannot make secure tmpdir"
|
||||
fi
|
||||
}
|
||||
|
||||
rm_tmpdir() {
|
||||
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||
rm -rf $TMPDIR
|
||||
fi
|
||||
TMPDIR=""
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# End tmpdir package
|
||||
# ###########################################################################
|
||||
|
||||
# Show current help and settings
|
||||
print_help() {
|
||||
cat <<-HELP
|
||||
@@ -72,19 +113,22 @@ main() {
|
||||
fi
|
||||
done
|
||||
|
||||
# Make a secure tmpdir.
|
||||
mk_tmpdir
|
||||
|
||||
# We need to generate a list of timestamps, and ask the user to choose one if
|
||||
# there is no PREFIX yet. NOTE: we rely on the "-df" files here.
|
||||
ls "${BASEDIR}" | grep -- '-df$' | cut -d- -f1 | sort > /tmp/pt-sift.prefixes
|
||||
ls "${BASEDIR}" | grep -- '-df$' | cut -d- -f1 | sort > $TMPDIR/pt-sift.prefixes
|
||||
if [ -z "${PREFIX}" ]; then
|
||||
if [ "$(grep -c . /tmp/pt-sift.prefixes)" = "1" ]; then
|
||||
if [ "$(grep -c . $TMPDIR/pt-sift.prefixes)" = "1" ]; then
|
||||
# If there is only one sample, we use it as the prefix.
|
||||
PREFIX="$(cat /tmp/pt-sift.prefixes)"
|
||||
PREFIX="$(cat $TMPDIR/pt-sift.prefixes)"
|
||||
fi
|
||||
fi
|
||||
if [ -z "${PREFIX}" ]; then
|
||||
echo
|
||||
i=0
|
||||
cat /tmp/pt-sift.prefixes | while read line; do
|
||||
cat $TMPDIR/pt-sift.prefixes | while read line; do
|
||||
i=$(($i + 1))
|
||||
echo -n " $line"
|
||||
if [ "${i}" = "3" ]; then
|
||||
@@ -94,14 +138,14 @@ main() {
|
||||
done
|
||||
# We might have ended mid-line or we might have printed a newline; print a
|
||||
# newline if required to end the list of timestamp prefixes.
|
||||
awk 'BEGIN { i = 0 } { i++ } END { if ( i % 3 != 0 ) { print "" } }' /tmp/pt-sift.prefixes
|
||||
awk 'BEGIN { i = 0 } { i++ } END { if ( i % 3 != 0 ) { print "" } }' $TMPDIR/pt-sift.prefixes
|
||||
echo
|
||||
while [ -z "${PREFIX}" -o "$(grep -c "${PREFIX}" /tmp/pt-sift.prefixes)" -ne 1 ]; do
|
||||
DEFAULT="$(tail -1 /tmp/pt-sift.prefixes)"
|
||||
while [ -z "${PREFIX}" -o "$(grep -c "${PREFIX}" $TMPDIR/pt-sift.prefixes)" -ne 1 ]; do
|
||||
DEFAULT="$(tail -1 $TMPDIR/pt-sift.prefixes)"
|
||||
read -e -p "Select a timestamp from the list [${DEFAULT}] " ARG
|
||||
ARG="${ARG:-${DEFAULT}}"
|
||||
if [ "$(grep -c "${ARG}" /tmp/pt-sift.prefixes)" -eq 1 ]; then
|
||||
PREFIX="$(grep "${ARG}" /tmp/pt-sift.prefixes)"
|
||||
if [ "$(grep -c "${ARG}" $TMPDIR/pt-sift.prefixes)" -eq 1 ]; then
|
||||
PREFIX="$(grep "${ARG}" $TMPDIR/pt-sift.prefixes)"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
@@ -113,7 +157,7 @@ main() {
|
||||
if [ "${ACTION}" != "INVALID" ]; then
|
||||
# Print the current host, timestamp and action. Figure out if we're at
|
||||
# the first or last sample, to make it easy to navigate.
|
||||
PAGE="$(awk "/./{i++} /${PREFIX}/{c=i} END{print c, \"of\", i}" /tmp/pt-sift.prefixes)"
|
||||
PAGE="$(awk "/./{i++} /${PREFIX}/{c=i} END{print c, \"of\", i}" $TMPDIR/pt-sift.prefixes)"
|
||||
HOST="$(cat "${BASEDIR}/${PREFIX}-hostname" 2>/dev/null)"
|
||||
echo -e "======== ${HOST:-unknown} at \033[34m${PREFIX} \033[31m${ACTION}\033[0m (${PAGE}) ========"
|
||||
fi
|
||||
@@ -421,7 +465,7 @@ main() {
|
||||
if ( printed == 0 ) {
|
||||
print \"${PREFIX}\";
|
||||
}
|
||||
}" /tmp/pt-sift.prefixes)"
|
||||
}" $TMPDIR/pt-sift.prefixes)"
|
||||
;;
|
||||
1)
|
||||
ACTION="DEFAULT"
|
||||
@@ -458,6 +502,7 @@ main() {
|
||||
esac
|
||||
done
|
||||
|
||||
rm_tmpdir
|
||||
}
|
||||
|
||||
# Execute the program if it was not included from another file. This makes it
|
||||
|
1772
bin/pt-stalk
1772
bin/pt-stalk
File diff suppressed because it is too large
Load Diff
198
bin/pt-summary
198
bin/pt-summary
@@ -44,13 +44,53 @@ fuzz () {
|
||||
echo $1 | $AP_AWK "{fuzzy_var=\$1; ${fuzzy_formula} print fuzzy_var;}"
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# tmpdir package
|
||||
# This package is a copy without comments from the original. The original
|
||||
# with comments and its test file can be found in the Bazaar repository at,
|
||||
# lib/bash/tmpdir.sh
|
||||
# t/lib/bash/tmpdir.sh
|
||||
# See https://launchpad.net/percona-toolkit for more information.
|
||||
# ###########################################################################
|
||||
|
||||
set -u
|
||||
|
||||
TMPDIR=""
|
||||
|
||||
mk_tmpdir() {
|
||||
local dir=${1:-""}
|
||||
|
||||
if [ -n "$dir" ]; then
|
||||
if [ ! -d "$dir" ]; then
|
||||
mkdir $dir || die "Cannot make tmpdir $dir"
|
||||
fi
|
||||
TMPDIR="$dir"
|
||||
else
|
||||
local tool=`basename $0`
|
||||
local pid="$$"
|
||||
TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
|
||||
|| die "Cannot make secure tmpdir"
|
||||
fi
|
||||
}
|
||||
|
||||
rm_tmpdir() {
|
||||
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||
rm -rf $TMPDIR
|
||||
fi
|
||||
TMPDIR=""
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# End tmpdir package
|
||||
# ###########################################################################
|
||||
|
||||
# The temp files are for storing working results so we don't call commands many
|
||||
# times (gives inconsistent results, maybe adds load on things I don't want to
|
||||
# such as RAID controllers). They must not exist -- if they did, someone would
|
||||
# symlink them to /etc/passwd and then run this program as root. Call this
|
||||
# function with "rm" or "touch" as an argument.
|
||||
temp_files() {
|
||||
for file in /tmp/percona-toolkit /tmp/percona-toolkit2; do
|
||||
for file in $TMPDIR/percona-toolkit $TMPDIR/percona-toolkit2; do
|
||||
case "$1" in
|
||||
touch)
|
||||
if ! touch "${file}"; then
|
||||
@@ -128,12 +168,12 @@ group_concat () {
|
||||
# Functions for parsing specific files and getting desired info from them.
|
||||
# These are called from within main() and are separated so they can be tested
|
||||
# easily. The calling convention is that the data they need to run is prepared
|
||||
# first by putting it into /tmp/percona-toolkit. Then code that's testing just needs to
|
||||
# put sample data into /tmp/percona-toolkit and call it.
|
||||
# first by putting it into $TMPDIR/percona-toolkit. Then code that's testing
|
||||
# just needs to put sample data into $TMPDIR/percona-toolkit and call it.
|
||||
# ##############################################################################
|
||||
|
||||
# ##############################################################################
|
||||
# Parse Linux's /proc/cpuinfo, which should be stored in /tmp/percona-toolkit.
|
||||
# Parse Linux's /proc/cpuinfo, which should be stored in $TMPDIR/percona-toolkit.
|
||||
# ##############################################################################
|
||||
parse_proc_cpuinfo () {
|
||||
local file=$1
|
||||
@@ -189,8 +229,8 @@ parse_psrinfo_cpus() {
|
||||
start = index($0, " at ") + 4;
|
||||
end = length($0) - start - 4
|
||||
print substr($0, start, end);
|
||||
}' "$1" | sort | uniq -c > /tmp/percona-toolkit2
|
||||
name_val "Speeds" "$(group_concat /tmp/percona-toolkit2)"
|
||||
}' "$1" | sort | uniq -c > $TMPDIR/percona-toolkit2
|
||||
name_val "Speeds" "$(group_concat $TMPDIR/percona-toolkit2)"
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
@@ -292,7 +332,7 @@ parse_ip_s_link () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of 'netstat -antp' which should be in /tmp/percona-toolkit.
|
||||
# Parse the output of 'netstat -antp' which should be in $TMPDIR/percona-toolkit.
|
||||
# ##############################################################################
|
||||
parse_netstat () {
|
||||
local file=$1
|
||||
@@ -397,7 +437,7 @@ parse_filesystems () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of fdisk -l, which should be in /tmp/percona-toolkit; there might be
|
||||
# Parse the output of fdisk -l, which should be in $TMPDIR/percona-toolkit; there might be
|
||||
# multiple fdisk -l outputs in the file.
|
||||
# ##############################################################################
|
||||
parse_fdisk () {
|
||||
@@ -431,7 +471,7 @@ parse_fdisk () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of dmesg, which should be in /tmp/percona-toolkit, and detect
|
||||
# Parse the output of dmesg, which should be in $TMPDIR/percona-toolkit, and detect
|
||||
# virtualization.
|
||||
# ##############################################################################
|
||||
parse_virtualization_dmesg () {
|
||||
@@ -463,7 +503,7 @@ parse_virtualization_generic() {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of lspci, which should be in /tmp/percona-toolkit, and detect
|
||||
# Parse the output of lspci, which should be in $TMPDIR/percona-toolkit, and detect
|
||||
# Ethernet cards.
|
||||
# ##############################################################################
|
||||
parse_ethernet_controller_lspci () {
|
||||
@@ -474,7 +514,7 @@ parse_ethernet_controller_lspci () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of lspci, which should be in /tmp/percona-toolkit, and detect RAID
|
||||
# Parse the output of lspci, which should be in $TMPDIR/percona-toolkit, and detect RAID
|
||||
# controllers.
|
||||
# ##############################################################################
|
||||
parse_raid_controller_lspci () {
|
||||
@@ -497,7 +537,7 @@ parse_raid_controller_lspci () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of dmesg, which should be in /tmp/percona-toolkit, and detect RAID
|
||||
# Parse the output of dmesg, which should be in $TMPDIR/percona-toolkit, and detect RAID
|
||||
# controllers.
|
||||
# ##############################################################################
|
||||
parse_raid_controller_dmesg () {
|
||||
@@ -516,7 +556,7 @@ parse_raid_controller_dmesg () {
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of "hpacucli ctrl all show config", which should be stored in
|
||||
# /tmp/percona-toolkit
|
||||
# $TMPDIR/percona-toolkit
|
||||
# ##############################################################################
|
||||
parse_hpacucli () {
|
||||
local file=$1
|
||||
@@ -524,7 +564,7 @@ parse_hpacucli () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of arcconf, which should be stored in /tmp/percona-toolkit
|
||||
# Parse the output of arcconf, which should be stored in $TMPDIR/percona-toolkit
|
||||
# ##############################################################################
|
||||
parse_arcconf () {
|
||||
local file=$1
|
||||
@@ -634,7 +674,7 @@ parse_fusionmpt_lsiutil () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output of MegaCli64 -AdpAllInfo -aALL from /tmp/percona-toolkit.
|
||||
# Parse the output of MegaCli64 -AdpAllInfo -aALL from $TMPDIR/percona-toolkit.
|
||||
# ##############################################################################
|
||||
parse_lsi_megaraid_adapter_info () {
|
||||
local file=$1
|
||||
@@ -653,7 +693,7 @@ parse_lsi_megaraid_adapter_info () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse the output (saved in /tmp/percona-toolkit) of
|
||||
# Parse the output (saved in $TMPDIR/percona-toolkit) of
|
||||
# /opt/MegaRAID/MegaCli/MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL
|
||||
# ##############################################################################
|
||||
parse_lsi_megaraid_bbu_status () {
|
||||
@@ -665,7 +705,7 @@ parse_lsi_megaraid_bbu_status () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse physical devices from the output (saved in /tmp/percona-toolkit) of
|
||||
# Parse physical devices from the output (saved in $TMPDIR/percona-toolkit) of
|
||||
# /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
|
||||
# OR, it will also work with the output of
|
||||
# /opt/MegaRAID/MegaCli/MegaCli64 -PDList -aALL
|
||||
@@ -694,7 +734,7 @@ parse_lsi_megaraid_devices () {
|
||||
}
|
||||
|
||||
# ##############################################################################
|
||||
# Parse virtual devices from the output (saved in /tmp/percona-toolkit) of
|
||||
# Parse virtual devices from the output (saved in $TMPDIR/percona-toolkit) of
|
||||
# /opt/MegaRAID/MegaCli/MegaCli64 -LdPdInfo -aALL
|
||||
# OR, it will also work with the output of
|
||||
# /opt/MegaRAID/MegaCli/MegaCli64 -LDInfo -Lall -aAll
|
||||
@@ -826,6 +866,7 @@ main () {
|
||||
export PATH="${PATH}:/usr/StorMan/:/opt/MegaRAID/MegaCli/";
|
||||
|
||||
# Set up temporary files.
|
||||
mk_tmpdir
|
||||
temp_files "rm"
|
||||
temp_files "touch"
|
||||
section Percona_Toolkit_System_Summary_Report
|
||||
@@ -833,7 +874,7 @@ main () {
|
||||
# ########################################################################
|
||||
# Grab a bunch of stuff and put it into temp files for later.
|
||||
# ########################################################################
|
||||
sysctl -a > /tmp/percona-toolkit.sysctl 2>/dev/null
|
||||
sysctl -a > $TMPDIR/percona-toolkit.sysctl 2>/dev/null
|
||||
|
||||
# ########################################################################
|
||||
# General date, time, load, etc
|
||||
@@ -939,19 +980,19 @@ main () {
|
||||
# available to non-root users and usually has telltale signs. It's most
|
||||
# reliable to look at /var/log/dmesg if possible. There are a number of
|
||||
# other ways to find out if a system is virtualized.
|
||||
cat /var/log/dmesg > /tmp/percona-toolkit 2>/dev/null
|
||||
if [ ! -s /tmp/percona-toolkit ]; then
|
||||
dmesg > /tmp/percona-toolkit 2>/dev/null
|
||||
cat /var/log/dmesg > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
if [ ! -s $TMPDIR/percona-toolkit ]; then
|
||||
dmesg > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
fi
|
||||
if [ -s /tmp/percona-toolkit ]; then
|
||||
virt="$(parse_virtualization_dmesg /tmp/percona-toolkit)"
|
||||
if [ -s $TMPDIR/percona-toolkit ]; then
|
||||
virt="$(parse_virtualization_dmesg $TMPDIR/percona-toolkit)"
|
||||
fi
|
||||
if [ -z "${virt}" ]; then
|
||||
if which lspci >/dev/null 2>&1; then
|
||||
lspci > /tmp/percona-toolkit 2>/dev/null
|
||||
if grep -qi virtualbox /tmp/percona-toolkit; then
|
||||
lspci > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
if grep -qi virtualbox $TMPDIR/percona-toolkit; then
|
||||
virt=VirtualBox
|
||||
elif grep -qi vmware /tmp/percona-toolkit; then
|
||||
elif grep -qi vmware $TMPDIR/percona-toolkit; then
|
||||
virt=VMWare
|
||||
elif [ -e /proc/user_beancounters ]; then
|
||||
virt="OpenVZ/Virtuozzo"
|
||||
@@ -962,10 +1003,10 @@ main () {
|
||||
virt="FreeBSD Jail"
|
||||
fi
|
||||
elif [ "${platform}" = "SunOS" ]; then
|
||||
if which prtdiag >/dev/null 2>&1 && prtdiag > /tmp/percona-toolkit.prtdiag 2>/dev/null; then
|
||||
virt="$(parse_virtualization_generic /tmp/percona-toolkit.prtdiag)"
|
||||
elif which smbios >/dev/null 2>&1 && smbios > /tmp/percona-toolkit.smbios 2>/dev/null; then
|
||||
virt="$(parse_virtualization_generic /tmp/percona-toolkit.smbios)"
|
||||
if which prtdiag >/dev/null 2>&1 && prtdiag > $TMPDIR/percona-toolkit.prtdiag 2>/dev/null; then
|
||||
virt="$(parse_virtualization_generic $TMPDIR/percona-toolkit.prtdiag)"
|
||||
elif which smbios >/dev/null 2>&1 && smbios > $TMPDIR/percona-toolkit.smbios 2>/dev/null; then
|
||||
virt="$(parse_virtualization_generic $TMPDIR/percona-toolkit.smbios)"
|
||||
fi
|
||||
fi
|
||||
name_val Virtualized "${virt:-No virtualization detected}"
|
||||
@@ -975,23 +1016,23 @@ main () {
|
||||
# ########################################################################
|
||||
section Processor
|
||||
if [ -f /proc/cpuinfo ]; then
|
||||
cat /proc/cpuinfo > /tmp/percona-toolkit 2>/dev/null
|
||||
parse_proc_cpuinfo /tmp/percona-toolkit
|
||||
cat /proc/cpuinfo > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
parse_proc_cpuinfo $TMPDIR/percona-toolkit
|
||||
elif [ "${platform}" = "FreeBSD" ]; then
|
||||
parse_sysctl_cpu_freebsd /tmp/percona-toolkit.sysctl
|
||||
parse_sysctl_cpu_freebsd $TMPDIR/percona-toolkit.sysctl
|
||||
elif [ "${platform}" = "SunOS" ]; then
|
||||
psrinfo -v > /tmp/percona-toolkit
|
||||
parse_psrinfo_cpus /tmp/percona-toolkit
|
||||
psrinfo -v > $TMPDIR/percona-toolkit
|
||||
parse_psrinfo_cpus $TMPDIR/percona-toolkit
|
||||
# TODO: prtconf -v actually prints the CPU model name etc.
|
||||
fi
|
||||
|
||||
section Memory
|
||||
if [ "${platform}" = "Linux" ]; then
|
||||
free -b > /tmp/percona-toolkit
|
||||
cat /proc/meminfo >> /tmp/percona-toolkit
|
||||
parse_free_minus_b /tmp/percona-toolkit
|
||||
free -b > $TMPDIR/percona-toolkit
|
||||
cat /proc/meminfo >> $TMPDIR/percona-toolkit
|
||||
parse_free_minus_b $TMPDIR/percona-toolkit
|
||||
elif [ "${platform}" = "FreeBSD" ]; then
|
||||
parse_memory_sysctl_freebsd /tmp/percona-toolkit.sysctl
|
||||
parse_memory_sysctl_freebsd $TMPDIR/percona-toolkit.sysctl
|
||||
elif [ "${platform}" = "SunOS" ]; then
|
||||
name_val Memory "$(prtconf | awk -F: '/Memory/{print $2}')"
|
||||
fi
|
||||
@@ -1007,8 +1048,8 @@ main () {
|
||||
fi
|
||||
fi
|
||||
|
||||
if which dmidecode >/dev/null 2>&1 && dmidecode > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_dmidecode_mem_devices /tmp/percona-toolkit
|
||||
if which dmidecode >/dev/null 2>&1 && dmidecode > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_dmidecode_mem_devices $TMPDIR/percona-toolkit
|
||||
fi
|
||||
|
||||
# ########################################################################
|
||||
@@ -1023,25 +1064,25 @@ main () {
|
||||
if [ "${platform}" = "Linux" ]; then
|
||||
cmd="df -h -P"
|
||||
fi
|
||||
$cmd | sort > /tmp/percona-toolkit2
|
||||
mount | sort | join /tmp/percona-toolkit2 - > /tmp/percona-toolkit
|
||||
parse_filesystems /tmp/percona-toolkit "${platform}"
|
||||
$cmd | sort > $TMPDIR/percona-toolkit2
|
||||
mount | sort | join $TMPDIR/percona-toolkit2 - > $TMPDIR/percona-toolkit
|
||||
parse_filesystems $TMPDIR/percona-toolkit "${platform}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "${platform}" = "Linux" ]; then
|
||||
section "Disk_Schedulers_And_Queue_Size"
|
||||
echo "" > /tmp/percona-toolkit
|
||||
echo "" > $TMPDIR/percona-toolkit
|
||||
for disk in $(ls /sys/block/ | grep -v -e ram -e loop -e 'fd[0-9]'); do
|
||||
if [ -e "/sys/block/${disk}/queue/scheduler" ]; then
|
||||
name_val "${disk}" "$(cat /sys/block/${disk}/queue/scheduler | grep -o '\[.*\]') $(cat /sys/block/${disk}/queue/nr_requests)"
|
||||
fdisk -l "/dev/${disk}" >> /tmp/percona-toolkit 2>/dev/null
|
||||
fdisk -l "/dev/${disk}" >> $TMPDIR/percona-toolkit 2>/dev/null
|
||||
fi
|
||||
done
|
||||
|
||||
# Relies on /tmp/percona-toolkit having data from the Disk Schedulers loop.
|
||||
# Relies on $TMPDIR/percona-toolkit having data from the Disk Schedulers loop.
|
||||
section "Disk_Partioning"
|
||||
parse_fdisk /tmp/percona-toolkit
|
||||
parse_fdisk $TMPDIR/percona-toolkit
|
||||
|
||||
section "Kernel_Inode_State"
|
||||
for file in dentry-state file-nr inode-nr; do
|
||||
@@ -1064,15 +1105,15 @@ main () {
|
||||
# often available to non-root users. It's most reliable to look at
|
||||
# /var/log/dmesg if possible.
|
||||
# ########################################################################
|
||||
if which lspci >/dev/null 2>&1 && lspci > /tmp/percona-toolkit 2>/dev/null; then
|
||||
controller="$(parse_raid_controller_lspci /tmp/percona-toolkit)"
|
||||
if which lspci >/dev/null 2>&1 && lspci > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
controller="$(parse_raid_controller_lspci $TMPDIR/percona-toolkit)"
|
||||
fi
|
||||
if [ -z "${controller}" ]; then
|
||||
cat /var/log/dmesg > /tmp/percona-toolkit 2>/dev/null
|
||||
if [ ! -s /tmp/percona-toolkit ]; then
|
||||
dmesg > /tmp/percona-toolkit 2>/dev/null
|
||||
cat /var/log/dmesg > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
if [ ! -s $TMPDIR/percona-toolkit ]; then
|
||||
dmesg > $TMPDIR/percona-toolkit 2>/dev/null
|
||||
fi
|
||||
controller="$(parse_raid_controller_dmesg /tmp/percona-toolkit)"
|
||||
controller="$(parse_raid_controller_dmesg $TMPDIR/percona-toolkit)"
|
||||
fi
|
||||
|
||||
name_val Controller "${controller:-No RAID controller detected}"
|
||||
@@ -1085,29 +1126,29 @@ main () {
|
||||
# ########################################################################
|
||||
notfound=""
|
||||
if [ "${controller}" = "AACRAID" ]; then
|
||||
if arcconf getconfig 1 > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_arcconf /tmp/percona-toolkit
|
||||
if arcconf getconfig 1 > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_arcconf $TMPDIR/percona-toolkit
|
||||
elif ! which arcconf >/dev/null 2>&1; then
|
||||
notfound="e.g. http://www.adaptec.com/en-US/support/raid/scsi_raid/ASR-2120S/"
|
||||
fi
|
||||
elif [ "${controller}" = "HP Smart Array" ]; then
|
||||
if hpacucli ctrl all show config > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_hpacucli /tmp/percona-toolkit
|
||||
if hpacucli ctrl all show config > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_hpacucli $TMPDIR/percona-toolkit
|
||||
elif ! which hpacucli >/dev/null 2>&1; then
|
||||
notfound="your package repository or the manufacturer's website"
|
||||
fi
|
||||
elif [ "${controller}" = "LSI Logic MegaRAID SAS" ]; then
|
||||
if MegaCli64 -AdpAllInfo -aALL -NoLog > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_lsi_megaraid_adapter_info /tmp/percona-toolkit
|
||||
if MegaCli64 -AdpAllInfo -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_lsi_megaraid_adapter_info $TMPDIR/percona-toolkit
|
||||
elif ! which MegaCli64 >/dev/null 2>&1; then
|
||||
notfound="your package repository or the manufacturer's website"
|
||||
fi
|
||||
if MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_lsi_megaraid_bbu_status /tmp/percona-toolkit
|
||||
if MegaCli64 -AdpBbuCmd -GetBbuStatus -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_lsi_megaraid_bbu_status $TMPDIR/percona-toolkit
|
||||
fi
|
||||
if MegaCli64 -LdPdInfo -aALL -NoLog > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_lsi_megaraid_virtual_devices /tmp/percona-toolkit
|
||||
parse_lsi_megaraid_devices /tmp/percona-toolkit
|
||||
if MegaCli64 -LdPdInfo -aALL -NoLog > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_lsi_megaraid_virtual_devices $TMPDIR/percona-toolkit
|
||||
parse_lsi_megaraid_devices $TMPDIR/percona-toolkit
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1122,8 +1163,8 @@ main () {
|
||||
# #####################################################################
|
||||
if [ "${platform}" = "Linux" ]; then
|
||||
section Network_Config
|
||||
if which lspci > /dev/null 2>&1 && lspci > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_ethernet_controller_lspci /tmp/percona-toolkit
|
||||
if which lspci > /dev/null 2>&1 && lspci > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_ethernet_controller_lspci $TMPDIR/percona-toolkit
|
||||
fi
|
||||
if sysctl net.ipv4.tcp_fin_timeout > /dev/null 2>&1; then
|
||||
name_val "FIN Timeout" "$(sysctl net.ipv4.tcp_fin_timeout)"
|
||||
@@ -1135,15 +1176,15 @@ main () {
|
||||
# /proc/sys/net/netfilter/nf_conntrack_max or /proc/sys/net/nf_conntrack_max
|
||||
# in new kernels like Fedora 12?
|
||||
|
||||
if which ip >/dev/null 2>&1 && ip -s link > /tmp/percona-toolkit 2>/dev/null; then
|
||||
if which ip >/dev/null 2>&1 && ip -s link > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
section Interface_Statistics
|
||||
parse_ip_s_link /tmp/percona-toolkit
|
||||
parse_ip_s_link $TMPDIR/percona-toolkit
|
||||
fi
|
||||
|
||||
if [ "${platform}" = "Linux" ]; then
|
||||
section Network_Connections
|
||||
if netstat -antp > /tmp/percona-toolkit 2>/dev/null; then
|
||||
parse_netstat /tmp/percona-toolkit
|
||||
if netstat -antp > $TMPDIR/percona-toolkit 2>/dev/null; then
|
||||
parse_netstat $TMPDIR/percona-toolkit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -1164,12 +1205,12 @@ main () {
|
||||
fi
|
||||
if which vmstat > /dev/null 2>&1 ; then
|
||||
section "Simplified_and_fuzzy_rounded_vmstat_(wait_please)"
|
||||
vmstat 1 5 > /tmp/percona-toolkit
|
||||
vmstat 1 5 > $TMPDIR/percona-toolkit
|
||||
if [ "${platform}" = "Linux" ]; then
|
||||
format_vmstat /tmp/percona-toolkit
|
||||
format_vmstat $TMPDIR/percona-toolkit
|
||||
else
|
||||
# TODO: simplify/format for other platforms
|
||||
cat /tmp/percona-toolkit
|
||||
cat $TMPDIR/percona-toolkit
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -1179,6 +1220,7 @@ main () {
|
||||
# ########################################################################
|
||||
temp_files "rm"
|
||||
temp_files "check"
|
||||
rm_tmpdir
|
||||
section The_End
|
||||
}
|
||||
|
||||
@@ -1238,9 +1280,9 @@ See also L<"BUGS"> for more information on filing bugs and getting help.
|
||||
=head1 DESCRIPTION
|
||||
|
||||
pt-summary runs a large variety of commands to inspect system status and
|
||||
configuration, saves the output into files in /tmp, and then runs Unix
|
||||
commands on these results to format them nicely. It works best when
|
||||
executed as a privileged user, but will also work without privileges,
|
||||
configuration, saves the output into files in a temporary directory, and
|
||||
then runs Unix commands on these results to format them nicely. It works
|
||||
best when executed as a privileged user, but will also work without privileges,
|
||||
although some output might not be possible to generate without root.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
@@ -190,8 +190,8 @@ The syntax of the configuration files is as follows:
|
||||
|
||||
=item *
|
||||
|
||||
Whitespace followed by a hash (#) sign signifies that the rest of the line is a
|
||||
comment. This is deleted.
|
||||
Whitespace followed by a hash sign (#) signifies that the rest of the line is a
|
||||
comment. This is deleted. For example:
|
||||
|
||||
=item *
|
||||
|
||||
@@ -208,7 +208,9 @@ Each line is permitted to be in either of the following formats:
|
||||
option
|
||||
option=value
|
||||
|
||||
Whitespace around the equals sign is deleted during processing.
|
||||
Do not prefix the option with C<-->. Do not quote the values, even if
|
||||
it has spaces; value are literal. Whitespace around the equals sign is
|
||||
deleted during processing.
|
||||
|
||||
=item *
|
||||
|
||||
@@ -222,6 +224,22 @@ program.
|
||||
|
||||
=back
|
||||
|
||||
=head2 EXAMPLE
|
||||
|
||||
This config file for pt-stalk,
|
||||
|
||||
# Config for pt-stalk
|
||||
variable=Threads_connected
|
||||
cycles=2 # trigger if problem seen twice in a row
|
||||
--
|
||||
--user daniel
|
||||
|
||||
is equivalent to this command line:
|
||||
|
||||
pt-stalk --variable Threads_connected --cycles 2 -- --user daniel
|
||||
|
||||
Options after C<--> are passed literally to mysql and mysqladmin.
|
||||
|
||||
=head2 READ ORDER
|
||||
|
||||
The tools read several configuration files in order:
|
||||
|
@@ -25,10 +25,24 @@ set -u
|
||||
|
||||
# seq N, return 1, ..., 5
|
||||
_seq() {
|
||||
local i=$1
|
||||
local i="$1"
|
||||
awk "BEGIN { for(i=1; i<=$i; i++) print i; }"
|
||||
}
|
||||
|
||||
_pidof() {
|
||||
local cmd="$1"
|
||||
if ! pidof "$cmd" 2>/dev/null; then
|
||||
ps -eo pid,ucomm | awk -v comm="$cmd" '$2 == comm { print $1 }'
|
||||
fi
|
||||
}
|
||||
|
||||
_lsof() {
|
||||
local pid="$1"
|
||||
if ! lsof -p $pid 2>/dev/null; then
|
||||
/bin/ls -l /proc/$pid/fd 2>/dev/null
|
||||
fi
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# End alt_cmds package
|
||||
# ###########################################################################
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This program is copyright 2011 Percona Inc.
|
||||
# This program is copyright 2011-2012 Percona Inc.
|
||||
# Feedback and improvements are welcome.
|
||||
#
|
||||
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
@@ -24,33 +24,31 @@
|
||||
set -u
|
||||
|
||||
# Global variables.
|
||||
CMD_GDB=${CMD_GDB:-"gdb"}
|
||||
CMD_IOSTAT=${CMD_IOSTAT:-"iostat"}
|
||||
CMD_MPSTAT=${CMD_MPSTAT:-"mpstat"}
|
||||
CMD_MYSQL=${CMD_MSSQL:-"mysql"}
|
||||
CMD_MYSQLADMIN=${CMD_MYSQL_ADMIN:-"mysqladmin"}
|
||||
CMD_OPCONTROL=${CMD_OPCONTROL:-"opcontrol"}
|
||||
CMD_OPREPORT=${CMD_OPREPORT:-"opreport"}
|
||||
CMD_PMAP=${CMD_PMAP:-"pmap"}
|
||||
CMD_STRACE=${CMD_STRACE:-"strace"}
|
||||
CMD_TCPDUMP=${CMD_TCPDUMP:-"tcpdump"}
|
||||
CMD_VMSTAT=${CMD_VMSTAT:-"vmstat"}
|
||||
CMD_GDB="$(which gdb)"
|
||||
CMD_IOSTAT="$(which iostat)"
|
||||
CMD_MPSTAT="$(which mpstat)"
|
||||
CMD_MYSQL="$(which mysql)"
|
||||
CMD_MYSQLADMIN="$(which mysqladmin)"
|
||||
CMD_OPCONTROL="$(which opcontrol)"
|
||||
CMD_OPREPORT="$(which opreport)"
|
||||
CMD_PMAP="$(which pmap)"
|
||||
CMD_STRACE="$(which strace)"
|
||||
CMD_SYSCTL="$(which sysctl)"
|
||||
CMD_TCPDUMP="$(which tcpdump)"
|
||||
CMD_VMSTAT="$(which vmstat)"
|
||||
|
||||
# Try to find command manually.
|
||||
[ -z "$CMD_SYSCTL" -a -x "/sbin/sysctl" ] && CMD_SYSCTL="/sbin/sysctl"
|
||||
|
||||
collect() {
|
||||
local d=$1 # directory to save results in
|
||||
local p=$2 # prefix for each result file
|
||||
local d="$1" # directory to save results in
|
||||
local p="$2" # prefix for each result file
|
||||
|
||||
# Get pidof mysqld; pidof doesn't exist on some systems. We try our best...
|
||||
local mysqld_pid=$(pidof -s mysqld);
|
||||
if [ -z "$mysqld_pid" ]; then
|
||||
mysqld_pid=$(pgrep -o -x mysqld);
|
||||
fi
|
||||
if [ -z "$mysqld_pid" ]; then
|
||||
mysqld_pid=$(ps -eaf | grep 'mysql[d]' | grep -v mysqld_safe | awk '{print $2}' | head -n1);
|
||||
fi
|
||||
# Get pidof mysqld.
|
||||
local mysqld_pid=$(_pidof mysqld | head -n1)
|
||||
|
||||
# Get memory allocation info before anything else.
|
||||
if [ -x "$CMD_PMAP" -a "$mysqld_pid" ]; then
|
||||
if [ "$CMD_PMAP" -a "$mysqld_pid" ]; then
|
||||
if $CMD_PMAP --help 2>&1 | grep -- -x >/dev/null 2>&1 ; then
|
||||
$CMD_PMAP -x $mysqld_pid > "$d/$p-pmap"
|
||||
else
|
||||
@@ -60,21 +58,19 @@ collect() {
|
||||
fi
|
||||
|
||||
# Getting a GDB stacktrace can be an intensive operation,
|
||||
# so do this only if necessary.
|
||||
if [ "$OPT_COLLECT_GDB" = "yes" -a "$mysqld_pid" ]; then
|
||||
# so do this only if necessary (and possible).
|
||||
if [ "$CMD_GDB" -a "$OPT_COLLECT_GDB" -a "$mysqld_pid" ]; then
|
||||
$CMD_GDB \
|
||||
-ex "set pagination 0" \
|
||||
-ex "thread apply all bt" \
|
||||
--batch -p $mysqld_pid \
|
||||
>> "$d/$p-stacktrace"
|
||||
else
|
||||
echo "GDB (--collect-gdb) was not enabled" >> "$d/$p-stacktrace"
|
||||
fi
|
||||
|
||||
# Get MySQL's variables if possible. Then sleep long enough that we probably
|
||||
# complete SHOW VARIABLES if all's well. (We don't want to run mysql in the
|
||||
# foreground, because it could hang.)
|
||||
$CMD_MYSQL $EXT_ARGV -e 'SHOW GLOBAL VARIABLES' >> "$d/$p-variables" 2>&1 &
|
||||
$CMD_MYSQL $EXT_ARGV -e 'SHOW GLOBAL VARIABLES' >> "$d/$p-variables" &
|
||||
sleep .2
|
||||
|
||||
# Get the major.minor version number. Version 3.23 doesn't matter for our
|
||||
@@ -90,14 +86,15 @@ collect() {
|
||||
|
||||
local tail_error_log_pid=""
|
||||
if [ "$mysql_error_log" ]; then
|
||||
echo "The MySQL error log seems to be ${mysql_error_log}"
|
||||
tail -f "$mysql_error_log" >"$d/$p-log_error" 2>&1 &
|
||||
log "The MySQL error log seems to be $mysql_error_log"
|
||||
tail -f "$mysql_error_log" >"$d/$p-log_error" &
|
||||
tail_error_log_pid=$!
|
||||
|
||||
# Send a mysqladmin debug to the server so we can potentially learn about
|
||||
# locking etc.
|
||||
$CMD_MYSQLADMIN $EXT_ARGV debug
|
||||
else
|
||||
echo "Could not find the MySQL error log"
|
||||
log "Could not find the MySQL error log"
|
||||
fi
|
||||
|
||||
# Get a sample of these right away, so we can get these without interaction
|
||||
@@ -108,13 +105,13 @@ collect() {
|
||||
else
|
||||
local mutex="SHOW MUTEX STATUS"
|
||||
fi
|
||||
$CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus1" 2>&1 &
|
||||
$CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status1" 2>&1 &
|
||||
open_tables >> "$d/$p-opentables1" 2>&1 &
|
||||
$CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus1" &
|
||||
$CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status1" &
|
||||
open_tables >> "$d/$p-opentables1" &
|
||||
|
||||
# If TCP dumping is specified, start that on the server's port.
|
||||
local tcpdump_pid=""
|
||||
if [ "$OPT_COLLECT_TCPDUMP" = "yes" ]; then
|
||||
if [ "$CMD_TCPDUMP" -a "$OPT_COLLECT_TCPDUMP" ]; then
|
||||
local port=$(awk '/^port/{print $2}' "$d/$p-variables")
|
||||
if [ "$port" ]; then
|
||||
$CMD_TCPDUMP -i any -s 4096 -w "$d/$p-tcpdump" port ${port} &
|
||||
@@ -124,30 +121,40 @@ collect() {
|
||||
|
||||
# Next, start oprofile gathering data during the whole rest of this process.
|
||||
# The --init should be a no-op if it has already been init-ed.
|
||||
local have_oprofile="no"
|
||||
if [ "$OPT_COLLECT_OPROFILE" = "yes" ]; then
|
||||
local have_oprofile=""
|
||||
if [ "$CMD_OPCONTROL" -a "$OPT_COLLECT_OPROFILE" ]; then
|
||||
if $CMD_OPCONTROL --init; then
|
||||
$CMD_OPCONTROL --start --no-vmlinux
|
||||
have_oprofile="yes"
|
||||
fi
|
||||
elif [ "$OPT_COLLECT_STRACE" = "yes" ]; then
|
||||
elif [ "$CMD_STRACE" -a "$OPT_COLLECT_STRACE" -a "$mysqld_pid" ]; then
|
||||
# Don't run oprofile and strace at the same time.
|
||||
$CMD_STRACE -T -s 0 -f -p $mysqld_pid > "${DEST}/$d-strace" 2>&1 &
|
||||
$CMD_STRACE -T -s 0 -f -p $mysqld_pid > "${DEST}/$d-strace" &
|
||||
local strace_pid=$!
|
||||
fi
|
||||
|
||||
# Grab a few general things first. Background all of these so we can start
|
||||
# them all up as quickly as possible.
|
||||
ps -eaf >> "$d/$p-ps" 2>&1 &
|
||||
sysctl -a >> "$d/$p-sysctl" 2>&1 &
|
||||
top -bn1 >> "$d/$p-top" 2>&1 &
|
||||
$CMD_VMSTAT 1 $OPT_INTERVAL >> "$d/$p-vmstat" 2>&1 &
|
||||
$CMD_VMSTAT $OPT_INTERVAL 2 >> "$d/$p-vmstat-overall" 2>&1 &
|
||||
$CMD_IOSTAT -dx 1 $OPT_INTERVAL >> "$d/$p-iostat" 2>&1 &
|
||||
$CMD_IOSTAT -dx $OPT_INTERVAL 2 >> "$d/$p-iostat-overall" 2>&1 &
|
||||
$CMD_MPSTAT -P ALL 1 $OPT_INTERVAL >> "$d/$p-mpstat" 2>&1 &
|
||||
$CMD_MPSTAT -P ALL $OPT_INTERVAL 1 >> "$d/$p-mpstat-overall" 2>&1 &
|
||||
lsof -nP -p $mysqld_pid -bw >> "$d/$p-lsof" 2>&1 &
|
||||
ps -eaf >> "$d/$p-ps" &
|
||||
top -bn1 >> "$d/$p-top" &
|
||||
|
||||
[ "$mysqld_pid" ] && _lsof $mysqld_pid >> "$d/$p-lsof" &
|
||||
|
||||
if [ "$CMD_SYSCTL" ]; then
|
||||
$CMD_SYSCTL -a >> "$d/$p-sysctl" &
|
||||
fi
|
||||
if [ "$CMD_VMSTAT" ]; then
|
||||
$CMD_VMSTAT 1 $OPT_INTERVAL >> "$d/$p-vmstat" &
|
||||
$CMD_VMSTAT $OPT_INTERVAL 2 >> "$d/$p-vmstat-overall" &
|
||||
fi
|
||||
if [ "$CMD_IOSTAT" ]; then
|
||||
$CMD_IOSTAT -dx 1 $OPT_INTERVAL >> "$d/$p-iostat" &
|
||||
$CMD_IOSTAT -dx $OPT_INTERVAL 2 >> "$d/$p-iostat-overall" &
|
||||
fi
|
||||
if [ "$CMD_MPSTAT" ]; then
|
||||
$CMD_MPSTAT -P ALL 1 $OPT_INTERVAL >> "$d/$p-mpstat" &
|
||||
$CMD_MPSTAT -P ALL $OPT_INTERVAL 1 >> "$d/$p-mpstat-overall" &
|
||||
fi
|
||||
|
||||
# Collect multiple snapshots of the status variables. We use
|
||||
# mysqladmin -c even though it is buggy and won't stop on its
|
||||
@@ -155,57 +162,83 @@ collect() {
|
||||
# get and keep a connection to the database; in troubled times
|
||||
# the database tends to exceed max_connections, so reconnecting
|
||||
# in the loop tends not to work very well.
|
||||
$CMD_MYSQLADMIN $EXT_ARGV ext -i1 -c$OPT_RUN_TIME >>"$d/$p-mysqladmin" 2>&1 &
|
||||
$CMD_MYSQLADMIN $EXT_ARGV ext -i1 -c$OPT_RUN_TIME >>"$d/$p-mysqladmin" &
|
||||
local mysqladmin_pid=$!
|
||||
|
||||
local have_lock_waits_table=0
|
||||
local have_lock_waits_table=""
|
||||
$CMD_MYSQL $EXT_ARGV -e "SHOW TABLES FROM INFORMATION_SCHEMA" \
|
||||
| grep -qi "INNODB_LOCK_WAITS"
|
||||
| grep -i "INNODB_LOCK_WAITS" >/dev/null 2>&1
|
||||
if [ $? -eq 0 ]; then
|
||||
have_lock_waits_table=1
|
||||
have_lock_waits_table="yes"
|
||||
fi
|
||||
|
||||
# This loop gathers data for the rest of the duration, and defines the time
|
||||
# of the whole job.
|
||||
echo "Loop start: $(date +'TS %s.%N %F %T')"
|
||||
log "Loop start: $(date +'TS %s.%N %F %T')"
|
||||
for loopno in $(_seq $OPT_RUN_TIME); do
|
||||
# We check the disk, but don't exit, because we need to stop jobs if we
|
||||
# need to exit.
|
||||
disk_space $d > $d/$p-disk-space
|
||||
check_disk_space \
|
||||
$d/$p-disk-space \
|
||||
"$OPT_DISK_BYTE_LIMIT" \
|
||||
"$OPT_DISK_PCT_LIMIT" \
|
||||
"$OPT_DISK_BYTES_FREE" \
|
||||
"$OPT_DISK_PCT_FREE" \
|
||||
|| break
|
||||
|
||||
# Synchronize ourselves onto the clock tick, so the sleeps are 1-second
|
||||
sleep $(date +%s.%N | awk '{print 1 - ($1 % 1)}')
|
||||
local ts="$(date +"TS %s.%N %F %T")"
|
||||
|
||||
# Collect the stuff for this cycle
|
||||
(cat /proc/diskstats 2>&1; echo $ts) >> "$d/$p-diskstats" &
|
||||
(cat /proc/stat 2>&1; echo $ts) >> "$d/$p-procstat" &
|
||||
(cat /proc/vmstat 2>&1; echo $ts) >> "$d/$p-procvmstat" &
|
||||
(cat /proc/meminfo 2>&1; echo $ts) >> "$d/$p-meminfo" &
|
||||
(cat /proc/slabinfo 2>&1; echo $ts) >> "$d/$p-slabinfo" &
|
||||
(cat /proc/interrupts 2>&1; echo $ts) >> "$d/$p-interrupts" &
|
||||
(df -h 2>&1; echo $ts) >> "$d/$p-df" &
|
||||
(netstat -antp 2>&1; echo $ts) >> "$d/$p-netstat" &
|
||||
(netstat -s 2>&1; echo $ts) >> "$d/$p-netstat_s" &
|
||||
# #####################################################################
|
||||
# Collect data for this cycle.
|
||||
# #####################################################################
|
||||
|
||||
($CMD_MYSQL $EXT_ARGV -e "SHOW FULL PROCESSLIST\G" 2>&1; echo $ts) \
|
||||
>> "$d/$p-processlist"
|
||||
if [ -d "/proc" ]; then
|
||||
if [ -f "/proc/diskstats" ]; then
|
||||
(echo $ts; cat /proc/diskstats) >> "$d/$p-diskstats" &
|
||||
fi
|
||||
if [ -f "/proc/stat" ]; then
|
||||
(echo $ts; cat /proc/stat) >> "$d/$p-procstat" &
|
||||
fi
|
||||
if [ -f "/proc/vmstat" ]; then
|
||||
(echo $ts; cat /proc/vmstat) >> "$d/$p-procvmstat" &
|
||||
fi
|
||||
if [ -f "/proc/meminfo" ]; then
|
||||
(echo $ts; cat /proc/meminfo) >> "$d/$p-meminfo" &
|
||||
fi
|
||||
if [ -f "/proc/slabinfo" ]; then
|
||||
(echo $ts; cat /proc/slabinfo) >> "$d/$p-slabinfo" &
|
||||
fi
|
||||
if [ -f "/proc/interrupts" ]; then
|
||||
(echo $ts; cat /proc/interrupts) >> "$d/$p-interrupts" &
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $have_lock_waits_table -eq 1 ]; then
|
||||
(lock_waits 2>&1; echo $ts) >>"$d/$p-lock-waits"
|
||||
(echo $ts; df -h) >> "$d/$p-df" &
|
||||
|
||||
(echo $ts; netstat -antp) >> "$d/$p-netstat" &
|
||||
(echo $ts; netstat -s) >> "$d/$p-netstat_s" &
|
||||
|
||||
(echo $ts; $CMD_MYSQL $EXT_ARGV -e "SHOW FULL PROCESSLIST\G") \
|
||||
>> "$d/$p-processlist" &
|
||||
|
||||
if [ "$have_lock_waits_table" ]; then
|
||||
(echo $ts; lock_waits) >>"$d/$p-lock-waits" &
|
||||
fi
|
||||
done
|
||||
echo "Loop end: $(date +'TS %s.%N %F %T')"
|
||||
log "Loop end: $(date +'TS %s.%N %F %T')"
|
||||
|
||||
if [ "$have_oprofile" = "yes" ]; then
|
||||
if [ "$have_oprofile" ]; then
|
||||
$CMD_OPCONTROL --stop
|
||||
$CMD_OPCONTROL --dump
|
||||
kill $(pidof oprofiled); # TODO: what if system doesn't have pidof?
|
||||
|
||||
local oprofiled_pid=$(_pidof oprofiled)
|
||||
if [ "$oprofiled_pid" ]; then
|
||||
kill $oprofiled_pid
|
||||
else
|
||||
warn "Cannot kill oprofiled because its PID cannot be determined"
|
||||
fi
|
||||
|
||||
$CMD_OPCONTROL --save=pt_collect_$p
|
||||
|
||||
# Attempt to generate a report; if this fails, then just tell the user
|
||||
@@ -220,39 +253,51 @@ collect() {
|
||||
"$mysqld_path" \
|
||||
> "$d/$p-opreport"
|
||||
else
|
||||
echo "oprofile data saved to pt_collect_$p; you should be able" \
|
||||
log "oprofile data saved to pt_collect_$p; you should be able" \
|
||||
"to get a report by running something like 'opreport" \
|
||||
"--demangle=smart --symbols --merge tgid session:pt_collect_$p" \
|
||||
"/path/to/mysqld'" \
|
||||
> "$d/$p-opreport"
|
||||
fi
|
||||
elif [ "$OPT_COLLECT_STRACE" = "yes" ]; then
|
||||
elif [ "$CMD_STRACE" -a "$OPT_COLLECT_STRACE" ]; then
|
||||
kill -s 2 $strace_pid
|
||||
sleep 1
|
||||
kill -s 15 $strace_pid
|
||||
# Sometimes strace leaves threads/processes in T status.
|
||||
kill -s 18 $mysqld_pid
|
||||
[ "$mysqld_pid" ] && kill -s 18 $mysqld_pid
|
||||
fi
|
||||
|
||||
$CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus2" 2>&1 &
|
||||
$CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status2" 2>&1 &
|
||||
open_tables >> "$d/$p-opentables2" 2>&1 &
|
||||
$CMD_MYSQL $EXT_ARGV -e "$innostat" >> "$d/$p-innodbstatus2" &
|
||||
$CMD_MYSQL $EXT_ARGV -e "$mutex" >> "$d/$p-mutex-status2" &
|
||||
open_tables >> "$d/$p-opentables2" &
|
||||
|
||||
# Kill backgrounded tasks.
|
||||
kill $mysqladmin_pid
|
||||
[ "$tail_error_log_pid" ] && kill $tail_error_log_pid
|
||||
[ "$tcpdump_pid" ] && kill $tcpdump_pid
|
||||
[ "$tcpdump_pid" ] && kill $tcpdump_pid
|
||||
|
||||
# Finally, record what system we collected this data from.
|
||||
hostname > "$d/$p-hostname"
|
||||
|
||||
# Remove "empty" files, i.e. ones that are truly empty or
|
||||
# just contain timestamp lines. When a command above fails,
|
||||
# it may leave an empty file.
|
||||
for file in "$d/$p-"*; do
|
||||
# If there's not at least 1 line that's not a TS,
|
||||
# then the file is empty.
|
||||
if [ -z "$(grep -v '^TS ' --max-count 1 "$file")" ]; then
|
||||
log "Removing empty file $file";
|
||||
rm "$file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
open_tables() {
|
||||
local open_tables=$($CMD_MYSQLADMIN $EXT_ARGV ext | grep "Open_tables" | awk '{print $4}')
|
||||
if [ -n "$open_tables" -a $open_tables -le 1000 ]; then
|
||||
$CMD_MYSQL $EXT_ARGV -e 'SHOW OPEN TABLES' 2>&1 &
|
||||
$CMD_MYSQL $EXT_ARGV -e 'SHOW OPEN TABLES' &
|
||||
else
|
||||
echo "Too many open tables: $open_tables"
|
||||
log "Too many open tables: $open_tables"
|
||||
fi
|
||||
}
|
||||
|
||||
|
@@ -30,8 +30,8 @@ set -u
|
||||
# file - File to write PID to.
|
||||
# pid - PID to write into file.
|
||||
make_pid_file() {
|
||||
local file=$1
|
||||
local pid=$2
|
||||
local file="$1"
|
||||
local pid="$2"
|
||||
|
||||
# Yes there's a race condition here, between checking if the file exists
|
||||
# and creating it, but it's not important enough to handle.
|
||||
@@ -39,7 +39,7 @@ make_pid_file() {
|
||||
if [ -f "$file" ]; then
|
||||
# PID file already exists. See if the pid it contains is still running.
|
||||
# If yes, then die. Else, the pid file is stale and we can reclaim it.
|
||||
local old_pid=$(cat $file)
|
||||
local old_pid=$(cat "$file")
|
||||
if [ -z "$old_pid" ]; then
|
||||
# PID file is empty, so be safe and die since we can't check a
|
||||
# non-existent pid.
|
||||
@@ -56,13 +56,16 @@ make_pid_file() {
|
||||
fi
|
||||
|
||||
# PID file doesn't exist, or it does but its pid is stale.
|
||||
echo "$pid" > $file
|
||||
echo "$pid" > "$file"
|
||||
if [ $? -ne 0 ]; then
|
||||
die "Cannot create or write PID file $file"
|
||||
fi
|
||||
}
|
||||
|
||||
remove_pid_file() {
|
||||
local file=$1
|
||||
local file="$1"
|
||||
if [ -f "$file" ]; then
|
||||
rm $file
|
||||
rm "$file"
|
||||
fi
|
||||
}
|
||||
|
||||
|
@@ -28,16 +28,16 @@ EXIT_STATUS=0
|
||||
|
||||
log() {
|
||||
TS=$(date +%F-%T | tr :- _);
|
||||
echo "$TS $1"
|
||||
echo "$TS $*"
|
||||
}
|
||||
|
||||
warn() {
|
||||
log "$1" >&2
|
||||
EXIT_STATUS=$((EXIT_STATUS | 1))
|
||||
log "$*" >&2
|
||||
EXIT_STATUS=1
|
||||
}
|
||||
|
||||
die() {
|
||||
warn "$1"
|
||||
warn "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This program is copyright 2011 Percona Inc.
|
||||
# This program is copyright 2011-2012 Percona Inc.
|
||||
# Feedback and improvements are welcome.
|
||||
#
|
||||
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
@@ -22,15 +22,39 @@
|
||||
# parse_options parses Perl POD options from Bash tools and creates
|
||||
# global variables for each option.
|
||||
|
||||
# XXX
|
||||
# GLOBAL $TMPDIR AND $TOOL MUST BE SET BEFORE USING THIS LIB!
|
||||
# XXX
|
||||
|
||||
# Parsing command line options with Bash is easy until we have to dealt
|
||||
# with values that have spaces, e.g. --option="hello world". This is
|
||||
# further complicated by command line vs. config file. From the command
|
||||
# line, <--option "hello world"> is put into $@ as "--option", "hello world",
|
||||
# i.e. 2 args. From a config file, <option=hello world> is either 2 args
|
||||
# split on the space, or 1 arg as a whole line. It needs to be 2 args
|
||||
# split on the = but this isn't possible; see the note before while read
|
||||
# in _parse_config_files(). Perl tool config files do not work when the
|
||||
# value is quoted, so we can't quote it either. And in any case, that
|
||||
# wouldn't work because then the value would include the literal quotes
|
||||
# because it's a line from a file, not a command line where Bash will
|
||||
# interpret the quotes and return a single value in the code. So...
|
||||
|
||||
# XXX
|
||||
# BE CAREFUL MAKING CHANGES TO THIS LIB AND MAKE SURE
|
||||
# t/lib/bash/parse_options.sh STILL PASSES!
|
||||
# XXX
|
||||
|
||||
set -u
|
||||
|
||||
# Global variables. These must be global because declare inside a
|
||||
# sub will be scoped locally.
|
||||
ARGV="" # Non-option args (probably input files)
|
||||
EXT_ARGV="" # Everything after -- (args for an external command)
|
||||
HAVE_EXT_ARGV="" # Got --, everything else is put into EXT_ARGV
|
||||
OPT_ERRS=0 # How many command line option errors
|
||||
OPT_VERSION="no" # If --version was specified
|
||||
OPT_HELP="no" # If --help was specified
|
||||
OPT_VERSION="" # If --version was specified
|
||||
OPT_HELP="" # If --help was specified
|
||||
PO_DIR="" # Directory with program option spec files
|
||||
|
||||
# Sub: usage
|
||||
# Print usage (--help) and list the program's options.
|
||||
@@ -41,44 +65,71 @@ OPT_HELP="no" # If --help was specified
|
||||
# Required Global Variables:
|
||||
# TIMDIR - Temp directory set by <set_TMPDIR()>.
|
||||
# TOOL - Tool's name.
|
||||
#
|
||||
# Optional Global Variables:
|
||||
# OPT_ERR - Command line option error message.
|
||||
usage() {
|
||||
local file=$1
|
||||
local file="$1"
|
||||
|
||||
local usage=$(grep '^Usage: ' $file)
|
||||
echo $usage >&2
|
||||
echo >&2
|
||||
echo "For more information, 'man $TOOL' or 'perldoc $file'." >&2
|
||||
local usage=$(grep '^Usage: ' "$file")
|
||||
echo $usage
|
||||
echo
|
||||
echo "For more information, 'man $TOOL' or 'perldoc $file'."
|
||||
}
|
||||
|
||||
usage_or_errors() {
|
||||
local file=$1
|
||||
local file="$1"
|
||||
|
||||
if [ "$OPT_VERSION" = "yes" ]; then
|
||||
local version=$(grep '^pt-[^ ]\+ [0-9]' $file)
|
||||
if [ "$OPT_VERSION" ]; then
|
||||
local version=$(grep '^pt-[^ ]\+ [0-9]' "$file")
|
||||
echo "$version"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$OPT_HELP" = "yes" ]; then
|
||||
if [ "$OPT_HELP" ]; then
|
||||
usage "$file"
|
||||
echo >&2
|
||||
echo "Command line options:" >&2
|
||||
echo >&2
|
||||
for opt in $(ls $TMPDIR/po/); do
|
||||
local desc=$(cat $TMPDIR/po/$opt | grep '^desc:' | sed -e 's/^desc://')
|
||||
echo "--$opt" >&2
|
||||
echo " $desc" >&2
|
||||
echo >&2
|
||||
echo
|
||||
echo "Command line options:"
|
||||
echo
|
||||
perl -e '
|
||||
use strict;
|
||||
use warnings FATAL => qw(all);
|
||||
my $lcol = 20; # Allow this much space for option names.
|
||||
my $rcol = 80 - $lcol; # The terminal is assumed to be 80 chars wide.
|
||||
my $name;
|
||||
while ( <> ) {
|
||||
my $line = $_;
|
||||
chomp $line;
|
||||
if ( $line =~ s/^long:/ --/ ) {
|
||||
$name = $line;
|
||||
}
|
||||
elsif ( $line =~ s/^desc:// ) {
|
||||
$line =~ s/ +$//mg;
|
||||
my @lines = grep { $_ }
|
||||
$line =~ m/(.{0,$rcol})(?:\s+|\Z)/g;
|
||||
if ( length($name) >= $lcol ) {
|
||||
print $name, "\n", (q{ } x $lcol);
|
||||
}
|
||||
else {
|
||||
printf "%-${lcol}s", $name;
|
||||
}
|
||||
print join("\n" . (q{ } x $lcol), @lines);
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
' "$PO_DIR"/*
|
||||
echo
|
||||
echo "Options and values after processing arguments:"
|
||||
echo
|
||||
for opt in $(ls "$PO_DIR"); do
|
||||
local varname="OPT_$(echo "$opt" | tr a-z- A-Z_)"
|
||||
local varvalue="${!varname}"
|
||||
printf -- " --%-30s %s" "$opt" "${varvalue:-(No value)}"
|
||||
echo
|
||||
done
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ $OPT_ERRS -gt 0 ]; then
|
||||
echo >&2
|
||||
usage $file
|
||||
echo
|
||||
usage "$file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -86,6 +137,12 @@ usage_or_errors() {
|
||||
return 0
|
||||
}
|
||||
|
||||
option_error() {
|
||||
local err="$1"
|
||||
OPT_ERRS=$(($OPT_ERRS + 1))
|
||||
echo "$err" >&2
|
||||
}
|
||||
|
||||
# Sub: parse_options
|
||||
# Parse Perl POD options from a program file.
|
||||
#
|
||||
@@ -100,9 +157,59 @@ usage_or_errors() {
|
||||
# option, removing the option's leading --, changing all - to _, and
|
||||
# prefixing with "OPT_". E.g. --foo-bar becomes OPT_FOO_BAR.
|
||||
parse_options() {
|
||||
local file=$1
|
||||
local file="$1"
|
||||
shift
|
||||
|
||||
# XXX
|
||||
# Reset all globals else t/lib/bash/parse_options.sh will fail.
|
||||
# XXX
|
||||
ARGV=""
|
||||
EXT_ARGV=""
|
||||
HAVE_EXT_ARGV=""
|
||||
OPT_ERRS=0
|
||||
OPT_VERSION=""
|
||||
OPT_HELP=""
|
||||
PO_DIR="$TMPDIR/po"
|
||||
|
||||
# Ready the directory for the program option (po) spec files.
|
||||
if [ ! -d "$PO_DIR" ]; then
|
||||
mkdir "$PO_DIR"
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Cannot mkdir $PO_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf "$PO_DIR"/*
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Cannot rm -rf $PO_DIR/*" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
_parse_pod "$file" # Parse POD into program option (po) spec files
|
||||
_eval_po # Eval po into existence with default values
|
||||
|
||||
# If the first option is --config FILES, then remove it and use
|
||||
# those files instead of the default config files.
|
||||
if [ $# -ge 2 ] && [ "$1" = "--config" ]; then
|
||||
shift # --config
|
||||
local user_config_files="$1"
|
||||
shift # that ^
|
||||
local IFS=","
|
||||
for user_config_file in $user_config_files; do
|
||||
_parse_config_files "$user_config_file"
|
||||
done
|
||||
else
|
||||
_parse_config_files "/etc/percona-toolkit/percona-toolkit.conf" "/etc/percona-toolkit/$TOOL.conf" "$HOME/.percona-toolkit.conf" "$HOME/.$TOOL.conf"
|
||||
fi
|
||||
|
||||
# Finally, parse the command line.
|
||||
_parse_command_line "$@"
|
||||
}
|
||||
|
||||
_parse_pod() {
|
||||
local file="$1"
|
||||
|
||||
# Parse the program options (po) from the POD. Each option has
|
||||
# a spec file like:
|
||||
# $ cat po/string-opt2
|
||||
@@ -111,51 +218,48 @@ parse_options() {
|
||||
# default=foo
|
||||
# That's the spec for --string-opt2. Each line is a key:value pair
|
||||
# from the option's POD line like "type: string; default: foo".
|
||||
mkdir $TMPDIR/po/ 2>/dev/null
|
||||
rm -rf $TMPDIR/po/*
|
||||
(
|
||||
export PO_DIR="$TMPDIR/po"
|
||||
cat $file | perl -ne '
|
||||
BEGIN { $/ = ""; }
|
||||
next unless $_ =~ m/^=head1 OPTIONS/;
|
||||
while ( defined(my $para = <>) ) {
|
||||
last if $para =~ m/^=head1/;
|
||||
cat "$file" | PO_DIR="$PO_DIR" perl -ne '
|
||||
BEGIN { $/ = ""; }
|
||||
next unless $_ =~ m/^=head1 OPTIONS/;
|
||||
while ( defined(my $para = <>) ) {
|
||||
last if $para =~ m/^=head1/;
|
||||
chomp;
|
||||
if ( $para =~ m/^=item --(\S+)/ ) {
|
||||
my $opt = $1;
|
||||
my $file = "$ENV{PO_DIR}/$opt";
|
||||
open my $opt_fh, ">", $file or die "Cannot open $file: $!";
|
||||
print $opt_fh "long:$opt\n";
|
||||
$para = <>;
|
||||
chomp;
|
||||
if ( $para =~ m/^=item --(\S+)/ ) {
|
||||
my $opt = $1;
|
||||
my $file = "$ENV{PO_DIR}/$opt";
|
||||
open my $opt_fh, ">", $file or die "Cannot open $file: $!";
|
||||
printf $opt_fh "long:$opt\n";
|
||||
if ( $para =~ m/^[a-z ]+:/ ) {
|
||||
map {
|
||||
chomp;
|
||||
my ($attrib, $val) = split(/: /, $_);
|
||||
print $opt_fh "$attrib:$val\n";
|
||||
} split(/; /, $para);
|
||||
$para = <>;
|
||||
chomp;
|
||||
if ( $para =~ m/^[a-z ]+:/ ) {
|
||||
map {
|
||||
chomp;
|
||||
my ($attrib, $val) = split(/: /, $_);
|
||||
printf $opt_fh "$attrib:$val\n";
|
||||
} split(/; /, $para);
|
||||
$para = <>;
|
||||
chomp;
|
||||
}
|
||||
my ($desc) = $para =~ m/^([^?.]+)/;
|
||||
printf $opt_fh "desc:$desc.\n";
|
||||
close $opt_fh;
|
||||
}
|
||||
my ($desc) = $para =~ m/^([^?.]+)/;
|
||||
print $opt_fh "desc:$desc.\n";
|
||||
close $opt_fh;
|
||||
}
|
||||
last;
|
||||
'
|
||||
)
|
||||
}
|
||||
last;
|
||||
'
|
||||
}
|
||||
|
||||
_eval_po() {
|
||||
# Evaluate the program options into existence as global variables
|
||||
# transformed like --my-op == $OPT_MY_OP. If an option has a default
|
||||
# value, it's assigned that value. Else, it's value is an empty string.
|
||||
for opt_spec in $(ls $TMPDIR/po/); do
|
||||
local IFS=":"
|
||||
for opt_spec in "$PO_DIR"/*; do
|
||||
local opt=""
|
||||
local default_val=""
|
||||
local neg=0
|
||||
while read line; do
|
||||
local key=`echo $line | cut -d ':' -f 1`
|
||||
local val=`echo $line | cut -d ':' -f 2`
|
||||
local size=0
|
||||
while read key val; do
|
||||
case "$key" in
|
||||
long)
|
||||
opt=$(echo $val | sed 's/-/_/g' | tr [:lower:] [:upper:])
|
||||
@@ -166,6 +270,7 @@ parse_options() {
|
||||
"short form")
|
||||
;;
|
||||
type)
|
||||
[ "$val" = "size" ] && size=1
|
||||
;;
|
||||
desc)
|
||||
;;
|
||||
@@ -175,13 +280,13 @@ parse_options() {
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Invalid attribute in $TMPDIR/po/$opt_spec: $line" >&2
|
||||
echo "Invalid attribute in $opt_spec: $line" >&2
|
||||
exit 1
|
||||
esac
|
||||
done < $TMPDIR/po/$opt_spec
|
||||
done < "$opt_spec"
|
||||
|
||||
if [ -z "$opt" ]; then
|
||||
echo "No long attribute in option spec $TMPDIR/po/$opt_spec" >&2
|
||||
echo "No long attribute in option spec $opt_spec" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -192,9 +297,58 @@ parse_options() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Convert sizes.
|
||||
if [ $size -eq 1 -a -n "$default_val" ]; then
|
||||
default_val=$(size_to_bytes $default_val)
|
||||
fi
|
||||
|
||||
# Eval the option into existence as a global variable.
|
||||
eval "OPT_${opt}"="$default_val"
|
||||
done
|
||||
}
|
||||
|
||||
_parse_config_files() {
|
||||
|
||||
for config_file in "$@"; do
|
||||
# Next config file if this one doesn't exist.
|
||||
test -f "$config_file" || continue
|
||||
|
||||
# We must use while read because values can contain spaces.
|
||||
# Else, if we for $(grep ...) then a line like "op=hello world"
|
||||
# will return 2 values: "op=hello" and "world". If we quote
|
||||
# the command like for "$(grep ...)" then the entire config
|
||||
# file is returned as 1 value like "opt=hello world\nop2=42".
|
||||
while read config_opt; do
|
||||
|
||||
# Skip the line if it begins with a # or is blank.
|
||||
echo "$config_opt" | grep '^[ ]*[^#]' >/dev/null 2>&1 || continue
|
||||
|
||||
# Strip leading and trailing spaces, and spaces around the first =,
|
||||
# and end-of-line # comments.
|
||||
config_opt="$(echo "$config_opt" | sed -e 's/^ *//g' -e 's/ *$//g' -e 's/[ ]*=[ ]*/=/' -e 's/[ ]*#.*$//')"
|
||||
|
||||
# Skip blank lines.
|
||||
[ "$config_opt" = "" ] && continue
|
||||
|
||||
# Options in a config file are not prefixed with --,
|
||||
# but command line options are, so one or the other has
|
||||
# to add or remove the -- prefix. We add it for config
|
||||
# files rather than trying to strip it from command line
|
||||
# options because it's a simpler operation here.
|
||||
if ! [ "$HAVE_EXT_ARGV" ]; then
|
||||
config_opt="--$config_opt"
|
||||
fi
|
||||
|
||||
_parse_command_line "$config_opt"
|
||||
|
||||
done < "$config_file"
|
||||
|
||||
HAVE_EXT_ARGV="" # reset for each file
|
||||
|
||||
done
|
||||
}
|
||||
|
||||
_parse_command_line() {
|
||||
# Parse the command line options. Anything after -- is put into
|
||||
# EXT_ARGV. Options must begin with one or two hyphens (--help or -h),
|
||||
# else the item is put into ARGV (it's probably a filename, directory,
|
||||
@@ -205,82 +359,135 @@ parse_options() {
|
||||
# a default value 100, then $OPT_FOO=100 already, but if --foo=500 is
|
||||
# specified on the command line, then we re-eval $OPT_FOO=500 to update
|
||||
# $OPT_FOO.
|
||||
for opt; do
|
||||
if [ $# -eq 0 ]; then
|
||||
break # no more opts
|
||||
local opt=""
|
||||
local val=""
|
||||
local next_opt_is_val=""
|
||||
local opt_is_ok=""
|
||||
local opt_is_negated=""
|
||||
local real_opt=""
|
||||
local required_arg=""
|
||||
local spec=""
|
||||
|
||||
for opt in "$@"; do
|
||||
if [ "$opt" = "--" -o "$opt" = "----" ]; then
|
||||
HAVE_EXT_ARGV=1
|
||||
continue
|
||||
fi
|
||||
opt=$1
|
||||
if [ "$opt" = "--" ]; then
|
||||
shift
|
||||
EXT_ARGV="$@"
|
||||
break
|
||||
fi
|
||||
shift
|
||||
if [ $(expr "$opt" : "-") -eq 0 ]; then
|
||||
# Option does not begin with a hyphen (-), so treat it as
|
||||
# a filename, directory, etc.
|
||||
if [ -z "$ARGV" ]; then
|
||||
ARGV="$opt"
|
||||
if [ "$HAVE_EXT_ARGV" ]; then
|
||||
# Previous line was -- so this and subsequent options are
|
||||
# really external argvs.
|
||||
if [ "$EXT_ARGV" ]; then
|
||||
EXT_ARGV="$EXT_ARGV $opt"
|
||||
else
|
||||
ARGV="$ARGV $opt"
|
||||
EXT_ARGV="$opt"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
# Save real opt from cmd line for error messages.
|
||||
local real_opt="$opt"
|
||||
|
||||
# Strip leading -- or --no- from option.
|
||||
if $(echo $opt | grep -q '^--no-'); then
|
||||
neg=1
|
||||
opt=$(echo $opt | sed 's/^--no-//')
|
||||
else
|
||||
neg=0
|
||||
opt=$(echo $opt | sed 's/^-*//')
|
||||
fi
|
||||
|
||||
# Find the option's spec file.
|
||||
if [ -f "$TMPDIR/po/$opt" ]; then
|
||||
spec="$TMPDIR/po/$opt"
|
||||
else
|
||||
spec=$(grep "^short form:-$opt\$" $TMPDIR/po/* | cut -d ':' -f 1)
|
||||
if [ -z "$spec" ]; then
|
||||
OPT_ERRS=$(($OPT_ERRS + 1))
|
||||
echo "Unknown option: $real_opt" >&2
|
||||
if [ "$next_opt_is_val" ]; then
|
||||
next_opt_is_val=""
|
||||
if [ $# -eq 0 ] || [ $(expr "$opt" : "-") -eq 1 ]; then
|
||||
option_error "$real_opt requires a $required_arg argument"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get the value specified for the option, if any. If the opt's spec
|
||||
# says it has a type, then it requires a value and that value should
|
||||
# be the next item ($1). Else, typeless options (like --version) are
|
||||
# either "yes" if specified, else "no" if negatable and --no-opt.
|
||||
required_arg=$(cat $spec | grep '^type:' | cut -d':' -f2)
|
||||
if [ -n "$required_arg" ]; then
|
||||
if [ $# -eq 0 ]; then
|
||||
OPT_ERRS=$(($OPT_ERRS + 1))
|
||||
echo "$real_opt requires a $required_arg argument" >&2
|
||||
continue
|
||||
else
|
||||
val="$1"
|
||||
shift
|
||||
fi
|
||||
val="$opt"
|
||||
opt_is_ok=1
|
||||
else
|
||||
if [ $neg -eq 0 ]; then
|
||||
val="yes"
|
||||
# If option does not begin with a hyphen (-), it's a filename, etc.
|
||||
if [ $(expr "$opt" : "-") -eq 0 ]; then
|
||||
if [ -z "$ARGV" ]; then
|
||||
ARGV="$opt"
|
||||
else
|
||||
ARGV="$ARGV $opt"
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
# Save real opt from cmd line for error messages.
|
||||
real_opt="$opt"
|
||||
|
||||
# Strip leading -- or --no- from option.
|
||||
if $(echo $opt | grep '^--no-' >/dev/null); then
|
||||
opt_is_negated=1
|
||||
opt=$(echo $opt | sed 's/^--no-//')
|
||||
else
|
||||
val="no"
|
||||
opt_is_negated=""
|
||||
opt=$(echo $opt | sed 's/^-*//')
|
||||
fi
|
||||
|
||||
# Split opt=val pair.
|
||||
if $(echo $opt | grep '^[a-z-][a-z-]*=' >/dev/null 2>&1); then
|
||||
val="$(echo $opt | awk -F= '{print $2}')"
|
||||
opt="$(echo $opt | awk -F= '{print $1}')"
|
||||
fi
|
||||
|
||||
# Find the option's spec file.
|
||||
if [ -f "$TMPDIR/po/$opt" ]; then
|
||||
spec="$TMPDIR/po/$opt"
|
||||
else
|
||||
spec=$(grep "^short form:-$opt\$" "$TMPDIR"/po/* | cut -d ':' -f 1)
|
||||
if [ -z "$spec" ]; then
|
||||
option_error "Unknown option: $real_opt"
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get the value specified for the option, if any. If the opt's spec
|
||||
# says it has a type, then it requires a value and that value should
|
||||
# be the next item ($1). Else, typeless options (like --version) are
|
||||
# either "yes" if specified, else "no" if negatable and --no-opt.
|
||||
required_arg=$(cat "$spec" | awk -F: '/^type:/{print $2}')
|
||||
if [ "$required_arg" ]; then
|
||||
# Option takes a value.
|
||||
if [ "$val" ]; then
|
||||
opt_is_ok=1
|
||||
else
|
||||
next_opt_is_val=1
|
||||
fi
|
||||
else
|
||||
# Option does not take a value.
|
||||
if [ "$val" ]; then
|
||||
option_error "Option $real_opt does not take a value"
|
||||
continue
|
||||
fi
|
||||
if [ "$opt_is_negated" ]; then
|
||||
val=""
|
||||
else
|
||||
val="yes"
|
||||
fi
|
||||
opt_is_ok=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get and transform the opt's long form. E.g.: -q == --quiet == QUIET.
|
||||
opt=$(cat $spec | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:])
|
||||
if [ "$opt_is_ok" ]; then
|
||||
# Get and transform the opt's long form. E.g.: -q == --quiet == QUIET.
|
||||
opt=$(cat "$spec" | grep '^long:' | cut -d':' -f2 | sed 's/-/_/g' | tr [:lower:] [:upper:])
|
||||
|
||||
# Re-eval the option to update its global variable value.
|
||||
eval "OPT_$opt"="$val"
|
||||
# Convert sizes.
|
||||
if grep "^type:size" "$spec" >/dev/null; then
|
||||
val=$(size_to_bytes $val)
|
||||
fi
|
||||
|
||||
# Re-eval the option to update its global variable value.
|
||||
eval "OPT_$opt"="'$val'"
|
||||
|
||||
opt=""
|
||||
val=""
|
||||
next_opt_is_val=""
|
||||
opt_is_ok=""
|
||||
opt_is_negated=""
|
||||
real_opt=""
|
||||
required_arg=""
|
||||
spec=""
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
size_to_bytes() {
|
||||
local size="$1"
|
||||
echo $size | perl -ne '%f=(B=>1, K=>1_024, M=>1_048_576, G=>1_073_741_824, T=>1_099_511_627_776); m/^(\d+)([kMGT])?/i; print $1 * $f{uc($2 || "B")};'
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
# End parse_options package
|
||||
# ###########################################################################
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# This program is copyright 2011 Percona Inc.
|
||||
# This program is copyright 2011-2012 Percona Inc.
|
||||
# Feedback and improvements are welcome.
|
||||
#
|
||||
# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
|
||||
@@ -24,55 +24,62 @@
|
||||
set -u
|
||||
|
||||
disk_space() {
|
||||
local filesystem=${1:-"$PWD"}
|
||||
local filesystem="${1:-$PWD}"
|
||||
# Filesystem 1024-blocks Used Available Capacity Mounted on
|
||||
# /dev/disk0s2 118153176 94409664 23487512 81% /
|
||||
df -P -k $filesystem
|
||||
df -P -k "$filesystem"
|
||||
}
|
||||
|
||||
# Sub: check_disk_space
|
||||
# Check if there is or will be enough disk space. Input is a file
|
||||
# with output from <disk_space()>, i.e. `df -P -k`. The df output
|
||||
# must use 1k blocks, but the mb arg from the user is in MB.
|
||||
# must use 1k blocks, which should be POSIX standard.
|
||||
#
|
||||
# Arguments:
|
||||
# file - File with output from <disk_space()>.
|
||||
# mb - Minimum MB free.
|
||||
# pc - Minimum percent free.
|
||||
# mb_margin - Add this many MB to the real MB used.
|
||||
# file - File with output from <disk_space()>.
|
||||
# min_free_bytes - Minimum free bytes.
|
||||
# min_free_pct - Minimum free percentage.
|
||||
# bytes_margin - Add this many bytes to the real bytes used.
|
||||
#
|
||||
# Returns:
|
||||
# 0 if there is/will be enough disk space, else 1.
|
||||
check_disk_space() {
|
||||
local file=$1
|
||||
local mb=${2:-"0"}
|
||||
local pc=${3:-"0"}
|
||||
local mb_margin=${4:-"0"}
|
||||
local file="$1"
|
||||
local min_free_bytes="${2:-0}"
|
||||
local min_free_pct="${3:-0}"
|
||||
local bytes_margin="${4:-0}"
|
||||
|
||||
# Convert MB to KB because the df output should be in 1k blocks.
|
||||
local kb=$(($mb * 1024))
|
||||
local kb_margin=$(($mb_margin * 1024))
|
||||
# Real/actual bytes used and bytes free.
|
||||
local used_bytes=$(cat "$file" | awk '/^\//{print $3 * 1024}');
|
||||
local free_bytes=$(cat "$file" | awk '/^\//{print $4 * 1024}');
|
||||
local pct_used=$(cat "$file" | awk '/^\//{print $5}' | sed -e 's/%//g');
|
||||
local pct_free=$((100 - $pct_used))
|
||||
|
||||
local kb_used=$(cat $file | awk '/^\//{print $3}');
|
||||
local kb_free=$(cat $file | awk '/^\//{print $4}');
|
||||
local pc_used=$(cat $file | awk '/^\//{print $5}' | sed -e 's/%//g');
|
||||
# Report the real values to the user.
|
||||
local real_free_bytes=$free_bytes
|
||||
local real_pct_free=$pct_free
|
||||
|
||||
if [ "$kb_margin" -gt "0" ]; then
|
||||
local kb_total=$(($kb_used + $kb_free))
|
||||
# If there's a margin, we need to adjust the real values.
|
||||
if [ $bytes_margin -gt 0 ]; then
|
||||
used_bytes=$(($used_bytes + $bytes_margin))
|
||||
free_bytes=$(($free_bytes - $bytes_margin))
|
||||
pct_used=$(awk "BEGIN { printf(\"%d\", ($used_bytes/($used_bytes + $free_bytes)) * 100) }")
|
||||
|
||||
kb_used=$(($kb_used + $kb_margin))
|
||||
kb_free=$(($kb_free - $kb_margin))
|
||||
pc_used=$(awk "BEGIN { printf(\"%d\", $kb_used/$kb_total * 100) }")
|
||||
pct_free=$((100 - $pct_used))
|
||||
fi
|
||||
|
||||
local pc_free=$((100 - $pc_used))
|
||||
if [ $free_bytes -lt $min_free_bytes -o $pct_free -lt $min_free_pct ]; then
|
||||
warn "Not enough free disk space:
|
||||
Limit: ${min_free_pct}% free, ${min_free_bytes} bytes free
|
||||
Actual: ${real_pct_free}% free, ${real_free_bytes} bytes free (- $bytes_margin bytes margin)
|
||||
"
|
||||
# Print the df that we used.
|
||||
cat "$file" >&2
|
||||
|
||||
if [ "$kb_free" -le "$kb" -o "$pc_free" -le "$pc" ]; then
|
||||
warn "Not enough free disk space: ${pc_free}% free, ${kb_free} KB free; wanted more than ${pc}% free or ${kb} KB free"
|
||||
return 1
|
||||
return 1 # not enough disk space
|
||||
fi
|
||||
|
||||
return 0
|
||||
return 0 # disk space is OK
|
||||
}
|
||||
|
||||
# ###########################################################################
|
||||
|
@@ -35,15 +35,15 @@ TMPDIR=""
|
||||
# Set Global Variables:
|
||||
# TMPDIR - Absolute path of secure temp directory.
|
||||
mk_tmpdir() {
|
||||
local dir=${1:-""}
|
||||
local dir="${1:-""}"
|
||||
|
||||
if [ -n "$dir" ]; then
|
||||
if [ ! -d "$dir" ]; then
|
||||
mkdir $dir || die "Cannot make tmpdir $dir"
|
||||
mkdir "$dir" || die "Cannot make tmpdir $dir"
|
||||
fi
|
||||
TMPDIR="$dir"
|
||||
else
|
||||
local tool=`basename $0`
|
||||
local tool="${0##*/}"
|
||||
local pid="$$"
|
||||
TMPDIR=`mktemp -d /tmp/${tool}.${pid}.XXXXX` \
|
||||
|| die "Cannot make secure tmpdir"
|
||||
@@ -60,7 +60,7 @@ mk_tmpdir() {
|
||||
# TMPDIR - Set to "".
|
||||
rm_tmpdir() {
|
||||
if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
|
||||
rm -rf $TMPDIR
|
||||
rm -rf "$TMPDIR"
|
||||
fi
|
||||
TMPDIR=""
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@ use PerconaTest;
|
||||
my ($tool) = $PROGRAM_NAME =~ m/([\w-]+)\.t$/;
|
||||
push @ARGV, "$trunk/t/lib/bash/*.sh" unless @ARGV;
|
||||
|
||||
$ENV{BIN_DIR} = "$trunk/bin";
|
||||
$ENV{LIB_DIR} = "$trunk/lib/bash";
|
||||
$ENV{T_LIB_DIR} = "$trunk/t/lib";
|
||||
|
||||
|
15
t/lib/bash/alt_cmds.sh
Normal file
15
t/lib/bash/alt_cmds.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TESTS=1
|
||||
|
||||
source "$LIB_DIR/alt_cmds.sh"
|
||||
|
||||
_seq 5 > $TEST_TMPDIR/out
|
||||
no_diff \
|
||||
$TEST_TMPDIR/out \
|
||||
$T_LIB_DIR/samples/bash/seq1.txt \
|
||||
"_seq 5"
|
||||
|
||||
# ###########################################################################
|
||||
# Done
|
||||
# ###########################################################################
|
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TESTS=18
|
||||
TESTS=20
|
||||
|
||||
TMPFILE="$TEST_TMPDIR/parse-opts-output"
|
||||
TMPDIR="$TEST_TMPDIR"
|
||||
PATH="$PATH:$PERCONA_TOOLKIT_SANDBOX/bin"
|
||||
TOOL="pt-stalk"
|
||||
|
||||
mkdir "$TMPDIR/collect" 2>/dev/null
|
||||
|
||||
@@ -14,7 +15,7 @@ source "$LIB_DIR/safeguards.sh"
|
||||
source "$LIB_DIR/alt_cmds.sh"
|
||||
source "$LIB_DIR/collect.sh"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po002.sh" --run-time 1 -- --defaults-file=/tmp/12345/my.sandbox.cnf
|
||||
parse_options "$BIN_DIR/pt-stalk" --run-time 1 -- --defaults-file=/tmp/12345/my.sandbox.cnf
|
||||
|
||||
# Prefix (with path) for the collect files.
|
||||
local p="$TMPDIR/collect/2011_12_05"
|
||||
@@ -23,12 +24,23 @@ local p="$TMPDIR/collect/2011_12_05"
|
||||
collect "$TMPDIR/collect" "2011_12_05" > $p-output 2>&1
|
||||
|
||||
# Even if this system doesn't have all the cmds, collect should still
|
||||
# create all the default files.
|
||||
# have created some files for cmds that (hopefully) all systems have.
|
||||
ls -1 $TMPDIR/collect | sort > $TMPDIR/collect-files
|
||||
no_diff \
|
||||
$TMPDIR/collect-files \
|
||||
$T_LIB_DIR/samples/bash/collect001.txt \
|
||||
"Default collect files"
|
||||
|
||||
# If this system has /proc, then some files should be collected.
|
||||
# Else, those files should not exist.
|
||||
if [ -f /proc/diskstats ]; then
|
||||
cmd_ok \
|
||||
"grep -q '[0-9]' $TMPDIR/collect/2011_12_05-diskstats" \
|
||||
"/proc/diskstats"
|
||||
else
|
||||
test -f $TMPDIR/collect/2011_12_05-diskstats
|
||||
is "$?" "1" "No /proc/diskstats"
|
||||
fi
|
||||
|
||||
cmd_ok \
|
||||
"grep -q '\-hostname\$' $TMPDIR/collect-files" \
|
||||
"Collected hostname"
|
||||
|
||||
cmd_ok \
|
||||
"grep -q 'Avail' $p-df" \
|
||||
@@ -96,11 +108,25 @@ cmd_ok \
|
||||
local iters=$(cat $p-df | grep -c '^TS ')
|
||||
is "$iters" "1" "1 iteration/1s run time"
|
||||
|
||||
empty_files=0
|
||||
for file in $p-*; do
|
||||
if ! [ -s $file ]; then
|
||||
empty_files=1
|
||||
break
|
||||
fi
|
||||
if [ -z "$(grep -v '^TS ' --max-count 1 $file)" ]; then
|
||||
empty_files=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
is "$empty_files" "0" "No empty files"
|
||||
|
||||
# ###########################################################################
|
||||
# Try longer run time.
|
||||
# ###########################################################################
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po002.sh" --run-time 2 -- --defaults-file=/tmp/12345/my.sandbox.cnf
|
||||
parse_options "$BIN_DIR/pt-stalk" --run-time 2 -- --defaults-file=/tmp/12345/my.sandbox.cnf
|
||||
|
||||
rm $TMPDIR/collect/*
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TESTS=7
|
||||
TESTS=9
|
||||
|
||||
TMPDIR="$TEST_TMPDIR"
|
||||
local file="$TMPDIR/pid-file"
|
||||
@@ -60,6 +60,22 @@ is \
|
||||
rm $file
|
||||
rm $TMPDIR/output
|
||||
|
||||
# ###########################################################################
|
||||
# Die if pid file can't be created.
|
||||
# ###########################################################################
|
||||
(
|
||||
make_pid_file "/root/pid" $$ >$TMPDIR/output 2>&1
|
||||
)
|
||||
|
||||
is \
|
||||
"$?" \
|
||||
"1" \
|
||||
"Exit 1 if PID file can't be created"
|
||||
|
||||
cmd_ok \
|
||||
"grep -q 'Cannot create or write PID file /root/pid' $TMPDIR/output" \
|
||||
"Error that PID file can't be created"
|
||||
|
||||
# ###########################################################################
|
||||
# Done.
|
||||
# ###########################################################################
|
||||
|
39
t/lib/bash/log_warn_die.sh
Normal file
39
t/lib/bash/log_warn_die.sh
Normal file
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TESTS=6
|
||||
|
||||
source "$LIB_DIR/log_warn_die.sh"
|
||||
|
||||
log "Hello world!" > $TEST_TMPDIR/log
|
||||
cmd_ok \
|
||||
"grep -q 'Hello world!' $TEST_TMPDIR/log" \
|
||||
"log msg"
|
||||
|
||||
log "Hello" "world!" > $TEST_TMPDIR/log
|
||||
cmd_ok \
|
||||
"grep -q 'Hello world!' $TEST_TMPDIR/log" \
|
||||
"log msg msg"
|
||||
|
||||
is \
|
||||
"$EXIT_STATUS" \
|
||||
"0" \
|
||||
"Exit status 0"
|
||||
|
||||
warn "Hello world!" 2> $TEST_TMPDIR/log
|
||||
cmd_ok \
|
||||
"grep -q 'Hello world!' $TEST_TMPDIR/log" \
|
||||
"warn msg"
|
||||
|
||||
warn "Hello" "world!" 2> $TEST_TMPDIR/log
|
||||
cmd_ok \
|
||||
"grep -q 'Hello world!' $TEST_TMPDIR/log" \
|
||||
"warn msg msg"
|
||||
|
||||
is \
|
||||
"$EXIT_STATUS" \
|
||||
"1" \
|
||||
"Exit status 1"
|
||||
|
||||
# ###########################################################################
|
||||
# Done
|
||||
# ###########################################################################
|
@@ -1,8 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
TESTS=26
|
||||
TESTS=78
|
||||
|
||||
TMPFILE="$TEST_TMPDIR/parse-opts-output"
|
||||
TOOL="pt-stalk"
|
||||
TMPDIR="$TEST_TMPDIR"
|
||||
|
||||
source "$LIB_DIR/log_warn_die.sh"
|
||||
source "$LIB_DIR/parse_options.sh"
|
||||
@@ -11,16 +13,14 @@ source "$LIB_DIR/parse_options.sh"
|
||||
# Parse options from POD using all default values.
|
||||
# ############################################################################
|
||||
|
||||
TOOL="pt-stalk"
|
||||
TMPDIR="$TEST_TMPDIR"
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" "" 2>$TMPFILE
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" 2>$TMPFILE
|
||||
|
||||
is "`cat $TMPFILE`" "" "No warnings or errors"
|
||||
|
||||
is "$OPT_STRING_OPT" "" "Default string option"
|
||||
is "$OPT_STRING_OPT2" "foo" "Default string option with default"
|
||||
is "$OPT_TYPELESS_OPTION" "" "Default typeless option"
|
||||
is "$OPT_NOPTION" "yes" "Defailt neg option"
|
||||
is "$OPT_NOPTION" "yes" "Default neg option"
|
||||
is "$OPT_INT_OPT" "" "Default int option"
|
||||
is "$OPT_INT_OPT2" "42" "Default int option with default"
|
||||
is "$OPT_VERSION" "" "--version"
|
||||
@@ -38,6 +38,20 @@ is "$OPT_NOPTION" "yes" "Default neg option (spec)"
|
||||
is "$OPT_INT_OPT" "50" "Specified int option (spec)"
|
||||
is "$OPT_INT_OPT2" "42" "Default int option with default (spec)"
|
||||
is "$OPT_VERSION" "" "--version (spec)"
|
||||
is "$ARGV" "" "ARGV"
|
||||
is "$EXT_ARGV" "" "External ARGV"
|
||||
|
||||
# ############################################################################
|
||||
# --option=value should work like --option value.
|
||||
# ############################################################################
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --int-opt=42
|
||||
|
||||
is "$OPT_INT_OPT" "42" "Specified int option (--option=value)"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --string-opt="hello world"
|
||||
|
||||
is "$OPT_STRING_OPT" "hello world" "Specified int option (--option=\"value\")"
|
||||
|
||||
# ############################################################################
|
||||
# Negate an option like --no-option.
|
||||
@@ -46,9 +60,9 @@ is "$OPT_VERSION" "" "--version (spec)"
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --no-noption
|
||||
|
||||
is "$OPT_STRING_OPT" "" "Default string option (neg)"
|
||||
is "$OPT_STRING_OPT2" "foo" "Default string option with default (net)"
|
||||
is "$OPT_STRING_OPT2" "foo" "Default string option with default (neg)"
|
||||
is "$OPT_TYPELESS_OPTION" "" "Default typeless option (neg)"
|
||||
is "$OPT_NOPTION" "no" "Negated option (neg)"
|
||||
is "$OPT_NOPTION" "" "Negated option (neg)"
|
||||
is "$OPT_INT_OPT" "" "Default int option (neg)"
|
||||
is "$OPT_INT_OPT2" "42" "Default int option with default (neg)"
|
||||
is "$OPT_VERSION" "" "--version (neg)"
|
||||
@@ -60,6 +74,16 @@ is "$OPT_VERSION" "" "--version (neg)"
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" -v
|
||||
is "$OPT_VERSION" "yes" "Short form"
|
||||
|
||||
# ############################################################################
|
||||
# Command line options plus externals args.
|
||||
# ############################################################################
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --no-noption -- --foo
|
||||
|
||||
is "$OPT_NOPTION" "" "Negated option (--)"
|
||||
is "$ARGV" "" "ARGV (--)"
|
||||
is "$EXT_ARGV" "--foo" "External ARGV (--)"
|
||||
|
||||
# ############################################################################
|
||||
# An unknown option should produce an error.
|
||||
# ############################################################################
|
||||
@@ -77,11 +101,129 @@ is "$err" "1" "Non-zero exit on unknown option"
|
||||
# ###########################################################################
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --help
|
||||
usage_or_errors "$T_LIB_DIR/samples/bash/po001.sh" >$TMPFILE 2>&1
|
||||
no_diff \
|
||||
"$TMPFILE" \
|
||||
"$T_LIB_DIR/samples/bash/help001.txt" \
|
||||
cmd_ok \
|
||||
"grep -q \"For more information, 'man pt-stalk' or 'perldoc\" $TMPFILE" \
|
||||
"--help"
|
||||
|
||||
cmd_ok \
|
||||
"grep -q ' --string-opt2[ ]*String option with a default.' $TMPFILE" \
|
||||
"Command line options"
|
||||
|
||||
cmd_ok \
|
||||
"grep -q '\-\-string-opt[ ]*(No value)' $TMPFILE" \
|
||||
"Options and values after processing arguments"
|
||||
|
||||
# Don't interpolate.
|
||||
parse_options "$T_LIB_DIR/samples/bash/po003.sh" --help
|
||||
usage_or_errors "$T_LIB_DIR/samples/bash/po003.sh" >$TMPFILE 2>&1
|
||||
|
||||
cmd_ok \
|
||||
"grep -q 'Exit if the disk is less than this %full.' $TMPFILE" \
|
||||
"Don't interpolate --help descriptions"
|
||||
|
||||
# ###########################################################################
|
||||
# Config files.
|
||||
# ###########################################################################
|
||||
TOOL="pt-test"
|
||||
cp "$T_LIB_DIR/samples/bash/config001.conf" "$HOME/.$TOOL.conf"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh"
|
||||
|
||||
is "$OPT_STRING_OPT" "abc" "Default string option (conf)"
|
||||
is "$OPT_STRING_OPT2" "foo" "Default string option with default (conf)"
|
||||
is "$OPT_TYPELESS_OPTION" "yes" "Default typeless option (conf)"
|
||||
is "$OPT_NOPTION" "yes" "Default neg option (conf)"
|
||||
is "$OPT_INT_OPT" "" "Default int option (conf)"
|
||||
is "$OPT_INT_OPT2" "42" "Default int option with default (conf)"
|
||||
is "$OPT_VERSION" "" "--version (conf)"
|
||||
is "$ARGV" "" "ARGV (conf)"
|
||||
is "$EXT_ARGV" "--host=127.1 --user=daniel" "External ARGV (conf)"
|
||||
|
||||
# Command line should override config file.
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --string-opt zzz
|
||||
|
||||
is "$OPT_STRING_OPT" "zzz" "Command line overrides config file"
|
||||
|
||||
# User-specified --config
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --config "$T_LIB_DIR/samples/bash/config003.conf" --string-opt bar
|
||||
|
||||
is "$OPT_STRING_OPT" "bar" "--config string option"
|
||||
is "$OPT_STRING_OPT2" "foo" "--config string option2"
|
||||
is "$OPT_TYPELESS_OPTION" "" "--config typeless option"
|
||||
is "$OPT_NOPTION" "yes" "--config negatable option"
|
||||
is "$OPT_INT_OPT" "123" "--config int option"
|
||||
is "$OPT_INT_OPT2" "42" "--config int option2"
|
||||
is "$OPT_VERSION" "" "--config version option"
|
||||
is "$ARGV" "" "--config ARGV"
|
||||
is "$EXT_ARGV" "" "--config External ARGV"
|
||||
|
||||
# Multiple --config files, last should take precedence.
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --config $T_LIB_DIR/samples/bash/config001.conf,$T_LIB_DIR/samples/bash/config002.conf
|
||||
|
||||
is "$OPT_STRING_OPT" "hello world" "Two --config string option"
|
||||
is "$OPT_TYPELESS_OPTION" "yes" "Two --config typeless option"
|
||||
is "$OPT_INT_OPT" "100" "Two --config int option"
|
||||
is "$ARGV" "" "Two --config ARGV"
|
||||
is "$EXT_ARGV" "--host=127.1 --user=daniel" "Two--config External ARGV"
|
||||
|
||||
# Spaces before and after the option[=value] lines.
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --config $T_LIB_DIR/samples/bash/config004.conf
|
||||
|
||||
is "$OPT_STRING_OPT" "foo" "Default string option (spacey)"
|
||||
is "$OPT_TYPELESS_OPTION" "yes" "Default typeless option (spacey)"
|
||||
is "$OPT_INT_OPT" "123" "Default int option (spacey)"
|
||||
is "$ARGV" "" "ARGV (spacey)"
|
||||
is "$EXT_ARGV" "" "External ARGV (spacey)"
|
||||
|
||||
# ############################################################################
|
||||
# Option values with spaces.
|
||||
# ############################################################################
|
||||
|
||||
# Config file
|
||||
cp "$T_LIB_DIR/samples/bash/config002.conf" "$HOME/.$TOOL.conf"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" ""
|
||||
|
||||
is "$OPT_STRING_OPT" "hello world" "Option value with space (conf)"
|
||||
is "$OPT_INT_OPT" "100" "Option = value # comment (conf)"
|
||||
|
||||
rm "$HOME/.$TOOL.conf"
|
||||
TOOL="pt-stalk"
|
||||
|
||||
# Command line
|
||||
parse_options "$T_LIB_DIR/samples/bash/po001.sh" --string-opt "hello world"
|
||||
is "$OPT_STRING_OPT" "hello world" "Option value with space (cmd line)"
|
||||
is "$ARGV" "" "ARGV (cmd line)"
|
||||
is "$EXT_ARGV" "" "External ARGV (cmd line)"
|
||||
|
||||
# ############################################################################
|
||||
# Size options.
|
||||
# ############################################################################
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1T
|
||||
is "$OPT_DISK_BYTES_FREE" "1099511627776" "Size: 1T"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1G
|
||||
is "$OPT_DISK_BYTES_FREE" "1073741824" "Size: 1G"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1M
|
||||
is "$OPT_DISK_BYTES_FREE" "1048576" "Size: 1M"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1K
|
||||
is "$OPT_DISK_BYTES_FREE" "1024" "Size: 1K"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1k
|
||||
is "$OPT_DISK_BYTES_FREE" "1024" "Size: 1k"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 1
|
||||
is "$OPT_DISK_BYTES_FREE" "1" "Size: 1"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh" --disk-bytes-free 100M
|
||||
is "$OPT_DISK_BYTES_FREE" "104857600" "Size: 100M"
|
||||
|
||||
parse_options "$T_LIB_DIR/samples/bash/po004.sh"
|
||||
is "$OPT_DISK_BYTES_FREE" "104857600" "Size: 100M default"
|
||||
|
||||
# ############################################################################
|
||||
# Done
|
||||
# ############################################################################
|
||||
|
@@ -18,36 +18,51 @@ is \
|
||||
"2" \
|
||||
"2-line df output"
|
||||
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 22000 18 >$TMPDIR/out 2>&1
|
||||
# Filesystem 1024-blocks Used Available Capacity Mounted on
|
||||
# /dev/disk0s2 118153176 94409664 23487512 81% /
|
||||
#
|
||||
# Those values are in Kb, so:
|
||||
# used = 94409664 (94.4G) = 96_675_495_936 bytes
|
||||
# free = 23487512 (23.4G) = 24_051_212_288 bytes
|
||||
# pct free = 100 - 81 = 19 %
|
||||
|
||||
# want free - 100, 18 < 19, so this should be ok.
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 18 >$TMPDIR/out 2>&1
|
||||
is "$?" "0" "Enough disk space"
|
||||
is \
|
||||
"`cat $TMPDIR/out`" \
|
||||
"" \
|
||||
"No output if enough disk space"
|
||||
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 24000 18 >$TMPDIR/out 2>&1
|
||||
# want free - 100 is ok, but 20 < 19 is not.
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 20 >$TMPDIR/out 2>&1
|
||||
is "$?" "1" "Not enough % free"
|
||||
|
||||
# want free + 100, so this should fail
|
||||
# (real free is 100 bytes under what we want)
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 24051212388 18 >$TMPDIR/out 2>&1
|
||||
is "$?" "1" "Not enough MB free"
|
||||
cmd_ok \
|
||||
"grep -q '19% free, 23487512 KB free; wanted more than 18% free or 24576000 KB free' $TMPDIR/out" \
|
||||
"grep -q 'Actual: 19% free, 24051212288 bytes free (- 0 bytes margin)' $TMPDIR/out" \
|
||||
"Warning if not enough disk space"
|
||||
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 22000 19 >$TMPDIR/out 2>&1
|
||||
is "$?" "1" "Not enough % free"
|
||||
|
||||
# ###########################################################################
|
||||
# Check with a margin (amount we plan to use in the future).
|
||||
# ###########################################################################
|
||||
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 22000 18 100
|
||||
# want free - 100 + 50 margin, so effectively want free - 50 is ok.
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 18 50
|
||||
is "$?" "0" "Enough disk space with margin"
|
||||
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 23000 18 100 >$TMPDIR/out 2>&1
|
||||
# want free - 100 + 101 margin, so real free is 1 byte under what we want.
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 18 101 >$TMPDIR/out 2>&1
|
||||
is "$?" "1" "Not enough MB free with margin"
|
||||
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 100 5 20000 >$TMPDIR/out 2>&1
|
||||
# want free - 100 + 50 margin ok but %free will be 19 which is < 25.
|
||||
check_disk_space "$SAMPLE/diskspace001.txt" 24051212188 25 50 >$TMPDIR/out 2>&1
|
||||
is "$?" "1" "Not enough % free with margin"
|
||||
cmd_ok \
|
||||
"grep -q '3% free,' $TMPDIR/out" \
|
||||
"grep -q 'Actual:[ ]*19% free,' $TMPDIR/out" \
|
||||
"Calculates % free with margin"
|
||||
|
||||
# ###########################################################################
|
||||
|
@@ -1,33 +0,0 @@
|
||||
2011_12_05-df
|
||||
2011_12_05-disk-space
|
||||
2011_12_05-diskstats
|
||||
2011_12_05-hostname
|
||||
2011_12_05-innodbstatus1
|
||||
2011_12_05-innodbstatus2
|
||||
2011_12_05-interrupts
|
||||
2011_12_05-iostat
|
||||
2011_12_05-iostat-overall
|
||||
2011_12_05-log_error
|
||||
2011_12_05-lsof
|
||||
2011_12_05-meminfo
|
||||
2011_12_05-mpstat
|
||||
2011_12_05-mpstat-overall
|
||||
2011_12_05-mutex-status1
|
||||
2011_12_05-mutex-status2
|
||||
2011_12_05-mysqladmin
|
||||
2011_12_05-netstat
|
||||
2011_12_05-netstat_s
|
||||
2011_12_05-opentables1
|
||||
2011_12_05-opentables2
|
||||
2011_12_05-output
|
||||
2011_12_05-processlist
|
||||
2011_12_05-procstat
|
||||
2011_12_05-procvmstat
|
||||
2011_12_05-ps
|
||||
2011_12_05-slabinfo
|
||||
2011_12_05-stacktrace
|
||||
2011_12_05-sysctl
|
||||
2011_12_05-top
|
||||
2011_12_05-variables
|
||||
2011_12_05-vmstat
|
||||
2011_12_05-vmstat-overall
|
5
t/lib/samples/bash/config001.conf
Normal file
5
t/lib/samples/bash/config001.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
string-opt=abc
|
||||
typeless-option
|
||||
--
|
||||
--host=127.1
|
||||
--user=daniel
|
5
t/lib/samples/bash/config002.conf
Normal file
5
t/lib/samples/bash/config002.conf
Normal file
@@ -0,0 +1,5 @@
|
||||
# Line comment.
|
||||
string-opt=hello world
|
||||
|
||||
|
||||
int-opt = 100 # Inline comment.
|
2
t/lib/samples/bash/config003.conf
Normal file
2
t/lib/samples/bash/config003.conf
Normal file
@@ -0,0 +1,2 @@
|
||||
string-opt=from config file
|
||||
int-opt=123
|
3
t/lib/samples/bash/config004.conf
Normal file
3
t/lib/samples/bash/config004.conf
Normal file
@@ -0,0 +1,3 @@
|
||||
typeless-option
|
||||
int-opt=123
|
||||
string-opt=foo
|
@@ -1,30 +0,0 @@
|
||||
Usage: pt-stalk [OPTIONS] [-- MYSQL_OPTIONS]
|
||||
|
||||
For more information, 'man pt-stalk' or 'perldoc /Users/daniel/p/bash-tool-libs/t/lib/samples/bash/po001.sh'.
|
||||
|
||||
Command line options:
|
||||
|
||||
--help
|
||||
Print help and exit.
|
||||
|
||||
--int-opt
|
||||
Int option without a default.
|
||||
|
||||
--int-opt2
|
||||
Int option with a default.
|
||||
|
||||
--noption
|
||||
Negatable option.
|
||||
|
||||
--string-opt
|
||||
String option without a default.
|
||||
|
||||
--string-opt2
|
||||
String option with a default.
|
||||
|
||||
--typeless-option
|
||||
Just an option.
|
||||
|
||||
--version
|
||||
Print tool's version and exit.
|
||||
|
@@ -1,212 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
:
|
||||
|
||||
# ############################################################################
|
||||
# Documentation
|
||||
# ############################################################################
|
||||
:<<'DOCUMENTATION'
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
pt-stalk - Wait for a condition to occur then begin collecting data.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item --collect
|
||||
|
||||
default: yes; negatable: yes
|
||||
|
||||
Collect system information.
|
||||
|
||||
=item --collect-gdb
|
||||
|
||||
Collect GDB stacktraces.
|
||||
|
||||
=item --collect-oprofile
|
||||
|
||||
Collect oprofile data.
|
||||
|
||||
=item --collect-strace
|
||||
|
||||
Collect strace data.
|
||||
|
||||
=item --collect-tcpdump
|
||||
|
||||
Collect tcpdump data.
|
||||
|
||||
=item --cycles
|
||||
|
||||
type: int; default: 5
|
||||
|
||||
Number of times condition must be met before triggering collection.
|
||||
|
||||
=item --daemonize
|
||||
|
||||
default: yes; negatable: yes
|
||||
|
||||
Daemonize the tool.
|
||||
|
||||
=item --dest
|
||||
|
||||
type: string
|
||||
|
||||
Where to store collected data.
|
||||
|
||||
=item --disk-byte-limit
|
||||
|
||||
type: int; default: 100
|
||||
|
||||
Exit if the disk has less than this many MB free.
|
||||
|
||||
=item --disk-pct-limit
|
||||
|
||||
type: int; default: 5
|
||||
|
||||
Exit if the disk is less than this %full.
|
||||
|
||||
=item --execute-command
|
||||
|
||||
type: string; default: pt-collect
|
||||
|
||||
Location of the C<pt-collect> tool.
|
||||
|
||||
=item --function
|
||||
|
||||
type: string; default: status
|
||||
|
||||
Built-in function name or plugin file name which returns the value of C<VARIABLE>.
|
||||
|
||||
Possible values are:
|
||||
|
||||
=over
|
||||
|
||||
=item * status
|
||||
|
||||
Grep the value of C<VARIABLE> from C<mysqladmin extended-status>.
|
||||
|
||||
=item * processlist
|
||||
|
||||
Count the number of processes in C<mysqladmin processlist> whose
|
||||
C<VARIABLE> column matches C<MATCH>. For example:
|
||||
|
||||
TRIGGER_FUNCTION="processlist" \
|
||||
VARIABLE="State" \
|
||||
MATCH="statistics" \
|
||||
THRESHOLD="10"
|
||||
|
||||
The above triggers when more than 10 processes are in the "statistics" state.
|
||||
C<MATCH> must be specified for this trigger function.
|
||||
|
||||
=item * magic
|
||||
|
||||
TODO
|
||||
|
||||
=item * plugin file name
|
||||
|
||||
A plugin file allows you to specify a custom trigger function. The plugin
|
||||
file must contain a function called C<trg_plugin>. For example:
|
||||
|
||||
trg_plugin() {
|
||||
# Do some stuff.
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
The last output if the function (its "return value") must be a number.
|
||||
This number is compared to C<THRESHOLD>. All L<"ENVIRONMENT"> variables
|
||||
are available to the function.
|
||||
|
||||
Do not alter the tool's existing global variables. Prefix any plugin-specific
|
||||
global variables with "PLUGIN_".
|
||||
|
||||
=back
|
||||
|
||||
=item --help
|
||||
|
||||
Print help and exit.
|
||||
|
||||
=item --interval
|
||||
|
||||
type: int; default: 1
|
||||
|
||||
Interval between checks.
|
||||
|
||||
=item --iterations
|
||||
|
||||
type: int
|
||||
|
||||
Exit after triggering C<pt-collect> this many times. By default, the tool
|
||||
will collect as many times as it's triggered.
|
||||
|
||||
=item --log
|
||||
|
||||
type: string; default: /var/log/pt-stalk.log
|
||||
|
||||
Print all output to this file when daemonized.
|
||||
|
||||
=item --match
|
||||
|
||||
type: string
|
||||
|
||||
Match pattern for C<processles> L<"--function">.
|
||||
|
||||
=item --notify-by-email
|
||||
|
||||
type: string
|
||||
|
||||
Send mail to this list of addresses when C<pt-collect> triggers.
|
||||
|
||||
=item --pid FILE
|
||||
|
||||
type: string; default: /var/run/pt-stalk.pid
|
||||
|
||||
Create a PID file when daemonized.
|
||||
|
||||
=item --retention-time
|
||||
|
||||
type: int; default: 30
|
||||
|
||||
Remove samples after this many days.
|
||||
|
||||
=item --run-time
|
||||
|
||||
type: int; default: 30
|
||||
|
||||
How long to collect statistics data for?
|
||||
|
||||
Make sure that this isn't longer than SLEEP.
|
||||
|
||||
=item --sleep
|
||||
|
||||
type: int; default: 300
|
||||
|
||||
How long to sleep after collecting?
|
||||
|
||||
=item --threshold N
|
||||
|
||||
type: int; default: 25
|
||||
|
||||
Max number of C<N> to tolerate.
|
||||
|
||||
=item --variable NAME
|
||||
|
||||
type: string; default: Threads_running
|
||||
|
||||
This is the thing to check for.
|
||||
|
||||
=item --version
|
||||
|
||||
Print tool's version and exit.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ENVIRONMENT
|
||||
|
||||
No env vars used.
|
||||
|
||||
=cut
|
||||
|
||||
DOCUMENTATION
|
37
t/lib/samples/bash/po003.sh
Normal file
37
t/lib/samples/bash/po003.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
:
|
||||
|
||||
# ############################################################################
|
||||
# Documentation
|
||||
# ############################################################################
|
||||
:<<'DOCUMENTATION'
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
pt-stalk - Wait for a condition to occur then begin collecting data.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item --disk-pct-limit
|
||||
|
||||
type: int; default: 5
|
||||
|
||||
Exit if the disk is less than this %full.
|
||||
|
||||
=item --help
|
||||
|
||||
Print help.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ENVIRONMENT
|
||||
|
||||
No env vars used.
|
||||
|
||||
=cut
|
||||
|
||||
DOCUMENTATION
|
37
t/lib/samples/bash/po004.sh
Normal file
37
t/lib/samples/bash/po004.sh
Normal file
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
:
|
||||
|
||||
# ############################################################################
|
||||
# Documentation
|
||||
# ############################################################################
|
||||
:<<'DOCUMENTATION'
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
pt-stalk - Wait for a condition to occur then begin collecting data.
|
||||
|
||||
=head1 OPTIONS
|
||||
|
||||
=over
|
||||
|
||||
=item --disk-bytes-free
|
||||
|
||||
type: size; default: 100M
|
||||
|
||||
Fall apart if there's less than this many bytes free on the disk.
|
||||
|
||||
=item --help
|
||||
|
||||
Print help.
|
||||
|
||||
=back
|
||||
|
||||
=head1 ENVIRONMENT
|
||||
|
||||
No env vars used.
|
||||
|
||||
=cut
|
||||
|
||||
DOCUMENTATION
|
5
t/lib/samples/bash/seq1.txt
Normal file
5
t/lib/samples/bash/seq1.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
@@ -1,25 +0,0 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
BEGIN {
|
||||
die "The PERCONA_TOOLKIT_BRANCH environment variable is not set.\n"
|
||||
unless $ENV{PERCONA_TOOLKIT_BRANCH} && -d $ENV{PERCONA_TOOLKIT_BRANCH};
|
||||
unshift @INC, "$ENV{PERCONA_TOOLKIT_BRANCH}/lib";
|
||||
};
|
||||
|
||||
use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Test::More tests => 1;
|
||||
|
||||
use PerconaTest;
|
||||
|
||||
like(
|
||||
`$trunk/bin/pt-collect --help 2>&1`,
|
||||
qr/Usage:/,
|
||||
'It runs'
|
||||
);
|
||||
|
||||
# #############################################################################
|
||||
# Done.
|
||||
# #############################################################################
|
||||
exit;
|
@@ -3,14 +3,14 @@
|
||||
TESTS=3
|
||||
|
||||
TEST_NAME="get_mysql_timezone"
|
||||
cp samples/mysql-variables-001.txt /tmp/percona-toolkit-mysql-variables
|
||||
cp samples/mysql-variables-001.txt $TMPDIR/percona-toolkit-mysql-variables
|
||||
is $(get_mysql_timezone) "EDT"
|
||||
|
||||
TEST_NAME="get_mysql_uptime"
|
||||
cat <<EOF > $TMPDIR/expected
|
||||
2010-05-27 11:38 (up 0+02:08:52)
|
||||
EOF
|
||||
cp samples/mysql-status-001.txt /tmp/percona-toolkit-mysql-status
|
||||
cp samples/mysql-status-001.txt $TMPDIR/percona-toolkit-mysql-status
|
||||
echo "2010-05-27 11:38" > $TMPDIR/in
|
||||
get_mysql_uptime $TMPDIR/in > $TMPDIR/got
|
||||
no_diff $TMPDIR/got $TMPDIR/expected
|
||||
@@ -20,6 +20,6 @@ cat <<EOF > $TMPDIR/expected
|
||||
Version | 5.0.51a-24+lenny2 (Debian)
|
||||
Built On | debian-linux-gnu i486
|
||||
EOF
|
||||
cp samples/mysql-variables-001.txt /tmp/percona-toolkit-mysql-variables
|
||||
cp samples/mysql-variables-001.txt $TMPDIR/percona-toolkit-mysql-variables
|
||||
get_mysql_version > $TMPDIR/got
|
||||
no_diff $TMPDIR/got $TMPDIR/expected
|
||||
|
@@ -9,16 +9,238 @@ BEGIN {
|
||||
use strict;
|
||||
use warnings FATAL => 'all';
|
||||
use English qw(-no_match_vars);
|
||||
use Test::More tests => 1;
|
||||
use Test::More;
|
||||
use Time::HiRes qw(sleep);
|
||||
|
||||
use PerconaTest;
|
||||
use DSNParser;
|
||||
use Sandbox;
|
||||
|
||||
TODO: {
|
||||
local $TODO = "Test pt-stalk";
|
||||
ok(1, 'ok');
|
||||
};
|
||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
||||
my $dbh = $sb->get_dbh_for('master');
|
||||
|
||||
if ( !$dbh ) {
|
||||
plan skip_all => 'Cannot connect to sandbox master';
|
||||
}
|
||||
else {
|
||||
plan tests => 21;
|
||||
}
|
||||
|
||||
my $cnf = "/tmp/12345/my.sandbox.cnf";
|
||||
my $pid_file = "/tmp/pt-stalk.pid.$PID";
|
||||
my $log_file = "/tmp/pt-stalk.log.$PID";
|
||||
my $dest = "/tmp/pt-stalk.collect.$PID";
|
||||
my $pid;
|
||||
|
||||
diag(`rm $pid_file 2>/dev/null`);
|
||||
diag(`rm $log_file 2>/dev/null`);
|
||||
diag(`rm -rf $dest 2>/dev/null`);
|
||||
|
||||
# ###########################################################################
|
||||
# Test that it won't run if can't connect to MySQL.
|
||||
# ###########################################################################
|
||||
|
||||
my $retval = system("$trunk/bin/pt-stalk >$log_file 2>&1");
|
||||
my $output = `cat $log_file`;
|
||||
|
||||
like(
|
||||
$output,
|
||||
qr/Cannot connect to MySQL/,
|
||||
"Cannot connect to MySQL"
|
||||
);
|
||||
|
||||
is(
|
||||
$retval >> 8,
|
||||
1,
|
||||
"Exit 1"
|
||||
);
|
||||
|
||||
# ###########################################################################
|
||||
# Test that it runs and dies normally.
|
||||
# ###########################################################################
|
||||
diag(`rm $pid_file 2>/dev/null`);
|
||||
diag(`rm $log_file 2>/dev/null`);
|
||||
diag(`rm -rf $dest 2>/dev/null`);
|
||||
|
||||
$retval = system("$trunk/bin/pt-stalk --daemonize --pid $pid_file --log $log_file --dest $dest -- --defaults-file=$cnf");
|
||||
|
||||
is(
|
||||
$retval >> 8,
|
||||
0,
|
||||
"Parent exit 0"
|
||||
);
|
||||
|
||||
PerconaTest::wait_for_files($pid_file, $log_file);
|
||||
ok(
|
||||
-f $pid_file,
|
||||
"Creates PID file"
|
||||
);
|
||||
|
||||
ok(
|
||||
-f $log_file,
|
||||
"Creates log file"
|
||||
);
|
||||
|
||||
sleep 1;
|
||||
|
||||
ok(
|
||||
-d $dest,
|
||||
"Creates --dest (collect) dir"
|
||||
);
|
||||
|
||||
chomp($pid = `cat $pid_file`);
|
||||
$retval = system("kill -0 $pid");
|
||||
is(
|
||||
$retval >> 0,
|
||||
0,
|
||||
"pt-stalk is running ($pid)"
|
||||
);
|
||||
|
||||
$output = `cat $log_file`;
|
||||
like(
|
||||
$output,
|
||||
qr/Check results: Threads_running=\d+, matched=no, cycles_true=0/,
|
||||
"Check results logged"
|
||||
);
|
||||
|
||||
$retval = system("kill $pid 2>/dev/null");
|
||||
is(
|
||||
$retval >> 0,
|
||||
0,
|
||||
"Killed pt-stalk"
|
||||
);
|
||||
|
||||
sleep 1;
|
||||
|
||||
ok(
|
||||
! -f $pid_file,
|
||||
"Removes PID file"
|
||||
);
|
||||
|
||||
$output = `cat $log_file`;
|
||||
like(
|
||||
$output,
|
||||
qr/Caught signal, exiting/,
|
||||
"Caught signal logged"
|
||||
);
|
||||
|
||||
# ###########################################################################
|
||||
# Test collect.
|
||||
# ###########################################################################
|
||||
diag(`rm $pid_file 2>/dev/null`);
|
||||
diag(`rm $log_file 2>/dev/null`);
|
||||
diag(`rm $dest/* 2>/dev/null`);
|
||||
|
||||
# We'll have to watch Uptime since it's the only status var that's going
|
||||
# to be predictable.
|
||||
my (undef, $uptime) = $dbh->selectrow_array("SHOW STATUS LIKE 'Uptime'");
|
||||
my $threshold = $uptime + 2;
|
||||
|
||||
$retval = system("$trunk/bin/pt-stalk --iterations 1 --dest $dest --variable Uptime --threshold $threshold --cycles 2 --run-time 2 --pid $pid_file -- --defaults-file=$cnf >$log_file 2>&1");
|
||||
|
||||
sleep 3;
|
||||
|
||||
$output = `cat $dest/*-trigger`;
|
||||
like(
|
||||
$output,
|
||||
qr/Check results: Uptime=\d+, matched=yes, cycles_true=2/,
|
||||
"Collect triggered"
|
||||
);
|
||||
|
||||
chomp($output = `cat $dest/*-df | grep -c '^TS'`);
|
||||
is(
|
||||
$output,
|
||||
2,
|
||||
"Collect ran for --run-time"
|
||||
);
|
||||
|
||||
$output = `ps x | grep -v grep | grep 'pt-stalk pt-stalk --iterations 1 --dest $dest'`;
|
||||
is(
|
||||
$output,
|
||||
"",
|
||||
"pt-stalk is not running"
|
||||
);
|
||||
|
||||
$output = `cat $dest/*-trigger`;
|
||||
like(
|
||||
$output,
|
||||
qr/pt-stalk ran with --function=status --variable=Uptime --threshold=$threshold/,
|
||||
"Trigger file logs how pt-stalk was ran"
|
||||
);
|
||||
|
||||
chomp($output = `cat $log_file | grep 'Collector PID'`);
|
||||
like(
|
||||
$output,
|
||||
qr/Collector PID \d+/,
|
||||
"Collector PID logged"
|
||||
);
|
||||
|
||||
# ###########################################################################
|
||||
# Triggered but --no-collect.
|
||||
# ###########################################################################
|
||||
diag(`rm $pid_file 2>/dev/null`);
|
||||
diag(`rm $log_file 2>/dev/null`);
|
||||
diag(`rm $dest/* 2>/dev/null`);
|
||||
|
||||
(undef, $uptime) = $dbh->selectrow_array("SHOW STATUS LIKE 'Uptime'");
|
||||
$threshold = $uptime + 2;
|
||||
|
||||
$retval = system("$trunk/bin/pt-stalk --no-collect --iterations 1 --dest $dest --variable Uptime --threshold $threshold --cycles 1 --run-time 1 --pid $pid_file -- --defaults-file=$cnf >$log_file 2>&1");
|
||||
|
||||
sleep 2;
|
||||
|
||||
$output = `cat $log_file`;
|
||||
like(
|
||||
$output,
|
||||
qr/Collect triggered/,
|
||||
"Collect triggered"
|
||||
);
|
||||
|
||||
ok(
|
||||
! -f "$dest/*",
|
||||
"No files collected"
|
||||
);
|
||||
|
||||
$output = `ps x | grep -v grep | grep 'pt-stalk pt-stalk --iterations 1 --dest $dest'`;
|
||||
is(
|
||||
$output,
|
||||
"",
|
||||
"pt-stalk is not running"
|
||||
);
|
||||
|
||||
# #############################################################################
|
||||
# --config
|
||||
# #############################################################################
|
||||
|
||||
diag(`cp $ENV{HOME}/.pt-stalk.conf $ENV{HOME}/.pt-stalk.conf.original 2>/dev/null`);
|
||||
diag(`cp $trunk/t/pt-stalk/samples/config001.conf $ENV{HOME}/.pt-stalk.conf`);
|
||||
|
||||
system "$trunk/bin/pt-stalk --dest $dest --pid $pid_file >$log_file 2>&1 &";
|
||||
PerconaTest::wait_for_files($pid_file);
|
||||
sleep 1;
|
||||
chomp($pid = `cat $pid_file`);
|
||||
$retval = system("kill $pid 2>/dev/null");
|
||||
is(
|
||||
$retval >> 0,
|
||||
0,
|
||||
"Killed pt-stalk"
|
||||
);
|
||||
|
||||
$output = `cat $log_file`;
|
||||
like(
|
||||
$output,
|
||||
qr/Check results: Aborted_connects=|variable=Aborted_connects/,
|
||||
"Read default config file"
|
||||
);
|
||||
|
||||
diag(`rm $ENV{HOME}/.pt-stalk.conf`);
|
||||
diag(`cp $ENV{HOME}/.pt-stalk.conf.original $ENV{HOME}/.pt-stalk.conf 2>/dev/null`);
|
||||
|
||||
# #############################################################################
|
||||
# Done.
|
||||
# #############################################################################
|
||||
diag(`rm $pid_file 2>/dev/null`);
|
||||
diag(`rm $log_file 2>/dev/null`);
|
||||
diag(`rm -rf $dest 2>/dev/null`);
|
||||
exit;
|
||||
|
8
t/pt-stalk/samples/config001.conf
Normal file
8
t/pt-stalk/samples/config001.conf
Normal file
@@ -0,0 +1,8 @@
|
||||
--iterations=1
|
||||
--variable=Aborted_connects
|
||||
--threshold=999999
|
||||
--
|
||||
-umsandbox
|
||||
-pmsandbox
|
||||
--host 127.1
|
||||
--port 12345
|
@@ -93,7 +93,7 @@ run_test() {
|
||||
# Print a TAP-style test result.
|
||||
result() {
|
||||
local result=$1
|
||||
local test_name=$2
|
||||
local test_name=${2:-""}
|
||||
if [ $result -eq 0 ]; then
|
||||
echo "ok $testno - $TEST_FILE $test_name"
|
||||
else
|
||||
@@ -115,7 +115,7 @@ result() {
|
||||
no_diff() {
|
||||
local got=$1
|
||||
local expected=$2
|
||||
local test_name=$3
|
||||
local test_name=${3:-""}
|
||||
test_command="diff $got $expected"
|
||||
eval $test_command > $TEST_TMPDIR/failed_result 2>&1
|
||||
result $? "$test_name"
|
||||
@@ -124,7 +124,7 @@ no_diff() {
|
||||
is() {
|
||||
local got=$1
|
||||
local expected=$2
|
||||
local test_name=$3
|
||||
local test_name=${3:-""}
|
||||
test_command="\"$got\" == \"$expected\""
|
||||
test "$got" = "$expected"
|
||||
result $? "$test_name"
|
||||
@@ -132,7 +132,7 @@ is() {
|
||||
|
||||
cmd_ok() {
|
||||
local test_command=$1
|
||||
local test_name=$2
|
||||
local test_name=${2:-""}
|
||||
eval $test_command
|
||||
result $? "$test_name"
|
||||
}
|
||||
|
Reference in New Issue
Block a user