From 95c13972891ee76de9023b0fde9669a806a5ed20 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 23 Mar 2016 16:18:36 +0100 Subject: [PATCH 01/44] #163 removing by default banned port 443,80, 8080,8443 and let bannedports managed this case. --- mysqltuner.pl | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 755426f..76196ef 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -570,15 +570,18 @@ sub mysql_setup { } } elsif ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) { - # It's a Plesk box, use the available credentials $mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`"; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; unless ( $loginstatus =~ /mysqld is alive/ ) { - badprint -"Attempted to use login credentials from Plesk, but they failed."; - exit 1; - } + # Plesk 10+ + $mysqllogin = "-u admin -p`/usr/local/psa/bin/admin --show-password`"; + $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; + unless ( $loginstatus =~ /mysqld is alive/ ) { + badprint "Attempted to use login credentials from Plesk and Plesk 10+, but they failed."; + exit 1; + } + } } elsif ( -r "/usr/local/directadmin/conf/mysql.conf" and $doremote == 0 ) { @@ -961,18 +964,18 @@ sub system_recommendations { goodprint "There is less than 10 opened ports on this server."; } - if ( is_open_port(80) or is_open_port(443) ) { - badprint "There is Apache like server running on 80 or 443 port."; - push( @generalrec, "Consider dedicating a server for Web server in production !" ); - } else { - goodprint "No Web server runing on 80 and 444 port."; - } - if ( is_open_port(8080) or is_open_port(8443) ) { - badprint "There is Application server running on 8080 or 8443 port."; - push( @generalrec, "Consider dedicating a server for Application server in production !" ); - } else { - goodprint "No Application server runing on 8080 or 8443 port."; - } +# if ( is_open_port(80) or is_open_port(443) ) { +# badprint "There is Apache like server running on 80 or 443 port."; +# push( @generalrec, "Consider dedicating a server for Web server in production !" ); +# } else { +# goodprint "No Web server runing on 80 and 443 port."; +# } +# if ( is_open_port(8080) or is_open_port(8443) ) { +# badprint "There is Application server running on 8080 or 8443 port."; +# push( @generalrec, "Consider dedicating a server for Application server in production !" ); +# } else { +# goodprint "No Application server runing on 8080 or 8443 port."; +# } foreach my $banport (@banned_ports) { if ( is_open_port($banport) ) { badprint "Banned port: $banport is opened.."; From 30b7f2e52c5b8ee6bc04edd0c7206440308a9db8 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 23 Mar 2016 16:33:02 +0100 Subject: [PATCH 02/44] #164 removing by default check for 10 opened ports. option maxallowedport added for this case. --- mysqltuner.pl | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 76196ef..0756ad3 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1,5 +1,5 @@ #!/usr/bin/env perl -# mysqltuner.pl - Version 1.6.7 +# mysqltuner.pl - Version 1.6.8 # High Performance MySQL Tuning Script # Copyright (C) 2006-2015 Major Hayden - major@mhtx.net # @@ -51,7 +51,7 @@ use Data::Dumper; $Data::Dumper::Pair = " : "; # Set up a few variables for use in the script -my $tunerversion = "1.6.7"; +my $tunerversion = "1.6.8"; my ( @adjvars, @generalrec ); # Set defaults @@ -73,7 +73,8 @@ my %opt = ( "checkversion" => 0, "buffers" => 0, "passwordfile" => 0, - "bannedports" => '', + "bannedports" => '', + "maxportallowed"= >0, "outputfile" => 0, "dbstat" => 0, "idxstat" => 0, @@ -93,7 +94,7 @@ GetOptions( 'mysqlcmd=s', 'help', 'buffers', 'skippassword', 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', - 'bannedports=s', + 'bannedports=s','maxportallowed=s' ); if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } @@ -136,7 +137,8 @@ sub usage { . " --debug Print debug information\n" . " --dbstat Print database information\n" . " --idxstat Print index information\n" - . " --bannedports ports banned separated by comma(,)\n" + . " --bannedports Ports banned separated by comma(,)\n" + . " --maxportallowed Number of ports opened allowed on this hosts\n" . " --cvefile CVE File for vulnerability checks\n" . " --nocolor Don't print output in color\n" . " --json Print result as JSON string\n" @@ -955,16 +957,17 @@ sub system_recommendations { #if ($omem > #exit 0; - my @opened_ports=get_opened_ports; - infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; - if (scalar(@opened_ports) > 10) { - badprint "There is too many listening ports: ". scalar(@opened_ports). " > 10"; - push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); - } else { - goodprint "There is less than 10 opened ports on this server."; + if ($opt{'maxportallowed'} > 0) { + my @opened_ports=get_opened_ports; + infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; + if (scalar(@opened_ports) > $opt{'maxportallowed'}) { + badprint "There is too many listening ports: ". scalar(@opened_ports). " > 10"; + push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); + } else { + goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; + } } - -# if ( is_open_port(80) or is_open_port(443) ) { + # if ( is_open_port(80) or is_open_port(443) ) { # badprint "There is Apache like server running on 80 or 443 port."; # push( @generalrec, "Consider dedicating a server for Web server in production !" ); # } else { @@ -3264,7 +3267,7 @@ __END__ =head1 NAME - MySQLTuner 1.6.7 - MySQL High Performance Tuning Script + MySQLTuner 1.6.8 - MySQL High Performance Tuning Script =head1 IMPORTANT USAGE GUIDELINES @@ -3302,7 +3305,8 @@ You must provide the remote server's total memory when connecting to other serve --debug Print debug information --dbstat Print database information --idxstat Print index information - --bannedports ports banned separated by comma(,) + --bannedports Ports banned separated by comma(,) + --maxportallowed Number of ports opened allowed on this hosts --cvefile CVE File for vulnerability checks --nocolor Don't print output in color --json Print result as JSON string From 84d108bc702dbae5e7209f949ec8c3ab8b04007c Mon Sep 17 00:00:00 2001 From: root Date: Wed, 23 Mar 2016 16:35:59 +0100 Subject: [PATCH 03/44] Broken code fixed --- mysqltuner.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 0756ad3..eb32211 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -74,7 +74,7 @@ my %opt = ( "buffers" => 0, "passwordfile" => 0, "bannedports" => '', - "maxportallowed"= >0, + "maxportallowed"=> 0, "outputfile" => 0, "dbstat" => 0, "idxstat" => 0, @@ -94,7 +94,7 @@ GetOptions( 'mysqlcmd=s', 'help', 'buffers', 'skippassword', 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', - 'bannedports=s','maxportallowed=s' + 'bannedports=s','maxportallowed=s', ); if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } From 10a5ad0fdb71547bec37362955cec51182e3ee2a Mon Sep 17 00:00:00 2001 From: root Date: Thu, 24 Mar 2016 10:21:02 +0100 Subject: [PATCH 04/44] Fix message for #164 issue --- mysqltuner.pl | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index eb32211..aa71a9b 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -961,24 +961,13 @@ sub system_recommendations { my @opened_ports=get_opened_ports; infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; if (scalar(@opened_ports) > $opt{'maxportallowed'}) { - badprint "There is too many listening ports: ". scalar(@opened_ports). " > 10"; + badprint "There is too many listening ports: ". scalar(@opened_ports) " opened > ".$opt{'maxportallowed'}. "allowed."; push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); } else { goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; } } - # if ( is_open_port(80) or is_open_port(443) ) { -# badprint "There is Apache like server running on 80 or 443 port."; -# push( @generalrec, "Consider dedicating a server for Web server in production !" ); -# } else { -# goodprint "No Web server runing on 80 and 443 port."; -# } -# if ( is_open_port(8080) or is_open_port(8443) ) { -# badprint "There is Application server running on 8080 or 8443 port."; -# push( @generalrec, "Consider dedicating a server for Application server in production !" ); -# } else { -# goodprint "No Application server runing on 8080 or 8443 port."; -# } + foreach my $banport (@banned_ports) { if ( is_open_port($banport) ) { badprint "Banned port: $banport is opened.."; From 0be6ebb62675566dc83fbfca756450eb492b8a5e Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 14:25:12 +0100 Subject: [PATCH 05/44] fix the build fail --- mysqltuner.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index aa71a9b..029fa08 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -961,7 +961,7 @@ sub system_recommendations { my @opened_ports=get_opened_ports; infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; if (scalar(@opened_ports) > $opt{'maxportallowed'}) { - badprint "There is too many listening ports: ". scalar(@opened_ports) " opened > ".$opt{'maxportallowed'}. "allowed."; + badprint "There is too many listening ports: ". scalar(@opened_ports). " opened > ".$opt{'maxportallowed'}. "allowed."; push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); } else { goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; From 9c87b5266f6d7d04dd09d63ae309eb03cb1eae47 Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 18:36:49 +0100 Subject: [PATCH 06/44] v1.6.9 --- mysqltuner.pl | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 029fa08..f864d2c 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1,7 +1,7 @@ #!/usr/bin/env perl -# mysqltuner.pl - Version 1.6.8 +# mysqltuner.pl - Version 1.6.9 # High Performance MySQL Tuning Script -# Copyright (C) 2006-2015 Major Hayden - major@mhtx.net +# Copyright (C) 2006-2016 Major Hayden - major@mhtx.net # # For the latest updates, please visit http://mysqltuner.com/ # Git repository available at http://github.com/major/MySQLTuner-perl @@ -51,7 +51,7 @@ use Data::Dumper; $Data::Dumper::Pair = " : "; # Set up a few variables for use in the script -my $tunerversion = "1.6.8"; +my $tunerversion = "1.6.9"; my ( @adjvars, @generalrec ); # Set defaults @@ -82,7 +82,8 @@ my %opt = ( "noask" => 0, "template" => 0, "json" => 0, - "reportfile" => 0 + "reportfile" => 0, + "prettyjson" => 0 ); # Gather the options from the command line @@ -94,7 +95,7 @@ GetOptions( 'mysqlcmd=s', 'help', 'buffers', 'skippassword', 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', - 'bannedports=s','maxportallowed=s', + 'bannedports=s','maxportallowed=s','prettyjson' ); if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } @@ -142,6 +143,7 @@ sub usage { . " --cvefile CVE File for vulnerability checks\n" . " --nocolor Don't print output in color\n" . " --json Print result as JSON string\n" + . " --prettyjson Print result as human readable JSON\n" . " --buffers Print global and per-thread buffer values\n" . " --outputfile Path to a output txt file\n" . "\n" . " --reportfile Path to a report txt file\n" . "\n" @@ -3208,7 +3210,7 @@ sub dump_result { exit 1; } my $json = JSON->new->allow_nonref; - print JSON->new->utf8(1)->pretty(1)->encode(%result); + print $json->utf8(1)->pretty((defined $opt{'prettyjson'} ? 1 : 0))->encode(\%result); } } From 58807c135f1c09e85ce999aa118be762a43cb97c Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 19:47:59 +0100 Subject: [PATCH 07/44] inconsistent performance schema info #167 --- mysqltuner.pl | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index f864d2c..7373273 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -82,8 +82,8 @@ my %opt = ( "noask" => 0, "template" => 0, "json" => 0, - "reportfile" => 0, - "prettyjson" => 0 + "prettyjson" => 0, + "reportfile" => 0 ); # Gather the options from the command line @@ -93,9 +93,10 @@ GetOptions( 'host=s', 'socket=s', 'port=i', 'user=s', 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', - 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', - 'bannedports=s','maxportallowed=s','prettyjson' + 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', + 'json', 'prettyjson', 'idxstat', 'noask', + 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', + 'maxportallowed=s' ); if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } @@ -191,7 +192,7 @@ my %result; # Functions that handle the print styles sub prettyprint { - print $_[0] . "\n" unless $opt{'silent'}; + print $_[0] . "\n" unless ($opt{'silent'} or $opt{'json'}); print $fh $_[0] . "\n" if defined($fh); } sub goodprint { prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 ); } @@ -388,7 +389,7 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { if ($opt{checkversion} eq 0) { - print "\n"; + print "\n" unless ($opt{'silent'} or $opt{'json'}); infoprint "Skipped version check for MySQLTuner script"; return; } @@ -2541,9 +2542,9 @@ sub mysqsl_pfs { # Performance Schema unless ( defined($myvar{'performance_schema'}) and $myvar{'performance_schema'} eq 'ON' ) { infoprint "Performance schema is disabled."; + } else { + infoprint "Performance schema is enabled."; } - - infoprint "Performance schema is enabled."; } @@ -2909,7 +2910,7 @@ sub mysql_databases { $result{'Databases'}{'All databases'}{'Index Pct'} = percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%"; $result{'Databases'}{'All databases'}{'Total Size'} = $totaldbinfo[3]; - print "\n"; + print "\n" unless ($opt{'silent'} or $opt{'json'}); foreach (@dblist) { chomp($_); if ( $_ eq "information_schema" @@ -3210,7 +3211,7 @@ sub dump_result { exit 1; } my $json = JSON->new->allow_nonref; - print $json->utf8(1)->pretty((defined $opt{'prettyjson'} ? 1 : 0))->encode(\%result); + print $json->utf8(1)->pretty(($opt{'prettyjson'} ? 1 : 0))->encode(\%result); } } From ae9bbe405b409ea8aa6c83b4d7b83af44834c04c Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 21:17:34 +0100 Subject: [PATCH 08/44] added version update functionality --- mysqltuner.pl | 130 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 30 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 7373273..1540b75 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -56,34 +56,35 @@ my ( @adjvars, @generalrec ); # Set defaults my %opt = ( - "silent" => 0, - "nobad" => 0, - "nogood" => 0, - "noinfo" => 0, - "debug" => 0, - "nocolor" => 0, - "forcemem" => 0, - "forceswap" => 0, - "host" => 0, - "socket" => 0, - "port" => 0, - "user" => 0, - "pass" => 0, - "skipsize" => 0, - "checkversion" => 0, - "buffers" => 0, - "passwordfile" => 0, - "bannedports" => '', - "maxportallowed"=> 0, - "outputfile" => 0, - "dbstat" => 0, - "idxstat" => 0, - "skippassword" => 0, - "noask" => 0, - "template" => 0, - "json" => 0, - "prettyjson" => 0, - "reportfile" => 0 + "silent" => 0, + "nobad" => 0, + "nogood" => 0, + "noinfo" => 0, + "debug" => 0, + "nocolor" => 0, + "forcemem" => 0, + "forceswap" => 0, + "host" => 0, + "socket" => 0, + "port" => 0, + "user" => 0, + "pass" => 0, + "skipsize" => 0, + "checkversion" => 0, + "updateversion" => 0, + "buffers" => 0, + "passwordfile" => 0, + "bannedports" => '', + "maxportallowed" => 0, + "outputfile" => 0, + "dbstat" => 0, + "idxstat" => 0, + "skippassword" => 0, + "noask" => 0, + "template" => 0, + "json" => 0, + "prettyjson" => 0, + "reportfile" => 0 ); # Gather the options from the command line @@ -96,7 +97,7 @@ GetOptions( 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', 'prettyjson', 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', - 'maxportallowed=s' + 'updateversion', 'maxportallowed=s' ); if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } @@ -128,6 +129,7 @@ sub usage { . " (Recommended for servers with many tables)\n" . " --skippassword Don't perform checks on user passwords(default: off)\n" . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" + . " --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check)\n" . " --forcemem Amount of RAM installed in megabytes\n" . " --forceswap Amount of swap memory configured in megabytes\n" . " --passwordfile Path to a password file list(one password by line)\n" @@ -388,7 +390,7 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { - if ($opt{checkversion} eq 0) { + if ($opt{'checkversion'} eq 0 and $opt{'updateversion'} eq 0) { print "\n" unless ($opt{'silent'} or $opt{'json'}); infoprint "Skipped version check for MySQLTuner script"; return; @@ -427,12 +429,80 @@ sub validate_tuner_version { infoprint "Unable to check for the latest MySQLTuner version"; } +# Checks for updates to MySQLTuner +sub update_tuner_version { + if ($opt{'updateversion'} eq 0) { + badprint "Skipped version update for MySQLTuner script"; + print "\n" unless ($opt{'silent'} or $opt{'json'}); + return; + } + + #use Cwd; + my $update; + my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/"; + my @scripts = ("mysqltuner.pl", "basic_passwords.txt", "vulnerabilities.csv"); + my $totalScripts = scalar(keys @scripts); + my $receivedScripts = 0; + my $httpcli =`which curl`; + + foreach my $script (@scripts) { + + chomp($httpcli); + if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; + + debugprint "$httpcli --connect-timeout 5 -silent '$url$script' > $script"; + $update = `$httpcli --connect-timeout 5 -silent '$url$script' > $script`; + chomp($update); + debugprint "$script updated: $update"; + + if ( -s $script eq 0) { + badprint "Couldn't update $script"; + } else { + ++$receivedScripts; + debugprint "$script updated: $update"; + } + } else { + + $httpcli=`which wget`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; + + debugprint "$httpcli -qe timestamping=off -T 5 -O $script '$url$script'"; + $update = `$httpcli -qe timestamping=off -T 5 -O $script '$url$script'`; + chomp($update); + + if ( -s $script eq 0) { + badprint "Couldn't update $script"; + } else { + ++$receivedScripts; + debugprint "$script updated: $update"; + } + + } else { + debugprint "curl and wget are not available."; + infoprint "Unable to check for the latest MySQLTuner version"; + } + } + } + + if ($receivedScripts eq $totalScripts) { + goodprint "Successfully updated MySQLTuner script"; + } else { + badprint "Couldn't update MySQLTuner script"; + } + + exit 0; +} + sub compare_tuner_version { my $remoteversion=shift; debugprint "Remote data: $remoteversion"; #exit 0; if ($remoteversion ne $tunerversion) { badprint "There is a new version of MySQLTuner available ($remoteversion)"; + update_tuner_version(); return; } goodprint "You have the latest version of MySQLTuner($tunerversion)"; From 5c9f9952aa17b6133ebd1a3755a5f4e56ce4e24f Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 21:20:48 +0100 Subject: [PATCH 09/44] added update version functionality --- mysqltuner.pl | 113 +++++++++++++++++++++++++------------------------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 1540b75..790b9a9 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -782,14 +782,14 @@ sub select_array { debugprint "PERFORM: $req "; my @result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + exit $?; } - debugprint "select_array: return code : $?"; + debugprint "select_array: return code : $?"; chomp(@result); return @result; } @@ -800,14 +800,14 @@ sub select_one { debugprint "PERFORM: $req "; my $result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + exit $?; } - debugprint "select_array: return code : $?"; + debugprint "select_array: return code : $?"; chomp($result); return $result; } @@ -960,9 +960,9 @@ sub cve_recommendations { sub get_opened_ports { my @opened_ports=`netstat -ltn`; map { - s/.*:(\d+)\s.*$/$1/; - s/\D//g; - } @opened_ports; + s/.*:(\d+)\s.*$/$1/; + s/\D//g; + } @opened_ports; @opened_ports = sort {$a <=> $b} grep { !/^$/ } @opened_ports; debugprint Dumper \@opened_ports; return @opened_ports; @@ -977,26 +977,26 @@ sub is_open_port { } sub get_process_memory { - my $pid=shift; - return 0 unless -f "/proc/$pid/status"; - my @pdata= grep { /RSS:/ } get_file_contents "/proc/$pid/status"; - map { - s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge - } @pdata; - return $pdata[0]; + my $pid=shift; + return 0 unless -f "/proc/$pid/status"; + my @pdata= grep { /RSS:/ } get_file_contents "/proc/$pid/status"; + map { + s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge + } @pdata; + return $pdata[0]; } sub get_other_process_memory { - my @procs=`ps -eo pid,cmd`; - map { s/.*mysqld.*//; s/.*\[.*\].*//; s/^\s+$//g; s/.*PID.*CMD.*//; s/.*systemd.*//;} @procs; - map {s/\s*?(\d+)\s*.*/$1/g;} @procs; - remove_cr @procs; - @procs=remove_empty @procs; - my $totalMemOther=0; - map { - $totalMemOther+=get_process_memory($_); - } @procs; - return $totalMemOther; + my @procs=`ps -eo pid,cmd`; + map { s/.*mysqld.*//; s/.*\[.*\].*//; s/^\s+$//g; s/.*PID.*CMD.*//; s/.*systemd.*//;} @procs; + map {s/\s*?(\d+)\s*.*/$1/g;} @procs; + remove_cr @procs; + @procs=remove_empty @procs; + my $totalMemOther=0; + map { + $totalMemOther+=get_process_memory($_); + } @procs; + return $totalMemOther; } sub get_os_release { @@ -1020,34 +1020,34 @@ sub system_recommendations { infoprint "User process except mysqld used ". hr_bytes_rnd($omem) . " RAM."; if ( (0.15*$physical_memory) < $omem) { badprint "Other user process except mysqld used more than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; - push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); - push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); + push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); + push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); } else { } #if ($omem > - #exit 0; + #exit 0; if ($opt{'maxportallowed'} > 0) { my @opened_ports=get_opened_ports; infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; if (scalar(@opened_ports) > $opt{'maxportallowed'}) { - badprint "There is too many listening ports: ". scalar(@opened_ports). " opened > ".$opt{'maxportallowed'}. "allowed."; - push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); + badprint "There is too many listening ports: ". scalar(@opened_ports). " opened > ".$opt{'maxportallowed'}. "allowed."; + push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); } else { - goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; + goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; } } foreach my $banport (@banned_ports) { - if ( is_open_port($banport) ) { - badprint "Banned port: $banport is opened.."; - push( @generalrec, "Port $banport is opened. Consider stopping program handling this port." ); - } else { - goodprint "$banport is not opened."; - } + if ( is_open_port($banport) ) { + badprint "Banned port: $banport is opened.."; + push( @generalrec, "Port $banport is opened. Consider stopping program handling this port." ); + } else { + goodprint "$banport is not opened."; + } } } @@ -1403,7 +1403,7 @@ sub check_storage_engines { $result{'Databases'}{'List'} = [@dblist]; infoprint "Status: $engines"; if ( mysql_version_ge( 5, 1, 5 ) ) { - # MySQL 5 servers can have table sizes calculated quickly from information schema + # MySQL 5 servers can have table sizes calculated quickly from information schema my @templist = select_array "SELECT ENGINE,SUM(DATA_LENGTH+INDEX_LENGTH),COUNT(ENGINE),SUM(DATA_LENGTH),SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;"; @@ -2584,12 +2584,12 @@ sub mariadb_threadpool { infoprint "Thread Pool Size: ".$myvar{'thread_pool_size'}. " thread(s)."; if ($myvar{'have_innodb'} eq 'YES') { - if ($myvar{'thread_pool_size'}< 16 or $myvar{'thread_pool_size'}>36) { - badprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; - push( @generalrec, "Thread pool size for InnoDB usage (".$myvar{'thread_pool_size'}.")" ); - push( @adjvars, "thread_pool_size between 16 and 36 for InnoDB usage" ); - } else { - goodprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; + if ($myvar{'thread_pool_size'}< 16 or $myvar{'thread_pool_size'}>36) { + badprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; + push( @generalrec, "Thread pool size for InnoDB usage (".$myvar{'thread_pool_size'}.")" ); + push( @adjvars, "thread_pool_size between 16 and 36 for InnoDB usage" ); + } else { + goodprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; } return; } @@ -3029,10 +3029,10 @@ sub mysql_databases { percentage( $dbinfo[3], $dbinfo[4] ) . "%"; $result{'Databases'}{ $dbinfo[0] }{'Total Size'} = $dbinfo[4]; if ($dbinfo[7]>1) { - badprint $dbinfo[7]. " differents collations for database ".$dbinfo[0]; + badprint $dbinfo[7]. " differents collations for database ".$dbinfo[0]; push(@generalrec, "Check all table collations are identical for all tables in ".$dbinfo[0]. " database."); } else { - goodprint $dbinfo[7]. " collation for ".$dbinfo[0]. " database."; + goodprint $dbinfo[7]. " collation for ".$dbinfo[0]. " database."; } if ($dbinfo[8]>1) { badprint $dbinfo[8]. " differents engines for database ".$dbinfo[0]; @@ -3296,7 +3296,7 @@ get_all_vars; # Toss variables/status into hashes get_tuning_info; # Get information about the tuning connexion validate_mysql_version; # Check current MySQL version check_architecture; # Suggest 64-bit upgrade -system_recommendations; # avoid to many service on the same host +system_recommendations; # avoid to many service on the same host check_storage_engines; # Show enabled storage engines mysql_databases; # Show informations about databases mysql_indexes; # Show informations about indexes @@ -3354,6 +3354,7 @@ You must provide the remote server's total memory when connecting to other serve (Recommended for servers with many tables) --skippassword Don't perform checks on user passwords(default: off) --checkversion Check for updates to MySQLTuner (default: don't check) + --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check) --forcemem Amount of RAM installed in megabytes --forceswap Amount of swap memory configured in megabytes --passwordfile Path to a password file list(one password by line) From 6c280de32fb7a2df80ae087ba73af3145f07feee Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 22:48:07 +0100 Subject: [PATCH 10/44] Added GetOptions check if fails show help Added verbose mode --- mysqltuner.pl | 184 +++++++++++++++++++++++++++----------------------- 1 file changed, 100 insertions(+), 84 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 790b9a9..1938e8e 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -84,74 +84,81 @@ my %opt = ( "template" => 0, "json" => 0, "prettyjson" => 0, - "reportfile" => 0 + "reportfile" => 0, + "verbose" => 0 ); # Gather the options from the command line -GetOptions( - \%opt, 'nobad', 'nogood', 'noinfo', - 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', - 'host=s', 'socket=s', 'port=i', 'user=s', - 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', - 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', - 'json', 'prettyjson', 'idxstat', 'noask', - 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', - 'updateversion', 'maxportallowed=s' +my $getOptionsCheck = GetOptions( + \%opt, 'nobad', 'nogood', 'noinfo', + 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', + 'host=s', 'socket=s', 'port=i', 'user=s', + 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', + 'mysqlcmd=s', 'help', 'buffers', 'skippassword', + 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', + 'json', 'prettyjson', 'idxstat', 'noask', + 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', + 'updateversion', 'maxportallowed=s', 'verbose' ); +#If params are incorrect return help +if ($getOptionsCheck ne 1) { + usage(); +} + if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } sub usage { - # Shown with --help option passed - print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" - . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" - . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" - . "\n" - . " Important Usage Guidelines:\n" - . " To run the script with the default options, run the script without arguments\n" - . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" - . " Some routines may require root level privileges (script will provide warnings)\n" - . " You must provide the remote server's total memory when connecting to other servers\n" - . "\n" - . " Connection and Authentication\n" - . " --host Connect to a remote host to perform tests (default: localhost)\n" - . " --socket Use a different socket for a local connection\n" - . " --port Port to use for connection (default: 3306)\n" - . " --user Username to use for authentication\n" - . " --pass Password to use for authentication\n" - . " --mysqladmin Path to a custom mysqladmin executable\n" - . " --mysqlcmd Path to a custom mysql executable\n" . "\n" - . " --noask Dont ask password if needed\n" . "\n" - . " Performance and Reporting Options\n" - . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" - . " (Recommended for servers with many tables)\n" - . " --skippassword Don't perform checks on user passwords(default: off)\n" - . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" - . " --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check)\n" - . " --forcemem Amount of RAM installed in megabytes\n" - . " --forceswap Amount of swap memory configured in megabytes\n" - . " --passwordfile Path to a password file list(one password by line)\n" - . " Output Options:\n" - . " --silent Don't output anything on screen\n" - . " --nogood Remove OK responses\n" - . " --nobad Remove negative/suggestion responses\n" - . " --noinfo Remove informational responses\n" - . " --debug Print debug information\n" - . " --dbstat Print database information\n" - . " --idxstat Print index information\n" - . " --bannedports Ports banned separated by comma(,)\n" - . " --maxportallowed Number of ports opened allowed on this hosts\n" - . " --cvefile CVE File for vulnerability checks\n" - . " --nocolor Don't print output in color\n" - . " --json Print result as JSON string\n" - . " --prettyjson Print result as human readable JSON\n" - . " --buffers Print global and per-thread buffer values\n" - . " --outputfile Path to a output txt file\n" . "\n" - . " --reportfile Path to a report txt file\n" . "\n" - . " --template Path to a template file\n" . "\n"; - exit 0; + # Shown with --help option passed + print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" + . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" + . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" + . "\n" + . " Important Usage Guidelines:\n" + . " To run the script with the default options, run the script without arguments\n" + . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" + . " Some routines may require root level privileges (script will provide warnings)\n" + . " You must provide the remote server's total memory when connecting to other servers\n" + . "\n" + . " Connection and Authentication\n" + . " --host Connect to a remote host to perform tests (default: localhost)\n" + . " --socket Use a different socket for a local connection\n" + . " --port Port to use for connection (default: 3306)\n" + . " --user Username to use for authentication\n" + . " --pass Password to use for authentication\n" + . " --mysqladmin Path to a custom mysqladmin executable\n" + . " --mysqlcmd Path to a custom mysql executable\n" . "\n" + . " --noask Dont ask password if needed\n" . "\n" + . " Performance and Reporting Options\n" + . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" + . " (Recommended for servers with many tables)\n" + . " --skippassword Don't perform checks on user passwords(default: off)\n" + . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" + . " --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check)\n" + . " --forcemem Amount of RAM installed in megabytes\n" + . " --forceswap Amount of swap memory configured in megabytes\n" + . " --passwordfile Path to a password file list(one password by line)\n" + . " Output Options:\n" + . " --silent Don't output anything on screen\n" + . " --nogood Remove OK responses\n" + . " --nobad Remove negative/suggestion responses\n" + . " --noinfo Remove informational responses\n" + . " --debug Print debug information\n" + . " --dbstat Print database information\n" + . " --idxstat Print index information\n" + . " --bannedports Ports banned separated by comma(,)\n" + . " --maxportallowed Number of ports opened allowed on this hosts\n" + . " --cvefile CVE File for vulnerability checks\n" + . " --nocolor Don't print output in color\n" + . " --json Print result as JSON string\n" + . " --prettyjson Print result as human readable JSON\n" + . " --buffers Print global and per-thread buffer values\n" + . " --outputfile Path to a output txt file\n" . "\n" + . " --reportfile Path to a report txt file\n" . "\n" + . " --template Path to a template file\n" . "\n" + . " --verbose Prints out all options (default: no verbose) \n" . "\n"; + exit 0; } my $devnull = File::Spec->devnull(); @@ -164,6 +171,15 @@ my $basic_password_files = $basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt" unless -f "$basic_password_files"; +# check if we need to enable verbose mode +if ($opt{verbose}) { + $opt{checkversion} = 1; #Check for updates to MySQLTuner + $opt{dbstat} = 1; #Print database information + $opt{idxstat} = 1; #Print index information + $opt{buffers} = 1; #Print global and per-thread buffer values + $opt{cvefile} = 'vulnerabilities.csv'; #CVE File for vulnerability checks +} + # for RPM distributions $opt{cvefile} = "/usr/share/mysqltuner/vulnerabilities.csv" unless ( defined $opt{cvefile} and -f "$opt{cvefile}"); @@ -3350,34 +3366,34 @@ You must provide the remote server's total memory when connecting to other serve =head1 PERFORMANCE AND REPORTING OPTIONS - --skipsize Don't enumerate tables and their types/sizes (default: on) - (Recommended for servers with many tables) - --skippassword Don't perform checks on user passwords(default: off) - --checkversion Check for updates to MySQLTuner (default: don't check) - --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check) - --forcemem Amount of RAM installed in megabytes - --forceswap Amount of swap memory configured in megabytes - --passwordfile Path to a password file list(one password by line) + --skipsize Don't enumerate tables and their types/sizes (default: on) + (Recommended for servers with many tables) + --skippassword Don't perform checks on user passwords(default: off) + --checkversion Check for updates to MySQLTuner (default: don't check) + --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check) + --forcemem Amount of RAM installed in megabytes + --forceswap Amount of swap memory configured in megabytes + --passwordfile Path to a password file list(one password by line) =head1 OUTPUT OPTIONS - --silent Don't output anything on screen - --nogood Remove OK responses - --nobad Remove negative/suggestion responses - --noinfo Remove informational responses - --debug Print debug information - --dbstat Print database information - --idxstat Print index information - --bannedports Ports banned separated by comma(,) - --maxportallowed Number of ports opened allowed on this hosts - --cvefile CVE File for vulnerability checks - --nocolor Don't print output in color - --json Print result as JSON string - --buffers Print global and per-thread buffer values - --outputfile Path to a output txt file - --reportfile Path to a report txt file - --template Path to a template file - + --silent Don't output anything on screen + --nogood Remove OK responses + --nobad Remove negative/suggestion responses + --noinfo Remove informational responses + --debug Print debug information + --dbstat Print database information + --idxstat Print index information + --bannedports Ports banned separated by comma(,) + --maxportallowed Number of ports opened allowed on this hosts + --cvefile CVE File for vulnerability checks + --nocolor Don't print output in color + --json Print result as JSON string + --buffers Print global and per-thread buffer values + --outputfile Path to a output txt file + --reportfile Path to a report txt file + --template Path to a template file + --verbose Prints out all options (default: no verbose) =head1 PERLDOC You can find documentation for this module with the perldoc command. From 5fb8e3dec773462accffa58219da7e04ce5bb0c8 Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 23:23:31 +0100 Subject: [PATCH 11/44] fix for perl 5.10 and 5.8 --- mysqltuner.pl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 1938e8e..5858efc 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -461,7 +461,9 @@ sub update_tuner_version { my $receivedScripts = 0; my $httpcli =`which curl`; - foreach my $script (@scripts) { + foreach my $scriptKey (keys @scripts) { + + my $script = $scripts[$scriptKey]; chomp($httpcli); if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { From 774dbd89147cc3b832d6ea55b57533f76238f1fd Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 23:31:52 +0100 Subject: [PATCH 12/44] fix for perl 5.10 and 5.8 --- mysqltuner.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 5858efc..b2a4481 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -461,10 +461,11 @@ sub update_tuner_version { my $receivedScripts = 0; my $httpcli =`which curl`; - foreach my $scriptKey (keys @scripts) { + #foreach my $scriptKey (%{@scripts}) { + for my $scriptKey (0 .. $#scripts) { my $script = $scripts[$scriptKey]; - + chomp($httpcli); if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { debugprint "$httpcli is available."; From 2d4afc2e82e51825a593cf459acd747567f7c1ed Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 23:41:30 +0100 Subject: [PATCH 13/44] fix for perl 5.10 and 5.8 --- mysqltuner.pl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index b2a4481..c6c20ee 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -461,10 +461,7 @@ sub update_tuner_version { my $receivedScripts = 0; my $httpcli =`which curl`; - #foreach my $scriptKey (%{@scripts}) { - for my $scriptKey (0 .. $#scripts) { - - my $script = $scripts[$scriptKey]; + foreach my $script (@scripts) { chomp($httpcli); if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { From 5faf456d7717d66fa6306bf403a829e5d1548a8b Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 23:56:45 +0100 Subject: [PATCH 14/44] fix for perl 5.10 and 5.8 --- mysqltuner.pl | 434 ++++++++++++++++++++------------------------------ 1 file changed, 172 insertions(+), 262 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index c6c20ee..aa71a9b 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1,7 +1,7 @@ #!/usr/bin/env perl -# mysqltuner.pl - Version 1.6.9 +# mysqltuner.pl - Version 1.6.8 # High Performance MySQL Tuning Script -# Copyright (C) 2006-2016 Major Hayden - major@mhtx.net +# Copyright (C) 2006-2015 Major Hayden - major@mhtx.net # # For the latest updates, please visit http://mysqltuner.com/ # Git repository available at http://github.com/major/MySQLTuner-perl @@ -51,114 +51,102 @@ use Data::Dumper; $Data::Dumper::Pair = " : "; # Set up a few variables for use in the script -my $tunerversion = "1.6.9"; +my $tunerversion = "1.6.8"; my ( @adjvars, @generalrec ); # Set defaults my %opt = ( - "silent" => 0, - "nobad" => 0, - "nogood" => 0, - "noinfo" => 0, - "debug" => 0, - "nocolor" => 0, - "forcemem" => 0, - "forceswap" => 0, - "host" => 0, - "socket" => 0, - "port" => 0, - "user" => 0, - "pass" => 0, - "skipsize" => 0, - "checkversion" => 0, - "updateversion" => 0, - "buffers" => 0, - "passwordfile" => 0, - "bannedports" => '', - "maxportallowed" => 0, - "outputfile" => 0, - "dbstat" => 0, - "idxstat" => 0, - "skippassword" => 0, - "noask" => 0, - "template" => 0, - "json" => 0, - "prettyjson" => 0, - "reportfile" => 0, - "verbose" => 0 + "silent" => 0, + "nobad" => 0, + "nogood" => 0, + "noinfo" => 0, + "debug" => 0, + "nocolor" => 0, + "forcemem" => 0, + "forceswap" => 0, + "host" => 0, + "socket" => 0, + "port" => 0, + "user" => 0, + "pass" => 0, + "skipsize" => 0, + "checkversion" => 0, + "buffers" => 0, + "passwordfile" => 0, + "bannedports" => '', + "maxportallowed"=> 0, + "outputfile" => 0, + "dbstat" => 0, + "idxstat" => 0, + "skippassword" => 0, + "noask" => 0, + "template" => 0, + "json" => 0, + "reportfile" => 0 ); # Gather the options from the command line -my $getOptionsCheck = GetOptions( - \%opt, 'nobad', 'nogood', 'noinfo', - 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', - 'host=s', 'socket=s', 'port=i', 'user=s', - 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', - 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', - 'json', 'prettyjson', 'idxstat', 'noask', - 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', - 'updateversion', 'maxportallowed=s', 'verbose' +GetOptions( + \%opt, 'nobad', 'nogood', 'noinfo', + 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', + 'host=s', 'socket=s', 'port=i', 'user=s', + 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', + 'mysqlcmd=s', 'help', 'buffers', 'skippassword', + 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', + 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', + 'bannedports=s','maxportallowed=s', ); -#If params are incorrect return help -if ($getOptionsCheck ne 1) { - usage(); -} - if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } sub usage { - # Shown with --help option passed - print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" - . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" - . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" - . "\n" - . " Important Usage Guidelines:\n" - . " To run the script with the default options, run the script without arguments\n" - . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" - . " Some routines may require root level privileges (script will provide warnings)\n" - . " You must provide the remote server's total memory when connecting to other servers\n" - . "\n" - . " Connection and Authentication\n" - . " --host Connect to a remote host to perform tests (default: localhost)\n" - . " --socket Use a different socket for a local connection\n" - . " --port Port to use for connection (default: 3306)\n" - . " --user Username to use for authentication\n" - . " --pass Password to use for authentication\n" - . " --mysqladmin Path to a custom mysqladmin executable\n" - . " --mysqlcmd Path to a custom mysql executable\n" . "\n" - . " --noask Dont ask password if needed\n" . "\n" - . " Performance and Reporting Options\n" - . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" - . " (Recommended for servers with many tables)\n" - . " --skippassword Don't perform checks on user passwords(default: off)\n" - . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" - . " --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check)\n" - . " --forcemem Amount of RAM installed in megabytes\n" - . " --forceswap Amount of swap memory configured in megabytes\n" - . " --passwordfile Path to a password file list(one password by line)\n" - . " Output Options:\n" - . " --silent Don't output anything on screen\n" - . " --nogood Remove OK responses\n" - . " --nobad Remove negative/suggestion responses\n" - . " --noinfo Remove informational responses\n" - . " --debug Print debug information\n" - . " --dbstat Print database information\n" - . " --idxstat Print index information\n" - . " --bannedports Ports banned separated by comma(,)\n" - . " --maxportallowed Number of ports opened allowed on this hosts\n" - . " --cvefile CVE File for vulnerability checks\n" - . " --nocolor Don't print output in color\n" - . " --json Print result as JSON string\n" - . " --prettyjson Print result as human readable JSON\n" - . " --buffers Print global and per-thread buffer values\n" - . " --outputfile Path to a output txt file\n" . "\n" - . " --reportfile Path to a report txt file\n" . "\n" - . " --template Path to a template file\n" . "\n" - . " --verbose Prints out all options (default: no verbose) \n" . "\n"; - exit 0; + # Shown with --help option passed + print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" + . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" + . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" + . "\n" + . " Important Usage Guidelines:\n" + . " To run the script with the default options, run the script without arguments\n" + . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" + . " Some routines may require root level privileges (script will provide warnings)\n" + . " You must provide the remote server's total memory when connecting to other servers\n" + . "\n" + . " Connection and Authentication\n" + . " --host Connect to a remote host to perform tests (default: localhost)\n" + . " --socket Use a different socket for a local connection\n" + . " --port Port to use for connection (default: 3306)\n" + . " --user Username to use for authentication\n" + . " --pass Password to use for authentication\n" + . " --mysqladmin Path to a custom mysqladmin executable\n" + . " --mysqlcmd Path to a custom mysql executable\n" . "\n" + . " --noask Dont ask password if needed\n" . "\n" + . " Performance and Reporting Options\n" + . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" + . " (Recommended for servers with many tables)\n" + . " --skippassword Don't perform checks on user passwords(default: off)\n" + . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" + . " --forcemem Amount of RAM installed in megabytes\n" + . " --forceswap Amount of swap memory configured in megabytes\n" + . " --passwordfile Path to a password file list(one password by line)\n" + . " Output Options:\n" + . " --silent Don't output anything on screen\n" + . " --nogood Remove OK responses\n" + . " --nobad Remove negative/suggestion responses\n" + . " --noinfo Remove informational responses\n" + . " --debug Print debug information\n" + . " --dbstat Print database information\n" + . " --idxstat Print index information\n" + . " --bannedports Ports banned separated by comma(,)\n" + . " --maxportallowed Number of ports opened allowed on this hosts\n" + . " --cvefile CVE File for vulnerability checks\n" + . " --nocolor Don't print output in color\n" + . " --json Print result as JSON string\n" + . " --buffers Print global and per-thread buffer values\n" + . " --outputfile Path to a output txt file\n" . "\n" + . " --reportfile Path to a report txt file\n" . "\n" + . " --template Path to a template file\n" . "\n"; + exit 0; } my $devnull = File::Spec->devnull(); @@ -171,15 +159,6 @@ my $basic_password_files = $basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt" unless -f "$basic_password_files"; -# check if we need to enable verbose mode -if ($opt{verbose}) { - $opt{checkversion} = 1; #Check for updates to MySQLTuner - $opt{dbstat} = 1; #Print database information - $opt{idxstat} = 1; #Print index information - $opt{buffers} = 1; #Print global and per-thread buffer values - $opt{cvefile} = 'vulnerabilities.csv'; #CVE File for vulnerability checks -} - # for RPM distributions $opt{cvefile} = "/usr/share/mysqltuner/vulnerabilities.csv" unless ( defined $opt{cvefile} and -f "$opt{cvefile}"); @@ -210,7 +189,7 @@ my %result; # Functions that handle the print styles sub prettyprint { - print $_[0] . "\n" unless ($opt{'silent'} or $opt{'json'}); + print $_[0] . "\n" unless $opt{'silent'}; print $fh $_[0] . "\n" if defined($fh); } sub goodprint { prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 ); } @@ -406,8 +385,8 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { - if ($opt{'checkversion'} eq 0 and $opt{'updateversion'} eq 0) { - print "\n" unless ($opt{'silent'} or $opt{'json'}); + if ($opt{checkversion} eq 0) { + print "\n"; infoprint "Skipped version check for MySQLTuner script"; return; } @@ -445,80 +424,12 @@ sub validate_tuner_version { infoprint "Unable to check for the latest MySQLTuner version"; } -# Checks for updates to MySQLTuner -sub update_tuner_version { - if ($opt{'updateversion'} eq 0) { - badprint "Skipped version update for MySQLTuner script"; - print "\n" unless ($opt{'silent'} or $opt{'json'}); - return; - } - - #use Cwd; - my $update; - my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/"; - my @scripts = ("mysqltuner.pl", "basic_passwords.txt", "vulnerabilities.csv"); - my $totalScripts = scalar(keys @scripts); - my $receivedScripts = 0; - my $httpcli =`which curl`; - - foreach my $script (@scripts) { - - chomp($httpcli); - if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; - - debugprint "$httpcli --connect-timeout 5 -silent '$url$script' > $script"; - $update = `$httpcli --connect-timeout 5 -silent '$url$script' > $script`; - chomp($update); - debugprint "$script updated: $update"; - - if ( -s $script eq 0) { - badprint "Couldn't update $script"; - } else { - ++$receivedScripts; - debugprint "$script updated: $update"; - } - } else { - - $httpcli=`which wget`; - chomp($httpcli); - if ( defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; - - debugprint "$httpcli -qe timestamping=off -T 5 -O $script '$url$script'"; - $update = `$httpcli -qe timestamping=off -T 5 -O $script '$url$script'`; - chomp($update); - - if ( -s $script eq 0) { - badprint "Couldn't update $script"; - } else { - ++$receivedScripts; - debugprint "$script updated: $update"; - } - - } else { - debugprint "curl and wget are not available."; - infoprint "Unable to check for the latest MySQLTuner version"; - } - } - } - - if ($receivedScripts eq $totalScripts) { - goodprint "Successfully updated MySQLTuner script"; - } else { - badprint "Couldn't update MySQLTuner script"; - } - - exit 0; -} - sub compare_tuner_version { my $remoteversion=shift; debugprint "Remote data: $remoteversion"; #exit 0; if ($remoteversion ne $tunerversion) { badprint "There is a new version of MySQLTuner available ($remoteversion)"; - update_tuner_version(); return; } goodprint "You have the latest version of MySQLTuner($tunerversion)"; @@ -798,14 +709,14 @@ sub select_array { debugprint "PERFORM: $req "; my @result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + exit $?; } - debugprint "select_array: return code : $?"; + debugprint "select_array: return code : $?"; chomp(@result); return @result; } @@ -816,14 +727,14 @@ sub select_one { debugprint "PERFORM: $req "; my $result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + exit $?; } - debugprint "select_array: return code : $?"; + debugprint "select_array: return code : $?"; chomp($result); return $result; } @@ -976,9 +887,9 @@ sub cve_recommendations { sub get_opened_ports { my @opened_ports=`netstat -ltn`; map { - s/.*:(\d+)\s.*$/$1/; - s/\D//g; - } @opened_ports; + s/.*:(\d+)\s.*$/$1/; + s/\D//g; + } @opened_ports; @opened_ports = sort {$a <=> $b} grep { !/^$/ } @opened_ports; debugprint Dumper \@opened_ports; return @opened_ports; @@ -993,26 +904,26 @@ sub is_open_port { } sub get_process_memory { - my $pid=shift; - return 0 unless -f "/proc/$pid/status"; - my @pdata= grep { /RSS:/ } get_file_contents "/proc/$pid/status"; - map { - s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge - } @pdata; - return $pdata[0]; + my $pid=shift; + return 0 unless -f "/proc/$pid/status"; + my @pdata= grep { /RSS:/ } get_file_contents "/proc/$pid/status"; + map { + s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge + } @pdata; + return $pdata[0]; } sub get_other_process_memory { - my @procs=`ps -eo pid,cmd`; - map { s/.*mysqld.*//; s/.*\[.*\].*//; s/^\s+$//g; s/.*PID.*CMD.*//; s/.*systemd.*//;} @procs; - map {s/\s*?(\d+)\s*.*/$1/g;} @procs; - remove_cr @procs; - @procs=remove_empty @procs; - my $totalMemOther=0; - map { - $totalMemOther+=get_process_memory($_); - } @procs; - return $totalMemOther; + my @procs=`ps -eo pid,cmd`; + map { s/.*mysqld.*//; s/.*\[.*\].*//; s/^\s+$//g; s/.*PID.*CMD.*//; s/.*systemd.*//;} @procs; + map {s/\s*?(\d+)\s*.*/$1/g;} @procs; + remove_cr @procs; + @procs=remove_empty @procs; + my $totalMemOther=0; + map { + $totalMemOther+=get_process_memory($_); + } @procs; + return $totalMemOther; } sub get_os_release { @@ -1036,34 +947,34 @@ sub system_recommendations { infoprint "User process except mysqld used ". hr_bytes_rnd($omem) . " RAM."; if ( (0.15*$physical_memory) < $omem) { badprint "Other user process except mysqld used more than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; - push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); - push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); + push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); + push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); } else { } #if ($omem > - #exit 0; + #exit 0; if ($opt{'maxportallowed'} > 0) { my @opened_ports=get_opened_ports; infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; if (scalar(@opened_ports) > $opt{'maxportallowed'}) { - badprint "There is too many listening ports: ". scalar(@opened_ports). " opened > ".$opt{'maxportallowed'}. "allowed."; - push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); + badprint "There is too many listening ports: ". scalar(@opened_ports) " opened > ".$opt{'maxportallowed'}. "allowed."; + push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); } else { - goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; + goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; } } foreach my $banport (@banned_ports) { - if ( is_open_port($banport) ) { - badprint "Banned port: $banport is opened.."; - push( @generalrec, "Port $banport is opened. Consider stopping program handling this port." ); - } else { - goodprint "$banport is not opened."; - } + if ( is_open_port($banport) ) { + badprint "Banned port: $banport is opened.."; + push( @generalrec, "Port $banport is opened. Consider stopping program handling this port." ); + } else { + goodprint "$banport is not opened."; + } } } @@ -1419,7 +1330,7 @@ sub check_storage_engines { $result{'Databases'}{'List'} = [@dblist]; infoprint "Status: $engines"; if ( mysql_version_ge( 5, 1, 5 ) ) { - # MySQL 5 servers can have table sizes calculated quickly from information schema + # MySQL 5 servers can have table sizes calculated quickly from information schema my @templist = select_array "SELECT ENGINE,SUM(DATA_LENGTH+INDEX_LENGTH),COUNT(ENGINE),SUM(DATA_LENGTH),SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;"; @@ -2600,12 +2511,12 @@ sub mariadb_threadpool { infoprint "Thread Pool Size: ".$myvar{'thread_pool_size'}. " thread(s)."; if ($myvar{'have_innodb'} eq 'YES') { - if ($myvar{'thread_pool_size'}< 16 or $myvar{'thread_pool_size'}>36) { - badprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; - push( @generalrec, "Thread pool size for InnoDB usage (".$myvar{'thread_pool_size'}.")" ); - push( @adjvars, "thread_pool_size between 16 and 36 for InnoDB usage" ); - } else { - goodprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; + if ($myvar{'thread_pool_size'}< 16 or $myvar{'thread_pool_size'}>36) { + badprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; + push( @generalrec, "Thread pool size for InnoDB usage (".$myvar{'thread_pool_size'}.")" ); + push( @adjvars, "thread_pool_size between 16 and 36 for InnoDB usage" ); + } else { + goodprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; } return; } @@ -2628,9 +2539,9 @@ sub mysqsl_pfs { # Performance Schema unless ( defined($myvar{'performance_schema'}) and $myvar{'performance_schema'} eq 'ON' ) { infoprint "Performance schema is disabled."; - } else { - infoprint "Performance schema is enabled."; } + + infoprint "Performance schema is enabled."; } @@ -2996,7 +2907,7 @@ sub mysql_databases { $result{'Databases'}{'All databases'}{'Index Pct'} = percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%"; $result{'Databases'}{'All databases'}{'Total Size'} = $totaldbinfo[3]; - print "\n" unless ($opt{'silent'} or $opt{'json'}); + print "\n"; foreach (@dblist) { chomp($_); if ( $_ eq "information_schema" @@ -3045,10 +2956,10 @@ sub mysql_databases { percentage( $dbinfo[3], $dbinfo[4] ) . "%"; $result{'Databases'}{ $dbinfo[0] }{'Total Size'} = $dbinfo[4]; if ($dbinfo[7]>1) { - badprint $dbinfo[7]. " differents collations for database ".$dbinfo[0]; + badprint $dbinfo[7]. " differents collations for database ".$dbinfo[0]; push(@generalrec, "Check all table collations are identical for all tables in ".$dbinfo[0]. " database."); } else { - goodprint $dbinfo[7]. " collation for ".$dbinfo[0]. " database."; + goodprint $dbinfo[7]. " collation for ".$dbinfo[0]. " database."; } if ($dbinfo[8]>1) { badprint $dbinfo[8]. " differents engines for database ".$dbinfo[0]; @@ -3297,7 +3208,7 @@ sub dump_result { exit 1; } my $json = JSON->new->allow_nonref; - print $json->utf8(1)->pretty(($opt{'prettyjson'} ? 1 : 0))->encode(\%result); + print JSON->new->utf8(1)->pretty(1)->encode(%result); } } @@ -3312,7 +3223,7 @@ get_all_vars; # Toss variables/status into hashes get_tuning_info; # Get information about the tuning connexion validate_mysql_version; # Check current MySQL version check_architecture; # Suggest 64-bit upgrade -system_recommendations; # avoid to many service on the same host +system_recommendations; # avoid to many service on the same host check_storage_engines; # Show enabled storage engines mysql_databases; # Show informations about databases mysql_indexes; # Show informations about indexes @@ -3366,34 +3277,33 @@ You must provide the remote server's total memory when connecting to other serve =head1 PERFORMANCE AND REPORTING OPTIONS - --skipsize Don't enumerate tables and their types/sizes (default: on) - (Recommended for servers with many tables) - --skippassword Don't perform checks on user passwords(default: off) - --checkversion Check for updates to MySQLTuner (default: don't check) - --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check) - --forcemem Amount of RAM installed in megabytes - --forceswap Amount of swap memory configured in megabytes - --passwordfile Path to a password file list(one password by line) + --skipsize Don't enumerate tables and their types/sizes (default: on) + (Recommended for servers with many tables) + --skippassword Don't perform checks on user passwords(default: off) + --checkversion Check for updates to MySQLTuner (default: don't check) + --forcemem Amount of RAM installed in megabytes + --forceswap Amount of swap memory configured in megabytes + --passwordfile Path to a password file list(one password by line) =head1 OUTPUT OPTIONS - --silent Don't output anything on screen - --nogood Remove OK responses - --nobad Remove negative/suggestion responses - --noinfo Remove informational responses - --debug Print debug information - --dbstat Print database information - --idxstat Print index information - --bannedports Ports banned separated by comma(,) - --maxportallowed Number of ports opened allowed on this hosts - --cvefile CVE File for vulnerability checks - --nocolor Don't print output in color - --json Print result as JSON string - --buffers Print global and per-thread buffer values - --outputfile Path to a output txt file - --reportfile Path to a report txt file - --template Path to a template file - --verbose Prints out all options (default: no verbose) + --silent Don't output anything on screen + --nogood Remove OK responses + --nobad Remove negative/suggestion responses + --noinfo Remove informational responses + --debug Print debug information + --dbstat Print database information + --idxstat Print index information + --bannedports Ports banned separated by comma(,) + --maxportallowed Number of ports opened allowed on this hosts + --cvefile CVE File for vulnerability checks + --nocolor Don't print output in color + --json Print result as JSON string + --buffers Print global and per-thread buffer values + --outputfile Path to a output txt file + --reportfile Path to a report txt file + --template Path to a template file + =head1 PERLDOC You can find documentation for this module with the perldoc command. From 031898858b24fb1b4bb8852b57d75561caaa8fb6 Mon Sep 17 00:00:00 2001 From: DutchProgrammer Date: Thu, 24 Mar 2016 23:58:48 +0100 Subject: [PATCH 15/44] fix for perl 5.10 and 5.8 --- mysqltuner.pl | 434 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 262 insertions(+), 172 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index aa71a9b..5d489af 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1,7 +1,7 @@ #!/usr/bin/env perl -# mysqltuner.pl - Version 1.6.8 +# mysqltuner.pl - Version 1.6.9 # High Performance MySQL Tuning Script -# Copyright (C) 2006-2015 Major Hayden - major@mhtx.net +# Copyright (C) 2006-2016 Major Hayden - major@mhtx.net # # For the latest updates, please visit http://mysqltuner.com/ # Git repository available at http://github.com/major/MySQLTuner-perl @@ -51,102 +51,114 @@ use Data::Dumper; $Data::Dumper::Pair = " : "; # Set up a few variables for use in the script -my $tunerversion = "1.6.8"; +my $tunerversion = "1.6.9"; my ( @adjvars, @generalrec ); # Set defaults my %opt = ( - "silent" => 0, - "nobad" => 0, - "nogood" => 0, - "noinfo" => 0, - "debug" => 0, - "nocolor" => 0, - "forcemem" => 0, - "forceswap" => 0, - "host" => 0, - "socket" => 0, - "port" => 0, - "user" => 0, - "pass" => 0, - "skipsize" => 0, - "checkversion" => 0, - "buffers" => 0, - "passwordfile" => 0, - "bannedports" => '', - "maxportallowed"=> 0, - "outputfile" => 0, - "dbstat" => 0, - "idxstat" => 0, - "skippassword" => 0, - "noask" => 0, - "template" => 0, - "json" => 0, - "reportfile" => 0 + "silent" => 0, + "nobad" => 0, + "nogood" => 0, + "noinfo" => 0, + "debug" => 0, + "nocolor" => 0, + "forcemem" => 0, + "forceswap" => 0, + "host" => 0, + "socket" => 0, + "port" => 0, + "user" => 0, + "pass" => 0, + "skipsize" => 0, + "checkversion" => 0, + "updateversion" => 0, + "buffers" => 0, + "passwordfile" => 0, + "bannedports" => '', + "maxportallowed" => 0, + "outputfile" => 0, + "dbstat" => 0, + "idxstat" => 0, + "skippassword" => 0, + "noask" => 0, + "template" => 0, + "json" => 0, + "prettyjson" => 0, + "reportfile" => 0, + "verbose" => 0 ); # Gather the options from the command line -GetOptions( - \%opt, 'nobad', 'nogood', 'noinfo', - 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', - 'host=s', 'socket=s', 'port=i', 'user=s', - 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', - 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', 'json', - 'idxstat', 'noask', 'template=s', 'reportfile=s', 'cvefile=s', - 'bannedports=s','maxportallowed=s', +my $getOptionsCheck = GetOptions( + \%opt, 'nobad', 'nogood', 'noinfo', + 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', + 'host=s', 'socket=s', 'port=i', 'user=s', + 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', + 'mysqlcmd=s', 'help', 'buffers', 'skippassword', + 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', + 'json', 'prettyjson', 'idxstat', 'noask', + 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', + 'updateversion', 'maxportallowed=s', 'verbose' ); +#If params are incorrect return help +if ($getOptionsCheck ne 1) { + usage(); +} + if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } sub usage { - # Shown with --help option passed - print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" - . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" - . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" - . "\n" - . " Important Usage Guidelines:\n" - . " To run the script with the default options, run the script without arguments\n" - . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" - . " Some routines may require root level privileges (script will provide warnings)\n" - . " You must provide the remote server's total memory when connecting to other servers\n" - . "\n" - . " Connection and Authentication\n" - . " --host Connect to a remote host to perform tests (default: localhost)\n" - . " --socket Use a different socket for a local connection\n" - . " --port Port to use for connection (default: 3306)\n" - . " --user Username to use for authentication\n" - . " --pass Password to use for authentication\n" - . " --mysqladmin Path to a custom mysqladmin executable\n" - . " --mysqlcmd Path to a custom mysql executable\n" . "\n" - . " --noask Dont ask password if needed\n" . "\n" - . " Performance and Reporting Options\n" - . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" - . " (Recommended for servers with many tables)\n" - . " --skippassword Don't perform checks on user passwords(default: off)\n" - . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" - . " --forcemem Amount of RAM installed in megabytes\n" - . " --forceswap Amount of swap memory configured in megabytes\n" - . " --passwordfile Path to a password file list(one password by line)\n" - . " Output Options:\n" - . " --silent Don't output anything on screen\n" - . " --nogood Remove OK responses\n" - . " --nobad Remove negative/suggestion responses\n" - . " --noinfo Remove informational responses\n" - . " --debug Print debug information\n" - . " --dbstat Print database information\n" - . " --idxstat Print index information\n" - . " --bannedports Ports banned separated by comma(,)\n" - . " --maxportallowed Number of ports opened allowed on this hosts\n" - . " --cvefile CVE File for vulnerability checks\n" - . " --nocolor Don't print output in color\n" - . " --json Print result as JSON string\n" - . " --buffers Print global and per-thread buffer values\n" - . " --outputfile Path to a output txt file\n" . "\n" - . " --reportfile Path to a report txt file\n" . "\n" - . " --template Path to a template file\n" . "\n"; - exit 0; + # Shown with --help option passed + print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" + . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" + . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" + . "\n" + . " Important Usage Guidelines:\n" + . " To run the script with the default options, run the script without arguments\n" + . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" + . " Some routines may require root level privileges (script will provide warnings)\n" + . " You must provide the remote server's total memory when connecting to other servers\n" + . "\n" + . " Connection and Authentication\n" + . " --host Connect to a remote host to perform tests (default: localhost)\n" + . " --socket Use a different socket for a local connection\n" + . " --port Port to use for connection (default: 3306)\n" + . " --user Username to use for authentication\n" + . " --pass Password to use for authentication\n" + . " --mysqladmin Path to a custom mysqladmin executable\n" + . " --mysqlcmd Path to a custom mysql executable\n" . "\n" + . " --noask Dont ask password if needed\n" . "\n" + . " Performance and Reporting Options\n" + . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" + . " (Recommended for servers with many tables)\n" + . " --skippassword Don't perform checks on user passwords(default: off)\n" + . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" + . " --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check)\n" + . " --forcemem Amount of RAM installed in megabytes\n" + . " --forceswap Amount of swap memory configured in megabytes\n" + . " --passwordfile Path to a password file list(one password by line)\n" + . " Output Options:\n" + . " --silent Don't output anything on screen\n" + . " --nogood Remove OK responses\n" + . " --nobad Remove negative/suggestion responses\n" + . " --noinfo Remove informational responses\n" + . " --debug Print debug information\n" + . " --dbstat Print database information\n" + . " --idxstat Print index information\n" + . " --bannedports Ports banned separated by comma(,)\n" + . " --maxportallowed Number of ports opened allowed on this hosts\n" + . " --cvefile CVE File for vulnerability checks\n" + . " --nocolor Don't print output in color\n" + . " --json Print result as JSON string\n" + . " --prettyjson Print result as human readable JSON\n" + . " --buffers Print global and per-thread buffer values\n" + . " --outputfile Path to a output txt file\n" . "\n" + . " --reportfile Path to a report txt file\n" . "\n" + . " --template Path to a template file\n" . "\n" + . " --verbose Prints out all options (default: no verbose) \n" . "\n"; + exit 0; } my $devnull = File::Spec->devnull(); @@ -159,6 +171,15 @@ my $basic_password_files = $basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt" unless -f "$basic_password_files"; +# check if we need to enable verbose mode +if ($opt{verbose}) { + $opt{checkversion} = 1; #Check for updates to MySQLTuner + $opt{dbstat} = 1; #Print database information + $opt{idxstat} = 1; #Print index information + $opt{buffers} = 1; #Print global and per-thread buffer values + $opt{cvefile} = 'vulnerabilities.csv'; #CVE File for vulnerability checks +} + # for RPM distributions $opt{cvefile} = "/usr/share/mysqltuner/vulnerabilities.csv" unless ( defined $opt{cvefile} and -f "$opt{cvefile}"); @@ -189,7 +210,7 @@ my %result; # Functions that handle the print styles sub prettyprint { - print $_[0] . "\n" unless $opt{'silent'}; + print $_[0] . "\n" unless ($opt{'silent'} or $opt{'json'}); print $fh $_[0] . "\n" if defined($fh); } sub goodprint { prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 ); } @@ -385,8 +406,8 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { - if ($opt{checkversion} eq 0) { - print "\n"; + if ($opt{'checkversion'} eq 0 and $opt{'updateversion'} eq 0) { + print "\n" unless ($opt{'silent'} or $opt{'json'}); infoprint "Skipped version check for MySQLTuner script"; return; } @@ -424,12 +445,80 @@ sub validate_tuner_version { infoprint "Unable to check for the latest MySQLTuner version"; } +# Checks for updates to MySQLTuner +sub update_tuner_version { + if ($opt{'updateversion'} eq 0) { + badprint "Skipped version update for MySQLTuner script"; + print "\n" unless ($opt{'silent'} or $opt{'json'}); + return; + } + + #use Cwd; + my $update; + my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/"; + my @scripts = ("mysqltuner.pl", "basic_passwords.txt", "vulnerabilities.csv"); + my $totalScripts = scalar(@scripts); + my $receivedScripts = 0; + my $httpcli =`which curl`; + + foreach my $script (@scripts) { + + chomp($httpcli); + if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; + + debugprint "$httpcli --connect-timeout 5 -silent '$url$script' > $script"; + $update = `$httpcli --connect-timeout 5 -silent '$url$script' > $script`; + chomp($update); + debugprint "$script updated: $update"; + + if ( -s $script eq 0) { + badprint "Couldn't update $script"; + } else { + ++$receivedScripts; + debugprint "$script updated: $update"; + } + } else { + + $httpcli=`which wget`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; + + debugprint "$httpcli -qe timestamping=off -T 5 -O $script '$url$script'"; + $update = `$httpcli -qe timestamping=off -T 5 -O $script '$url$script'`; + chomp($update); + + if ( -s $script eq 0) { + badprint "Couldn't update $script"; + } else { + ++$receivedScripts; + debugprint "$script updated: $update"; + } + + } else { + debugprint "curl and wget are not available."; + infoprint "Unable to check for the latest MySQLTuner version"; + } + } + } + + if ($receivedScripts eq $totalScripts) { + goodprint "Successfully updated MySQLTuner script"; + } else { + badprint "Couldn't update MySQLTuner script"; + } + + exit 0; +} + sub compare_tuner_version { my $remoteversion=shift; debugprint "Remote data: $remoteversion"; #exit 0; if ($remoteversion ne $tunerversion) { badprint "There is a new version of MySQLTuner available ($remoteversion)"; + update_tuner_version(); return; } goodprint "You have the latest version of MySQLTuner($tunerversion)"; @@ -709,14 +798,14 @@ sub select_array { debugprint "PERFORM: $req "; my @result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + exit $?; } - debugprint "select_array: return code : $?"; + debugprint "select_array: return code : $?"; chomp(@result); return @result; } @@ -727,14 +816,14 @@ sub select_one { debugprint "PERFORM: $req "; my $result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + exit $?; } - debugprint "select_array: return code : $?"; + debugprint "select_array: return code : $?"; chomp($result); return $result; } @@ -887,9 +976,9 @@ sub cve_recommendations { sub get_opened_ports { my @opened_ports=`netstat -ltn`; map { - s/.*:(\d+)\s.*$/$1/; - s/\D//g; - } @opened_ports; + s/.*:(\d+)\s.*$/$1/; + s/\D//g; + } @opened_ports; @opened_ports = sort {$a <=> $b} grep { !/^$/ } @opened_ports; debugprint Dumper \@opened_ports; return @opened_ports; @@ -904,26 +993,26 @@ sub is_open_port { } sub get_process_memory { - my $pid=shift; - return 0 unless -f "/proc/$pid/status"; - my @pdata= grep { /RSS:/ } get_file_contents "/proc/$pid/status"; - map { - s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge - } @pdata; - return $pdata[0]; + my $pid=shift; + return 0 unless -f "/proc/$pid/status"; + my @pdata= grep { /RSS:/ } get_file_contents "/proc/$pid/status"; + map { + s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge + } @pdata; + return $pdata[0]; } sub get_other_process_memory { - my @procs=`ps -eo pid,cmd`; - map { s/.*mysqld.*//; s/.*\[.*\].*//; s/^\s+$//g; s/.*PID.*CMD.*//; s/.*systemd.*//;} @procs; - map {s/\s*?(\d+)\s*.*/$1/g;} @procs; - remove_cr @procs; - @procs=remove_empty @procs; - my $totalMemOther=0; - map { - $totalMemOther+=get_process_memory($_); - } @procs; - return $totalMemOther; + my @procs=`ps -eo pid,cmd`; + map { s/.*mysqld.*//; s/.*\[.*\].*//; s/^\s+$//g; s/.*PID.*CMD.*//; s/.*systemd.*//;} @procs; + map {s/\s*?(\d+)\s*.*/$1/g;} @procs; + remove_cr @procs; + @procs=remove_empty @procs; + my $totalMemOther=0; + map { + $totalMemOther+=get_process_memory($_); + } @procs; + return $totalMemOther; } sub get_os_release { @@ -947,34 +1036,34 @@ sub system_recommendations { infoprint "User process except mysqld used ". hr_bytes_rnd($omem) . " RAM."; if ( (0.15*$physical_memory) < $omem) { badprint "Other user process except mysqld used more than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; - push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); - push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); + push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); + push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); } else { } #if ($omem > - #exit 0; + #exit 0; if ($opt{'maxportallowed'} > 0) { my @opened_ports=get_opened_ports; infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; if (scalar(@opened_ports) > $opt{'maxportallowed'}) { - badprint "There is too many listening ports: ". scalar(@opened_ports) " opened > ".$opt{'maxportallowed'}. "allowed."; - push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); + badprint "There is too many listening ports: ". scalar(@opened_ports). " opened > ".$opt{'maxportallowed'}. "allowed."; + push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); } else { - goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; + goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; } } foreach my $banport (@banned_ports) { - if ( is_open_port($banport) ) { - badprint "Banned port: $banport is opened.."; - push( @generalrec, "Port $banport is opened. Consider stopping program handling this port." ); - } else { - goodprint "$banport is not opened."; - } + if ( is_open_port($banport) ) { + badprint "Banned port: $banport is opened.."; + push( @generalrec, "Port $banport is opened. Consider stopping program handling this port." ); + } else { + goodprint "$banport is not opened."; + } } } @@ -1330,7 +1419,7 @@ sub check_storage_engines { $result{'Databases'}{'List'} = [@dblist]; infoprint "Status: $engines"; if ( mysql_version_ge( 5, 1, 5 ) ) { - # MySQL 5 servers can have table sizes calculated quickly from information schema + # MySQL 5 servers can have table sizes calculated quickly from information schema my @templist = select_array "SELECT ENGINE,SUM(DATA_LENGTH+INDEX_LENGTH),COUNT(ENGINE),SUM(DATA_LENGTH),SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;"; @@ -2511,12 +2600,12 @@ sub mariadb_threadpool { infoprint "Thread Pool Size: ".$myvar{'thread_pool_size'}. " thread(s)."; if ($myvar{'have_innodb'} eq 'YES') { - if ($myvar{'thread_pool_size'}< 16 or $myvar{'thread_pool_size'}>36) { - badprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; - push( @generalrec, "Thread pool size for InnoDB usage (".$myvar{'thread_pool_size'}.")" ); - push( @adjvars, "thread_pool_size between 16 and 36 for InnoDB usage" ); - } else { - goodprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; + if ($myvar{'thread_pool_size'}< 16 or $myvar{'thread_pool_size'}>36) { + badprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; + push( @generalrec, "Thread pool size for InnoDB usage (".$myvar{'thread_pool_size'}.")" ); + push( @adjvars, "thread_pool_size between 16 and 36 for InnoDB usage" ); + } else { + goodprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; } return; } @@ -2539,9 +2628,9 @@ sub mysqsl_pfs { # Performance Schema unless ( defined($myvar{'performance_schema'}) and $myvar{'performance_schema'} eq 'ON' ) { infoprint "Performance schema is disabled."; + } else { + infoprint "Performance schema is enabled."; } - - infoprint "Performance schema is enabled."; } @@ -2907,7 +2996,7 @@ sub mysql_databases { $result{'Databases'}{'All databases'}{'Index Pct'} = percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%"; $result{'Databases'}{'All databases'}{'Total Size'} = $totaldbinfo[3]; - print "\n"; + print "\n" unless ($opt{'silent'} or $opt{'json'}); foreach (@dblist) { chomp($_); if ( $_ eq "information_schema" @@ -2956,10 +3045,10 @@ sub mysql_databases { percentage( $dbinfo[3], $dbinfo[4] ) . "%"; $result{'Databases'}{ $dbinfo[0] }{'Total Size'} = $dbinfo[4]; if ($dbinfo[7]>1) { - badprint $dbinfo[7]. " differents collations for database ".$dbinfo[0]; + badprint $dbinfo[7]. " differents collations for database ".$dbinfo[0]; push(@generalrec, "Check all table collations are identical for all tables in ".$dbinfo[0]. " database."); } else { - goodprint $dbinfo[7]. " collation for ".$dbinfo[0]. " database."; + goodprint $dbinfo[7]. " collation for ".$dbinfo[0]. " database."; } if ($dbinfo[8]>1) { badprint $dbinfo[8]. " differents engines for database ".$dbinfo[0]; @@ -3208,7 +3297,7 @@ sub dump_result { exit 1; } my $json = JSON->new->allow_nonref; - print JSON->new->utf8(1)->pretty(1)->encode(%result); + print $json->utf8(1)->pretty(($opt{'prettyjson'} ? 1 : 0))->encode(\%result); } } @@ -3223,7 +3312,7 @@ get_all_vars; # Toss variables/status into hashes get_tuning_info; # Get information about the tuning connexion validate_mysql_version; # Check current MySQL version check_architecture; # Suggest 64-bit upgrade -system_recommendations; # avoid to many service on the same host +system_recommendations; # avoid to many service on the same host check_storage_engines; # Show enabled storage engines mysql_databases; # Show informations about databases mysql_indexes; # Show informations about indexes @@ -3277,33 +3366,34 @@ You must provide the remote server's total memory when connecting to other serve =head1 PERFORMANCE AND REPORTING OPTIONS - --skipsize Don't enumerate tables and their types/sizes (default: on) - (Recommended for servers with many tables) - --skippassword Don't perform checks on user passwords(default: off) - --checkversion Check for updates to MySQLTuner (default: don't check) - --forcemem Amount of RAM installed in megabytes - --forceswap Amount of swap memory configured in megabytes - --passwordfile Path to a password file list(one password by line) + --skipsize Don't enumerate tables and their types/sizes (default: on) + (Recommended for servers with many tables) + --skippassword Don't perform checks on user passwords(default: off) + --checkversion Check for updates to MySQLTuner (default: don't check) + --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check) + --forcemem Amount of RAM installed in megabytes + --forceswap Amount of swap memory configured in megabytes + --passwordfile Path to a password file list(one password by line) =head1 OUTPUT OPTIONS - --silent Don't output anything on screen - --nogood Remove OK responses - --nobad Remove negative/suggestion responses - --noinfo Remove informational responses - --debug Print debug information - --dbstat Print database information - --idxstat Print index information - --bannedports Ports banned separated by comma(,) - --maxportallowed Number of ports opened allowed on this hosts - --cvefile CVE File for vulnerability checks - --nocolor Don't print output in color - --json Print result as JSON string - --buffers Print global and per-thread buffer values - --outputfile Path to a output txt file - --reportfile Path to a report txt file - --template Path to a template file - + --silent Don't output anything on screen + --nogood Remove OK responses + --nobad Remove negative/suggestion responses + --noinfo Remove informational responses + --debug Print debug information + --dbstat Print database information + --idxstat Print index information + --bannedports Ports banned separated by comma(,) + --maxportallowed Number of ports opened allowed on this hosts + --cvefile CVE File for vulnerability checks + --nocolor Don't print output in color + --json Print result as JSON string + --buffers Print global and per-thread buffer values + --outputfile Path to a output txt file + --reportfile Path to a report txt file + --template Path to a template file + --verbose Prints out all options (default: no verbose) =head1 PERLDOC You can find documentation for this module with the perldoc command. From c9ebae1e26f80242e14e4a13dd3e9e23cbc92ab3 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 25 Mar 2016 11:57:35 +0100 Subject: [PATCH 16/44] #166 removing test when plugin is unix_socket or win_socket Support for bug https://bugs.mysql.com/bug.php?id=80860 --- mysqltuner.pl | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 5d489af..254338c 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -803,7 +803,7 @@ sub select_array { debugprint "CMD : $mysqlcmd"; debugprint "OPTIONS: $mysqllogin"; debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + #exit $?; } debugprint "select_array: return code : $?"; chomp(@result); @@ -821,7 +821,7 @@ sub select_one { debugprint "CMD : $mysqlcmd"; debugprint "OPTIONS: $mysqllogin"; debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - exit $?; + #exit $?; } debugprint "select_array: return code : $?"; chomp($result); @@ -1066,7 +1066,7 @@ sub system_recommendations { } } } - + sub security_recommendations { prettyprint "\n-------- Security Recommendations -------------------------------------------"; @@ -1102,7 +1102,7 @@ sub security_recommendations { # Looking for Empty Password @mysqlstatlist = select_array -"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE $PASS_COLUMN_NAME = '' OR $PASS_COLUMN_NAME IS NULL"; +"SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE ($PASS_COLUMN_NAME = '' OR $PASS_COLUMN_NAME IS NULL) AND plugin NOT IN ('unix_socket', 'win_socket')"; if (@mysqlstatlist) { foreach my $line ( sort @mysqlstatlist ) { chomp($line); @@ -1116,6 +1116,14 @@ sub security_recommendations { goodprint "All database users have passwords assigned"; } + if (mysql_version_ge(5,7)) { + my $valPlugin=select_one("select count(*) from information_schema.plugins where PLUGIN_NAME='validate_password' AND PLUGIN_STATUS='ACTIVE'"); + if ($valPlugin>=1) { + infoprint "Bug #80860 MySQL 5.7: Avoid testing password when validate_password is activated"; + return; + } + } + # Looking for User with user/ uppercase /capitalise user as password @mysqlstatlist = select_array "SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(user) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(UPPER(user)) OR CAST($PASS_COLUMN_NAME as Binary) = PASSWORD(UPPER(LEFT(User, 1)) + SUBSTRING(User, 2, LENGTH(User)))"; From 3f8ca6493eab4121939896be252385771a283a02 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 25 Mar 2016 14:32:15 +0100 Subject: [PATCH 17/44] Adding disk space and inode control other fs mountpoint #165 --- mysqltuner.pl | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 254338c..79d187a 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1021,6 +1021,40 @@ sub get_os_release { remove_cr @info_release; return $info_release[0]; } + +sub get_fs_info() { + my @sinfo=`df -P | grep '%'`; + shift @sinfo; + my @iinfo=`df -Pi| grep '%'`; + shift @iinfo; + map { + s/.*\s(\d+)%\s+(.*)/$1\t$2/g + } @sinfo; + foreach my $info (@sinfo) { + if ($info =~ /(\d+)\t(.*)/) { + if ($1 > 85) { + badprint "mount point $2 is using $1 % total space"; + push(@generalrec, "Add some space to $2 mountpoint.") + } else { + infoprint "mount point $2 is using $1 % of total space"; + } + } + } + + map { + s/.*\s(\d+)%\s+(.*)/$1\t$2/g + } @iinfo; + foreach my $info (@iinfo) { + if ($info =~ /(\d+)\t(.*)/) { + if ($1 > 85) { + badprint "mount point $2 is using $1 % of max allowed inodes"; + push(@generalrec, "Cleanup files from $2 mountpoint or reformat you filesystem.") + } else { + infoprint "mount point $2 is using $1 % of max allowed inodes"; + } + } + } +} sub system_recommendations { prettyprint "\n-------- System Linux Recommendations ---------------------------------------"; my $os = `uname`; @@ -1035,17 +1069,13 @@ sub system_recommendations { my $omem=get_other_process_memory; infoprint "User process except mysqld used ". hr_bytes_rnd($omem) . " RAM."; if ( (0.15*$physical_memory) < $omem) { - badprint "Other user process except mysqld used more than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; + badprint "Other user process except mysqld used more than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); - push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); - - + push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); } else { + infoprint "Other user process except mysqld used less than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; } - #if ($omem > - #exit 0; - if ($opt{'maxportallowed'} > 0) { my @opened_ports=get_opened_ports; infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; @@ -1065,6 +1095,8 @@ sub system_recommendations { goodprint "$banport is not opened."; } } + + get_fs_info; } sub security_recommendations { From a010416b930d4cf8757d0620cf632ecf078eb644 Mon Sep 17 00:00:00 2001 From: root Date: Fri, 25 Mar 2016 16:22:07 +0100 Subject: [PATCH 18/44] Adding lot of system indicators #165 --- mysqltuner.pl | 69 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 79d187a..11bbc7a 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1055,6 +1055,72 @@ sub get_fs_info() { } } } +sub is_virtual_machine() { + my $isVm=`grep -Ec '^flags.*\ hypervisor\ ' /proc/cpuinfo`; + return ($isVm==0?1:0); +} + + +sub infocmd { + my $cmd="@_"; + debugprint "CMD: $cmd"; + my @result=`$cmd`; + remove_cr @result; + for my $l (@result) { + infoprint "$l"; + } +} +sub infocmd_tab { + my $cmd="@_"; + debugprint "CMD: $cmd"; + my @result=`$cmd`; + remove_cr @result; + for my $l (@result) { + infoprint "\t$l"; + } +} +sub infocmd_one { + my $cmd="@_"; + my @result=`$cmd`; + remove_cr @result; + return join ', ' ,@result; +} + +sub get_system_info() +{ +infoprint get_os_release; +if (is_virtual_machine) { + infoprint "Machine type : Virtual machine"; + } else { + infoprint "Machine type : Physical machine"; +} + +`ping -c 1 google.com &>/dev/null`; +my $isConnected=$?; +if ($? == 0) { + infoprint "Internet : Connected"; +} else { + badprint "Internet : Disconnected"; + } +infoprint "Operating System Type : " . infocmd_one "uname -o"; +infoprint "Kernel Release : ". infocmd_one "uname -r"; +infoprint "Hostname : $ENV{'HOSTNAME'}"; +infoprint "Network Cards : "; +infocmd_tab "ifconfig| grep -A1 mtu"; +infoprint "Internal IP : ". infocmd_one "hostname -I"; +infoprint "External IP : ". infocmd_one "curl -s ipecho.net/plain" if $isConnected==0; +badprint "External IP : Can't check because of Internet connectivity" if $isConnected!=0; +infoprint "Name Servers : ". infocmd_one "grep 'nameserver' /etc/resolv.conf \| awk '{print \$2}'"; +infoprint "Logged In users : "; +infocmd_tab "who"; +infoprint "Ram Usages :"; +infocmd_tab "free -h | grep -v +"; +infoprint "Load Average : "; +infocmd_tab "top -n 1 -b | grep 'load average:'"; + +#infoprint "System Uptime Days/(HH:MM) : `uptime | awk '{print $3,$4}' | cut -f1 -d,`"; +} + sub system_recommendations { prettyprint "\n-------- System Linux Recommendations ---------------------------------------"; my $os = `uname`; @@ -1064,8 +1130,7 @@ sub system_recommendations { } prettyprint "Look for related Linux system recommandations"; #prettyprint '-'x78; - infoprint get_os_release; - + get_system_info(); my $omem=get_other_process_memory; infoprint "User process except mysqld used ". hr_bytes_rnd($omem) . " RAM."; if ( (0.15*$physical_memory) < $omem) { From ae48a655713398af01b1eaa870860bf3dfbcb62b Mon Sep 17 00:00:00 2001 From: zhil Date: Mon, 28 Mar 2016 16:31:52 +0300 Subject: [PATCH 19/44] Update README.md fixed parameter --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f431b20..c16c7fa 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ __Usage:__ Minimal usage locally __Usage:__ Minimal usage remotely - perl mysqltuner.pl --host targetDNS_IP --user admin_user --password admin_password + perl mysqltuner.pl --host targetDNS_IP --user admin_user --pass admin_password __Usage:__ Enable maximum output information around MySQL/MariaDb without debugging From 94ee62a6e0b2d988d7e7e4d41ed7e9aa91bc07a9 Mon Sep 17 00:00:00 2001 From: amq Date: Tue, 29 Mar 2016 01:27:41 +0200 Subject: [PATCH 20/44] Fix grammar --- mysqltuner.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 11bbc7a..cfa0d18 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1136,7 +1136,7 @@ sub system_recommendations { if ( (0.15*$physical_memory) < $omem) { badprint "Other user process except mysqld used more than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); - push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE IS TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCURS !" ); + push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE ARE TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCUR!" ); } else { infoprint "Other user process except mysqld used less than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; } From cb5a1c528ee3bea5d5288ef61b5547300d697479 Mon Sep 17 00:00:00 2001 From: Robert Milasan Date: Tue, 29 Mar 2016 09:51:48 +0200 Subject: [PATCH 21/44] Fix is_virtual_machine function return, drop ENV{'HOSTNAME'} for hostname cmd, some machines don't have HOSTNAME set --- mysqltuner.pl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 11bbc7a..b1a5816 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1057,7 +1057,7 @@ sub get_fs_info() { } sub is_virtual_machine() { my $isVm=`grep -Ec '^flags.*\ hypervisor\ ' /proc/cpuinfo`; - return ($isVm==0?1:0); + return ($isVm==0?0:1); } @@ -1102,9 +1102,9 @@ if ($? == 0) { } else { badprint "Internet : Disconnected"; } -infoprint "Operating System Type : " . infocmd_one "uname -o"; +infoprint "Operating System Type : ". infocmd_one "uname -o"; infoprint "Kernel Release : ". infocmd_one "uname -r"; -infoprint "Hostname : $ENV{'HOSTNAME'}"; +infoprint "Hostname : ". infocmd_one "hostname"; infoprint "Network Cards : "; infocmd_tab "ifconfig| grep -A1 mtu"; infoprint "Internal IP : ". infocmd_one "hostname -I"; @@ -1113,7 +1113,7 @@ badprint "External IP : Can't check because of Internet connectivity" infoprint "Name Servers : ". infocmd_one "grep 'nameserver' /etc/resolv.conf \| awk '{print \$2}'"; infoprint "Logged In users : "; infocmd_tab "who"; -infoprint "Ram Usages :"; +infoprint "Ram Usages : "; infocmd_tab "free -h | grep -v +"; infoprint "Load Average : "; infocmd_tab "top -n 1 -b | grep 'load average:'"; From 35ebd1cb5879d9c53dfb6ac295f63d9cb8f78e37 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Mar 2016 14:09:25 +0200 Subject: [PATCH 22/44] Update USAGE.md --- USAGE.md | 51 +++++++++++++++++++++++++++------------------------ 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/USAGE.md b/USAGE.md index ddaf5fc..c5fbc92 100644 --- a/USAGE.md +++ b/USAGE.md @@ -1,6 +1,6 @@ # NAME - MySQLTuner 1.6.4 - MySQL High Performance Tuning Script + MySQLTuner 1.6.8 - MySQL High Performance Tuning Script # IMPORTANT USAGE GUIDELINES @@ -21,32 +21,35 @@ You must provide the remote server's total memory when connecting to other serve # PERFORMANCE AND REPORTING OPTIONS - --skipsize Don't enumerate tables and their types/sizes (default: on) - (Recommended for servers with many tables) - --skippassword Don't perform checks on user passwords(default: off) - --checkversion Check for updates to MySQLTuner (default: don't check) - --forcemem Amount of RAM installed in megabytes - --forceswap Amount of swap memory configured in megabytes - --passwordfile Path to a password file list(one password by line) + --skipsize Don't enumerate tables and their types/sizes (default: on) + (Recommended for servers with many tables) + --skippassword Don't perform checks on user passwords(default: off) + --checkversion Check for updates to MySQLTuner (default: don't check) + --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check) + --forcemem Amount of RAM installed in megabytes + --forceswap Amount of swap memory configured in megabytes + --passwordfile Path to a password file list(one password by line) # OUTPUT OPTIONS - --silent Don't output anything on screen - --nogood Remove OK responses - --nobad Remove negative/suggestion responses - --noinfo Remove informational responses - --debug Print debug information - --dbstat Print database information - --idxstat Print index information - --cvefile CVE File for vulnerability checks - --nocolor Don't print output in color - --json Print result as JSON string - --buffers Print global and per-thread buffer values - --outputfile Path to a output txt file - --reportfile Path to a report txt file - --template Path to a template file - -# PERLDOC + --silent Don't output anything on screen + --nogood Remove OK responses + --nobad Remove negative/suggestion responses + --noinfo Remove informational responses + --debug Print debug information + --dbstat Print database information + --idxstat Print index information + --bannedports Ports banned separated by comma(,) + --maxportallowed Number of ports opened allowed on this hosts + --cvefile CVE File for vulnerability checks + --nocolor Don't print output in color + --json Print result as JSON string + --buffers Print global and per-thread buffer values + --outputfile Path to a output txt file + --reportfile Path to a report txt file + --template Path to a template file + --verbose Prints out all options (default: no verbose) + =head1 PERLDOC You can find documentation for this module with the perldoc command. From d3a2d83d7d23887afabbd452368b04d842e82323 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Mar 2016 14:22:45 +0200 Subject: [PATCH 23/44] Just a simple perltidy to cleanup ident in the code. --- mysqltuner.pl | 1283 ++++++++++++++++++++++++++++--------------------- 1 file changed, 727 insertions(+), 556 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 93335e9..6a24c33 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -73,9 +73,9 @@ my %opt = ( "checkversion" => 0, "updateversion" => 0, "buffers" => 0, - "passwordfile" => 0, + "passwordfile" => 0, "bannedports" => '', - "maxportallowed" => 0, + "maxportallowed" => 0, "outputfile" => 0, "dbstat" => 0, "idxstat" => 0, @@ -90,75 +90,85 @@ my %opt = ( # Gather the options from the command line my $getOptionsCheck = GetOptions( - \%opt, 'nobad', 'nogood', 'noinfo', - 'debug', 'nocolor', 'forcemem=i', 'forceswap=i', - 'host=s', 'socket=s', 'port=i', 'user=s', - 'pass=s', 'skipsize', 'checkversion', 'mysqladmin=s', - 'mysqlcmd=s', 'help', 'buffers', 'skippassword', - 'passwordfile=s', 'outputfile=s', 'silent', 'dbstat', - 'json', 'prettyjson', 'idxstat', 'noask', - 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', - 'updateversion', 'maxportallowed=s', 'verbose' + \%opt, 'nobad', + 'nogood', 'noinfo', + 'debug', 'nocolor', + 'forcemem=i', 'forceswap=i', + 'host=s', 'socket=s', + 'port=i', 'user=s', + 'pass=s', 'skipsize', + 'checkversion', 'mysqladmin=s', + 'mysqlcmd=s', 'help', + 'buffers', 'skippassword', + 'passwordfile=s', 'outputfile=s', + 'silent', 'dbstat', + 'json', 'prettyjson', + 'idxstat', 'noask', + 'template=s', 'reportfile=s', + 'cvefile=s', 'bannedports=s', + 'updateversion', 'maxportallowed=s', + 'verbose' ); #If params are incorrect return help -if ($getOptionsCheck ne 1) { - usage(); +if ( $getOptionsCheck ne 1 ) { + usage(); } if ( defined $opt{'help'} && $opt{'help'} == 1 ) { usage(); } sub usage { - # Shown with --help option passed - print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" - . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" - . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" - . "\n" - . " Important Usage Guidelines:\n" - . " To run the script with the default options, run the script without arguments\n" - . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" - . " Some routines may require root level privileges (script will provide warnings)\n" - . " You must provide the remote server's total memory when connecting to other servers\n" - . "\n" - . " Connection and Authentication\n" - . " --host Connect to a remote host to perform tests (default: localhost)\n" - . " --socket Use a different socket for a local connection\n" - . " --port Port to use for connection (default: 3306)\n" - . " --user Username to use for authentication\n" - . " --pass Password to use for authentication\n" - . " --mysqladmin Path to a custom mysqladmin executable\n" - . " --mysqlcmd Path to a custom mysql executable\n" . "\n" - . " --noask Dont ask password if needed\n" . "\n" - . " Performance and Reporting Options\n" - . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" - . " (Recommended for servers with many tables)\n" - . " --skippassword Don't perform checks on user passwords(default: off)\n" - . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" - . " --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check)\n" - . " --forcemem Amount of RAM installed in megabytes\n" - . " --forceswap Amount of swap memory configured in megabytes\n" - . " --passwordfile Path to a password file list(one password by line)\n" - . " Output Options:\n" - . " --silent Don't output anything on screen\n" - . " --nogood Remove OK responses\n" - . " --nobad Remove negative/suggestion responses\n" - . " --noinfo Remove informational responses\n" - . " --debug Print debug information\n" - . " --dbstat Print database information\n" - . " --idxstat Print index information\n" - . " --bannedports Ports banned separated by comma(,)\n" - . " --maxportallowed Number of ports opened allowed on this hosts\n" - . " --cvefile CVE File for vulnerability checks\n" - . " --nocolor Don't print output in color\n" - . " --json Print result as JSON string\n" - . " --prettyjson Print result as human readable JSON\n" - . " --buffers Print global and per-thread buffer values\n" - . " --outputfile Path to a output txt file\n" . "\n" - . " --reportfile Path to a report txt file\n" . "\n" - . " --template Path to a template file\n" . "\n" - . " --verbose Prints out all options (default: no verbose) \n" . "\n"; - exit 0; + # Shown with --help option passed + print " MySQLTuner $tunerversion - MySQL High Performance Tuning Script\n" + . " Bug reports, feature requests, and downloads at http://mysqltuner.com/\n" + . " Maintained by Major Hayden (major\@mhtx.net) - Licensed under GPL\n" + . "\n" + . " Important Usage Guidelines:\n" + . " To run the script with the default options, run the script without arguments\n" + . " Allow MySQL server to run for at least 24-48 hours before trusting suggestions\n" + . " Some routines may require root level privileges (script will provide warnings)\n" + . " You must provide the remote server's total memory when connecting to other servers\n" + . "\n" + . " Connection and Authentication\n" + . " --host Connect to a remote host to perform tests (default: localhost)\n" + . " --socket Use a different socket for a local connection\n" + . " --port Port to use for connection (default: 3306)\n" + . " --user Username to use for authentication\n" + . " --pass Password to use for authentication\n" + . " --mysqladmin Path to a custom mysqladmin executable\n" + . " --mysqlcmd Path to a custom mysql executable\n" . "\n" + . " --noask Dont ask password if needed\n" . "\n" + . " Performance and Reporting Options\n" + . " --skipsize Don't enumerate tables and their types/sizes (default: on)\n" + . " (Recommended for servers with many tables)\n" + . " --skippassword Don't perform checks on user passwords(default: off)\n" + . " --checkversion Check for updates to MySQLTuner (default: don't check)\n" + . " --updateversion Check for updates to MySQLTuner and update when newer version is available (default: don't check)\n" + . " --forcemem Amount of RAM installed in megabytes\n" + . " --forceswap Amount of swap memory configured in megabytes\n" + . " --passwordfile Path to a password file list(one password by line)\n" + . " Output Options:\n" + . " --silent Don't output anything on screen\n" + . " --nogood Remove OK responses\n" + . " --nobad Remove negative/suggestion responses\n" + . " --noinfo Remove informational responses\n" + . " --debug Print debug information\n" + . " --dbstat Print database information\n" + . " --idxstat Print index information\n" + . " --bannedports Ports banned separated by comma(,)\n" + . " --maxportallowed Number of ports opened allowed on this hosts\n" + . " --cvefile CVE File for vulnerability checks\n" + . " --nocolor Don't print output in color\n" + . " --json Print result as JSON string\n" + . " --prettyjson Print result as human readable JSON\n" + . " --buffers Print global and per-thread buffer values\n" + . " --outputfile Path to a output txt file\n" . "\n" + . " --reportfile Path to a report txt file\n" . "\n" + . " --template Path to a template file\n" . "\n" + . " --verbose Prints out all options (default: no verbose) \n" + . "\n"; + exit 0; } my $devnull = File::Spec->devnull(); @@ -172,22 +182,22 @@ $basic_password_files = "/usr/share/mysqltuner/basic_passwords.txt" unless -f "$basic_password_files"; # check if we need to enable verbose mode -if ($opt{verbose}) { - $opt{checkversion} = 1; #Check for updates to MySQLTuner - $opt{dbstat} = 1; #Print database information - $opt{idxstat} = 1; #Print index information - $opt{buffers} = 1; #Print global and per-thread buffer values - $opt{cvefile} = 'vulnerabilities.csv'; #CVE File for vulnerability checks +if ( $opt{verbose} ) { + $opt{checkversion} = 1; #Check for updates to MySQLTuner + $opt{dbstat} = 1; #Print database information + $opt{idxstat} = 1; #Print index information + $opt{buffers} = 1; #Print global and per-thread buffer values + $opt{cvefile} = 'vulnerabilities.csv'; #CVE File for vulnerability checks } # for RPM distributions $opt{cvefile} = "/usr/share/mysqltuner/vulnerabilities.csv" - unless ( defined $opt{cvefile} and -f "$opt{cvefile}"); -$opt{cvefile} ='' unless -f "$opt{cvefile}"; -$opt{cvefile} ='./vulnerabilities.csv' if -f './vulnerabilities.csv'; + unless ( defined $opt{cvefile} and -f "$opt{cvefile}" ); +$opt{cvefile} = '' unless -f "$opt{cvefile}"; +$opt{cvefile} = './vulnerabilities.csv' if -f './vulnerabilities.csv'; -$opt{'bannedports'}='' unless defined($opt{'bannedports'}); -my @banned_ports=split ',', $opt{'bannedports'}; +$opt{'bannedports'} = '' unless defined( $opt{'bannedports'} ); +my @banned_ports = split ',', $opt{'bannedports'}; # my $outputfile = undef; @@ -210,13 +220,14 @@ my %result; # Functions that handle the print styles sub prettyprint { - print $_[0] . "\n" unless ($opt{'silent'} or $opt{'json'}); + print $_[0] . "\n" unless ( $opt{'silent'} or $opt{'json'} ); print $fh $_[0] . "\n" if defined($fh); } sub goodprint { prettyprint $good. " " . $_[0] unless ( $opt{nogood} == 1 ); } sub infoprint { prettyprint $info. " " . $_[0] unless ( $opt{noinfo} == 1 ); } sub badprint { prettyprint $bad. " " . $_[0] unless ( $opt{nobad} == 1 ); } sub debugprint { prettyprint $deb. " " . $_[0] unless ( $opt{debug} == 0 ); } + sub redwrap { return ( $opt{nocolor} == 0 ) ? "\e[0;31m" . $_[0] . "\e[0m" : $_[0]; } @@ -312,6 +323,7 @@ sub pretty_uptime { my ( $physical_memory, $swap_memory, $duflags ); sub os_setup { + sub memerror { badprint "Unable to determine total memory/swap; use '--forcemem' and '--forceswap'"; @@ -328,8 +340,7 @@ sub os_setup { } else { $swap_memory = 0; - badprint - "Assuming 0 MB of swap space (use --forceswap to specify)"; + badprint "Assuming 0 MB of swap space (use --forceswap to specify)"; } } else { @@ -382,12 +393,12 @@ sub os_setup { chomp($swap_memory); $swap_memory = $swap_memory * 1024 * 1024; } - elsif( $os =~ /windows/i ) { + elsif ( $os =~ /windows/i ) { $physical_memory = - `wmic ComputerSystem get TotalPhysicalMemory | perl -ne "chomp; print if /[0-9]+/;"` +`wmic ComputerSystem get TotalPhysicalMemory | perl -ne "chomp; print if /[0-9]+/;"` or memerror; - $swap_memory = - `wmic OS get FreeVirtualMemory | perl -ne "chomp; print if /[0-9]+/;"` + $swap_memory = +`wmic OS get FreeVirtualMemory | perl -ne "chomp; print if /[0-9]+/;"` or memerror; } } @@ -406,135 +417,152 @@ sub os_setup { # Checks for updates to MySQLTuner sub validate_tuner_version { - if ($opt{'checkversion'} eq 0 and $opt{'updateversion'} eq 0) { - print "\n" unless ($opt{'silent'} or $opt{'json'}); - infoprint "Skipped version check for MySQLTuner script"; - return; - } + if ( $opt{'checkversion'} eq 0 and $opt{'updateversion'} eq 0 ) { + print "\n" unless ( $opt{'silent'} or $opt{'json'} ); + infoprint "Skipped version check for MySQLTuner script"; + return; + } - my $update; - my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl"; - my $httpcli=`which curl`; - chomp($httpcli); - if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; + my $update; + my $url = +"https://raw.githubusercontent.com/major/MySQLTuner-perl/master/mysqltuner.pl"; + my $httpcli = `which curl`; + chomp($httpcli); + if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; - debugprint "$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = `$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - debugprint "VERSION: $update"; + debugprint +"$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2"; + $update = +`$httpcli --connect-timeout 5 -silent '$url' 2>/dev/null | grep 'my \$tunerversion'| cut -d\\\" -f2`; + chomp($update); + debugprint "VERSION: $update"; + compare_tuner_version($update); + return; + } - compare_tuner_version($update); - return; - } + $httpcli = `which wget`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; - - $httpcli=`which wget`; - chomp($httpcli); - if ( defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; - - debugprint "$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2"; - $update = `$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`; - chomp($update); - compare_tuner_version($update); - return; - } - debugprint "curl and wget are not available."; - infoprint "Unable to check for the latest MySQLTuner version"; + debugprint +"$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2"; + $update = +`$httpcli -e timestamping=off -T 5 -O - '$url' 2>$devnull| grep 'my \$tunerversion'| cut -d\\\" -f2`; + chomp($update); + compare_tuner_version($update); + return; + } + debugprint "curl and wget are not available."; + infoprint "Unable to check for the latest MySQLTuner version"; } # Checks for updates to MySQLTuner sub update_tuner_version { - if ($opt{'updateversion'} eq 0) { - badprint "Skipped version update for MySQLTuner script"; - print "\n" unless ($opt{'silent'} or $opt{'json'}); - return; - } + if ( $opt{'updateversion'} eq 0 ) { + badprint "Skipped version update for MySQLTuner script"; + print "\n" unless ( $opt{'silent'} or $opt{'json'} ); + return; + } - #use Cwd; - my $update; - my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/"; - my @scripts = ("mysqltuner.pl", "basic_passwords.txt", "vulnerabilities.csv"); - my $totalScripts = scalar(@scripts); - my $receivedScripts = 0; - my $httpcli =`which curl`; + #use Cwd; + my $update; + my $url = "https://raw.githubusercontent.com/major/MySQLTuner-perl/master/"; + my @scripts = + ( "mysqltuner.pl", "basic_passwords.txt", "vulnerabilities.csv" ); + my $totalScripts = scalar(@scripts); + my $receivedScripts = 0; + my $httpcli = `which curl`; - foreach my $script (@scripts) { - - chomp($httpcli); - if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; + foreach my $script (@scripts) { - debugprint "$httpcli --connect-timeout 5 -silent '$url$script' > $script"; - $update = `$httpcli --connect-timeout 5 -silent '$url$script' > $script`; - chomp($update); - debugprint "$script updated: $update"; - - if ( -s $script eq 0) { - badprint "Couldn't update $script"; - } else { - ++$receivedScripts; - debugprint "$script updated: $update"; - } - } else { + chomp($httpcli); + if ( 1 != 1 and defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; - $httpcli=`which wget`; - chomp($httpcli); - if ( defined($httpcli) and -e "$httpcli" ) { - debugprint "$httpcli is available."; + debugprint + "$httpcli --connect-timeout 5 -silent '$url$script' > $script"; + $update = + `$httpcli --connect-timeout 5 -silent '$url$script' > $script`; + chomp($update); + debugprint "$script updated: $update"; - debugprint "$httpcli -qe timestamping=off -T 5 -O $script '$url$script'"; - $update = `$httpcli -qe timestamping=off -T 5 -O $script '$url$script'`; - chomp($update); - - if ( -s $script eq 0) { - badprint "Couldn't update $script"; - } else { - ++$receivedScripts; - debugprint "$script updated: $update"; + if ( -s $script eq 0 ) { + badprint "Couldn't update $script"; + } + else { + ++$receivedScripts; + debugprint "$script updated: $update"; + } } + else { - } else { - debugprint "curl and wget are not available."; - infoprint "Unable to check for the latest MySQLTuner version"; - } - } - } + $httpcli = `which wget`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + debugprint "$httpcli is available."; - if ($receivedScripts eq $totalScripts) { - goodprint "Successfully updated MySQLTuner script"; - } else { - badprint "Couldn't update MySQLTuner script"; + debugprint + "$httpcli -qe timestamping=off -T 5 -O $script '$url$script'"; + $update = + `$httpcli -qe timestamping=off -T 5 -O $script '$url$script'`; + chomp($update); + + if ( -s $script eq 0 ) { + badprint "Couldn't update $script"; + } + else { + ++$receivedScripts; + debugprint "$script updated: $update"; + } + + } + else { + debugprint "curl and wget are not available."; + infoprint "Unable to check for the latest MySQLTuner version"; + } + } } - exit 0; + if ( $receivedScripts eq $totalScripts ) { + goodprint "Successfully updated MySQLTuner script"; + } + else { + badprint "Couldn't update MySQLTuner script"; + } + + exit 0; } sub compare_tuner_version { - my $remoteversion=shift; - debugprint "Remote data: $remoteversion"; - #exit 0; - if ($remoteversion ne $tunerversion) { - badprint "There is a new version of MySQLTuner available ($remoteversion)"; - update_tuner_version(); - return; - } - goodprint "You have the latest version of MySQLTuner($tunerversion)"; - return; + my $remoteversion = shift; + debugprint "Remote data: $remoteversion"; + + #exit 0; + if ( $remoteversion ne $tunerversion ) { + badprint + "There is a new version of MySQLTuner available ($remoteversion)"; + update_tuner_version(); + return; + } + goodprint "You have the latest version of MySQLTuner($tunerversion)"; + return; } # Checks to see if a MySQL login is possible my ( $mysqllogin, $doremote, $remotestring, $mysqlcmd, $mysqladmincmd ); my $osname = $^O; -if( $osname eq 'MSWin32' ) { - eval { require Win32; } or last; - $osname = Win32::GetOSName(); - infoprint "* Windows OS($osname) is not fully supported.\n"; - #exit 1; +if ( $osname eq 'MSWin32' ) { + eval { require Win32; } or last; + $osname = Win32::GetOSName(); + infoprint "* Windows OS($osname) is not fully supported.\n"; + + #exit 1; } + sub mysql_setup { $doremote = 0; $remotestring = ''; @@ -551,8 +579,7 @@ sub mysql_setup { exit 1; } elsif ( !-e $mysqladmincmd ) { - badprint - "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?"; + badprint "Couldn't find mysqladmin in your \$PATH. Is MySQL installed?"; exit 1; } if ( $opt{mysqlcmd} ) { @@ -572,11 +599,12 @@ sub mysql_setup { exit 1; } $mysqlcmd =~ s/\n$//g; - my $mysqlclidefaults=`$mysqlcmd --print-defaults`; + my $mysqlclidefaults = `$mysqlcmd --print-defaults`; debugprint "MySQL Client: $mysqlclidefaults"; - if ( $mysqlclidefaults=~/auto-vertical-output/ ) { - badprint "Avoid auto-vertical-output in configuration file(s) for MySQL like"; - exit 1; + if ( $mysqlclidefaults =~ /auto-vertical-output/ ) { + badprint + "Avoid auto-vertical-output in configuration file(s) for MySQL like"; + exit 1; } debugprint "MySQL Client: $mysqlcmd"; @@ -591,25 +619,27 @@ sub mysql_setup { chomp( $opt{host} ); $opt{port} = ( $opt{port} eq 0 ) ? 3306 : $opt{port}; - # If we're doing a remote connection, but forcemem wasn't specified, we need to exit - if ( $opt{'forcemem'} eq 0 && ($opt{host} ne "127.0.0.1") && ($opt{host} ne "localhost")) { - badprint - "The --forcemem option is required for remote connections"; +# If we're doing a remote connection, but forcemem wasn't specified, we need to exit + if ( $opt{'forcemem'} eq 0 + && ( $opt{host} ne "127.0.0.1" ) + && ( $opt{host} ne "localhost" ) ) + { + badprint "The --forcemem option is required for remote connections"; exit 1; } infoprint "Performing tests on $opt{host}:$opt{port}"; $remotestring = " -h $opt{host} -P $opt{port}"; - if (($opt{host} ne "127.0.0.1") && ($opt{host} ne "localhost")) { - $doremote = 1; + if ( ( $opt{host} ne "127.0.0.1" ) && ( $opt{host} ne "localhost" ) ) { + $doremote = 1; } } + # Did we already get a username without password on the command line? if ( $opt{user} ne 0 and $opt{pass} eq 0 ) { $mysqllogin = "-u $opt{user} " . $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials passed on the command line"; + goodprint "Logged in using credentials passed on the command line"; return 1; } else { @@ -618,13 +648,13 @@ sub mysql_setup { exit 1; } } + # Did we already get a username and password passed on the command line? if ( $opt{user} ne 0 and $opt{pass} ne 0 ) { $mysqllogin = "-u $opt{user} -p\"$opt{pass}\"" . $remotestring; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials passed on the command line"; + goodprint "Logged in using credentials passed on the command line"; return 1; } else { @@ -649,8 +679,7 @@ sub mysql_setup { $mysqllogin = "-u $mysql_login -p$mysql_pass"; my $loginstatus = `mysqladmin $mysqllogin ping 2>&1`; if ( $loginstatus =~ /mysqld is alive/ ) { - goodprint - "Logged in using credentials from mysql-quickbackup."; + goodprint "Logged in using credentials from mysql-quickbackup."; return 1; } else { @@ -661,18 +690,22 @@ sub mysql_setup { } } elsif ( -r "/etc/psa/.psa.shadow" and $doremote == 0 ) { + # It's a Plesk box, use the available credentials $mysqllogin = "-u admin -p`cat /etc/psa/.psa.shadow`"; my $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; unless ( $loginstatus =~ /mysqld is alive/ ) { + # Plesk 10+ - $mysqllogin = "-u admin -p`/usr/local/psa/bin/admin --show-password`"; + $mysqllogin = + "-u admin -p`/usr/local/psa/bin/admin --show-password`"; $loginstatus = `$mysqladmincmd ping $mysqllogin 2>&1`; unless ( $loginstatus =~ /mysqld is alive/ ) { - badprint "Attempted to use login credentials from Plesk and Plesk 10+, but they failed."; - exit 1; + badprint +"Attempted to use login credentials from Plesk and Plesk 10+, but they failed."; + exit 1; } - } + } } elsif ( -r "/usr/local/directadmin/conf/mysql.conf" and $doremote == 0 ) { @@ -722,7 +755,7 @@ sub mysql_setup { # Login went just fine $mysqllogin = " $remotestring "; - # Did this go well because of a .my.cnf file or is there no password set? + # Did this go well because of a .my.cnf file or is there no password set? my $userpath = `printenv HOME`; if ( length($userpath) > 0 ) { chomp($userpath); @@ -735,27 +768,29 @@ sub mysql_setup { return 1; } else { - if ( $opt{'noask'}==1 ) { - badprint "Attempted to use login credentials, but they were invalid"; + if ( $opt{'noask'} == 1 ) { + badprint + "Attempted to use login credentials, but they were invalid"; exit 1; } - my ($name, $password); + my ( $name, $password ); + # If --user is defined no need to ask for username - if( $opt{user} ne 0 ) - { + if ( $opt{user} ne 0 ) { $name = $opt{user}; } - else{ + else { print STDERR "Please enter your MySQL administrative login: "; $name = ; } + # If --pass is defined no need to ask for password - if( $opt{pass} ne 0 ) - { + if ( $opt{pass} ne 0 ) { $password = $opt{pass}; } - else{ - print STDERR "Please enter your MySQL administrative password: "; + else { + print STDERR + "Please enter your MySQL administrative password: "; system("stty -echo >$devnull 2>&1"); $password = ; system("stty echo >$devnull 2>&1"); @@ -784,7 +819,8 @@ sub mysql_setup { return 1; } else { - badprint "Attempted to use login credentials, but they were invalid."; + badprint + "Attempted to use login credentials, but they were invalid."; exit 1; } exit 1; @@ -797,15 +833,16 @@ sub select_array { my $req = shift; debugprint "PERFORM: $req "; my @result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; - if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - #exit $?; - } - debugprint "select_array: return code : $?"; + if ( $? != 0 ) { + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + + #exit $?; + } + debugprint "select_array: return code : $?"; chomp(@result); return @result; } @@ -815,15 +852,16 @@ sub select_one { my $req = shift; debugprint "PERFORM: $req "; my $result = `$mysqlcmd $mysqllogin -Bse "$req" 2>>/dev/null`; - if ($? != 0) { - badprint "failed to execute: $req"; - badprint "FAIL Execute SQL / return code: $?"; - debugprint "CMD : $mysqlcmd"; - debugprint "OPTIONS: $mysqllogin"; - debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; - #exit $?; - } - debugprint "select_array: return code : $?"; + if ( $? != 0 ) { + badprint "failed to execute: $req"; + badprint "FAIL Execute SQL / return code: $?"; + debugprint "CMD : $mysqlcmd"; + debugprint "OPTIONS: $mysqllogin"; + debugprint `$mysqlcmd $mysqllogin -Bse "$req" 2>&1`; + + #exit $?; + } + debugprint "select_array: return code : $?"; chomp($result); return $result; } @@ -879,11 +917,14 @@ sub get_all_vars { if ( ( $myvar{'ignore_builtin_innodb'} || "" ) eq "ON" ) { $myvar{'have_innodb'} = "NO"; } - + $myvar{'have_threadpool'} = "NO"; - if ( defined ( $myvar{'thread_pool_size'} ) and $myvar{'thread_pool_size'} > 0 ) { + if ( defined( $myvar{'thread_pool_size'} ) + and $myvar{'thread_pool_size'} > 0 ) + { $myvar{'have_threadpool'} = "YES"; } + # have_* for engines is deprecated and will be removed in MySQL 5.6; # check SHOW ENGINES and set corresponding old style variables. # Also works around MySQL bug #59393 wrt. skip-innodb @@ -927,9 +968,11 @@ sub get_all_vars { sub remove_cr { map { s/\n$//g; } @_; } + sub remove_empty { grep { $_ ne '' } @_; } + sub get_file_contents { my $file = shift; open( FH, "< $file" ) or die "Can't open $file for read: $!"; @@ -946,219 +989,260 @@ sub get_basic_passwords { sub cve_recommendations { prettyprint "\n-------- CVE Security Recommendations ---------------------------------------"; - unless ( defined($opt{cvefile}) && -f "$opt{cvefile}" ) { + unless ( defined( $opt{cvefile} ) && -f "$opt{cvefile}" ) { infoprint "Skipped due to --cvefile option undefined"; return; } - #prettyprint "Look for related CVE for $myvar{'version'} or lower in $opt{cvefile}"; - my $cvefound=0; +#prettyprint "Look for related CVE for $myvar{'version'} or lower in $opt{cvefile}"; + my $cvefound = 0; open( FH, "<$opt{cvefile}" ) or die "Can't open $opt{cvefile} for read: $!"; - while (my $cveline = ) - { - my @cve=split (';', $cveline); - if (mysql_micro_version_le ($cve[1], $cve[2], $cve[3])) { - badprint "$cve[4] : $cve[5]"; - $cvefound++; - } - + while ( my $cveline = ) { + my @cve = split( ';', $cveline ); + if ( mysql_micro_version_le( $cve[1], $cve[2], $cve[3] ) ) { + badprint "$cve[4] : $cve[5]"; + $cvefound++; + } + } close FH or die "Cannot close $opt{cvefile}: $!"; - if ($cvefound==0) { - goodprint "NO SECURITY CVE FOUND FOR YOUR VERSION"; - return; - } + if ( $cvefound == 0 ) { + goodprint "NO SECURITY CVE FOUND FOR YOUR VERSION"; + return; + } badprint $cvefound . " CVE(s) found for your MySQL release."; - push( @generalrec, $cvefound . " CVE(s) found for your MySQL release. Consider upgrading your version !" ); + push( @generalrec, + $cvefound + . " CVE(s) found for your MySQL release. Consider upgrading your version !" + ); } - sub get_opened_ports { - my @opened_ports=`netstat -ltn`; - map { - s/.*:(\d+)\s.*$/$1/; - s/\D//g; - } @opened_ports; - @opened_ports = sort {$a <=> $b} grep { !/^$/ } @opened_ports; - debugprint Dumper \@opened_ports; - return @opened_ports; + my @opened_ports = `netstat -ltn`; + map { + s/.*:(\d+)\s.*$/$1/; + s/\D//g; + } @opened_ports; + @opened_ports = sort { $a <=> $b } grep { !/^$/ } @opened_ports; + debugprint Dumper \@opened_ports; + return @opened_ports; } sub is_open_port { - my $port=shift; - if ( grep { /^$port$/ } get_opened_ports ) { - return 1; - } - return 0; + my $port = shift; + if ( grep { /^$port$/ } get_opened_ports ) { + return 1; + } + return 0; } sub get_process_memory { - my $pid=shift; - return 0 unless -f "/proc/$pid/status"; - my @pdata= grep { /RSS:/ } get_file_contents "/proc/$pid/status"; - map { - s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge - } @pdata; - return $pdata[0]; + my $pid = shift; + return 0 unless -f "/proc/$pid/status"; + my @pdata = grep { /RSS:/ } get_file_contents "/proc/$pid/status"; + map { s/.*RSS:\s*(\d+)\s*kB\s*$/$1*1024/ge } @pdata; + return $pdata[0]; } sub get_other_process_memory { - my @procs=`ps -eo pid,cmd`; - map { s/.*mysqld.*//; s/.*\[.*\].*//; s/^\s+$//g; s/.*PID.*CMD.*//; s/.*systemd.*//;} @procs; - map {s/\s*?(\d+)\s*.*/$1/g;} @procs; - remove_cr @procs; - @procs=remove_empty @procs; - my $totalMemOther=0; - map { - $totalMemOther+=get_process_memory($_); - } @procs; - return $totalMemOther; + my @procs = `ps -eo pid,cmd`; + map { + s/.*mysqld.*//; + s/.*\[.*\].*//; + s/^\s+$//g; + s/.*PID.*CMD.*//; + s/.*systemd.*//; + } @procs; + map { s/\s*?(\d+)\s*.*/$1/g; } @procs; + remove_cr @procs; + @procs = remove_empty @procs; + my $totalMemOther = 0; + map { $totalMemOther += get_process_memory($_); } @procs; + return $totalMemOther; } sub get_os_release { return "Unknown OS release" unless -f "/etc/system-release"; - my @info_release=get_file_contents "/etc/system-release"; + my @info_release = get_file_contents "/etc/system-release"; remove_cr @info_release; return $info_release[0]; } sub get_fs_info() { - my @sinfo=`df -P | grep '%'`; + my @sinfo = `df -P | grep '%'`; shift @sinfo; - my @iinfo=`df -Pi| grep '%'`; + my @iinfo = `df -Pi| grep '%'`; shift @iinfo; - map { - s/.*\s(\d+)%\s+(.*)/$1\t$2/g - } @sinfo; + map { s/.*\s(\d+)%\s+(.*)/$1\t$2/g } @sinfo; foreach my $info (@sinfo) { - if ($info =~ /(\d+)\t(.*)/) { - if ($1 > 85) { - badprint "mount point $2 is using $1 % total space"; - push(@generalrec, "Add some space to $2 mountpoint.") - } else { - infoprint "mount point $2 is using $1 % of total space"; - } - } + if ( $info =~ /(\d+)\t(.*)/ ) { + if ( $1 > 85 ) { + badprint "mount point $2 is using $1 % total space"; + push( @generalrec, "Add some space to $2 mountpoint." ); + } + else { + infoprint "mount point $2 is using $1 % of total space"; + } + } } - - map { - s/.*\s(\d+)%\s+(.*)/$1\t$2/g - } @iinfo; + + map { s/.*\s(\d+)%\s+(.*)/$1\t$2/g } @iinfo; foreach my $info (@iinfo) { - if ($info =~ /(\d+)\t(.*)/) { - if ($1 > 85) { - badprint "mount point $2 is using $1 % of max allowed inodes"; - push(@generalrec, "Cleanup files from $2 mountpoint or reformat you filesystem.") - } else { - infoprint "mount point $2 is using $1 % of max allowed inodes"; - } - } + if ( $info =~ /(\d+)\t(.*)/ ) { + if ( $1 > 85 ) { + badprint "mount point $2 is using $1 % of max allowed inodes"; + push( @generalrec, +"Cleanup files from $2 mountpoint or reformat you filesystem." + ); + } + else { + infoprint "mount point $2 is using $1 % of max allowed inodes"; + } + } } } -sub is_virtual_machine() { - my $isVm=`grep -Ec '^flags.*\ hypervisor\ ' /proc/cpuinfo`; - return ($isVm==0?0:1); -} +sub is_virtual_machine() { + my $isVm = `grep -Ec '^flags.*\ hypervisor\ ' /proc/cpuinfo`; + return ( $isVm == 0 ? 0 : 1 ); +} sub infocmd { - my $cmd="@_"; - debugprint "CMD: $cmd"; - my @result=`$cmd`; - remove_cr @result; - for my $l (@result) { - infoprint "$l"; - } + my $cmd = "@_"; + debugprint "CMD: $cmd"; + my @result = `$cmd`; + remove_cr @result; + for my $l (@result) { + infoprint "$l"; + } } + sub infocmd_tab { - my $cmd="@_"; - debugprint "CMD: $cmd"; - my @result=`$cmd`; - remove_cr @result; - for my $l (@result) { - infoprint "\t$l"; - } + my $cmd = "@_"; + debugprint "CMD: $cmd"; + my @result = `$cmd`; + remove_cr @result; + for my $l (@result) { + infoprint "\t$l"; + } } + sub infocmd_one { - my $cmd="@_"; - my @result=`$cmd`; - remove_cr @result; - return join ', ' ,@result; + my $cmd = "@_"; + my @result = `$cmd`; + remove_cr @result; + return join ', ', @result; } -sub get_system_info() -{ -infoprint get_os_release; -if (is_virtual_machine) { - infoprint "Machine type : Virtual machine"; - } else { - infoprint "Machine type : Physical machine"; -} +sub get_system_info() { + infoprint get_os_release; + if (is_virtual_machine) { + infoprint "Machine type : Virtual machine"; + } + else { + infoprint "Machine type : Physical machine"; + } -`ping -c 1 google.com &>/dev/null`; -my $isConnected=$?; -if ($? == 0) { - infoprint "Internet : Connected"; -} else { - badprint "Internet : Disconnected"; - } -infoprint "Operating System Type : ". infocmd_one "uname -o"; -infoprint "Kernel Release : ". infocmd_one "uname -r"; -infoprint "Hostname : ". infocmd_one "hostname"; -infoprint "Network Cards : "; -infocmd_tab "ifconfig| grep -A1 mtu"; -infoprint "Internal IP : ". infocmd_one "hostname -I"; -infoprint "External IP : ". infocmd_one "curl -s ipecho.net/plain" if $isConnected==0; -badprint "External IP : Can't check because of Internet connectivity" if $isConnected!=0; -infoprint "Name Servers : ". infocmd_one "grep 'nameserver' /etc/resolv.conf \| awk '{print \$2}'"; -infoprint "Logged In users : "; -infocmd_tab "who"; -infoprint "Ram Usages : "; -infocmd_tab "free -h | grep -v +"; -infoprint "Load Average : "; -infocmd_tab "top -n 1 -b | grep 'load average:'"; + `ping -c 1 google.com &>/dev/null`; + my $isConnected = $?; + if ( $? == 0 ) { + infoprint "Internet : Connected"; + } + else { + badprint "Internet : Disconnected"; + } + infoprint "Operating System Type : " . infocmd_one "uname -o"; + infoprint "Kernel Release : " . infocmd_one "uname -r"; + infoprint "Hostname : " . infocmd_one "hostname"; + infoprint "Network Cards : "; + infocmd_tab "ifconfig| grep -A1 mtu"; + infoprint "Internal IP : " . infocmd_one "hostname -I"; + infoprint "External IP : " + . infocmd_one "curl -s ipecho.net/plain" + if $isConnected == 0; + badprint + "External IP : Can't check because of Internet connectivity" + if $isConnected != 0; + infoprint "Name Servers : " + . infocmd_one "grep 'nameserver' /etc/resolv.conf \| awk '{print \$2}'"; + infoprint "Logged In users : "; + infocmd_tab "who"; + infoprint "Ram Usages : "; + infocmd_tab "free -h | grep -v +"; + infoprint "Load Average : "; + infocmd_tab "top -n 1 -b | grep 'load average:'"; #infoprint "System Uptime Days/(HH:MM) : `uptime | awk '{print $3,$4}' | cut -f1 -d,`"; } sub system_recommendations { - prettyprint "\n-------- System Linux Recommendations ---------------------------------------"; + prettyprint +"\n-------- System Linux Recommendations ---------------------------------------"; my $os = `uname`; - unless ($os =~ /Linux/i) { + unless ( $os =~ /Linux/i ) { infoprint "Skipped due to non Linux server"; return; - } + } prettyprint "Look for related Linux system recommandations"; + #prettyprint '-'x78; get_system_info(); - my $omem=get_other_process_memory; - infoprint "User process except mysqld used ". hr_bytes_rnd($omem) . " RAM."; - if ( (0.15*$physical_memory) < $omem) { - badprint "Other user process except mysqld used more than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; - push( @generalrec, "Consider stopping or dedicate server for additionnal process other than mysqld." ); - push( @adjvars, "DON'T APPLY SETTINGS BECAUSE THERE ARE TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCUR!" ); - } else { - infoprint "Other user process except mysqld used less than 15% of total physical memory ". percentage($omem, $physical_memory). "% (".hr_bytes_rnd($omem). " / ".hr_bytes_rnd($physical_memory).")"; + my $omem = get_other_process_memory; + infoprint "User process except mysqld used " + . hr_bytes_rnd($omem) . " RAM."; + if ( ( 0.15 * $physical_memory ) < $omem ) { + badprint +"Other user process except mysqld used more than 15% of total physical memory " + . percentage( $omem, $physical_memory ) . "% (" + . hr_bytes_rnd($omem) . " / " + . hr_bytes_rnd($physical_memory) . ")"; + push( @generalrec, +"Consider stopping or dedicate server for additionnal process other than mysqld." + ); + push( @adjvars, +"DON'T APPLY SETTINGS BECAUSE THERE ARE TOO MANY PROCESSES RUNNING ON THIS SERVER. OOM KILL CAN OCCUR!" + ); + } + else { + infoprint +"Other user process except mysqld used less than 15% of total physical memory " + . percentage( $omem, $physical_memory ) . "% (" + . hr_bytes_rnd($omem) . " / " + . hr_bytes_rnd($physical_memory) . ")"; } - if ($opt{'maxportallowed'} > 0) { - my @opened_ports=get_opened_ports; - infoprint "There is ". scalar @opened_ports. " listening port(s) on this server."; - if (scalar(@opened_ports) > $opt{'maxportallowed'}) { - badprint "There is too many listening ports: ". scalar(@opened_ports). " opened > ".$opt{'maxportallowed'}. "allowed."; - push( @generalrec, "Consider dedicating a server for your database installation with less services running on !" ); - } else { - goodprint "There is less than ".$opt{'maxportallowed'}." opened ports on this server."; - } + if ( $opt{'maxportallowed'} > 0 ) { + my @opened_ports = get_opened_ports; + infoprint "There is " + . scalar @opened_ports + . " listening port(s) on this server."; + if ( scalar(@opened_ports) > $opt{'maxportallowed'} ) { + badprint "There is too many listening ports: " + . scalar(@opened_ports) + . " opened > " + . $opt{'maxportallowed'} + . "allowed."; + push( @generalrec, +"Consider dedicating a server for your database installation with less services running on !" + ); + } + else { + goodprint "There is less than " + . $opt{'maxportallowed'} + . " opened ports on this server."; + } } foreach my $banport (@banned_ports) { - if ( is_open_port($banport) ) { - badprint "Banned port: $banport is opened.."; - push( @generalrec, "Port $banport is opened. Consider stopping program handling this port." ); - } else { - goodprint "$banport is not opened."; - } + if ( is_open_port($banport) ) { + badprint "Banned port: $banport is opened.."; + push( @generalrec, +"Port $banport is opened. Consider stopping program handling this port." + ); + } + else { + goodprint "$banport is not opened."; + } } get_fs_info; @@ -1172,16 +1256,17 @@ sub security_recommendations { return; } - my $PASS_COLUMN_NAME='password'; - if ($myvar{'version'} =~ /5.7/) { - $PASS_COLUMN_NAME='authentication_string'; + my $PASS_COLUMN_NAME = 'password'; + if ( $myvar{'version'} =~ /5.7/ ) { + $PASS_COLUMN_NAME = 'authentication_string'; } debugprint "Password column = $PASS_COLUMN_NAME"; - + # Looking for Anonymous users my @mysqlstatlist = select_array "SELECT CONCAT(user, '\@', host) FROM mysql.user WHERE TRIM(USER) = '' OR USER IS NULL"; debugprint Dumper \@mysqlstatlist; + #exit 0; if (@mysqlstatlist) { foreach my $line ( sort @mysqlstatlist ) { @@ -1213,12 +1298,15 @@ sub security_recommendations { goodprint "All database users have passwords assigned"; } - if (mysql_version_ge(5,7)) { - my $valPlugin=select_one("select count(*) from information_schema.plugins where PLUGIN_NAME='validate_password' AND PLUGIN_STATUS='ACTIVE'"); - if ($valPlugin>=1) { - infoprint "Bug #80860 MySQL 5.7: Avoid testing password when validate_password is activated"; - return; - } + if ( mysql_version_ge( 5, 7 ) ) { + my $valPlugin = select_one( +"select count(*) from information_schema.plugins where PLUGIN_NAME='validate_password' AND PLUGIN_STATUS='ACTIVE'" + ); + if ( $valPlugin >= 1 ) { + infoprint +"Bug #80860 MySQL 5.7: Avoid testing password when validate_password is activated"; + return; + } } # Looking for User with user/ uppercase /capitalise user as password @@ -1322,10 +1410,10 @@ sub get_replication_status { and ( $io_running !~ /yes/i or $sql_running !~ /yes/i ) ) { badprint -"This replication slave is not running but seems to be configurated."; + "This replication slave is not running but seems to be configurated."; } if ( defined($io_running) - && $io_running =~ /yes/i + && $io_running =~ /yes/i && $sql_running =~ /yes/i ) { if ( $myvar{'read_only'} eq 'OFF' ) { @@ -1359,10 +1447,13 @@ sub validate_mysql_version { . $myvar{'version'} . " is EOL software! Upgrade soon!"; } - elsif ( ( mysql_version_ge(6) and mysql_version_le(9) ) or mysql_version_ge(12) ) { + elsif ( ( mysql_version_ge(6) and mysql_version_le(9) ) + or mysql_version_ge(12) ) + { badprint "Currently running unsupported MySQL version " . $myvar{'version'} . ""; - } else { + } + else { goodprint "Currently running supported MySQL version " . $myvar{'version'} . ""; } @@ -1374,8 +1465,7 @@ sub mysql_version_ge { $min ||= 0; $mic ||= 0; return $mysqlvermajor > $maj - || $mysqlvermajor == $maj - && ( $mysqlverminor > $min + || $mysqlvermajor == $maj && ( $mysqlverminor > $min || $mysqlverminor == $min && $mysqlvermicro >= $mic ); } @@ -1385,8 +1475,7 @@ sub mysql_version_le { $min ||= 0; $mic ||= 0; return $mysqlvermajor < $maj - || $mysqlvermajor == $maj - && ( $mysqlverminor < $min + || $mysqlvermajor == $maj && ( $mysqlverminor < $min || $mysqlverminor == $min && $mysqlvermicro <= $mic ); } @@ -1395,7 +1484,7 @@ sub mysql_micro_version_le { my ( $maj, $min, $mic ) = @_; return $mysqlvermajor == $maj && ( $mysqlverminor == $min - && $mysqlvermicro <= $mic ); + && $mysqlvermicro <= $mic ); } # Checks for 32-bit boxes with more than 2GB of RAM @@ -1442,8 +1531,7 @@ sub check_architecture { "Switch to 64-bit OS - MySQL cannot currently use all of your RAM"; } else { - goodprint - "Operating on 32-bit architecture with less than 2GB RAM"; + goodprint "Operating on 32-bit architecture with less than 2GB RAM"; } } $result{'OS'}{'Architecture'} = "$arch bits"; @@ -1519,12 +1607,13 @@ sub check_storage_engines { : redwrap "-NDBCluster "; } - my @dblist = grep {$_ ne 'lost+found' } select_array "SHOW DATABASES"; + my @dblist = grep { $_ ne 'lost+found' } select_array "SHOW DATABASES"; $result{'Databases'}{'List'} = [@dblist]; infoprint "Status: $engines"; if ( mysql_version_ge( 5, 1, 5 ) ) { - # MySQL 5 servers can have table sizes calculated quickly from information schema + +# MySQL 5 servers can have table sizes calculated quickly from information schema my @templist = select_array "SELECT ENGINE,SUM(DATA_LENGTH+INDEX_LENGTH),COUNT(ENGINE),SUM(DATA_LENGTH),SUM(INDEX_LENGTH) FROM information_schema.TABLES WHERE TABLE_SCHEMA NOT IN ('information_schema', 'performance_schema', 'mysql') AND ENGINE IS NOT NULL GROUP BY ENGINE ORDER BY ENGINE ASC;"; @@ -1554,7 +1643,7 @@ sub check_storage_engines { # MySQL < 5 servers take a lot of work to get table sizes my @tblist; - # Now we build a database list, and loop through it to get storage engine stats for tables +# Now we build a database list, and loop through it to get storage engine stats for tables foreach my $db (@dblist) { chomp($db); if ( $db eq "information_schema" @@ -1578,11 +1667,11 @@ sub check_storage_engines { # Parse through the table list to generate storage engine counts/statistics $fragtables = 0; foreach my $tbl (@tblist) { - debugprint "Data dump ". Dumper (@$tbl); + debugprint "Data dump " . Dumper(@$tbl); my ( $engine, $size, $datafree ) = @$tbl; next if $engine eq 'NULL'; - $size=0 if $size eq 'NULL'; - $datafree=0 if $datafree eq 'NULL'; + $size = 0 if $size eq 'NULL'; + $datafree = 0 if $datafree eq 'NULL'; if ( defined $enginestats{$engine} ) { $enginestats{$engine} += $size; $enginecount{$engine} += 1; @@ -1801,7 +1890,7 @@ sub calculations { $myvar{'key_cache_block_size'} ) / $myvar{'key_buffer_size'} ) - ) * 100 + ) * 100 ); } else { @@ -1894,14 +1983,14 @@ sub calculations { ( $mystat{'Qcache_hits'} / ( $mystat{'Com_select'} + $mystat{'Qcache_hits'} ) - ) * 100 + ) * 100 ); if ( $myvar{'query_cache_size'} ) { $mycalc{'pct_query_cache_used'} = sprintf( "%.1f", 100 - ( $mystat{'Qcache_free_memory'} / $myvar{'query_cache_size'} - ) * 100 + ) * 100 ); } if ( $mystat{'Qcache_lowmem_prunes'} == 0 ) { @@ -2030,26 +2119,17 @@ sub calculations { . $mystat{'Innodb_buffer_pool_reads'} . ""; debugprint "Innodb_buffer_pool_read_requests: " . $mystat{'Innodb_buffer_pool_read_requests'} . ""; - - # InnoDB log write cache effiency - ( - $mystat{'Innodb_log_write_requests'}, - $mystat{'Innodb_log_writes'} - ) - = ( 1, 1 ) + ( $mystat{'Innodb_log_write_requests'}, $mystat{'Innodb_log_writes'} ) = + ( 1, 1 ) unless defined $mystat{'Innodb_log_writes'}; $mycalc{'pct_write_efficiency'} = percentage( - ( - $mystat{'Innodb_log_write_requests'} - - $mystat{'Innodb_log_writes'} - ), + ( $mystat{'Innodb_log_write_requests'} - $mystat{'Innodb_log_writes'} ), $mystat{'Innodb_log_write_requests'} ) if defined $mystat{'Innodb_log_write_requests'}; debugprint "pct_write_efficiency: " . $mycalc{'pct_write_efficiency'} . ""; - debugprint "Innodb_log_writes: " - . $mystat{'Innodb_log_writes'} . ""; + debugprint "Innodb_log_writes: " . $mystat{'Innodb_log_writes'} . ""; debugprint "Innodb_log_write_requests: " . $mystat{'Innodb_log_write_requests'} . ""; $mycalc{'pct_innodb_buffer_used'} = percentage( @@ -2125,8 +2205,10 @@ sub mysql_stats { . ( $myvar{'query_cache_type'} eq 0 | $myvar{'query_cache_type'} eq 'OFF' ? "DISABLED" - : ( $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS" - : "ON DEMAND" ) + : ( + $myvar{'query_cache_type'} eq 1 ? "ALL REQUESTS" + : "ON DEMAND" + ) ) . ""; infoprint " +-- Query Cache Size: " . hr_bytes( $myvar{'query_cache_size'} ) . ""; @@ -2155,7 +2237,7 @@ sub mysql_stats { && $mycalc{'max_used_memory'} > 2 * 1024 * 1024 * 1024 ) { badprint -"Allocating > 2GB RAM on 32-bit systems can cause system instability"; + "Allocating > 2GB RAM on 32-bit systems can cause system instability"; badprint "Maximum reached memory usage: " . hr_bytes( $mycalc{'max_used_memory'} ) . " ($mycalc{'pct_max_used_memory'}% of installed RAM)"; @@ -2242,14 +2324,16 @@ sub mysql_stats { push( @generalrec, "Upgrade MySQL to version 4+ to utilize query caching" ); } - elsif (mysql_version_ge(5,5)) - { - if ( $myvar{'query_cache_type'} ne "OFF" ) { - badprint "Query cache should be disabled by default due to mutex contention."; - push( @adjvars, "query_cache_type (=0)" ); - } else { - goodprint "Query cache is disabled by default due to mutex contention."; - } + elsif ( mysql_version_ge( 5, 5 ) ) { + if ( $myvar{'query_cache_type'} ne "OFF" ) { + badprint +"Query cache should be disabled by default due to mutex contention."; + push( @adjvars, "query_cache_type (=0)" ); + } + else { + goodprint + "Query cache is disabled by default due to mutex contention."; + } } elsif ( $myvar{'query_cache_size'} < 1 ) { badprint "Query cache is disabled"; @@ -2380,7 +2464,8 @@ sub mysql_stats { "When making adjustments, make tmp_table_size/max_heap_table_size equal" ); push( @generalrec, - "Reduce your SELECT DISTINCT queries which have no LIMIT clause" ); + "Reduce your SELECT DISTINCT queries which have no LIMIT clause" + ); } elsif ($mycalc{'pct_temp_disk'} > 25 && $mycalc{'max_tmp_table_size'} >= 256 * 1024 * 1024 ) @@ -2568,6 +2653,7 @@ sub mysql_stats { sub mysql_myisam { prettyprint "\n-------- MyISAM Metrics ------------------------------------------------------"; + # Key buffer usage if ( defined( $mycalc{'pct_key_buffer_used'} ) ) { if ( $mycalc{'pct_key_buffer_used'} < 90 ) { @@ -2701,26 +2787,43 @@ sub mariadb_threadpool { infoprint "ThreadPool stat is disabled."; return; } - infoprint "ThreadPool stat is enabled."; - infoprint "Thread Pool Size: ".$myvar{'thread_pool_size'}. " thread(s)."; + infoprint "ThreadPool stat is enabled."; + infoprint "Thread Pool Size: " . $myvar{'thread_pool_size'} . " thread(s)."; - if ($myvar{'have_innodb'} eq 'YES') { - if ($myvar{'thread_pool_size'}< 16 or $myvar{'thread_pool_size'}>36) { - badprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; - push( @generalrec, "Thread pool size for InnoDB usage (".$myvar{'thread_pool_size'}.")" ); - push( @adjvars, "thread_pool_size between 16 and 36 for InnoDB usage" ); - } else { - goodprint "thread_pool_size between 16 and 36 when using InnoDB storage engine."; + if ( $myvar{'have_innodb'} eq 'YES' ) { + if ( $myvar{'thread_pool_size'} < 16 + or $myvar{'thread_pool_size'} > 36 ) + { + badprint +"thread_pool_size between 16 and 36 when using InnoDB storage engine."; + push( @generalrec, + "Thread pool size for InnoDB usage (" + . $myvar{'thread_pool_size'} + . ")" ); + push( @adjvars, + "thread_pool_size between 16 and 36 for InnoDB usage" ); + } + else { + goodprint +"thread_pool_size between 16 and 36 when using InnoDB storage engine."; } return; - } - if ($myvar{'have_isam'} eq 'YES') { - if ($myvar{'thread_pool_size'}<4 or $myvar{'thread_pool_size'}>8) { - badprint "thread_pool_size between 4 and 8 when using MyIsam storage engine."; - push( @generalrec, "Thread pool size for MyIsam usage (".$myvar{'thread_pool_size'}.")" ); - push( @adjvars, "thread_pool_size between 4 and 8 for MyIsam usage" ); - } else { - goodprint "thread_pool_size between 4 and 8 when using MyISAM storage engine."; + } + if ( $myvar{'have_isam'} eq 'YES' ) { + if ( $myvar{'thread_pool_size'} < 4 or $myvar{'thread_pool_size'} > 8 ) + { + badprint +"thread_pool_size between 4 and 8 when using MyIsam storage engine."; + push( @generalrec, + "Thread pool size for MyIsam usage (" + . $myvar{'thread_pool_size'} + . ")" ); + push( @adjvars, + "thread_pool_size between 4 and 8 for MyIsam usage" ); + } + else { + goodprint +"thread_pool_size between 4 and 8 when using MyISAM storage engine."; } } } @@ -2731,14 +2834,16 @@ sub mysqsl_pfs { "\n-------- Performance schema --------------------------------------------------"; # Performance Schema - unless ( defined($myvar{'performance_schema'}) and $myvar{'performance_schema'} eq 'ON' ) { - infoprint "Performance schema is disabled."; - } else { - infoprint "Performance schema is enabled."; - } + unless ( defined( $myvar{'performance_schema'} ) + and $myvar{'performance_schema'} eq 'ON' ) + { + infoprint "Performance schema is disabled."; + } + else { + infoprint "Performance schema is enabled."; + } } - # Recommendations for Ariadb sub mariadb_ariadb { prettyprint @@ -2810,7 +2915,6 @@ sub mariadb_ariadb { } } - # Recommendations for TokuDB sub mariadb_tokudb { prettyprint @@ -2843,6 +2947,7 @@ sub mariadb_galera { return; } infoprint "Galera is enabled."; + # All is to done here } @@ -3087,11 +3192,19 @@ sub mysql_databases { . percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%)"; infoprint " +-- SIZE : " . hr_bytes( $totaldbinfo[3] ) . ""; infoprint " +-- COLLA : " - . ( $totaldbinfo[5] eq 'NULL' ? 0 : $totaldbinfo[5] ) . " (". (join ", ", select_array ("SELECT DISTINCT(TABLE_COLLATION) FROM information_schema.TABLES;")) .")"; + . ( $totaldbinfo[5] eq 'NULL' ? 0 : $totaldbinfo[5] ) . " (" + . ( + join ", ", + select_array( + "SELECT DISTINCT(TABLE_COLLATION) FROM information_schema.TABLES;") + ) . ")"; infoprint " +-- ENGIN : " - . ( $totaldbinfo[6] eq 'NULL' ? 0 : $totaldbinfo[6] ) . " (". (join ", ", select_array ("SELECT DISTINCT(ENGINE) FROM information_schema.TABLES;")) .")"; - - + . ( $totaldbinfo[6] eq 'NULL' ? 0 : $totaldbinfo[6] ) . " (" + . ( + join ", ", + select_array("SELECT DISTINCT(ENGINE) FROM information_schema.TABLES;") + ) . ")"; + $result{'Databases'}{'All databases'}{'Rows'} = ( $totaldbinfo[0] eq 'NULL' ? 0 : $totaldbinfo[0] ); $result{'Databases'}{'All databases'}{'Data Size'} = $totaldbinfo[1]; @@ -3100,14 +3213,18 @@ sub mysql_databases { $result{'Databases'}{'All databases'}{'Index Size'} = $totaldbinfo[2]; $result{'Databases'}{'All databases'}{'Index Pct'} = percentage( $totaldbinfo[2], $totaldbinfo[3] ) . "%"; - $result{'Databases'}{'All databases'}{'Total Size'} = $totaldbinfo[3]; - print "\n" unless ($opt{'silent'} or $opt{'json'}); + $result{'Databases'}{'All databases'}{'Total Size'} = $totaldbinfo[3]; + print "\n" unless ( $opt{'silent'} or $opt{'json'} ); + foreach (@dblist) { chomp($_); - if ( $_ eq "information_schema" + if ( + $_ eq "information_schema" or $_ eq "performance_schema" - # or $_ eq "mysql" - or $_ eq "" ) + + # or $_ eq "mysql" + or $_ eq "" + ) { next; } @@ -3122,7 +3239,13 @@ sub mysql_databases { . ( !defined( $dbinfo[6] ) or $dbinfo[6] eq 'NULL' ? 0 : $dbinfo[6] ) . ""; infoprint " +-- COLL : " - . ( $dbinfo[7] eq 'NULL' ? 0 : $dbinfo[7] ) . " (". (join ", ", select_array ("SELECT DISTINCT(TABLE_COLLATION) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_';")) .")"; + . ( $dbinfo[7] eq 'NULL' ? 0 : $dbinfo[7] ) . " (" + . ( + join ", ", + select_array( +"SELECT DISTINCT(TABLE_COLLATION) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_';" + ) + ) . ")"; infoprint " +-- ROWS : " . ( !defined( $dbinfo[1] ) or $dbinfo[1] eq 'NULL' ? 0 : $dbinfo[1] ) . ""; @@ -3134,53 +3257,93 @@ sub mysql_databases { . percentage( $dbinfo[3], $dbinfo[4] ) . "%)"; infoprint " +-- TOTAL: " . hr_bytes( $dbinfo[4] ) . ""; infoprint " +-- ENGIN : " - . ( $dbinfo[8] eq 'NULL' ? 0 : $dbinfo[8] ) . " (". (join ", ", select_array ("SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_'")) .")"; + . ( $dbinfo[8] eq 'NULL' ? 0 : $dbinfo[8] ) . " (" + . ( + join ", ", + select_array( +"SELECT DISTINCT(ENGINE) FROM information_schema.TABLES WHERE TABLE_SCHEMA='$_'" + ) + ) . ")"; badprint "Index size is larger than data size for $dbinfo[0] \n" if $dbinfo[2] < $dbinfo[3]; badprint "There are " . $dbinfo[5] . " storage engines. Be careful. \n" if $dbinfo[5] > 1; - $result{'Databases'}{ $dbinfo[0] }{'Rows'} = $dbinfo[1]; - $result{'Databases'}{ $dbinfo[0] }{'Tables'} = $dbinfo[6]; - $result{'Databases'}{ $dbinfo[0] }{'Collations'} = $dbinfo[7]; - $result{'Databases'}{ $dbinfo[0] }{'Data Size'} = $dbinfo[2]; + $result{'Databases'}{ $dbinfo[0] }{'Rows'} = $dbinfo[1]; + $result{'Databases'}{ $dbinfo[0] }{'Tables'} = $dbinfo[6]; + $result{'Databases'}{ $dbinfo[0] }{'Collations'} = $dbinfo[7]; + $result{'Databases'}{ $dbinfo[0] }{'Data Size'} = $dbinfo[2]; $result{'Databases'}{ $dbinfo[0] }{'Data Pct'} = percentage( $dbinfo[2], $dbinfo[4] ) . "%"; $result{'Databases'}{ $dbinfo[0] }{'Index Size'} = $dbinfo[3]; $result{'Databases'}{ $dbinfo[0] }{'Index Pct'} = percentage( $dbinfo[3], $dbinfo[4] ) . "%"; $result{'Databases'}{ $dbinfo[0] }{'Total Size'} = $dbinfo[4]; - if ($dbinfo[7]>1) { - badprint $dbinfo[7]. " differents collations for database ".$dbinfo[0]; - push(@generalrec, "Check all table collations are identical for all tables in ".$dbinfo[0]. " database."); - } else { - goodprint $dbinfo[7]. " collation for ".$dbinfo[0]. " database."; - } - if ($dbinfo[8]>1) { - badprint $dbinfo[8]. " differents engines for database ".$dbinfo[0]; - push(@generalrec, "Check all table engines are identical for all tables in ".$dbinfo[0]. " database."); - } else { - goodprint $dbinfo[8]. " engine for ".$dbinfo[0]. " database."; - } - - my @distinct_column_charset=select_array("select DISTINCT(CHARACTER_SET_NAME) from information_schema.COLUMNS where CHARACTER_SET_NAME IS NOT NULL AND TABLE_SCHEMA ='$_'"); - infoprint "Charsets for $dbinfo[0] database table column: ". join (', ', @distinct_column_charset); - if (scalar (@distinct_column_charset)>1 ) { - badprint $dbinfo[0]. " table column(s) has several charsets defined for all text like column(s)."; - push(@generalrec, "Limit charset for column to one charset if possible for ".$dbinfo[0]." database."); - } else { - goodprint $dbinfo[0]. " table column(s) has same charset defined for all text like column(s)."; - } - my @distinct_column_collation=select_array("select DISTINCT(COLLATION_NAME) from information_schema.COLUMNS where COLLATION_NAME IS NOT NULL AND TABLE_SCHEMA ='$_'"); - infoprint "Collations for $dbinfo[0] database table column: ". join (', ', @distinct_column_collation); - if (scalar (@distinct_column_collation)>1 ) { - badprint $dbinfo[0]. " table column(s) has several collations defined for all text like column(s)."; - push(@generalrec, "Limit collations for column to one collation if possible for ".$dbinfo[0]." database."); - } else { - goodprint $dbinfo[0]. " table column(s) has same collation defined for all text like column(s)."; + if ( $dbinfo[7] > 1 ) { + badprint $dbinfo[7] + . " differents collations for database " + . $dbinfo[0]; + push( @generalrec, + "Check all table collations are identical for all tables in " + . $dbinfo[0] + . " database." ); + } + else { + goodprint $dbinfo[7] + . " collation for " + . $dbinfo[0] + . " database."; + } + if ( $dbinfo[8] > 1 ) { + badprint $dbinfo[8] + . " differents engines for database " + . $dbinfo[0]; + push( @generalrec, + "Check all table engines are identical for all tables in " + . $dbinfo[0] + . " database." ); + } + else { + goodprint $dbinfo[8] . " engine for " . $dbinfo[0] . " database."; + } + + my @distinct_column_charset = select_array( +"select DISTINCT(CHARACTER_SET_NAME) from information_schema.COLUMNS where CHARACTER_SET_NAME IS NOT NULL AND TABLE_SCHEMA ='$_'" + ); + infoprint "Charsets for $dbinfo[0] database table column: " + . join( ', ', @distinct_column_charset ); + if ( scalar(@distinct_column_charset) > 1 ) { + badprint $dbinfo[0] + . " table column(s) has several charsets defined for all text like column(s)."; + push( @generalrec, + "Limit charset for column to one charset if possible for " + . $dbinfo[0] + . " database." ); + } + else { + goodprint $dbinfo[0] + . " table column(s) has same charset defined for all text like column(s)."; + } + + my @distinct_column_collation = select_array( +"select DISTINCT(COLLATION_NAME) from information_schema.COLUMNS where COLLATION_NAME IS NOT NULL AND TABLE_SCHEMA ='$_'" + ); + infoprint "Collations for $dbinfo[0] database table column: " + . join( ', ', @distinct_column_collation ); + if ( scalar(@distinct_column_collation) > 1 ) { + badprint $dbinfo[0] + . " table column(s) has several collations defined for all text like column(s)."; + push( @generalrec, + "Limit collations for column to one collation if possible for " + . $dbinfo[0] + . " database." ); + } + else { + goodprint $dbinfo[0] + . " table column(s) has same collation defined for all text like column(s)."; + } } - } - + } # Recommendations for Indexes metrics @@ -3191,9 +3354,10 @@ sub mysql_indexes { "\n-------- Indexes Metrics -----------------------------------------------------"; unless ( mysql_version_ge( 5, 5 ) ) { infoprint -"Skip Index metrics from information schema missing in this version"; + "Skip Index metrics from information schema missing in this version"; return; } + # unless ( mysql_version_ge( 5, 6 ) ) { # infoprint #"Skip Index metrics from information schema due to erronous information provided in this version"; @@ -3246,7 +3410,7 @@ ENDSQL infoprint " +-- NB COLS : " . $info[3] . " column(s)"; infoprint " +-- CARDINALITY : " . $info[4] . " distinct values"; infoprint " +-- NB ROWS : " . $info[5] . " rows"; - infoprint " +-- TYPE : " . $info[6] ; + infoprint " +-- TYPE : " . $info[6]; infoprint " +-- SELECTIVITY : " . $info[7] . "%"; $result{'Indexes'}{ $info[1] }{'Colunm'} = $info[0]; @@ -3259,7 +3423,7 @@ ENDSQL if ( $info[7] < 25 ) { badprint "$info[1] has a low selectivity"; } - } + } return unless ( defined( $myvar{'performance_schema'} ) @@ -3304,8 +3468,7 @@ sub make_recommendations { foreach (@adjvars) { prettyprint " " . $_ . ""; } } if ( @generalrec == 0 && @adjvars == 0 ) { - prettyprint - "No additional performance recommendations are available."; + prettyprint "No additional performance recommendations are available."; } } @@ -3321,18 +3484,19 @@ sub headerprint { } sub string2file { - my $filename=shift; - my $content=shift; - open my $fh, q(>), $filename - or die "Unable to open $filename in write mode. Please check permissions for this file or directory"; - print $fh $content if defined($content); - close $fh; - debugprint $content if ($opt{'debug'}); + my $filename = shift; + my $content = shift; + open my $fh, q(>), $filename + or die +"Unable to open $filename in write mode. Please check permissions for this file or directory"; + print $fh $content if defined($content); + close $fh; + debugprint $content if ( $opt{'debug'} ); } sub file2array { my $filename = shift; - debugprint "* reading $filename" if ($opt{'debug'}); + debugprint "* reading $filename" if ( $opt{'debug'} ); my $fh; open( $fh, q(<), "$filename" ) or die "Couldn't open $filename for reading: $!\n"; @@ -3342,15 +3506,16 @@ sub file2array { } sub file2string { - return join ( '', file2array(@_) ); + return join( '', file2array(@_) ); } my $templateModel; -if ($opt{'template'} ne 0 ) { - $templateModel=file2string ($opt{'template'}); -}else { - # DEFAULT REPORT TEMPLATE - $templateModel=<<'END_TEMPLATE'; +if ( $opt{'template'} ne 0 ) { + $templateModel = file2string( $opt{'template'} ); +} +else { + # DEFAULT REPORT TEMPLATE + $templateModel = <<'END_TEMPLATE'; @@ -3368,41 +3533,47 @@ if ($opt{'template'} ne 0 ) { END_TEMPLATE } + sub dump_result { - if ($opt{'debug'}) { - debugprint Dumper( \%result ); + if ( $opt{'debug'} ) { + debugprint Dumper( \%result ); } debugprint "HTML REPORT: $opt{'reportfile'}"; - if ($opt{'reportfile'} ne 0 ) { - eval "{ use Text::Template }"; - if ($@) { - badprint "Text::Template Module is needed."; - exit 1; - } + if ( $opt{'reportfile'} ne 0 ) { + eval "{ use Text::Template }"; + if ($@) { + badprint "Text::Template Module is needed."; + exit 1; + } - my $vars= {'data' => Dumper( \%result ) }; + my $vars = { 'data' => Dumper( \%result ) }; - my $template; - { - no warnings 'once'; - $template = Text::Template->new(TYPE => 'STRING', PREPEND => q{;}, SOURCE => $templateModel) - or die "Couldn't construct template: $Text::Template::ERROR"; - } - open my $fh, q(>), $opt{'reportfile'} - or die "Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory"; - $template->fill_in(HASH =>$vars, OUTPUT=>$fh ); - close $fh; + my $template; + { + no warnings 'once'; + $template = Text::Template->new( + TYPE => 'STRING', + PREPEND => q{;}, + SOURCE => $templateModel + ) or die "Couldn't construct template: $Text::Template::ERROR"; + } + open my $fh, q(>), $opt{'reportfile'} + or die +"Unable to open $opt{'reportfile'} in write mode. please check permissions for this file or directory"; + $template->fill_in( HASH => $vars, OUTPUT => $fh ); + close $fh; } - if ($opt{'json'} ne 0 ) { - eval "{ use JSON }"; - if ($@) { - badprint "JSON Module is needed."; - exit 1; - } - my $json = JSON->new->allow_nonref; - print $json->utf8(1)->pretty(($opt{'prettyjson'} ? 1 : 0))->encode(\%result); + if ( $opt{'json'} ne 0 ) { + eval "{ use JSON }"; + if ($@) { + badprint "JSON Module is needed."; + exit 1; + } + my $json = JSON->new->allow_nonref; + print $json->utf8(1)->pretty( ( $opt{'prettyjson'} ? 1 : 0 ) ) + ->encode( \%result ); } } @@ -3426,7 +3597,7 @@ cve_recommendations; # Display related CVE calculations; # Calculate everything we need mysql_stats; # Print the server stats mysqsl_pfs # Print Performance schema info -mariadb_threadpool; # Print MaraiDB ThreadPool stats + mariadb_threadpool; # Print MaraiDB ThreadPool stats mysql_myisam; # Print MyISAM stats mariadb_ariadb; # Print MaraiDB AriaDB stats mysql_innodb; # Print InnoDB stats From bbf0db87d5120dd9f607bdae7b2f7a6c19d11bf7 Mon Sep 17 00:00:00 2001 From: Jean-Marie Renouard Date: Tue, 29 Mar 2016 14:32:52 +0200 Subject: [PATCH 24/44] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f0fc025..f1ce64b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,4 +35,4 @@ install: - cpanm --quiet --notest JSON script: - - ./mysqltuner.pl -idxstat --dbstat + - ./mysqltuner.pl --idxstat --dbstat From 21e5fe895ed0680779f914a3629568e920ac448f Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Mar 2016 14:41:33 +0200 Subject: [PATCH 25/44] Changing travis tests for 5.2 tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f1ce64b..0c1963a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,4 +35,4 @@ install: - cpanm --quiet --notest JSON script: - - ./mysqltuner.pl --idxstat --dbstat + - ./mysqltuner.pl --idxstat --dbstat --user travis -pass '' From 48a346ee0cbc6f135fee75d5a7626f5df0c05fcb Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Mar 2016 14:52:54 +0200 Subject: [PATCH 26/44] Trying to fix travis config. --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0c1963a..acd34a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,14 +14,14 @@ perl: matrix: include: - addons: - mariadb: 5.5 - perl: 5.20 + mariadb: "5.5" + perl: "5.20" - addons: - mariadb: 10.0 - perl: 5.20 + mariadb: "10.0" + perl: "5.20" - addons: - mariadb: 10.1 - perl: 5.20 + mariadb: "10.1" + perl: "5.20" before_install: - git clone git://github.com/haarg/perl-travis-helper From 3d9660807a9b582370b2c1bff0f2805d9c503021 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Mar 2016 15:12:17 +0200 Subject: [PATCH 27/44] Missing - in command option pass --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index acd34a7..8106686 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,4 +35,4 @@ install: - cpanm --quiet --notest JSON script: - - ./mysqltuner.pl --idxstat --dbstat --user travis -pass '' + - ./mysqltuner.pl --idxstat --dbstat --user travis --pass '' From 598da33e91bfbfbc6e3e073a21a53b90f88a39c1 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 29 Mar 2016 17:28:12 +0200 Subject: [PATCH 28/44] new version --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8106686..853a9c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,4 +35,6 @@ install: - cpanm --quiet --notest JSON script: - - ./mysqltuner.pl --idxstat --dbstat --user travis --pass '' + - echo -e "[client]\nuser=root\npassword=\"\"" > .my.cnf + - chmod 600 .my.cnf + - ./mysqltuner.pl --idxstat --dbstat From 45c468f32436287b99a505a2b601fc2c609a241b Mon Sep 17 00:00:00 2001 From: Sergei A Mamonov Date: Wed, 30 Mar 2016 14:44:30 +0300 Subject: [PATCH 29/44] add fixes to get_os_release for debian 6/7/8 --- mysqltuner.pl | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 6a24c33..173a43d 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1062,10 +1062,31 @@ sub get_other_process_memory { } sub get_os_release { - return "Unknown OS release" unless -f "/etc/system-release"; - my @info_release = get_file_contents "/etc/system-release"; - remove_cr @info_release; - return $info_release[0]; + if( -f "/etc/system-release") { + my @info_release = get_file_contents "/etc/system-release"; + remove_cr @info_release; + return $info_release[0]; + } + + if ( -f "/etc/os-release") { + my @info_release = get_file_contents "/etc/os-release"; + remove_cr @info_release; + my $os_relase = $info_release[0]; + $os_relase =~ s/.*="//; + $os_relase =~ s/"$//; + return $os_relase; + } + + if ( -f "/etc/issue") { + my @info_release = get_file_contents "/etc/issue"; + remove_cr @info_release; + my $os_relase = $info_release[0]; + $os_relase =~ s/\s+\\n.*//; + return $os_relase; + } + + return "Unknown OS release"; + } sub get_fs_info() { From ff796b48bf5681f9be213fb69d9b1e4d17546509 Mon Sep 17 00:00:00 2001 From: Sergei A Mamonov Date: Wed, 30 Mar 2016 15:59:47 +0300 Subject: [PATCH 30/44] fix mount point output for linux --- mysqltuner.pl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 173a43d..77c8e6a 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1091,11 +1091,11 @@ sub get_os_release { sub get_fs_info() { my @sinfo = `df -P | grep '%'`; - shift @sinfo; my @iinfo = `df -Pi| grep '%'`; shift @iinfo; map { s/.*\s(\d+)%\s+(.*)/$1\t$2/g } @sinfo; foreach my $info (@sinfo) { + next if $info =~ m{(\d+)\t/(run|dev|sys|proc)($|/)}; if ( $info =~ /(\d+)\t(.*)/ ) { if ( $1 > 85 ) { badprint "mount point $2 is using $1 % total space"; @@ -1109,6 +1109,7 @@ sub get_fs_info() { map { s/.*\s(\d+)%\s+(.*)/$1\t$2/g } @iinfo; foreach my $info (@iinfo) { + next if $info =~ m{(\d+)\t/(run|dev|sys|proc)($|/)}; if ( $info =~ /(\d+)\t(.*)/ ) { if ( $1 > 85 ) { badprint "mount point $2 is using $1 % of max allowed inodes"; From c74d51e065a8995094d2ea24117bc86c87bedcd6 Mon Sep 17 00:00:00 2001 From: root Date: Wed, 30 Mar 2016 17:16:16 +0200 Subject: [PATCH 31/44] system information are only available with --sysstat #165 --- mysqltuner.pl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 173a43d..aa645db 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -79,6 +79,7 @@ my %opt = ( "outputfile" => 0, "dbstat" => 0, "idxstat" => 0, + "sysstat" => 0, "skippassword" => 0, "noask" => 0, "template" => 0, @@ -107,7 +108,7 @@ my $getOptionsCheck = GetOptions( 'template=s', 'reportfile=s', 'cvefile=s', 'bannedports=s', 'updateversion', 'maxportallowed=s', - 'verbose' + 'verbose', 'sysstat' ); #If params are incorrect return help @@ -156,6 +157,7 @@ sub usage { . " --debug Print debug information\n" . " --dbstat Print database information\n" . " --idxstat Print index information\n" + . "--sysstat Print system information\n" . " --bannedports Ports banned separated by comma(,)\n" . " --maxportallowed Number of ports opened allowed on this hosts\n" . " --cvefile CVE File for vulnerability checks\n" @@ -186,6 +188,7 @@ if ( $opt{verbose} ) { $opt{checkversion} = 1; #Check for updates to MySQLTuner $opt{dbstat} = 1; #Print database information $opt{idxstat} = 1; #Print index information + $opt{sysstat} = 1; #Print index information $opt{buffers} = 1; #Print global and per-thread buffer values $opt{cvefile} = 'vulnerabilities.csv'; #CVE File for vulnerability checks } @@ -1197,6 +1200,7 @@ sub get_system_info() { } sub system_recommendations { + return if ( $opt{sysstat} == 0 ); prettyprint "\n-------- System Linux Recommendations ---------------------------------------"; my $os = `uname`; @@ -3681,6 +3685,7 @@ You must provide the remote server's total memory when connecting to other serve --debug Print debug information --dbstat Print database information --idxstat Print index information + --sysstat Print system information --bannedports Ports banned separated by comma(,) --maxportallowed Number of ports opened allowed on this hosts --cvefile CVE File for vulnerability checks From 6f5f5c4b45b3a200175a2ad935d8eff24987ee7f Mon Sep 17 00:00:00 2001 From: root Date: Wed, 30 Mar 2016 17:24:44 +0200 Subject: [PATCH 32/44] removing mariaDB 10.1 as test from travis - temporary --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 853a9c7..a38ba37 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,9 +19,9 @@ matrix: - addons: mariadb: "10.0" perl: "5.20" - - addons: - mariadb: "10.1" - perl: "5.20" +# - addons: +# mariadb: "10.1" +# perl: "5.20" before_install: - git clone git://github.com/haarg/perl-travis-helper From 292cbfedad72a56717e14526bc48e48758b77076 Mon Sep 17 00:00:00 2001 From: Christine Date: Mon, 4 Apr 2016 12:12:09 +0200 Subject: [PATCH 33/44] Adding some sysctl control in sysstat mode --- mysqltuner.pl | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/mysqltuner.pl b/mysqltuner.pl index 0d228c7..3b7f4db 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1159,6 +1159,44 @@ sub infocmd_one { return join ', ', @result; } + +sub get_kernel_info() +{ + my @params=('fs.aio-max-nr', 'fs.aio-nr', 'fs.file-max', 'sunrpc.tcp_fin_timeout', + 'sunrpc.tcp_max_slot_table_entries', 'sunrpc.tcp_slot_table_entries', + 'vm.swappiness'); + infoprint "Informations about kernel tuning:"; + foreach my $param (@params) { + infocmd_tab("sysctl $param"); + } + if (`sysctl -n vm.swappiness` > 10) { + badprint "Swappiness is > 10, please consider having a value lower than 10"; + push @generalrec, "setup swappieness lower or equals to 10"; + push @adjvars, 'vm.swappiness <= 10 (echo 0 > /proc/sys/vm/swappiness)'; + } else { + infoprint "Swappiness is < 10."; + } + + if (`sysctl -n sunrpc.tcp_slot_table_entries` < 100) { + badprint "Initial TCP slot entries is < 1M, please consider having a value greater than 100"; + push @generalrec, "setup Initial TCP slot entries greater than 100"; + push @adjvars, 'sunrpc.tcp_slot_table_entries > 100 (echo 128 > /proc/sys/sunrpc/tcp_slot_table_entries)'; + } else { + infoprint "TCP slot entries is > 100."; + } + + + if (`sysctl -n fs.aio-max-nr` < 1000000) { + badprint "Max running total of the number of events is < 1M, please consider having a value greater than 1M"; + push @generalrec, "setup Max running number events greater than 1M"; + push @adjvars, 'fs.aio-max-nr > 1M (echo 1048576 > /proc/sys/fs/aio-max-nr)'; + } else { + infoprint "Max Number of AIO events is > 1M."; + } + +} + + sub get_system_info() { infoprint get_os_release; if (is_virtual_machine) { @@ -1272,6 +1310,7 @@ sub system_recommendations { } get_fs_info; + get_kernel_info; } sub security_recommendations { From c9cea2cc800cb15345305680c3e64fd4919f5635 Mon Sep 17 00:00:00 2001 From: Christine Date: Mon, 4 Apr 2016 13:58:52 +0200 Subject: [PATCH 34/44] #177 bug with free -h changed to free -mwq --- mysqltuner.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 3b7f4db..f244b53 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1230,8 +1230,8 @@ sub get_system_info() { . infocmd_one "grep 'nameserver' /etc/resolv.conf \| awk '{print \$2}'"; infoprint "Logged In users : "; infocmd_tab "who"; - infoprint "Ram Usages : "; - infocmd_tab "free -h | grep -v +"; + infoprint "Ram Usages in Mb : "; + infocmd_tab "free -m | grep -v +"; infoprint "Load Average : "; infocmd_tab "top -n 1 -b | grep 'load average:'"; From 8203dcd686af015c1b5fbba877caff0d69382ac3 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Mon, 4 Apr 2016 11:23:43 -0400 Subject: [PATCH 35/44] Dumping galera options and status #150 --- mysqltuner.pl | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index f244b53..4077877 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -907,7 +907,6 @@ sub get_all_vars { $result{'Variables'}{$1} = $2; debugprint "V: $1 = $2"; } - my @mysqlstatlist = select_array "SHOW /*!50000 GLOBAL */ STATUS"; foreach my $line (@mysqlstatlist) { $line =~ /([a-zA-Z_]*)\s*(.*)/; @@ -915,7 +914,11 @@ sub get_all_vars { $result{'Status'}{$1} = $2; debugprint "S: $1 = $2"; } - + $myvar{'have_galera'} = "NO"; + if (defined($myvar{'wsrep_provider_options'})) { + $myvar{'have_galera'} = "YES"; + debugprint "Galera options: ". $myvar{'wsrep_provider_options'}; + } # Workaround for MySQL bug #59393 wrt. ignore-builtin-innodb if ( ( $myvar{'ignore_builtin_innodb'} || "" ) eq "ON" ) { $myvar{'have_innodb'} = "NO"; @@ -970,6 +973,7 @@ sub get_all_vars { sub remove_cr { map { s/\n$//g; } @_; + map { s/^\s+$//g; } @_; } sub remove_empty { @@ -1445,8 +1449,8 @@ sub security_recommendations { sub get_replication_status { prettyprint -"\n-------- Replication Metrics -------------------------------------------------"; - +"\n-------- Replication Metrics -------------------------------------------------"; + infoprint "Galera Synchronous replication: ". $myvar{'have_galera'}; if ( scalar( keys %myslaves ) == 0 ) { infoprint "No replication slave(s) for this server."; } @@ -2998,6 +3002,14 @@ sub mariadb_tokudb { # All is to done here } +# Perl trim function to remove whitespace from the start and end of the string +sub trim { + my $string = shift; + $string =~ s/^\s+//; + $string =~ s/\s+$//; + return $string; +} + # Recommendations for Galera sub mariadb_galera { prettyprint @@ -3005,15 +3017,26 @@ sub mariadb_galera { # AriaDB unless ( defined $myvar{'have_galera'} - && $myvar{'have_galera'} eq "YES" - && defined $enginestats{'Galera'} ) + && $myvar{'have_galera'} eq "YES" ) { infoprint "Galera is disabled."; return; } infoprint "Galera is enabled."; - # All is to done here + infoprint "Galera Options:"; + my @galera_options=split /;/,$myvar{'wsrep_provider_options'} ; + remove_cr @galera_options; + @galera_options=remove_empty @galera_options; + foreach my $gparam ( @galera_options ) { + infoprint "\t".trim($gparam); + } + infoprint "Galera status:"; + foreach my $gstatus ( keys %mystat ) { + next unless $gstatus =~ /^wsrep.*/; + infoprint "\t".trim($gstatus). " = ".$mystat{$gstatus}; + } + } # Recommendations for InnoDB From 3ef98d83c184d5241b7756545228ebce7af9b259 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Mon, 4 Apr 2016 11:30:16 -0400 Subject: [PATCH 36/44] Dumping mysql options for galera* #150 --- mysqltuner.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mysqltuner.pl b/mysqltuner.pl index 4077877..6ccc3cc 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -3023,6 +3023,12 @@ sub mariadb_galera { return; } infoprint "Galera is enabled."; + infoprint "Galera variables:"; + foreach my $gvar ( keys %myvar ) { + next unless $gvar =~ /^wsrep.*/; + next if $gvar eq 'wsrep_provider_options'; + infoprint "\t".trim($gvar). " = ".$myvar{$gvar}; + } infoprint "Galera Options:"; my @galera_options=split /;/,$myvar{'wsrep_provider_options'} ; From 2d483e6b755de67b6db9bfd1711b9900b6a363a0 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Mon, 4 Apr 2016 12:32:35 -0400 Subject: [PATCH 37/44] Check tables are all innodb table and check all table gets pk #150 --- mysqltuner.pl | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 6ccc3cc..4d69c14 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -3041,7 +3041,25 @@ sub mariadb_galera { foreach my $gstatus ( keys %mystat ) { next unless $gstatus =~ /^wsrep.*/; infoprint "\t".trim($gstatus). " = ".$mystat{$gstatus}; - } + } + my @primaryKeysNbTables=select_array("select CONCAT(table_schema,CONCAT('.', table_name)) from information_schema.columns where table_schema not in ('mysql', 'information_schema', 'performance_schema') group by table_schema,table_name having sum(if(column_key in ('PRI','UNI'), 1,0)) = 0"); + if (scalar (@primaryKeysNbTables) > 0 ) { + badprint "Following table(s) don't have primary key:"; + foreach my $badtable( @primaryKeysNbTables ) { + badprint "\t$badtable"; + } + } else { + goodprint "All tables get a primary key"; + } + my @nonInnoDbTables=select_array("select CONCAT(table_schema,CONCAT('.', table_name)) from information_schema.tables where ENGINE <> 'InnoDb' and table_schema not in ('mysql', 'performance_schema', 'information_schema')"); + if (scalar (@nonInnoDbTables) > 0 ) { + badprint "Following table(s) are not InnoDB table:"; + foreach my $badtable( @nonInnoDbTables ) { + badprint "\t$badtable"; + } + } else { + goodprint "All tables are InnoDB tables"; + } } From 974bd03ddfac11b97623633ce6cc3c9614cf13a5 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Tue, 5 Apr 2016 05:16:52 -0400 Subject: [PATCH 38/44] Adding new control for Galera cluster #150 --- mysqltuner.pl | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 4d69c14..e86c7fb 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -3030,7 +3030,7 @@ sub mariadb_galera { infoprint "\t".trim($gvar). " = ".$myvar{$gvar}; } - infoprint "Galera Options:"; + infoprint "Galera wsrep provider Options:"; my @galera_options=split /;/,$myvar{'wsrep_provider_options'} ; remove_cr @galera_options; @galera_options=remove_empty @galera_options; @@ -3054,13 +3054,46 @@ sub mariadb_galera { my @nonInnoDbTables=select_array("select CONCAT(table_schema,CONCAT('.', table_name)) from information_schema.tables where ENGINE <> 'InnoDb' and table_schema not in ('mysql', 'performance_schema', 'information_schema')"); if (scalar (@nonInnoDbTables) > 0 ) { badprint "Following table(s) are not InnoDB table:"; + push @generalrec, "Ensure that all table(s) are InnoDB tabls for Galera replication"; foreach my $badtable( @nonInnoDbTables ) { badprint "\t$badtable"; } } else { goodprint "All tables are InnoDB tables"; } + if ($myvar{'binlog_format'} ne 'ROW') { + badprint "Binlog format should be in ROW mode."; + push @adjvars, "binlog_format = ROW"; + } else { + goodprint "Binlog format is in ROW mode."; + } + if ($myvar{'innodb_flush_log_at_trx_commit'} != 0 ) { + badprint "Innodb flush log at each commit should be disabled."; + push @adjvars, "innodb_flush_log_at_trx_commit = 0"; + } else { + goodprint "Innodb flush log at each commit is disabled for Galera."; + } + infoprint "Read consistency mode :". $myvar{'wsrep_causal_reads'}; + + if ( defined($myvar{'wsrep_cluster_name'}) and $myvar{'wsrep_on'} eq "ON" ) { + goodprint "Galera WsREP is enabled."; + } else { + badprint "Galera Wsesp is disabled"; + } + if ( defined($myvar{'wsrep_cluster_address'}) and trim("$myvar{'wsrep_cluster_address'}") ne "") { + goodprint "Galera Cluster address is defined: ".$myvar{'wsrep_cluster_address'}; + } else { + badprint "Galera Cluster address is undefined"; + push @adjvars, "set up wsrep_cluster_address variable for Galera replication"; + } + + if ( defined($myvar{'wsrep_cluster_name'}) and trim($myvar{'wsrep_cluster_name'}) ne "") { + goodprint "Galera Cluster name is defined: ".$myvar{'wsrep_cluster_name'}; + } else { + badprint "Galera Cluster name is undefined"; + push @adjvars, "set up wsrep_cluster_name variable for Galera replication"; + } } # Recommendations for InnoDB From 89ade050a765c496e8a9b48bb39c1e2aada6c551 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Tue, 5 Apr 2016 09:18:10 -0400 Subject: [PATCH 39/44] Update Internals checks documentation --- INTERNALS.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/INTERNALS.md b/INTERNALS.md index d24b3c4..dbefe6f 100644 --- a/INTERNALS.md +++ b/INTERNALS.md @@ -70,6 +70,12 @@ * Check 80, 8080, 443 and 8443 ports if warning are raised if there are opened * Check if some banned ports are not opened (option --bannedports separated by comma) * Check if non kernel and user process except mysqld are not using more than 15% of total physical memory) +* Check vm.swapiness +* Check /etc/security/limit.conf +* Check sysctl entries: sunrpc.tcp_slot_entries, vm.swappiness, fs.aio-fs-nr +* Check mount point +* Check Ethernet card +* Check load average ## MySQLTuner Server version checks * EOL MySQL version check From 97cce402ae090e80708405c1d3c16cdf3ca23f65 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Tue, 5 Apr 2016 09:49:43 -0400 Subject: [PATCH 40/44] Adding get_http_cli for curl or wget detecting in sysstat --- mysqltuner.pl | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index e86c7fb..2113de6 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -418,6 +418,20 @@ sub os_setup { } +sub get_http_cli { + my $httpcli = `which curl`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + return $httpcli; + } + + $httpcli = `which wget`; + chomp($httpcli); + if ( defined($httpcli) and -e "$httpcli" ) { + return $httpcli; + } + return ""; +} # Checks for updates to MySQLTuner sub validate_tuner_version { if ( $opt{'checkversion'} eq 0 and $opt{'updateversion'} eq 0 ) { @@ -443,6 +457,8 @@ sub validate_tuner_version { compare_tuner_version($update); return; + } else { + } $httpcli = `which wget`; @@ -1210,7 +1226,7 @@ sub get_system_info() { infoprint "Machine type : Physical machine"; } - `ping -c 1 google.com &>/dev/null`; + `ping -c 1 ipecho.net &>/dev/null`; my $isConnected = $?; if ( $? == 0 ) { infoprint "Internet : Connected"; @@ -1224,12 +1240,12 @@ sub get_system_info() { infoprint "Network Cards : "; infocmd_tab "ifconfig| grep -A1 mtu"; infoprint "Internal IP : " . infocmd_one "hostname -I"; + my $httpcli=get_http_cli(); + infoprint "HTTP client found: $httpcli" if defined $httpcli; infoprint "External IP : " - . infocmd_one "curl -s ipecho.net/plain" - if $isConnected == 0; + . infocmd_one "$httpcli ipecho.net/plain" if defined ($httpcli); badprint - "External IP : Can't check because of Internet connectivity" - if $isConnected != 0; + "External IP : Can't check because of Internet connectivity" unless defined($httpcli); infoprint "Name Servers : " . infocmd_one "grep 'nameserver' /etc/resolv.conf \| awk '{print \$2}'"; infoprint "Logged In users : "; From a5449a0d22cb8ced2fa6a310a94b0d1d3aa9d98f Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Tue, 5 Apr 2016 11:40:38 -0400 Subject: [PATCH 41/44] Adding performance_schema.memoty value to max_used_memory and max_peak_memory calculation There is always MySQL 5.7+ and adaptaive memory usage to handle porperly --- mysqltuner.pl | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index 2113de6..deccf52 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -1923,14 +1923,14 @@ sub calculations { # Max used memory is memory used by MySQL based on Max_used_connections # This is the max memory used theorically calculated with the max concurrent connection number reached by mysql $mycalc{'max_used_memory'} = - $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"}; + $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"} +get_pf_memory(); $mycalc{'pct_max_used_memory'} = percentage( $mycalc{'max_used_memory'}, $physical_memory ); # Total possible memory is memory needed by MySQL based on max_connections # This is the max memory MySQL can theorically used if all connections allowed has opened by mysql $mycalc{'max_peak_memory'} = - $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'}; + $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'} + get_pf_memory(); $mycalc{'pct_max_physical_memory'} = percentage( $mycalc{'max_peak_memory'}, $physical_memory ); @@ -1943,7 +1943,7 @@ sub calculations { . hr_bytes( $mycalc{'max_peak_memory'} ) . ""; debugprint "Max Peak Percentage RAM: " . $mycalc{'pct_max_physical_memory'} . "%"; - + # Slow queries $mycalc{'pct_slow_queries'} = int( ( $mystat{'Slow_queries'} / $mystat{'Questions'} ) * 100 ); @@ -2275,7 +2275,7 @@ sub mysql_stats { . " global + " . hr_bytes( $mycalc{'per_thread_buffers'} ) . " per thread ($myvar{'max_connections'} max threads)"; - + infoprint "P_S Max memory usage: ".hr_bytes_rnd(get_pf_memory()); if ( $opt{buffers} ne 0 ) { infoprint "Global Buffers"; infoprint " +-- Key Buffer: " @@ -2913,6 +2913,13 @@ sub mariadb_threadpool { } } +sub get_pf_memory +{ + + my @infoPFSMemory=grep /performance_schema.memory/, select_array("SHOW ENGINE PERFORMANCE_SCHEMA STATUS"); + $infoPFSMemory[0] =~ s/.*\s+(\d+)$/$1/g; + return $infoPFSMemory[0]; +} # Recommendations for Performance Schema sub mysqsl_pfs { prettyprint @@ -2927,6 +2934,7 @@ sub mysqsl_pfs { else { infoprint "Performance schema is enabled."; } + infoprint "Memory used by P_S: ". hr_bytes(get_pf_memory()); } # Recommendations for Ariadb @@ -3758,7 +3766,7 @@ cve_recommendations; # Display related CVE calculations; # Calculate everything we need mysql_stats; # Print the server stats mysqsl_pfs # Print Performance schema info - mariadb_threadpool; # Print MaraiDB ThreadPool stats +mariadb_threadpool; # Print MaraiDB ThreadPool stats mysql_myisam; # Print MyISAM stats mariadb_ariadb; # Print MaraiDB AriaDB stats mysql_innodb; # Print InnoDB stats From 83010dd32ef09a1d7edf9e65bc3a1af0306a2ae6 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Tue, 5 Apr 2016 12:25:20 -0400 Subject: [PATCH 42/44] Bug fix on aria metrics --- mysqltuner.pl | 51 +++++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index deccf52..e1086f9 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -966,7 +966,7 @@ sub get_all_vars { $result{'Storage Engines'}{$engine} = $2; } } - + debugprint Dumper(@mysqlenginelist); my @mysqlslave = select_array "SHOW SLAVE STATUS\\G"; foreach my $line (@mysqlslave) { @@ -1923,14 +1923,14 @@ sub calculations { # Max used memory is memory used by MySQL based on Max_used_connections # This is the max memory used theorically calculated with the max concurrent connection number reached by mysql $mycalc{'max_used_memory'} = - $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"} +get_pf_memory(); + $mycalc{'server_buffers'} + $mycalc{"max_total_per_thread_buffers"} +get_pf_memory() + get_gcache_memory(); $mycalc{'pct_max_used_memory'} = percentage( $mycalc{'max_used_memory'}, $physical_memory ); # Total possible memory is memory needed by MySQL based on max_connections # This is the max memory MySQL can theorically used if all connections allowed has opened by mysql $mycalc{'max_peak_memory'} = - $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'} + get_pf_memory(); + $mycalc{'server_buffers'} + $mycalc{'total_per_thread_buffers'} + get_pf_memory()+ get_gcache_memory(); $mycalc{'pct_max_physical_memory'} = percentage( $mycalc{'max_peak_memory'}, $physical_memory ); @@ -2055,7 +2055,7 @@ sub calculations { if ( defined $mycalc{'total_aria_indexes'} and $mycalc{'total_aria_indexes'} == 0 ) { - $mycalc{'total_aria_indexes'} = "fail"; + $mycalc{'total_aria_indexes'} = 1; } elsif ( defined $mycalc{'total_aria_indexes'} ) { chomp( $mycalc{'total_aria_indexes'} ); @@ -2276,6 +2276,7 @@ sub mysql_stats { . hr_bytes( $mycalc{'per_thread_buffers'} ) . " per thread ($myvar{'max_connections'} max threads)"; infoprint "P_S Max memory usage: ".hr_bytes_rnd(get_pf_memory()); + infoprint "Galera GCache Max memory usage: ".hr_bytes_rnd(get_gcache_memory()); if ( $opt{buffers} ne 0 ) { infoprint "Global Buffers"; infoprint " +-- Key Buffer: " @@ -2944,8 +2945,7 @@ sub mariadb_ariadb { # AriaDB unless ( defined $myvar{'have_aria'} - && $myvar{'have_aria'} eq "YES" - && defined $enginestats{'Aria'} ) + and $myvar{'have_aria'} eq "YES" ) { infoprint "AriaDB is disabled."; return; @@ -3034,12 +3034,31 @@ sub trim { return $string; } +sub get_wsrep_options { + return () unless defined $myvar{'wsrep_provider_options'} ; + + my @galera_options=split /;/,$myvar{'wsrep_provider_options'} ; + remove_cr @galera_options; + @galera_options=remove_empty @galera_options; + return @galera_options; +} +sub get_gcache_memory { + return 0 unless defined $myvar{'wsrep_provider_options'} ; + + my @galera_options=get_wsrep_options; + return 0 unless scalar(@galera_options) >0; + my @memValues= grep /gcache.mem_size/, @galera_options; + my $memValue=$memValues[0]; + $memValue =~ s/.*=\s*(\d+)$/$1/g; + return $memValue; + +} # Recommendations for Galera sub mariadb_galera { prettyprint "\n-------- Galera Metrics ------------------------------------------------------"; - # AriaDB + # Galera Cluster unless ( defined $myvar{'have_galera'} && $myvar{'have_galera'} eq "YES" ) { @@ -3047,26 +3066,26 @@ sub mariadb_galera { return; } infoprint "Galera is enabled."; - infoprint "Galera variables:"; + debugprint "Galera variables:"; foreach my $gvar ( keys %myvar ) { next unless $gvar =~ /^wsrep.*/; next if $gvar eq 'wsrep_provider_options'; - infoprint "\t".trim($gvar). " = ".$myvar{$gvar}; + debugprint "\t".trim($gvar). " = ".$myvar{$gvar}; } - infoprint "Galera wsrep provider Options:"; - my @galera_options=split /;/,$myvar{'wsrep_provider_options'} ; - remove_cr @galera_options; - @galera_options=remove_empty @galera_options; + debugprint "Galera wsrep provider Options:"; + my @galera_options=get_wsrep_options; foreach my $gparam ( @galera_options ) { - infoprint "\t".trim($gparam); + debugprint "\t".trim($gparam); } - infoprint "Galera status:"; + debugprint "Galera status:"; foreach my $gstatus ( keys %mystat ) { next unless $gstatus =~ /^wsrep.*/; - infoprint "\t".trim($gstatus). " = ".$mystat{$gstatus}; + debugprint "\t".trim($gstatus). " = ".$mystat{$gstatus}; } + infoprint "GCache is using ".hr_bytes_rnd(get_gcache_memory()); my @primaryKeysNbTables=select_array("select CONCAT(table_schema,CONCAT('.', table_name)) from information_schema.columns where table_schema not in ('mysql', 'information_schema', 'performance_schema') group by table_schema,table_name having sum(if(column_key in ('PRI','UNI'), 1,0)) = 0"); + if (scalar (@primaryKeysNbTables) > 0 ) { badprint "Following table(s) don't have primary key:"; foreach my $badtable( @primaryKeysNbTables ) { From 15854a6c94d78d4b4a942af4832edd1bbd1bcac5 Mon Sep 17 00:00:00 2001 From: "Jean-Marie RENOUARDjmrenouard@gmail.com" Date: Tue, 5 Apr 2016 12:34:24 -0400 Subject: [PATCH 43/44] #138 for threadpool in mariadb and percona use default values --- mysqltuner.pl | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mysqltuner.pl b/mysqltuner.pl index e1086f9..c1a9696 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -2876,6 +2876,12 @@ sub mariadb_threadpool { infoprint "ThreadPool stat is enabled."; infoprint "Thread Pool Size: " . $myvar{'thread_pool_size'} . " thread(s)."; + if ($myvar{'version'} =~ /mariadb|percona/i ) { + infoprint "Using default value is good enougth for your version (".$myvar{'version'}.")"; + return; + } + + if ( $myvar{'have_innodb'} eq 'YES' ) { if ( $myvar{'thread_pool_size'} < 16 or $myvar{'thread_pool_size'} > 36 ) From 8f0c2e6b3380c14584116c6f3ad4874e64951c9b Mon Sep 17 00:00:00 2001 From: Robert Milasan Date: Wed, 6 Apr 2016 10:33:56 +0200 Subject: [PATCH 44/44] Fix Galera checks, make sure we dont say that Galera is enabled when is not, clean-up typos --- mysqltuner.pl | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/mysqltuner.pl b/mysqltuner.pl index c1a9696..6269799 100755 --- a/mysqltuner.pl +++ b/mysqltuner.pl @@ -931,7 +931,7 @@ sub get_all_vars { debugprint "S: $1 = $2"; } $myvar{'have_galera'} = "NO"; - if (defined($myvar{'wsrep_provider_options'})) { + if ( defined($myvar{'wsrep_provider_options'}) && $myvar{'wsrep_provider_options'} ne "") { $myvar{'have_galera'} = "YES"; debugprint "Galera options: ". $myvar{'wsrep_provider_options'}; } @@ -1477,7 +1477,7 @@ sub get_replication_status { } if ( scalar( keys %myrepl ) == 0 and scalar( keys %myslaves ) == 0 ) { - infoprint "This is a standalone server.."; + infoprint "This is a standalone server."; return; } if ( scalar( keys %myrepl ) == 0 ) { @@ -3127,22 +3127,21 @@ sub mariadb_galera { if ( defined($myvar{'wsrep_cluster_name'}) and $myvar{'wsrep_on'} eq "ON" ) { goodprint "Galera WsREP is enabled."; + if ( defined($myvar{'wsrep_cluster_address'}) and trim("$myvar{'wsrep_cluster_address'}") ne "") { + goodprint "Galera Cluster address is defined: ".$myvar{'wsrep_cluster_address'}; + } else { + badprint "Galera Cluster address is undefined"; + push @adjvars, "set up wsrep_cluster_address variable for Galera replication"; + } + if ( defined($myvar{'wsrep_cluster_name'}) and trim($myvar{'wsrep_cluster_name'}) ne "") { + goodprint "Galera Cluster name is defined: ".$myvar{'wsrep_cluster_name'}; + } else { + badprint "Galera Cluster name is undefined"; + push @adjvars, "set up wsrep_cluster_name variable for Galera replication"; + } } else { - badprint "Galera Wsesp is disabled"; + badprint "Galera WsREP is disabled"; } - if ( defined($myvar{'wsrep_cluster_address'}) and trim("$myvar{'wsrep_cluster_address'}") ne "") { - goodprint "Galera Cluster address is defined: ".$myvar{'wsrep_cluster_address'}; - } else { - badprint "Galera Cluster address is undefined"; - push @adjvars, "set up wsrep_cluster_address variable for Galera replication"; - } - - if ( defined($myvar{'wsrep_cluster_name'}) and trim($myvar{'wsrep_cluster_name'}) ne "") { - goodprint "Galera Cluster name is defined: ".$myvar{'wsrep_cluster_name'}; - } else { - badprint "Galera Cluster name is undefined"; - push @adjvars, "set up wsrep_cluster_name variable for Galera replication"; - } } # Recommendations for InnoDB