diff --git a/bin/pt-agent b/bin/pt-agent index 8ab2934b..16bce8dc 100755 --- a/bin/pt-agent +++ b/bin/pt-agent @@ -7746,7 +7746,7 @@ Once the previous step are complete, run: $ pt-agent --daemonize -The tool daemonizes and continues running in the background. +The agent daemonizes and continues running in the background. =item 5. Configure the agent at L @@ -7812,7 +7812,7 @@ See the L<"--help"> output for a list of default config files. =item --daemonize -Daemonize the tool. This causes the tool to fork into the background and +Daemonize the agent. This causes the agent to fork into the background and L<"--log"> all output. Fork to the background and detach from the shell. POSIX operating systems only. @@ -7824,9 +7824,30 @@ short form: -F; type: string Only read MySQL options from the given file. You must give an absolute pathname. +=item --disk-bytes-free + +type: size; default: 100M + +Stop all services if the disk has less than this much free space. +This prevents the agent from filling up the disk with service data. + +Valid size value suffixes are k, M, G, and T. + +=item --disk-pct-free + +type: int; default: 5 + +Stop all services if the disk has less than this percent free space. +This prevents the agent from filling up the disk with service data. + +This option works similarly to L<"--disk-bytes-free"> but specifies a +percentage margin of safety instead of a bytes margin of safety. +The agent honors both options, and will not collect any data unless both +margins are satisfied. + =item --help -Print the tool's help and exit. +Print the agent's help and exit. =item --host @@ -7911,7 +7932,7 @@ type: Array Set the MySQL variables in this comma-separated list of C pairs. -By default, the tool sets: +By default, the agent sets: =for comment ignore-pt-internal-value MAGIC_set_vars @@ -7922,7 +7943,7 @@ Variables specified on the command line override these defaults. For example, specifying C<--set-vars wait_timeout=500> overrides the default value of C<10000>. -The tool prints a warning and continues if a variable cannot be set. +The agent prints a warning and continues if a variable cannot be set. =item --socket @@ -7956,7 +7977,7 @@ MySQL user, if not the current system user. =item --version -Print the tool's version and exit. +Print the agent's version and exit. =back diff --git a/lib/Safeguards.pm b/lib/Safeguards.pm new file mode 100644 index 00000000..1cee49af --- /dev/null +++ b/lib/Safeguards.pm @@ -0,0 +1,94 @@ +# This program is copyright 2013 Percona Ireland Ltd. +# 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 +# MERCHANTIBILITY 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. +# ########################################################################### +# Safeguards package +# ########################################################################### +package Safeguards; + +use strict; +use warnings FATAL => 'all'; +use English qw(-no_match_vars); + +use constant PTDEBUG => $ENV{PTDEBUG} || 0; + +sub new { + my ($class, %args) = @_; + my $self = { + disk_bytes_free => $args{disk_bytes_free} || 104857600, # 100 MiB + disk_pct_free => $args{disk_pct_free} || 5, + }; + return bless $self, $class; +} + +sub get_disk_space { + my ($self, %args) = @_; + my $filesystem = $args{filesystem} || $ENV{PWD}; + + # Filesystem 1024-blocks Used Available Capacity Mounted on + # /dev/disk0s2 118153176 94409664 23487512 81% / + my $disk_space = `df -P -k "$filesystem"`; + chop($disk_space) if $disk_space; + PTDEBUG && _d('Disk space on', $filesystem, $disk_space); + + return $disk_space; +} + +sub check_disk_space() { + my ($self, %args) = @_; + my $disk_space = $args{disk_space}; + PTDEBUG && _d("Checking disk space:\n", $disk_space); + + # There may be other info, so extract just the partition line, + # i.e. the first line starting with /, as in: + # Filesystem 1024-blocks Used Available Capacity Mounted on + # /dev/disk0s2 118153176 94409664 23487512 81% / + my ($partition) = $disk_space =~ m/^\s*(\/.+)/m; + PTDEBUG && _d('Partition:', $partition); + die "Failed to parse partition from disk space:\n$disk_space" + unless $partition; + + # Parse the partition line. + my (undef, undef, $bytes_used, $bytes_free, $pct_used, undef) + = $partition =~ m/(\S+)/g; + PTDEBUG && _d('Bytes used:', $bytes_used, 'free:', $bytes_free, + 'Percentage used:', $pct_used); + + # Convert 1024-blocks blocks to bytes. + $bytes_used = ($bytes_used || 0) * 1024; + $bytes_free = ($bytes_free || 0) * 1024; + + # Convert pct used to free. + $pct_used =~ s/%//; + my $pct_free = 100 - ($pct_used || 0); + + # Return true if both thresholds are ok. + return $bytes_free >= $self->{disk_bytes_free} + && $pct_free >= $self->{disk_pct_free}; +} + +sub _d { + my ($package, undef, $line) = caller 0; + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } + map { defined $_ ? $_ : 'undef' } + @_; + print STDERR "# $package:$line $PID ", join(' ', @_), "\n"; +} + +1; +# ########################################################################### +# End Safeguards package +# ########################################################################### diff --git a/t/lib/Safeguards.t b/t/lib/Safeguards.t new file mode 100644 index 00000000..43d6db5c --- /dev/null +++ b/t/lib/Safeguards.t @@ -0,0 +1,69 @@ +#!/usr/bin/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; + +use Safeguards; +use Percona::Test; + +use Data::Dumper; +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; +$Data::Dumper::Quotekeys = 0; + +my $sample = "t/lib/samples/bash/"; + +my $safeguards = Safeguards->new( + disk_bytes_free => 104857600, + disk_pct_free => 10, +); + +# 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 % +my $df = slurp_file("$trunk/$sample/diskspace001.txt"); + +ok( + $safeguards->check_disk_space( + disk_space => $df, + ), + "diskspace001: Enough bytes and pct free" +); + +$safeguards = Safeguards->new( + disk_bytes_free => 104857600, + disk_pct_free => 20, +); + +ok( + !$safeguards->check_disk_space( + disk_space => $df, + ), + "diskspace001: Not enough pct free" +); + +$safeguards = Safeguards->new( + disk_bytes_free => 24_051_212_289, + disk_pct_free => 5, +); + +ok( + !$safeguards->check_disk_space( + disk_space => $df, + ), + "diskspace001: Not enough bytes free" +); + +done_testing;