mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-22 11:54:54 +00:00
Merge pt-agent-fixes.
This commit is contained in:
334
bin/pt-agent
334
bin/pt-agent
@@ -3691,7 +3691,7 @@ sub new {
|
|||||||
|
|
||||||
sub connect {
|
sub connect {
|
||||||
my ( $self, %opts ) = @_;
|
my ( $self, %opts ) = @_;
|
||||||
my $dsn = $self->{dsn};
|
my $dsn = $opts{dsn} || $self->{dsn};
|
||||||
my $dp = $self->{DSNParser};
|
my $dp = $self->{DSNParser};
|
||||||
|
|
||||||
my $dbh = $self->{dbh};
|
my $dbh = $self->{dbh};
|
||||||
@@ -3710,6 +3710,13 @@ sub connect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dbh = $self->set_dbh($dbh);
|
$dbh = $self->set_dbh($dbh);
|
||||||
|
if ( $opts{dsn} ) {
|
||||||
|
$self->{dsn} = $dsn;
|
||||||
|
$self->{dsn_name} = $dp->as_string($dsn, [qw(h P S)])
|
||||||
|
|| $dp->as_string($dsn, [qw(F)])
|
||||||
|
|| '';
|
||||||
|
|
||||||
|
}
|
||||||
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
|
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
|
||||||
return $dbh;
|
return $dbh;
|
||||||
}
|
}
|
||||||
@@ -3873,6 +3880,8 @@ sub quote_val {
|
|||||||
return $val if $val =~ m/^0x[0-9a-fA-F]+$/ # quote hex data
|
return $val if $val =~ m/^0x[0-9a-fA-F]+$/ # quote hex data
|
||||||
&& !$args{is_char}; # unless is_char is true
|
&& !$args{is_char}; # unless is_char is true
|
||||||
|
|
||||||
|
return $val if $args{is_float};
|
||||||
|
|
||||||
$val =~ s/(['\\])/\\$1/g;
|
$val =~ s/(['\\])/\\$1/g;
|
||||||
return "'$val'";
|
return "'$val'";
|
||||||
}
|
}
|
||||||
@@ -5092,7 +5101,7 @@ sub level_name {
|
|||||||
sub debug {
|
sub debug {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return if $self->online_logging;
|
return if $self->online_logging;
|
||||||
return $self->_log(0, 'DEBUG', 1, @_);
|
return $self->_log(0, 'DEBUG', @_);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub info {
|
sub info {
|
||||||
@@ -5128,7 +5137,7 @@ sub _set_exit_status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub _log {
|
sub _log {
|
||||||
my ($self, $online, $level, $msg, $offline) = @_;
|
my ($self, $online, $level, $msg) = @_;
|
||||||
|
|
||||||
my $ts = ts(time, 1); # 1=UTC
|
my $ts = ts(time, 1); # 1=UTC
|
||||||
my $level_number = level_number($level);
|
my $level_number = level_number($level);
|
||||||
@@ -5792,6 +5801,7 @@ sub init_agent {
|
|||||||
# Optional args
|
# Optional args
|
||||||
my $_oktorun = $args{oktorun} || sub { return $oktorun };
|
my $_oktorun = $args{oktorun} || sub { return $oktorun };
|
||||||
my $actions = $args{actions};
|
my $actions = $args{actions};
|
||||||
|
my $quiet = $args{quiet};
|
||||||
|
|
||||||
# Update these attribs every time the agent is initialized.
|
# Update these attribs every time the agent is initialized.
|
||||||
# Other optional attribs, like versions, are left to the caller.
|
# Other optional attribs, like versions, are left to the caller.
|
||||||
@@ -5802,7 +5812,7 @@ sub init_agent {
|
|||||||
# Try to create/update the Agent.
|
# Try to create/update the Agent.
|
||||||
my $success = 0;
|
my $success = 0;
|
||||||
while ( $_oktorun->() && $tries-- ) {
|
while ( $_oktorun->() && $tries-- ) {
|
||||||
if ( !$state->{init_action}++ ) {
|
if ( !$state->{init_action}++ && !$quiet ) {
|
||||||
$logger->info($action eq 'put' ? "Updating agent " . $agent->name
|
$logger->info($action eq 'put' ? "Updating agent " . $agent->name
|
||||||
: "Creating new agent");
|
: "Creating new agent");
|
||||||
}
|
}
|
||||||
@@ -5841,7 +5851,7 @@ sub init_agent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
elsif ( !$agent_uri ) {
|
elsif ( !$agent_uri ) {
|
||||||
$logger->info("No URI for Agent " . $agent->name);
|
$logger->warning("No URI for Agent " . $agent->name);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
# The Agent URI will have been returned in the Location header
|
# The Agent URI will have been returned in the Location header
|
||||||
@@ -5868,7 +5878,7 @@ sub init_agent {
|
|||||||
delete $state->{init_action};
|
delete $state->{init_action};
|
||||||
delete $state->{too_many_agents};
|
delete $state->{too_many_agents};
|
||||||
|
|
||||||
if ( $agent && $success ) {
|
if ( $agent && $success && !$quiet ) {
|
||||||
$logger->info("Agent " . $agent->name . " (" . $agent->uuid . ") is ready");
|
$logger->info("Agent " . $agent->name . " (" . $agent->uuid . ") is ready");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5955,7 +5965,7 @@ sub start_agent {
|
|||||||
my $entry_links = $args{entry_links}; # for testing
|
my $entry_links = $args{entry_links}; # for testing
|
||||||
my $logger_client = $args{logger_client}; # for testing
|
my $logger_client = $args{logger_client}; # for testing
|
||||||
|
|
||||||
$logger->info('Starting agent');
|
# $logger->info('Starting agent');
|
||||||
|
|
||||||
# Daemonize first so all output goes to the --log.
|
# Daemonize first so all output goes to the --log.
|
||||||
my $daemon = Daemon->new(
|
my $daemon = Daemon->new(
|
||||||
@@ -6126,8 +6136,9 @@ sub run_agent {
|
|||||||
# #######################################################################
|
# #######################################################################
|
||||||
# Main agent loop
|
# Main agent loop
|
||||||
# #######################################################################
|
# #######################################################################
|
||||||
|
$state->{need_mysql_version} = 1;
|
||||||
$state->{first_config} = 1;
|
$state->{first_config} = 1;
|
||||||
my $first_config_interval = 60;
|
my $first_config_interval = 20;
|
||||||
$logger->info("Checking silently every $first_config_interval seconds"
|
$logger->info("Checking silently every $first_config_interval seconds"
|
||||||
. " for the first config");
|
. " for the first config");
|
||||||
|
|
||||||
@@ -6136,38 +6147,6 @@ sub run_agent {
|
|||||||
my $config;
|
my $config;
|
||||||
my $services = {};
|
my $services = {};
|
||||||
while ( $_oktorun->() ) {
|
while ( $_oktorun->() ) {
|
||||||
check_if_mysql_restarted(
|
|
||||||
Cxn => $cxn,
|
|
||||||
);
|
|
||||||
|
|
||||||
if ( $state->{need_mysql_version} ) {
|
|
||||||
my $versions = get_versions(
|
|
||||||
Cxn => $cxn,
|
|
||||||
);
|
|
||||||
if ( $versions->{MySQL} ) {
|
|
||||||
$agent->versions($versions);
|
|
||||||
my $updated_agent;
|
|
||||||
($agent, $updated_agent) = init_agent(
|
|
||||||
agent => $agent,
|
|
||||||
action => 'put',
|
|
||||||
link => $agent->links->{self},
|
|
||||||
client => $client,
|
|
||||||
interval => sub { return; },
|
|
||||||
tries => 1, # optional
|
|
||||||
);
|
|
||||||
if ( $updated_agent ) {
|
|
||||||
$logger->info("Got MySQL versions");
|
|
||||||
save_agent(
|
|
||||||
agent => $agent,
|
|
||||||
lib_dir => $lib_dir,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$state->{need_mysql_version} = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
($config, $lib_dir, $new_daemon, $success) = get_config(
|
($config, $lib_dir, $new_daemon, $success) = get_config(
|
||||||
link => $agent->links->{config},
|
link => $agent->links->{config},
|
||||||
agent => $agent,
|
agent => $agent,
|
||||||
@@ -6185,6 +6164,7 @@ sub run_agent {
|
|||||||
delete $state->{first_config};
|
delete $state->{first_config};
|
||||||
$logger->info('Agent has been configured');
|
$logger->info('Agent has been configured');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( $new_daemon ) {
|
if ( $new_daemon ) {
|
||||||
# NOTE: Daemon objects use DESTROY to auto-remove their pid file
|
# NOTE: Daemon objects use DESTROY to auto-remove their pid file
|
||||||
# when they lose scope (i.e. ref count goes to zero). This
|
# when they lose scope (i.e. ref count goes to zero). This
|
||||||
@@ -6200,6 +6180,60 @@ sub run_agent {
|
|||||||
$daemon = $new_daemon;
|
$daemon = $new_daemon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Connect to MySQL, then check stuff.
|
||||||
|
my $o = new OptionParser();
|
||||||
|
$o->get_specs();
|
||||||
|
$o->get_opts();
|
||||||
|
my $dp = $o->DSNParser();
|
||||||
|
$dp->prop('set-vars', $o->set_vars());
|
||||||
|
my $dsn = $dp->parse_options($o);
|
||||||
|
eval {
|
||||||
|
$cxn->connect(dsn => $dsn);
|
||||||
|
};
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
$logger->warning("MySQL connection failure: $EVAL_ERROR");
|
||||||
|
$state->{have_mysql} = 0;
|
||||||
|
$state->{need_mysql_version} = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( !$state->{have_mysql} ) {
|
||||||
|
$logger->info("MySQL connection OK");
|
||||||
|
}
|
||||||
|
$state->{have_mysql} = 1;
|
||||||
|
check_if_mysql_restarted(
|
||||||
|
dbh => $cxn->dbh,
|
||||||
|
);
|
||||||
|
if ( $state->{need_mysql_version} ) {
|
||||||
|
$logger->debug("Need MySQL version");
|
||||||
|
my $versions = get_versions(Cxn => $cxn);
|
||||||
|
if ( $versions->{MySQL} ) {
|
||||||
|
$agent->versions($versions);
|
||||||
|
my $updated_agent;
|
||||||
|
($agent, $updated_agent) = init_agent(
|
||||||
|
agent => $agent,
|
||||||
|
action => 'put',
|
||||||
|
link => $agent->links->{self},
|
||||||
|
client => $client,
|
||||||
|
tries => 1,
|
||||||
|
interval => sub { return; },
|
||||||
|
quiet => 1,
|
||||||
|
);
|
||||||
|
if ( $updated_agent ) {
|
||||||
|
$logger->debug("Got MySQL version");
|
||||||
|
save_agent(
|
||||||
|
agent => $agent,
|
||||||
|
lib_dir => $lib_dir,
|
||||||
|
);
|
||||||
|
delete $state->{need_mysql_version};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$logger->debug("Failed to get MySQL version");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cxn->dbh->disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
# Check the safeguards.
|
# Check the safeguards.
|
||||||
my ($disk_space, $disk_space_ok);
|
my ($disk_space, $disk_space_ok);
|
||||||
eval {
|
eval {
|
||||||
@@ -6341,6 +6375,8 @@ sub get_config {
|
|||||||
$config = $new_config;
|
$config = $new_config;
|
||||||
$success = 1;
|
$success = 1;
|
||||||
$logger->info('Config ' . $config->ts . ' applied');
|
$logger->info('Config ' . $config->ts . ' applied');
|
||||||
|
|
||||||
|
$state->{need_mysql_version} = 1;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$success = 1;
|
$success = 1;
|
||||||
@@ -8319,7 +8355,7 @@ sub get_agent_pid {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
# Match the first digits, which should be the PID.
|
# Match the first digits, which should be the PID.
|
||||||
($pid) =~ $ps_output =~ m/(\d+)/;
|
($pid) = $ps_output =~ m/(\d+)/;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( !$pid ) {
|
if ( !$pid ) {
|
||||||
@@ -8405,6 +8441,7 @@ sub install {
|
|||||||
|
|
||||||
my $agent_my_cnf = '/etc/percona/agent/my.cnf';
|
my $agent_my_cnf = '/etc/percona/agent/my.cnf';
|
||||||
my $config_file = get_config_file();
|
my $config_file = get_config_file();
|
||||||
|
my $lib_dir = $o->get('lib');
|
||||||
|
|
||||||
my $step_result;
|
my $step_result;
|
||||||
my $stepno = 0;
|
my $stepno = 0;
|
||||||
@@ -8414,6 +8451,7 @@ sub install {
|
|||||||
"Verify the user is root",
|
"Verify the user is root",
|
||||||
"Check Perl module dependencies",
|
"Check Perl module dependencies",
|
||||||
"Check for crontab",
|
"Check for crontab",
|
||||||
|
"Verify pt-agent is not installed",
|
||||||
"Verify the API key",
|
"Verify the API key",
|
||||||
"Connect to MySQL",
|
"Connect to MySQL",
|
||||||
"Check if MySQL is a slave",
|
"Check if MySQL is a slave",
|
||||||
@@ -8483,7 +8521,25 @@ sub install {
|
|||||||
die "cron is not installed, or crontab is not in your PATH.\n";
|
die "cron is not installed, or crontab is not in your PATH.\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Verify pt-agent is not installed
|
||||||
|
$next_step->();
|
||||||
|
my @install_files = ($agent_my_cnf, $config_file, "$lib_dir/agent");
|
||||||
|
my @have_files;
|
||||||
|
foreach my $file (@install_files) {
|
||||||
|
push @have_files, $file if -f $file;
|
||||||
|
}
|
||||||
|
if ( scalar @have_files ) {
|
||||||
|
print "FAIL\n";
|
||||||
|
die "It looks like pt-agent is already installed because these files exist:\n"
|
||||||
|
. join("\n", map { " $_" } @have_files)
|
||||||
|
. "\nRun pt-agent --uninstall to remove these files. To upgrade pt-agent, "
|
||||||
|
. "install the new version, run pt-agent --stop, then pt-agent --daemonize "
|
||||||
|
. "to restart pt-agent with the new version.\n";
|
||||||
|
}
|
||||||
|
|
||||||
# Must have a valid API key.
|
# Must have a valid API key.
|
||||||
|
$next_step->();
|
||||||
|
my $got_api_key = 0;
|
||||||
my $api_key = $o->get('api-key');
|
my $api_key = $o->get('api-key');
|
||||||
if ( !$api_key ) {
|
if ( !$api_key ) {
|
||||||
print "\n";
|
print "\n";
|
||||||
@@ -8497,19 +8553,22 @@ sub install {
|
|||||||
$api_key = '';
|
$api_key = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$next_step->(repeat => 1); # repeat
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
die "Please specify your --api-key.\n";
|
die "Please specify your --api-key.\n";
|
||||||
}
|
}
|
||||||
|
$got_api_key = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
my $client;
|
my $client;
|
||||||
my $entry_links;
|
my $entry_links;
|
||||||
if ( $flags->{offline} ) {
|
if ( $flags->{offline} ) {
|
||||||
$skip++;
|
$skip++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
$next_step->();
|
if ($got_api_key) {
|
||||||
|
$next_step->(repeat => 1);
|
||||||
|
}
|
||||||
eval {
|
eval {
|
||||||
($client, $entry_links) = get_api_client(
|
($client, $entry_links) = get_api_client(
|
||||||
api_key => $api_key,
|
api_key => $api_key,
|
||||||
@@ -8571,30 +8630,13 @@ sub install {
|
|||||||
if ( $flags->{force_dangerous_slave_install} ) {
|
if ( $flags->{force_dangerous_slave_install} ) {
|
||||||
create_mysql_user($cxn, $agent_my_cnf);
|
create_mysql_user($cxn, $agent_my_cnf);
|
||||||
}
|
}
|
||||||
elsif ( $interactive || -t STDIN ) {
|
|
||||||
print "\nMySQL is a slave and $agent_my_cnf does not exist. "
|
|
||||||
. "To install the agent, please enter the MySQL username and "
|
|
||||||
. "password to use. The MySQL user must have SUPER and USAGE "
|
|
||||||
. "privileges on all databases, for example: "
|
|
||||||
. "GRANT SUPER,USAGE ON *.* TO pt_agent'\@'localhost'. "
|
|
||||||
. "If the agent has been installed on the master, you can use "
|
|
||||||
. "the MySQL username and password in $agent_my_cnf on the "
|
|
||||||
. "master. CTRL-C to abort install.\n";
|
|
||||||
print "MySQL username: ";
|
|
||||||
my $user = <STDIN>;
|
|
||||||
chomp($user) if $user;
|
|
||||||
my $pass = OptionParser::prompt_noecho("MySQL password: ");
|
|
||||||
create_mysql_user($cxn, $agent_my_cnf, $user, $pass);
|
|
||||||
$next_step->(repeat => 1); # repeat
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
die "Sorry, cannot install the agent because MySQL is a slave "
|
die "Sorry, cannot install the agent because MySQL is a slave "
|
||||||
. "and $agent_my_cnf does not exist. It is not safe to "
|
. "and $agent_my_cnf does not exist. It is not safe to "
|
||||||
. "write to a slave, so a MySQL user for the agent cannot "
|
. "write to a slave, so a MySQL user for the agent cannot "
|
||||||
. "be created. First install the agent on the master, then "
|
. "be created. First install the agent on the master, then "
|
||||||
. "copy $agent_my_cnf from the master to this server. "
|
. "copy $agent_my_cnf from the master to this server. "
|
||||||
. "See --install-options for how to force a dangerous slave "
|
. "See SLAVE INSTALL in the docs for more information.\n";
|
||||||
. "install.\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8616,7 +8658,7 @@ sub install {
|
|||||||
# do it now in case there are problems.
|
# do it now in case there are problems.
|
||||||
$next_step->();
|
$next_step->();
|
||||||
init_lib_dir(
|
init_lib_dir(
|
||||||
lib_dir => $o->get('lib'),
|
lib_dir => $lib_dir,
|
||||||
);
|
);
|
||||||
init_spool_dir(
|
init_spool_dir(
|
||||||
spool_dir => $o->get('spool'),
|
spool_dir => $o->get('spool'),
|
||||||
@@ -8997,33 +9039,7 @@ sub get_versions {
|
|||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
my $cxn = $args{Cxn};
|
my $cxn = $args{Cxn};
|
||||||
my $tries = $args{tries} || 1;
|
my $tries = $args{tries} || 1;
|
||||||
my $interval = $args{interval} || sub { sleep 3; };
|
my $interval = $args{interval} || sub { return; };
|
||||||
|
|
||||||
my $have_mysql = 0;
|
|
||||||
if ( $cxn ) {
|
|
||||||
$logger->debug("Connecting to MySQL");
|
|
||||||
foreach my $tryno ( 1..$tries ) {
|
|
||||||
eval {
|
|
||||||
$cxn->connect();
|
|
||||||
};
|
|
||||||
if ( $EVAL_ERROR ) {
|
|
||||||
$logger->debug("Cannot connect to MySQL: $EVAL_ERROR");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$have_mysql = 1;
|
|
||||||
delete $state->{need_mysql_version};
|
|
||||||
last; # success
|
|
||||||
}
|
|
||||||
if ( $tryno < $tries ) {
|
|
||||||
sleep $interval; # failure, try again
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$state->{need_mysql_version} = 1;
|
|
||||||
$logger->warning("Cannot get MySQL version, will try again later");
|
|
||||||
last; # failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# This is currently the actual response from GET v.percona.com
|
# This is currently the actual response from GET v.percona.com
|
||||||
my $fake_response = <<EOL;
|
my $fake_response = <<EOL;
|
||||||
@@ -9032,6 +9048,10 @@ MySQL;mysql_variable;version_comment,version
|
|||||||
Perl;perl_version
|
Perl;perl_version
|
||||||
DBD::mysql;perl_module_version
|
DBD::mysql;perl_module_version
|
||||||
Percona::Toolkit;perl_module_version
|
Percona::Toolkit;perl_module_version
|
||||||
|
JSON;perl_module_version
|
||||||
|
LWP;perl_module_version
|
||||||
|
IO::Socket::SSL;perl_module_version
|
||||||
|
DBD::mysql;perl_module_version
|
||||||
EOL
|
EOL
|
||||||
|
|
||||||
my $items = VersionCheck::parse_server_response(
|
my $items = VersionCheck::parse_server_response(
|
||||||
@@ -9042,12 +9062,39 @@ EOL
|
|||||||
{ name => 'system', id => 0, },
|
{ name => 'system', id => 0, },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
my $have_mysql = -1;
|
||||||
|
if ( !$cxn->dbh || !$cxn->dbh->ping() ) {
|
||||||
|
$logger->debug("Connecting to MySQL");
|
||||||
|
eval {
|
||||||
|
$cxn->connect();
|
||||||
|
};
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
$logger->debug("Cannot connect to MySQL: $EVAL_ERROR");
|
||||||
|
$have_mysql = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$have_mysql = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ( $have_mysql ) {
|
if ( $have_mysql ) {
|
||||||
|
$logger->debug("Have MySQL connection");
|
||||||
my ($name, $id) = VersionCheck::get_instance_id(
|
my ($name, $id) = VersionCheck::get_instance_id(
|
||||||
{ dbh => $cxn->dbh, dsn => $cxn->dsn },
|
{ dbh => $cxn->dbh, dsn => $cxn->dsn },
|
||||||
);
|
);
|
||||||
push @$instances,
|
push @$instances,
|
||||||
{ name => $name, id => $id, dbh => $cxn->dbh, dsn => $cxn->dsn };
|
{ name => $name, id => $id, dbh => $cxn->dbh, dsn => $cxn->dsn };
|
||||||
|
|
||||||
|
# Disconnect MySQL if we connected it.
|
||||||
|
if ( $have_mysql == 1 ) {
|
||||||
|
$logger->debug("Disconnecting MySQL");
|
||||||
|
eval {
|
||||||
|
$cxn->dbh->disconnect();
|
||||||
|
};
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
$logger->debug($EVAL_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
my $versions = VersionCheck::get_versions(
|
my $versions = VersionCheck::get_versions(
|
||||||
@@ -9112,47 +9159,21 @@ sub _safe_mkdir {
|
|||||||
sub check_if_mysql_restarted {
|
sub check_if_mysql_restarted {
|
||||||
my (%args) = @_;
|
my (%args) = @_;
|
||||||
have_required_args(\%args, qw(
|
have_required_args(\%args, qw(
|
||||||
Cxn
|
dbh
|
||||||
)) or die;
|
)) or die;
|
||||||
my $cxn = $args{Cxn};
|
my $dbh = $args{dbh};
|
||||||
|
|
||||||
# Optional args
|
# Optional args
|
||||||
my $uptime = $args{uptime}; # for testing
|
my $uptime = $args{uptime}; # for testing
|
||||||
my $margin = $args{margin} || 5;
|
my $margin = $args{margin} || 5;
|
||||||
|
|
||||||
if ( !$uptime ) {
|
if ( !$uptime ) {
|
||||||
$logger->debug("Connecting to MySQL");
|
my $sql = "SHOW STATUS LIKE 'uptime'";
|
||||||
my $t0 = time;
|
eval {
|
||||||
my $e;
|
(undef, $uptime) = $dbh->selectrow_array($sql);
|
||||||
my $tries = 2;
|
};
|
||||||
my $have_mysql = 0;
|
if ( $EVAL_ERROR ) {
|
||||||
TRY:
|
$logger->error("$sql: $EVAL_ERROR");
|
||||||
foreach my $tryno ( 1..$tries ) {
|
|
||||||
eval {
|
|
||||||
$cxn->connect();
|
|
||||||
};
|
|
||||||
$e = $EVAL_ERROR;
|
|
||||||
if ( $e ) {
|
|
||||||
sleep 3 if $tryno < $tries; # failure, try again
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$have_mysql = 1;
|
|
||||||
last TRY; # success
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ( $have_mysql ) {
|
|
||||||
eval {
|
|
||||||
(undef, $uptime) = $cxn->dbh->selectrow_array("SHOW STATUS LIKE 'uptime'");
|
|
||||||
};
|
|
||||||
if ( $EVAL_ERROR ) {
|
|
||||||
$logger->warning("Cannot check if MySQL restarted because "
|
|
||||||
. "SHOW STATUS query failed: $EVAL_ERROR");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$logger->warning("Cannot check if MySQL restarted because "
|
|
||||||
. "connection to MySQL failed: $e");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9174,6 +9195,7 @@ sub check_if_mysql_restarted {
|
|||||||
. "elapsed=$elapsed_time expected=$exepected_uptime "
|
. "elapsed=$elapsed_time expected=$exepected_uptime "
|
||||||
. "+/- ${margin}s actual=$uptime");
|
. "+/- ${margin}s actual=$uptime");
|
||||||
$state->{mysql_restarted} = ts(time, 1); # 1=UTC
|
$state->{mysql_restarted} = ts(time, 1); # 1=UTC
|
||||||
|
$state->{need_mysql_version} = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9259,14 +9281,13 @@ Usage: pt-agent [OPTIONS]
|
|||||||
pt-agent is the client-side agent for Percona Cloud Tools. It is not
|
pt-agent is the client-side agent for Percona Cloud Tools. It is not
|
||||||
a general command line tool like other tools in Percona Toolkit, it is
|
a general command line tool like other tools in Percona Toolkit, it is
|
||||||
configured and controlled through the web at https://cloud.percona.com.
|
configured and controlled through the web at https://cloud.percona.com.
|
||||||
Please contact Percona or visit https://cloud.percona.com for more information.
|
Visit https://cloud.percona.com for more information and to sign up.
|
||||||
|
|
||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
pt-agent is the client-side agent for Percona Cloud Tools (PCT). It is
|
pt-agent is the client-side agent for Percona Cloud Tools (PCT). It is
|
||||||
controlled and configured through the web app at https://cloud.percona.com.
|
controlled and configured through the web app at https://cloud.percona.com.
|
||||||
An account with Percona is required to use pt-agent. Please contact Percona
|
Visit https://cloud.percona.com for more information and to sign up.
|
||||||
or visit https://cloud.percona.com for more information.
|
|
||||||
|
|
||||||
pt-agent, or "the agent", is a single, unique instance of the tool running
|
pt-agent, or "the agent", is a single, unique instance of the tool running
|
||||||
on a server. Two agents cannot run on the same server (see L<"--pid">).
|
on a server. Two agents cannot run on the same server (see L<"--pid">).
|
||||||
@@ -9274,9 +9295,9 @@ on a server. Two agents cannot run on the same server (see L<"--pid">).
|
|||||||
The agent is a daemon that runs as root. It should be started with
|
The agent is a daemon that runs as root. It should be started with
|
||||||
L<"--daemonize">. It connects periodically to Percona to update
|
L<"--daemonize">. It connects periodically to Percona to update
|
||||||
its configuration and services, and it schedules L<"--run-service"> and
|
its configuration and services, and it schedules L<"--run-service"> and
|
||||||
L<"--send-data"> instances of itself. Other than L<"INSTALLING"> and starting
|
L<"--send-data"> instances of itself using cron. Other than L<"INSTALLING">
|
||||||
the agent locally, all control and configuration is done through the web
|
and starting the agent locally, all control and configuration is done through
|
||||||
at https://cloud.percona.com.
|
the web at https://cloud.percona.com.
|
||||||
|
|
||||||
=head1 INSTALLING
|
=head1 INSTALLING
|
||||||
|
|
||||||
@@ -9295,6 +9316,44 @@ services for agent.
|
|||||||
|
|
||||||
Please contact Percona if you need help installing the agent.
|
Please contact Percona if you need help installing the agent.
|
||||||
|
|
||||||
|
=head2 SLAVE INSTALL
|
||||||
|
|
||||||
|
There are two ways to install pt-agent on a slave. The first and best way
|
||||||
|
is to install the agent on the master so that the L<"MYSQL USER"> is created
|
||||||
|
on the master and replicates to slaves. This is best because it avoids
|
||||||
|
writing to the slave. Then create the C</etc/percona/agent/> directory on
|
||||||
|
the slave and copy in to it C</etc/percona/agent/my.cnf> from the master.
|
||||||
|
Run L<"--install"> on the slave and pt-agent will automatically detect and
|
||||||
|
use the MySQL user and password in C</etc/percona/agent/my.cnf>. Repeat the
|
||||||
|
process for other slaves.
|
||||||
|
|
||||||
|
The second way to install pt-agent on a slave is not safe because it writes
|
||||||
|
directly to the slave: specify L<"--install-options">
|
||||||
|
C<force_dangerous_slave_install> in addition to L<"--install">. As the
|
||||||
|
install option name implies, this is dangerous, but it forces pt-agent
|
||||||
|
to ignore that MySQL is a slave.
|
||||||
|
|
||||||
|
=head2 Percona XtraDB Cluster (PXC) INSTALL
|
||||||
|
|
||||||
|
Installing pt-agent on Percona XtraDB Cluster (PXC) nodes is the same as
|
||||||
|
installing it safely on slaves. First install the agent on any node. This
|
||||||
|
will create the L<"MYSQL USER"> that will replicate to all other nodes.
|
||||||
|
Then create the C</etc/percona/agent/> directory on another node and copy in
|
||||||
|
to it C</etc/percona/agent/my.cnf> from the first node where pt-agent was
|
||||||
|
installed. Run L<"--install"> on the node and pt-agent will automatically
|
||||||
|
detect and use the MySQL user and password in C</etc/percona/agent/my.cnf>.
|
||||||
|
Repeat the process for other nodes.
|
||||||
|
|
||||||
|
=head1 MYSQL USER
|
||||||
|
|
||||||
|
During L<"--install">, pt-agent creates the following MySQL user:
|
||||||
|
|
||||||
|
GRANT SUPER, USAGE ON *.* TO 'pt_agent'@'localhost' IDENTIFIED BY 'pass'
|
||||||
|
|
||||||
|
C<pass> is a random string. MySQL options for the agent are stored in
|
||||||
|
C</etc/percona/agent/my.cnf>. The C<SUPER> privilege is required so that
|
||||||
|
the agent can set global MySQL variables like C<long_query_time>.
|
||||||
|
|
||||||
=head1 EXIT STATUS
|
=head1 EXIT STATUS
|
||||||
|
|
||||||
pt-agent exists zero if no errors or warnings occurred, else it exits non-zero.
|
pt-agent exists zero if no errors or warnings occurred, else it exits non-zero.
|
||||||
@@ -9637,7 +9696,7 @@ pt-agent requires:
|
|||||||
|
|
||||||
=over
|
=over
|
||||||
|
|
||||||
=item * An account with Percona
|
=item * A Percona Cloud Tools account (https://cloud.percona.com)
|
||||||
|
|
||||||
=item * Access to https://cloud-api.percona.com
|
=item * Access to https://cloud-api.percona.com
|
||||||
|
|
||||||
@@ -9688,20 +9747,7 @@ see L<"ENVIRONMENT">.
|
|||||||
=head1 DOWNLOADING
|
=head1 DOWNLOADING
|
||||||
|
|
||||||
Visit L<http://www.percona.com/software/percona-toolkit/> to download the
|
Visit L<http://www.percona.com/software/percona-toolkit/> to download the
|
||||||
latest release of Percona Toolkit. Or, get the latest release from the
|
latest release of Percona Toolkit.
|
||||||
command line:
|
|
||||||
|
|
||||||
wget percona.com/get/percona-toolkit.tar.gz
|
|
||||||
|
|
||||||
wget percona.com/get/percona-toolkit.rpm
|
|
||||||
|
|
||||||
wget percona.com/get/percona-toolkit.deb
|
|
||||||
|
|
||||||
You can also get individual tools from the latest release:
|
|
||||||
|
|
||||||
wget percona.com/get/TOOL
|
|
||||||
|
|
||||||
Replace C<TOOL> with the name of any tool.
|
|
||||||
|
|
||||||
=head1 AUTHORS
|
=head1 AUTHORS
|
||||||
|
|
||||||
|
@@ -119,7 +119,7 @@ sub new {
|
|||||||
|
|
||||||
sub connect {
|
sub connect {
|
||||||
my ( $self, %opts ) = @_;
|
my ( $self, %opts ) = @_;
|
||||||
my $dsn = $self->{dsn};
|
my $dsn = $opts{dsn} || $self->{dsn};
|
||||||
my $dp = $self->{DSNParser};
|
my $dp = $self->{DSNParser};
|
||||||
|
|
||||||
my $dbh = $self->{dbh};
|
my $dbh = $self->{dbh};
|
||||||
@@ -139,6 +139,13 @@ sub connect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$dbh = $self->set_dbh($dbh);
|
$dbh = $self->set_dbh($dbh);
|
||||||
|
if ( $opts{dsn} ) {
|
||||||
|
$self->{dsn} = $dsn;
|
||||||
|
$self->{dsn_name} = $dp->as_string($dsn, [qw(h P S)])
|
||||||
|
|| $dp->as_string($dsn, [qw(F)])
|
||||||
|
|| '';
|
||||||
|
|
||||||
|
}
|
||||||
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
|
PTDEBUG && _d($dbh, 'Connected dbh to', $self->{hostname},$self->{dsn_name});
|
||||||
return $dbh;
|
return $dbh;
|
||||||
}
|
}
|
||||||
|
@@ -250,7 +250,7 @@ sub level_name {
|
|||||||
sub debug {
|
sub debug {
|
||||||
my $self = shift;
|
my $self = shift;
|
||||||
return if $self->online_logging;
|
return if $self->online_logging;
|
||||||
return $self->_log(0, 'DEBUG', 1, @_);
|
return $self->_log(0, 'DEBUG', @_);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub info {
|
sub info {
|
||||||
@@ -287,7 +287,7 @@ sub _set_exit_status {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sub _log {
|
sub _log {
|
||||||
my ($self, $online, $level, $msg, $offline) = @_;
|
my ($self, $online, $level, $msg) = @_;
|
||||||
|
|
||||||
my $ts = ts(time, 1); # 1=UTC
|
my $ts = ts(time, 1); # 1=UTC
|
||||||
my $level_number = level_number($level);
|
my $level_number = level_number($level);
|
||||||
|
54
t/lib/Cxn.t
54
t/lib/Cxn.t
@@ -24,6 +24,8 @@ my $q = new Quoter();
|
|||||||
my $dp = new DSNParser(opts=>$dsn_opts);
|
my $dp = new DSNParser(opts=>$dsn_opts);
|
||||||
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
my $sb = new Sandbox(basedir => '/tmp', DSNParser => $dp);
|
||||||
my $master_dbh = $sb->get_dbh_for('master');
|
my $master_dbh = $sb->get_dbh_for('master');
|
||||||
|
my $slave1_dbh = $sb->get_dbh_for('slave1');
|
||||||
|
my $slave1_dsn = $sb->dsn_for('slave1');
|
||||||
|
|
||||||
if ( !$master_dbh ) {
|
if ( !$master_dbh ) {
|
||||||
plan skip_all => 'Cannot connect to sandbox master';
|
plan skip_all => 'Cannot connect to sandbox master';
|
||||||
@@ -319,6 +321,58 @@ is(
|
|||||||
unlink $sync_file if -f $sync_file;
|
unlink $sync_file if -f $sync_file;
|
||||||
unlink $outfile if -f $outfile;
|
unlink $outfile if -f $outfile;
|
||||||
|
|
||||||
|
# #############################################################################
|
||||||
|
# Re-connect with new DSN.
|
||||||
|
# #############################################################################
|
||||||
|
|
||||||
|
SKIP: {
|
||||||
|
skip "Cannot connect to slave1", 4 unless $slave1_dbh;
|
||||||
|
|
||||||
|
$cxn = make_cxn(
|
||||||
|
dsn_string => 'h=127.1,P=12345,u=msandbox,p=msandbox',
|
||||||
|
);
|
||||||
|
|
||||||
|
$cxn->connect();
|
||||||
|
ok(
|
||||||
|
$cxn->dbh()->ping(),
|
||||||
|
"First connect()"
|
||||||
|
);
|
||||||
|
|
||||||
|
($row) = $cxn->dbh()->selectrow_hashref('SHOW SLAVE STATUS');
|
||||||
|
ok(
|
||||||
|
!defined $row,
|
||||||
|
"First connect() to master"
|
||||||
|
) or diag(Dumper($row));
|
||||||
|
|
||||||
|
$cxn->dbh->disconnect();
|
||||||
|
$cxn->connect(dsn => $dp->parse($slave1_dsn));
|
||||||
|
|
||||||
|
ok(
|
||||||
|
$cxn->dbh()->ping(),
|
||||||
|
"Re-connect connect()"
|
||||||
|
);
|
||||||
|
|
||||||
|
($row) = $cxn->dbh()->selectrow_hashref('SHOW SLAVE STATUS');
|
||||||
|
ok(
|
||||||
|
$row,
|
||||||
|
"Re-connect connect(slave_dsn) to slave"
|
||||||
|
) or diag(Dumper($row));
|
||||||
|
|
||||||
|
$cxn->dbh->disconnect();
|
||||||
|
$cxn->connect();
|
||||||
|
|
||||||
|
ok(
|
||||||
|
$cxn->dbh()->ping(),
|
||||||
|
"Re-re-connect connect()"
|
||||||
|
);
|
||||||
|
|
||||||
|
($row) = $cxn->dbh()->selectrow_hashref('SHOW SLAVE STATUS');
|
||||||
|
ok(
|
||||||
|
$row,
|
||||||
|
"Re-re-connect connect() to slave"
|
||||||
|
) or diag(Dumper($row));
|
||||||
|
}
|
||||||
|
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
# Done.
|
# Done.
|
||||||
# #############################################################################
|
# #############################################################################
|
||||||
|
Reference in New Issue
Block a user