From 1ae8661271ba7813a835f806dd12dab8cae8a9ac Mon Sep 17 00:00:00 2001 From: Daniel Nichter Date: Wed, 26 Oct 2011 10:50:55 -0600 Subject: [PATCH] First Bash parse_options lib with tests. --- lib/bash/parse_options.sh | 119 +++++++++++++ t/lib/bash.t | 23 +++ t/lib/bash/parse_options.sh | 25 +++ t/lib/samples/bash/dummy.sh | 4 + t/lib/samples/bash/po001.sh | 340 ++++++++++++++++++++++++++++++++++++ 5 files changed, 511 insertions(+) create mode 100644 lib/bash/parse_options.sh create mode 100755 t/lib/bash.t create mode 100644 t/lib/bash/parse_options.sh create mode 100644 t/lib/samples/bash/dummy.sh create mode 100644 t/lib/samples/bash/po001.sh diff --git a/lib/bash/parse_options.sh b/lib/bash/parse_options.sh new file mode 100644 index 00000000..e9003cad --- /dev/null +++ b/lib/bash/parse_options.sh @@ -0,0 +1,119 @@ +# Print usage (--help). +usage() { + local file=$1 + + local usage=$(grep '^Usage: ' $file) + local opts=$(grep -A 2 '^=item --' $file | sed -e 's/^=item //' -e 's/^\([A-Z]\)/ \1/' -e 's/^--$//' > $TMPDIR/help) + + if [ "${OPT_ERR}" ]; then + echo "Error: ${OPT_ERR}" >&2 + fi + echo $usage >&2 + echo >&2 + echo "Options:" >&2 + echo >&2 + cat $TMPDIR/help >&2 + echo >&2 + echo "For more information, 'man $TOOL' or 'perldoc $file'." >&2 +} + +# Parse command line options. +declare -a ARGV # non-option args (probably input files) +declare EXT_ARGV # everything after -- (args for an external command) +parse_options() { + local file=$1 + shift + + local opt="" + local val="" + local default="" + local version="" + local i=0 + + awk ' + /^=head1 OPTIONS/ { + getline + while ($0 !~ /^=head1/) { + if ($0 ~ /^=item --.*/) { + long_opt=substr($2, 3, length($2) - 2) + short_opt="" + required_arg="" + + if ($3) { + if ($3 ~ /-[a-z]/) + short_opt=substr($3, 3, length($3) - 3) + else + required_arg=$3 + } + + if ($4 ~ /[A-Z]/) + required_arg=$4 + + getline # blank line + getline # short description line + + if ($0 ~ /default: /) { + i=index($0, "default: ") + default=substr($0, i + 9, length($0) - (i + 9)) + } + else + default="" + + print long_opt "," short_opt "," required_arg "," default + } + getline + } + exit + }' $file > $TMPDIR/options + + while read spec; do + opt=$(echo $spec | cut -d',' -f1 | sed 's/-/_/g' | tr [:lower:] [:upper:]) + default=$(echo $spec | cut -d',' -f4) + eval "$opt"="$default" + done < <(cat $TMPDIR/options) + + for opt; do + if [ $# -eq 0 ]; then + break + fi + opt=$1 + if [ "$opt" = "--" ]; then + shift + EXT_ARGV="$@" + break + fi + if [ "$opt" = "--version" ]; then + version=$(grep '^pt-[^ ]\+ [0-9]' $0) + echo "$version" + exit 0 + fi + if [ "$opt" = "--help" ]; then + usage $file + exit 0 + fi + shift + if [ $(expr "$opt" : "-") -eq 0 ]; then + ARGV[i]="$opt" + i=$((i+1)) + continue + fi + opt=$(echo $opt | sed 's/^-*//') + spec=$(grep -E "^$opt,|,$opt," "$TMPDIR/options") + if [ -z "$spec" ]; then + die "Unknown option: $opt" + fi + opt=$(echo $spec | cut -d',' -f1) + required_arg=$(echo $spec | cut -d',' -f3) + val=1 + if [ -n "$required_arg" ]; then + if [ $# -eq 0 ]; then + die "--$opt requires a $required_arg argument" + else + val="$1" + shift + fi + fi + opt=$(echo $opt | sed 's/-/_/g' | tr [:lower:] [:upper:]) + eval "$opt"="$val" + done +} diff --git a/t/lib/bash.t b/t/lib/bash.t new file mode 100755 index 00000000..aaf1fe28 --- /dev/null +++ b/t/lib/bash.t @@ -0,0 +1,23 @@ +#!/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 PerconaTest; + +my ($tool) = $PROGRAM_NAME =~ m/([\w-]+)\.t$/; +push @ARGV, "$trunk/t/lib/bash/*.sh" unless @ARGV; + +$ENV{LIB_DIR} = "$trunk/lib/bash"; +$ENV{T_LIB_DIR} = "$trunk/t/lib"; + +system("$trunk/util/test-bash-functions $trunk/t/lib/samples/bash/dummy.sh @ARGV"); + +exit; diff --git a/t/lib/bash/parse_options.sh b/t/lib/bash/parse_options.sh new file mode 100644 index 00000000..00c8de65 --- /dev/null +++ b/t/lib/bash/parse_options.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +TESTS=17 + +source "$LIB_DIR/parse_options.sh" + +parse_options "$T_LIB_DIR/samples/bash/po001.sh" + +is "$THRESHOLD" "100" +is "$VARIABLE" "Threads_connected" +is "$CYCLES" "1" +is "$GDB" "no" +is "$OPROFILE" "yes" +is "$STRACE" "no" +is "$TCPDUMP" "yes" +is "$EMAIL" "" +is "$INTERVAL" "30" +is "$MAYBE_EMPTY" "no" +is "$COLLECT" "${HOME}/bin/pt-collect" +is "$DEST" "${HOME}/collected/" +is "$DURATION" "30" +is "$SLEEP" "300" +is "$PCT_THRESHOLD" "95" +is "$MB_THRESHOLD" "100" +is "$PURGE" "30" diff --git a/t/lib/samples/bash/dummy.sh b/t/lib/samples/bash/dummy.sh new file mode 100644 index 00000000..a7eea1ee --- /dev/null +++ b/t/lib/samples/bash/dummy.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +# This is a dummy script for testing the Bash libs. t/lib/bashLibs.t +# calls "util/test-bash-functions dummy.sh ". diff --git a/t/lib/samples/bash/po001.sh b/t/lib/samples/bash/po001.sh new file mode 100644 index 00000000..0fdd943d --- /dev/null +++ b/t/lib/samples/bash/po001.sh @@ -0,0 +1,340 @@ +#!/usr/bin/env bash + +# This is a fake script for testing the parse_options.sh lib. +exit 0; + +# ############################################################################ +# Documentation +# ############################################################################ +:<<'DOCUMENTATION' +=pod + +=head1 NAME + +pt-stalk - Wait for a condition to occur then begin collecting data. + +=head1 SYNOPSIS + +Usage: pt-stalk [OPTIONS] [-- MYSQL_OPTIONS] + +pt-stalk watches for a condition to become true, and when it does, executes +a script. By default it executes L, but that can be customized. +This tool is useful for gathering diagnostic data when an infrequent event +occurs, so an expert person can review the data later. + +=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-stalk 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. + +See also L<"BUGS"> for more information on filing bugs and getting help. + +=head1 DESCRIPTION + +Although pt-stalk comes pre-configured to do a specific thing, in general +this tool is just a skeleton script for the following flow of actions: + +=over + +=item 1. + +Loop infinitely, sleeping between iterations. + +=item 2. + +In each iteration, run some command and get the output. + +=item 3. + +If the command fails or the output is larger than the threshold, +execute the collection script; but do not execute if the destination disk +is too full. + +=back + +By default, the tool is configured to execute mysqladmin extended-status and +extract the value of the Threads_connected variable; if this is greater than +100, it runs the collection script. This is really just placeholder code, +and almost certainly needs to be customized! + +If the tool does execute the collection script, it will wait for a while +before checking and executing again. This is to prevent a continuous +condition from causing a huge number of executions to fire off. + +The name 'stalk' is because 'watch' is already taken, and 'stalk' is fun. + +=head1 CONFIGURING + +If the file F exists in the current working directory, then +L<"ENVIRONMENT"> variables are imported from it. For example, the config +file has the format: + + INTERVAL=10 + GDB=yes + +See L<"ENVIRONMENT">. + +=head1 OPTIONS + +=over + +=item --threshold N + +Max number of C to tolerate. (default: 100) + +=item --variable NAME + +This is the thing to check for. (default: Threads_connected) + +=item --cycles N + +How many times must the condition be met before the script will fire? (default: 1) + +=item --gdb + +Collect GDB stacktraces? (default: no) + +=item --oprofile + +Collect oprofile data? (default: yes) + +=item --strace + +Collect strace data? (default: no) + +=item --tcpdump + +Collect tcpdump data? (default: yes) + +=item --email ADDRESS + +Send mail to this list of addresses when the script triggers. + +=item --interval SECONDS + +This is the interval between checks. (default: 30) + +=item --maybe-empty + +Result of checks may be empty. (default: no) +If the command you're running to detect the condition is allowed to return +nothing (e.g. a grep line that might not even exist if there's no problem), +then set this to "yes". + +=item --collect DIRECTORY + +Location of the C tool. (default: ${HOME}/bin/pt-collect) + +=item --dest DIRECTORY + +Where to store collected data. (default: ${HOME}/collected/) + +=item --duration SECONDS + +How long to collect statistics data for? (default: 30) +Make sure that this isn't longer than SLEEP. + +=item --sleep SECONDS + +How long to sleep after collecting? (default: 300) + +=item --pct-threshold PCT + +Bail out if the disk is more than this %full. (default: 95) + +=item --mb-threshold MEGABYTES + +Bail out if the disk has less than this many MB free. (default: 100) + +=item --purge DAYS + +Remove samples after this many days. (default: 30) + +=item --version + +Print tool's version and exit. + +=back + +=head1 ENVIRONMENT + +The following environment variables configure how, what, and when the tool +runs. They are all optional and can be specified either on the command line +or in the F config file (see L<"CONFIGURING">). + +=over + +=item THRESHOLD (default 100) + +This is the max number of we want to tolerate. + +=item VARIABLE (default Threads_connected} + +This is the thing to check for. + +=item CYCLES (default 1) + +How many times must the condition be met before the script will fire? + +=item GDB (default no) + +Collect GDB stacktraces? + +=item OPROFILE (default yes) + +Collect oprofile data? + +=item STRACE (default no) + +Collect strace data? + +=item TCPDUMP (default yes) + +Collect tcpdump data? + +=item EMAIL + +Send mail to this list of addresses when the script triggers. + +=item MYSQLOPTIONS + +Any options to pass to mysql/mysqladmin, such as -u, -p, etc + +=item INTERVAL (default 30) + +This is the interval between checks. + +=item MAYBE_EMPTY (default no) + +If the command you're running to detect the condition is allowed to return +nothing (e.g. a grep line that might not even exist if there's no problem), +then set this to "yes". + +=item COLLECT (default ${HOME}/bin/pt-collect) + +This is the location of the 'collect' script. + +=item DEST (default ${HOME}/collected/) + +This is where to store the collected data. + +=item DURATION (default 30) + +How long to collect statistics data for? Make sure that this isn't longer +than SLEEP. + +=item SLEEP (default DURATION * 10) + +How long to sleep after collecting? + +=item PCT_THRESHOLD (default 95) + +Bail out if the disk is more than this %full. + +=item MB_THRESHOLD (default 100) + +Bail out if the disk has less than this many MB free. + +=item PURGE (default 30) + +Remove samples after this many days. + +=back + +=head1 SYSTEM REQUIREMENTS + +This tool requires Bash v3 or newer. + +=head1 BUGS + +For a list of known bugs, see L. + +Please report bugs at L. +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; +see L<"ENVIRONMENT">. + +=head1 DOWNLOADING + +Visit L 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 with the name of any tool. + +=head1 AUTHORS + +Baron Schwartz, Justin Swanhart, and Fernando Ipar + +=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 for more software developed by Percona. + +=head1 COPYRIGHT, LICENSE, AND WARRANTY + +This program is copyright 2010-2011 Baron Schwartz, 2011 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-stalk 1.0.1 + +=cut + +DOCUMENTATION