From d2308be64ccf7002b6ef6da61f8ad65f4a32e3ca Mon Sep 17 00:00:00 2001 From: Brian Fraser Date: Thu, 31 Jan 2013 21:11:14 -0300 Subject: [PATCH 1/2] Merged VersionCheck into Pingback --- lib/Pingback.pm | 262 +++++++++++++++++++++++++++++++++++- lib/VersionCheck.pm | 312 ------------------------------------------- t/lib/VersionCheck.t | 4 +- 3 files changed, 257 insertions(+), 321 deletions(-) delete mode 100644 lib/VersionCheck.pm diff --git a/lib/Pingback.pm b/lib/Pingback.pm index 93b8d764..0f7a352e 100644 --- a/lib/Pingback.pm +++ b/lib/Pingback.pm @@ -15,7 +15,7 @@ # this program; if not, write to the Free Software Foundation, Inc., 59 Temple # Place, Suite 330, Boston, MA 02111-1307 USA. # ########################################################################### -# VersionCheck package +# Pingback package # ########################################################################### { # Package: Pingback @@ -51,7 +51,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -137,10 +136,9 @@ sub pingback { my ($url) = @args{@required_args}; # Optional args - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); # GET https://upgrade.percona.com, the server will return # a plaintext list of items/programs it wants the tool @@ -165,7 +163,7 @@ sub pingback { # type => "mysql_variables", # vars => ["version", "version_comment"], # } - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" @@ -174,7 +172,7 @@ sub pingback { # Get the versions for those items in another hashref also keyed on # the items like: # "MySQL" => "MySQL Community Server 5.1.49-log", - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -212,7 +210,7 @@ sub pingback { # If the server has suggestions for items, it sends them back in # the same format: ITEM:TYPE:SUGGESTION\n. ITEM:TYPE is mostly for # debugging; the tool just repports the suggestions. - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -424,6 +422,256 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + # For Gentoo, which returns a value in quotes + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + # If there's a var, then its an explicit Perl variable name to get, + # else the item name is an implicity Perl module name to which we + # append ::VERSION to get the module's version. + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + # Explicitly stringify this else $PERL_VERSION will return + # as a version object. + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/lib/VersionCheck.pm b/lib/VersionCheck.pm deleted file mode 100644 index a765aa1a..00000000 --- a/lib/VersionCheck.pm +++ /dev/null @@ -1,312 +0,0 @@ -# This program is copyright 2012 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. -# ########################################################################### -# VersionCheck package -# ########################################################################### -{ -# Package: VersionCheck -# VersionCheck checks program versions with Percona. -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - # For Gentoo, which returns a value in quotes - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - # If there's a var, then its an explicit Perl variable name to get, - # else the item name is an implicity Perl module name to which we - # append ::VERSION to get the module's version. - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - # Explicitly stringify this else $PERL_VERSION will return - # as a version object. - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### diff --git a/t/lib/VersionCheck.t b/t/lib/VersionCheck.t index f5650c71..c00e54e0 100644 --- a/t/lib/VersionCheck.t +++ b/t/lib/VersionCheck.t @@ -12,7 +12,7 @@ use English qw(-no_match_vars); use Test::More; use Data::Dumper; -use VersionCheck; +use Pingback; use DSNParser; use Sandbox; use PerconaTest; @@ -23,7 +23,7 @@ my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp); my $master_dbh = $sb->get_dbh_for('master'); my $slave1_dbh = $sb->get_dbh_for('slave1'); -my $vc = VersionCheck->new(); +my $vc = 'Pingback'; sub test_v { my (%args) = @_; From 10634cd27c7cadb1a7475f11ae4c6547d896e1f4 Mon Sep 17 00:00:00 2001 From: Brian Fraser Date: Thu, 31 Jan 2013 22:24:47 -0300 Subject: [PATCH 2/2] Update files to use the merged Pingback+VersionCheck --- bin/pt-archiver | 560 ++++++++++++++---------------- bin/pt-config-diff | 549 ++++++++++++++---------------- bin/pt-deadlock-logger | 560 ++++++++++++++---------------- bin/pt-diskstats | 587 +++++++++++++++----------------- bin/pt-duplicate-key-checker | 549 ++++++++++++++---------------- bin/pt-find | 549 ++++++++++++++---------------- bin/pt-fk-error-logger | 587 +++++++++++++++----------------- bin/pt-heartbeat | 587 +++++++++++++++----------------- bin/pt-index-usage | 642 +++++++++++++++-------------------- bin/pt-kill | 598 +++++++++++++++----------------- bin/pt-online-schema-change | 549 ++++++++++++++---------------- bin/pt-query-advisor | 549 ++++++++++++++---------------- bin/pt-query-digest | 549 ++++++++++++++---------------- bin/pt-slave-delay | 598 +++++++++++++++----------------- bin/pt-slave-restart | 560 ++++++++++++++---------------- bin/pt-table-checksum | 598 +++++++++++++++----------------- bin/pt-table-sync | 598 +++++++++++++++----------------- bin/pt-upgrade | 549 ++++++++++++++---------------- bin/pt-variable-advisor | 560 ++++++++++++++---------------- 19 files changed, 4960 insertions(+), 5918 deletions(-) diff --git a/bin/pt-archiver b/bin/pt-archiver index 54aa0158..4ac1bab3 100755 --- a/bin/pt-archiver +++ b/bin/pt-archiver @@ -23,7 +23,6 @@ BEGIN { TableNibbler Daemon MasterSlave - VersionCheck HTTPMicro Pingback )); @@ -1220,6 +1219,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1516,6 +1516,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -3829,300 +3839,6 @@ sub _d { # End MasterSlave package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -4812,7 +4528,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -4888,10 +4603,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -4902,13 +4616,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -4938,7 +4652,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -5121,6 +4835,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-config-diff b/bin/pt-config-diff index 6a2bfc5f..5a33dcd7 100755 --- a/bin/pt-config-diff +++ b/bin/pt-config-diff @@ -23,7 +23,6 @@ BEGIN { MySQLConfig MySQLConfigComparer ReportFormatter - VersionCheck HTTPMicro Pingback )); @@ -3515,300 +3514,6 @@ sub _d { # End ReportFormatter package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -4498,7 +4203,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -4574,10 +4278,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -4588,13 +4291,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -4624,7 +4327,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -4807,6 +4510,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-deadlock-logger b/bin/pt-deadlock-logger index 601fdba2..3bd09558 100755 --- a/bin/pt-deadlock-logger +++ b/bin/pt-deadlock-logger @@ -20,7 +20,6 @@ BEGIN { Quoter DSNParser Daemon - VersionCheck HTTPMicro Pingback )); @@ -1217,6 +1216,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1513,6 +1513,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -2418,300 +2428,6 @@ sub _d { # End Daemon package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -3401,7 +3117,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -3477,10 +3192,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -3491,13 +3205,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -3527,7 +3241,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -3710,6 +3424,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-diskstats b/bin/pt-diskstats index dc5dc822..0ccff69e 100755 --- a/bin/pt-diskstats +++ b/bin/pt-diskstats @@ -22,7 +22,6 @@ BEGIN { DiskstatsGroupByDisk DiskstatsGroupBySample DiskstatsMenu - VersionCheck HTTPMicro Pingback )); @@ -1091,24 +1090,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -3509,300 +3510,6 @@ sub _d { # End DiskstatsMenu package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -4492,7 +4199,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -4568,10 +4274,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -4582,13 +4287,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -4618,7 +4323,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -4801,6 +4506,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-duplicate-key-checker b/bin/pt-duplicate-key-checker index fbc359ef..4cc7de13 100755 --- a/bin/pt-duplicate-key-checker +++ b/bin/pt-duplicate-key-checker @@ -23,7 +23,6 @@ BEGIN { Daemon Schema SchemaIterator - VersionCheck HTTPMicro Pingback )); @@ -3372,300 +3371,6 @@ sub _d { # End SchemaIterator package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -4355,7 +4060,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -4431,10 +4135,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -4445,13 +4148,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -4481,7 +4184,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -4664,6 +4367,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-find b/bin/pt-find index 70a54a2b..1a7cea77 100755 --- a/bin/pt-find +++ b/bin/pt-find @@ -19,7 +19,6 @@ BEGIN { Quoter TableParser Daemon - VersionCheck HTTPMicro Pingback )); @@ -2184,300 +2183,6 @@ sub _d { # End Daemon package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -3167,7 +2872,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -3243,10 +2947,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -3257,13 +2960,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -3293,7 +2996,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -3476,6 +3179,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-fk-error-logger b/bin/pt-fk-error-logger index d2a2eb90..9de07934 100755 --- a/bin/pt-fk-error-logger +++ b/bin/pt-fk-error-logger @@ -19,7 +19,6 @@ BEGIN { DSNParser Daemon Transformers - VersionCheck HTTPMicro Pingback )); @@ -1793,24 +1792,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -2124,300 +2125,6 @@ sub _d { # End Transformers package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -3107,7 +2814,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -3183,10 +2889,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -3197,13 +2902,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -3233,7 +2938,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -3416,6 +3121,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-heartbeat b/bin/pt-heartbeat index 35ab1432..df33eeb2 100755 --- a/bin/pt-heartbeat +++ b/bin/pt-heartbeat @@ -22,7 +22,6 @@ BEGIN { TableParser Retry Transformers - VersionCheck HTTPMicro Pingback )); @@ -3019,24 +3018,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -3350,300 +3351,6 @@ sub _d { # End Transformers package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -4333,7 +4040,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -4409,10 +4115,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -4423,13 +4128,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -4459,7 +4164,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -4642,6 +4347,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-index-usage b/bin/pt-index-usage index 7049e3fa..78250927 100755 --- a/bin/pt-index-usage +++ b/bin/pt-index-usage @@ -29,7 +29,6 @@ BEGIN { ExplainAnalyzer IndexUsage Progress - VersionCheck HTTPMicro Pingback )); @@ -3211,24 +3210,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -4434,61 +4435,6 @@ sub fingerprint { my ($explain) = @args{@required_args}; } -sub sparkline { - my ( $self, %args ) = @_; - my @required_args = qw(explain); - foreach my $arg ( @required_args ) { - die "I need a $arg argument" unless defined $args{$arg}; - } - my ($explain) = @args{@required_args}; - PTDEBUG && _d("Making sparkline for", Dumper($explain)); - - my $access_code = { - 'ALL' => 'a', - 'const' => 'c', - 'eq_ref' => 'e', - 'fulltext' => 'f', - 'index' => 'i', - 'index_merge' => 'm', - 'range' => 'n', - 'ref_or_null' => 'o', - 'ref' => 'r', - 'system' => 's', - 'unique_subquery' => 'u', - }; - - my $sparkline = ''; - my ($T, $F); # Using temporary, Using filesort - - foreach my $tbl ( @$explain ) { - my $code; - if ( defined $tbl->{type} ) { - $code = $access_code->{$tbl->{type}} || "?"; - $code = uc $code if $tbl->{Extra}->{'Using index'}; - } - else { - $code = '-' - }; - $sparkline .= $code; - - $T = 1 if $tbl->{Extra}->{'Using temporary'}; - $F = 1 if $tbl->{Extra}->{'Using filesort'}; - } - - if ( $T || $F ) { - if ( $explain->[-1]->{Extra}->{'Using temporary'} - || $explain->[-1]->{Extra}->{'Using filesort'} ) { - $sparkline .= ">" . ($T ? "T" : "") . ($F ? "F" : ""); - } - else { - $sparkline = ($T ? "T" : "") . ($F ? "F" : "") . ">$sparkline"; - } - } - - PTDEBUG && _d("sparkline:", $sparkline); - return $sparkline; -} - sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } @@ -4880,300 +4826,6 @@ sub _d { # End Progress package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -5863,7 +5515,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -5939,10 +5590,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -5953,13 +5603,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -5989,7 +5639,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -6172,6 +5822,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-kill b/bin/pt-kill index d6fa3869..51218b91 100755 --- a/bin/pt-kill +++ b/bin/pt-kill @@ -27,7 +27,6 @@ BEGIN { QueryRewriter Retry Cxn - VersionCheck HTTPMicro Pingback )); @@ -1224,6 +1223,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1520,6 +1520,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -2126,24 +2136,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -4850,300 +4862,6 @@ sub _d { # End Cxn package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -5833,7 +5551,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -5909,10 +5626,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -5923,13 +5639,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -5959,7 +5675,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -6142,6 +5858,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-online-schema-change b/bin/pt-online-schema-change index d65a8ee7..ffaac8fd 100755 --- a/bin/pt-online-schema-change +++ b/bin/pt-online-schema-change @@ -34,7 +34,6 @@ BEGIN { Transformers CleanupTask IndexLength - VersionCheck HTTPMicro Pingback Percona::XtraDB::Cluster @@ -6311,300 +6310,6 @@ sub _d { # End IndexLength package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -7294,7 +6999,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -7370,10 +7074,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -7384,13 +7087,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -7420,7 +7123,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -7603,6 +7306,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-query-advisor b/bin/pt-query-advisor index 9daace80..1ce8b838 100755 --- a/bin/pt-query-advisor +++ b/bin/pt-query-advisor @@ -31,7 +31,6 @@ BEGIN { SQLParser TableParser ReportFormatter - VersionCheck HTTPMicro Pingback )); @@ -6597,300 +6596,6 @@ sub _d { # End ReportFormatter package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -7580,7 +7285,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -7656,10 +7360,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -7670,13 +7373,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -7706,7 +7409,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -7889,6 +7592,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-query-digest b/bin/pt-query-digest index 695c5056..b02983e1 100755 --- a/bin/pt-query-digest +++ b/bin/pt-query-digest @@ -48,7 +48,6 @@ BEGIN { FileIterator Runtime Pipeline - VersionCheck HTTPMicro Pingback )); @@ -12194,300 +12193,6 @@ sub _d { # End Pipeline package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -13177,7 +12882,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -13253,10 +12957,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -13267,13 +12970,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -13303,7 +13006,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -13486,6 +13189,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-slave-delay b/bin/pt-slave-delay index 2c702d12..b0d0e16d 100755 --- a/bin/pt-slave-delay +++ b/bin/pt-slave-delay @@ -20,7 +20,6 @@ BEGIN { Daemon Transformers Retry - VersionCheck HTTPMicro Pingback )); @@ -1217,6 +1216,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1513,6 +1513,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -2119,24 +2129,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -2528,300 +2540,6 @@ sub _d { # End Retry package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -3511,7 +3229,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -3587,10 +3304,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -3601,13 +3317,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -3637,7 +3353,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -3820,6 +3536,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-slave-restart b/bin/pt-slave-restart index bd04d04a..fc1ac196 100755 --- a/bin/pt-slave-restart +++ b/bin/pt-slave-restart @@ -21,7 +21,6 @@ BEGIN { DSNParser MasterSlave Daemon - VersionCheck HTTPMicro Pingback )); @@ -1344,6 +1343,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1640,6 +1640,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -3154,300 +3164,6 @@ sub _d { # End Daemon package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -4137,7 +3853,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -4213,10 +3928,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -4227,13 +3941,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -4263,7 +3977,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -4446,6 +4160,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-table-checksum b/bin/pt-table-checksum index 574a03a4..54e1110b 100755 --- a/bin/pt-table-checksum +++ b/bin/pt-table-checksum @@ -14,7 +14,6 @@ use warnings FATAL => 'all'; BEGIN { $INC{$_} = __FILE__ for map { (my $pkg = "$_.pm") =~ s!::!/!g; $pkg } (qw( Percona::Toolkit - VersionCheck HTTPMicro Pingback DSNParser @@ -61,300 +60,6 @@ our $VERSION = '2.1.8'; # End Percona::Toolkit package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -1044,7 +749,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -1120,10 +824,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -1134,13 +837,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -1170,7 +873,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -1353,6 +1056,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } @@ -2917,6 +2864,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -3213,6 +3161,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -7211,24 +7169,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; diff --git a/bin/pt-table-sync b/bin/pt-table-sync index e27d5713..d38fd2e3 100755 --- a/bin/pt-table-sync +++ b/bin/pt-table-sync @@ -36,7 +36,6 @@ BEGIN { SchemaIterator Transformers Retry - VersionCheck HTTPMicro Pingback )); @@ -1233,6 +1232,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1529,6 +1529,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -7887,24 +7897,26 @@ use Time::Local qw(timegm timelocal); use Digest::MD5 qw(md5_hex); use B qw(); -require Exporter; -our @ISA = qw(Exporter); -our %EXPORT_TAGS = (); -our @EXPORT = (); -our @EXPORT_OK = qw( - micro_t - percentage_of - secs_to_time - time_to_secs - shorten - ts - parse_timestamp - unix_timestamp - any_unix_timestamp - make_checksum - crc32 - encode_json -); +BEGIN { + require Exporter; + our @ISA = qw(Exporter); + our %EXPORT_TAGS = (); + our @EXPORT = (); + our @EXPORT_OK = qw( + micro_t + percentage_of + secs_to_time + time_to_secs + shorten + ts + parse_timestamp + unix_timestamp + any_unix_timestamp + make_checksum + crc32 + encode_json + ); +} our $mysql_ts = qr/(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)(\.\d+)?/; our $proper_ts = qr/(\d\d\d\d)-(\d\d)-(\d\d)[T ](\d\d):(\d\d):(\d\d)(\.\d+)?/; @@ -8296,300 +8308,6 @@ sub _d { # End Retry package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -9279,7 +8997,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -9355,10 +9072,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -9369,13 +9085,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -9405,7 +9121,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -9588,6 +9304,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-upgrade b/bin/pt-upgrade index ffe73898..143133fb 100755 --- a/bin/pt-upgrade +++ b/bin/pt-upgrade @@ -43,7 +43,6 @@ BEGIN { CompareQueryTimes CompareWarnings Retry - VersionCheck HTTPMicro Pingback )); @@ -10823,300 +10822,6 @@ sub _d { # End Retry package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -11806,7 +11511,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -11882,10 +11586,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -11896,13 +11599,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -11932,7 +11635,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -12115,6 +11818,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } diff --git a/bin/pt-variable-advisor b/bin/pt-variable-advisor index 0732fa2c..91d8da8a 100755 --- a/bin/pt-variable-advisor +++ b/bin/pt-variable-advisor @@ -24,7 +24,6 @@ BEGIN { Advisor AdvisorRules VariableAdvisorRules - VersionCheck HTTPMicro Pingback )); @@ -1221,6 +1220,7 @@ sub Mo::import { _set_package_isa($caller, @_); _set_inherited_metadata($caller); }, + override => \&override, has => sub { my $names = shift; for my $attribute ( ref $names ? @$names : $names ) { @@ -1517,6 +1517,16 @@ BEGIN { } } +sub override { + my ($methods, $code) = @_; + my $caller = scalar caller; + + for my $method ( ref($methods) ? @$methods : $methods ) { + my $full_method = "${caller}::${method}"; + *{_glob_for $full_method} = $code; + } +} + } 1; } @@ -3437,300 +3447,6 @@ sub _d { # End VariableAdvisorRules package # ########################################################################### -# ########################################################################### -# VersionCheck 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/VersionCheck.pm -# t/lib/VersionCheck.t -# See https://launchpad.net/percona-toolkit for more information. -# ########################################################################### -{ -package VersionCheck; - -use strict; -use warnings FATAL => 'all'; -use English qw(-no_match_vars); - -use constant PTDEBUG => $ENV{PTDEBUG} || 0; - -use File::Basename (); -use Data::Dumper (); - -sub Dumper { - local $Data::Dumper::Indent = 1; - local $Data::Dumper::Sortkeys = 1; - local $Data::Dumper::Quotekeys = 0; - - Data::Dumper::Dumper(@_); -} - -sub new { - my ($class, %args) = @_; - my $self = { - valid_types => qr/ - ^(?: - os_version - |perl_version - |perl_module_version - |mysql_variable - |bin_version - )$/x, - }; - return bless $self, $class; -} - -sub parse_server_response { - my ($self, %args) = @_; - my @required_args = qw(response); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($response) = @args{@required_args}; - - my %items = map { - my ($item, $type, $vars) = split(";", $_); - if ( !defined $args{split_vars} || $args{split_vars} ) { - $vars = [ split(",", ($vars || '')) ]; - } - $item => { - item => $item, - type => $type, - vars => $vars, - }; - } split("\n", $response); - - PTDEBUG && _d('Items:', Dumper(\%items)); - - return \%items; -} - -sub get_versions { - my ($self, %args) = @_; - my @required_args = qw(items); - foreach my $arg ( @required_args ) { - die "I need a $arg arugment" unless $args{$arg}; - } - my ($items) = @args{@required_args}; - - my %versions; - foreach my $item ( values %$items ) { - next unless $self->valid_item($item); - - eval { - my $func = 'get_' . $item->{type}; - my $version = $self->$func( - item => $item, - instances => $args{instances}, - ); - if ( $version ) { - chomp $version unless ref($version); - $versions{$item->{item}} = $version; - } - }; - if ( $EVAL_ERROR ) { - PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); - } - } - - return \%versions; -} - -sub valid_item { - my ($self, $item) = @_; - return unless $item; - - if ( ($item->{type} || '') !~ m/$self->{valid_types}/ ) { - PTDEBUG && _d('Invalid type:', $item->{type}); - return; - } - - return 1; -} - -sub get_os_version { - my ($self) = @_; - - if ( $OSNAME eq 'MSWin32' ) { - require Win32; - return Win32::GetOSDisplayName(); - } - - chomp(my $platform = `uname -s`); - PTDEBUG && _d('platform:', $platform); - return $OSNAME unless $platform; - - chomp(my $lsb_release - = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); - PTDEBUG && _d('lsb_release:', $lsb_release); - - my $release = ""; - - if ( $platform eq 'Linux' ) { - if ( -f "/etc/fedora-release" ) { - $release = `cat /etc/fedora-release`; - } - elsif ( -f "/etc/redhat-release" ) { - $release = `cat /etc/redhat-release`; - } - elsif ( -f "/etc/system-release" ) { - $release = `cat /etc/system-release`; - } - elsif ( $lsb_release ) { - $release = `$lsb_release -ds`; - } - elsif ( -f "/etc/lsb-release" ) { - $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; - $release =~ s/^\w+="([^"]+)".+/$1/; - } - elsif ( -f "/etc/debian_version" ) { - chomp(my $rel = `cat /etc/debian_version`); - $release = "Debian $rel"; - if ( -f "/etc/apt/sources.list" ) { - chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); - $release .= " ($code_name)" if $code_name; - } - } - elsif ( -f "/etc/os-release" ) { # openSUSE - chomp($release = `grep PRETTY_NAME /etc/os-release`); - $release =~ s/^PRETTY_NAME="(.+)"$/$1/; - } - elsif ( `ls /etc/*release 2>/dev/null` ) { - if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { - $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; - } - else { - $release = `cat /etc/*release | head -n1`; - } - } - } - elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { - my $rel = `uname -r`; - $release = "$platform $rel"; - } - elsif ( $platform eq "SunOS" ) { - my $rel = `head -n1 /etc/release` || `uname -r`; - $release = "$platform $rel"; - } - - if ( !$release ) { - PTDEBUG && _d('Failed to get the release, using platform'); - $release = $platform; - } - chomp($release); - - $release =~ s/^"|"$//g; - - PTDEBUG && _d('OS version =', $release); - return $release; -} - -sub get_perl_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $version = sprintf '%vd', $PERL_VERSION; - PTDEBUG && _d('Perl version', $version); - return $version; -} - -sub get_perl_module_version { - my ($self, %args) = @_; - my $item = $args{item}; - return unless $item; - - my $var = $item->{item} . '::VERSION'; - my $version = _get_scalar($var); - PTDEBUG && _d('Perl version for', $var, '=', "$version"); - - return $version ? "$version" : $version; -} - -sub _get_scalar { - no strict; - return ${*{shift()}}; -} - -sub get_mysql_variable { - my $self = shift; - return $self->_get_from_mysql( - show => 'VARIABLES', - @_, - ); -} - -sub _get_from_mysql { - my ($self, %args) = @_; - my $show = $args{show}; - my $item = $args{item}; - my $instances = $args{instances}; - return unless $show && $item; - - if ( !$instances || !@$instances ) { - if ( $ENV{PTVCDEBUG} || PTDEBUG ) { - _d('Cannot check', $item, 'because there are no MySQL instances'); - } - return; - } - - my @versions; - my %version_for; - foreach my $instance ( @$instances ) { - my $dbh = $instance->{dbh}; - local $dbh->{FetchHashKeyName} = 'NAME_lc'; - my $sql = qq/SHOW $show/; - PTDEBUG && _d($sql); - my $rows = $dbh->selectall_hashref($sql, 'variable_name'); - - my @versions; - foreach my $var ( @{$item->{vars}} ) { - $var = lc($var); - my $version = $rows->{$var}->{value}; - PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, - 'on', $instance->{name}); - push @versions, $version; - } - - $version_for{ $instance->{id} } = join(' ', @versions); - } - - return \%version_for; -} - -sub get_bin_version { - my ($self, %args) = @_; - my $item = $args{item}; - my $cmd = $item->{item}; - return unless $cmd; - - my $sanitized_command = File::Basename::basename($cmd); - PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); - return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; - - my $output = `$sanitized_command --version 2>&1`; - PTDEBUG && _d('output:', $output); - - my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; - - PTDEBUG && _d('Version for', $sanitized_command, '=', $version); - return $version; -} - -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 VersionCheck package -# ########################################################################### - # ########################################################################### # HTTPMicro package # This package is a copy without comments from the original. The original @@ -4420,7 +4136,6 @@ local $EVAL_ERROR; eval { require Percona::Toolkit; require HTTPMicro; - require VersionCheck; }; sub version_check { @@ -4496,10 +4211,9 @@ sub pingback { } my ($url) = @args{@required_args}; - my ($instances, $ua, $vc) = @args{qw(instances ua VersionCheck)}; + my ($instances, $ua) = @args{qw(instances ua)}; $ua ||= HTTPMicro->new( timeout => 5 ); - $vc ||= VersionCheck->new(); my $response = $ua->request('GET', $url); ($ENV{PTVCDEBUG} || PTDEBUG) && _d('Server response:', Dumper($response)); @@ -4510,13 +4224,13 @@ sub pingback { die("GET on $url did not return any programs to check") if !$response->{content}; - my $items = $vc->parse_server_response( + my $items = __PACKAGE__->parse_server_response( response => $response->{content} ); die "Failed to parse server requested programs: $response->{content}" if !scalar keys %$items; - my $versions = $vc->get_versions( + my $versions = __PACKAGE__->get_versions( items => $items, instances => $instances, ); @@ -4546,7 +4260,7 @@ sub pingback { return unless $response->{content}; - $items = $vc->parse_server_response( + $items = __PACKAGE__->parse_server_response( response => $response->{content}, split_vars => 0, ); @@ -4729,6 +4443,250 @@ sub validate_options { . join(", ", @values[0..$#values-1]) . " and $values[-1]" ); } +sub parse_server_response { + my ($self, %args) = @_; + my @required_args = qw(response); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($response) = @args{@required_args}; + + my %items = map { + my ($item, $type, $vars) = split(";", $_); + if ( !defined $args{split_vars} || $args{split_vars} ) { + $vars = [ split(",", ($vars || '')) ]; + } + $item => { + item => $item, + type => $type, + vars => $vars, + }; + } split("\n", $response); + + PTDEBUG && _d('Items:', Dumper(\%items)); + + return \%items; +} + +sub get_versions { + my ($self, %args) = @_; + my @required_args = qw(items); + foreach my $arg ( @required_args ) { + die "I need a $arg arugment" unless $args{$arg}; + } + my ($items) = @args{@required_args}; + + my %versions; + foreach my $item ( values %$items ) { + next unless $self->valid_item($item); + + eval { + my $func = 'get_' . $item->{type}; + my $version = $self->$func( + item => $item, + instances => $args{instances}, + ); + if ( $version ) { + chomp $version unless ref($version); + $versions{$item->{item}} = $version; + } + }; + if ( $EVAL_ERROR ) { + PTDEBUG && _d('Error getting version for', Dumper($item), $EVAL_ERROR); + } + } + + return \%versions; +} + +sub valid_item { + my ($self, $item) = @_; + return unless $item; + + if ( ($item->{type} || '') !~ m/ + ^(?: + os_version + |perl_version + |perl_module_version + |mysql_variable + |bin_version + )$/x ) { + PTDEBUG && _d('Invalid type:', $item->{type}); + return; + } + + return 1; +} + +sub get_os_version { + my ($self) = @_; + + if ( $OSNAME eq 'MSWin32' ) { + require Win32; + return Win32::GetOSDisplayName(); + } + + chomp(my $platform = `uname -s`); + PTDEBUG && _d('platform:', $platform); + return $OSNAME unless $platform; + + chomp(my $lsb_release + = `which lsb_release 2>/dev/null | awk '{print \$1}'` || ''); + PTDEBUG && _d('lsb_release:', $lsb_release); + + my $release = ""; + + if ( $platform eq 'Linux' ) { + if ( -f "/etc/fedora-release" ) { + $release = `cat /etc/fedora-release`; + } + elsif ( -f "/etc/redhat-release" ) { + $release = `cat /etc/redhat-release`; + } + elsif ( -f "/etc/system-release" ) { + $release = `cat /etc/system-release`; + } + elsif ( $lsb_release ) { + $release = `$lsb_release -ds`; + } + elsif ( -f "/etc/lsb-release" ) { + $release = `grep DISTRIB_DESCRIPTION /etc/lsb-release`; + $release =~ s/^\w+="([^"]+)".+/$1/; + } + elsif ( -f "/etc/debian_version" ) { + chomp(my $rel = `cat /etc/debian_version`); + $release = "Debian $rel"; + if ( -f "/etc/apt/sources.list" ) { + chomp(my $code_name = `awk '/^deb/ {print \$3}' /etc/apt/sources.list | awk -F/ '{print \$1}'| awk 'BEGIN {FS="|"} {print \$1}' | sort | uniq -c | sort -rn | head -n1 | awk '{print \$2}'`); + $release .= " ($code_name)" if $code_name; + } + } + elsif ( -f "/etc/os-release" ) { # openSUSE + chomp($release = `grep PRETTY_NAME /etc/os-release`); + $release =~ s/^PRETTY_NAME="(.+)"$/$1/; + } + elsif ( `ls /etc/*release 2>/dev/null` ) { + if ( `grep DISTRIB_DESCRIPTION /etc/*release 2>/dev/null` ) { + $release = `grep DISTRIB_DESCRIPTION /etc/*release | head -n1`; + } + else { + $release = `cat /etc/*release | head -n1`; + } + } + } + elsif ( $platform =~ m/(?:BSD|^Darwin)$/ ) { + my $rel = `uname -r`; + $release = "$platform $rel"; + } + elsif ( $platform eq "SunOS" ) { + my $rel = `head -n1 /etc/release` || `uname -r`; + $release = "$platform $rel"; + } + + if ( !$release ) { + PTDEBUG && _d('Failed to get the release, using platform'); + $release = $platform; + } + chomp($release); + + $release =~ s/^"|"$//g; + + PTDEBUG && _d('OS version =', $release); + return $release; +} + +sub get_perl_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $version = sprintf '%vd', $PERL_VERSION; + PTDEBUG && _d('Perl version', $version); + return $version; +} + +sub get_perl_module_version { + my ($self, %args) = @_; + my $item = $args{item}; + return unless $item; + + my $var = $item->{item} . '::VERSION'; + my $version = _get_scalar($var); + PTDEBUG && _d('Perl version for', $var, '=', "$version"); + + return $version ? "$version" : $version; +} + +sub _get_scalar { + no strict; + return ${*{shift()}}; +} + +sub get_mysql_variable { + my $self = shift; + return $self->_get_from_mysql( + show => 'VARIABLES', + @_, + ); +} + +sub _get_from_mysql { + my ($self, %args) = @_; + my $show = $args{show}; + my $item = $args{item}; + my $instances = $args{instances}; + return unless $show && $item; + + if ( !$instances || !@$instances ) { + if ( $ENV{PTVCDEBUG} || PTDEBUG ) { + _d('Cannot check', $item, 'because there are no MySQL instances'); + } + return; + } + + my @versions; + my %version_for; + foreach my $instance ( @$instances ) { + my $dbh = $instance->{dbh}; + local $dbh->{FetchHashKeyName} = 'NAME_lc'; + my $sql = qq/SHOW $show/; + PTDEBUG && _d($sql); + my $rows = $dbh->selectall_hashref($sql, 'variable_name'); + + my @versions; + foreach my $var ( @{$item->{vars}} ) { + $var = lc($var); + my $version = $rows->{$var}->{value}; + PTDEBUG && _d('MySQL version for', $item->{item}, '=', $version, + 'on', $instance->{name}); + push @versions, $version; + } + + $version_for{ $instance->{id} } = join(' ', @versions); + } + + return \%version_for; +} + +sub get_bin_version { + my ($self, %args) = @_; + my $item = $args{item}; + my $cmd = $item->{item}; + return unless $cmd; + + my $sanitized_command = File::Basename::basename($cmd); + PTDEBUG && _d('cmd:', $cmd, 'sanitized:', $sanitized_command); + return if $sanitized_command !~ /\A[a-zA-Z0-9_-]+\z/; + + my $output = `$sanitized_command --version 2>&1`; + PTDEBUG && _d('output:', $output); + + my ($version) = $output =~ /v?([0-9]+\.[0-9]+(?:\.[\w-]+)?)/; + + PTDEBUG && _d('Version for', $sanitized_command, '=', $version); + return $version; +} + sub _d { my ($package, undef, $line) = caller 0; @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; }