mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-07 12:58:45 +00:00
Add --install-options, handle install on slave, use /etc/percona/agent/my.cnf if it exists.
This commit is contained in:
350
bin/pt-agent
350
bin/pt-agent
@@ -5315,6 +5315,7 @@ sub main {
|
||||
OptionParser => $o,
|
||||
Cxn => $cxn,
|
||||
interactive => $o->get('interactive'),
|
||||
flags => $o->get('install-options'),
|
||||
);
|
||||
return $exit_status;
|
||||
}
|
||||
@@ -8042,9 +8043,11 @@ sub install {
|
||||
have_required_args(\%args, qw(
|
||||
OptionParser
|
||||
Cxn
|
||||
flags
|
||||
)) or die;
|
||||
my $o = $args{OptionParser};
|
||||
my $cxn = $args{Cxn};
|
||||
my $o = $args{OptionParser};
|
||||
my $cxn = $args{Cxn};
|
||||
my $flags = $args{flags};
|
||||
|
||||
# Optional args
|
||||
my $interactive = $args{interactive};
|
||||
@@ -8054,12 +8057,16 @@ sub install {
|
||||
my $agent_my_cnf = '/etc/percona/agent/my.cnf';
|
||||
my $config_file = get_config_file();
|
||||
|
||||
my $stepno = 0;
|
||||
my $step_result;
|
||||
my $stepno = 0;
|
||||
my $skip = 0;
|
||||
my $step_fmt = "Step %d of %d: %s: ";
|
||||
my @steps = (
|
||||
"Verify the user is root",
|
||||
"Check Perl module dependencies",
|
||||
"Connect to MySQL",
|
||||
"Verify the API key",
|
||||
"Connect to MySQL",
|
||||
"Check if MySQL is a slave",
|
||||
"Create a MySQL user for the agent",
|
||||
"Initialize $agent_my_cnf",
|
||||
"Initialize $config_file",
|
||||
@@ -8072,9 +8079,25 @@ sub install {
|
||||
my (%args) = @_;
|
||||
my $repeat = $args{repeat};
|
||||
my $done = $args{done};
|
||||
print "OK\n" if $stepno && !$repeat;
|
||||
# Result of the previous step
|
||||
my $result = 'OK';
|
||||
if ( $step_result ) {
|
||||
$result = $step_result;
|
||||
$step_result = undef;
|
||||
}
|
||||
print "$result\n" if $stepno && !$repeat;
|
||||
return if $done;
|
||||
printf "Step %d of %d: %s: ",
|
||||
while ( $skip ) {
|
||||
printf $step_fmt,
|
||||
$stepno + ($repeat ? 0 : 1),
|
||||
$n_steps,
|
||||
$steps[$repeat ? $stepno - 1 : $stepno];
|
||||
$stepno++;
|
||||
print "SKIP\n";
|
||||
$skip--;
|
||||
}
|
||||
# This step
|
||||
printf $step_fmt,
|
||||
$stepno + ($repeat ? 0 : 1),
|
||||
$n_steps,
|
||||
$steps[$repeat ? $stepno - 1 : $stepno];
|
||||
@@ -8095,7 +8118,55 @@ sub install {
|
||||
$next_step->();
|
||||
exit 1 if check_deps();
|
||||
|
||||
# 2. Must be able to connect to MySQL to create pt_agent user.
|
||||
# Must have a valid API key.
|
||||
my $api_key = $o->get('api-key');
|
||||
my $client;
|
||||
my $entry_links;
|
||||
if ( $flags->{offline} ) {
|
||||
$skip++;
|
||||
}
|
||||
else {
|
||||
$next_step->();
|
||||
if ( !$api_key ) {
|
||||
print "\n";
|
||||
if ( $interactive || -t STDIN ) {
|
||||
while ( !$api_key ) {
|
||||
print "Enter your API key: ";
|
||||
$api_key = <STDIN>;
|
||||
chomp($api_key) if $api_key;
|
||||
if ( !$api_key || length($api_key) < 32 ) {
|
||||
warn "Invalid API key; it should be at least 32 characters long. Please try again.\n";
|
||||
$api_key = '';
|
||||
}
|
||||
}
|
||||
$next_step->(repeat => 1); # repeat
|
||||
}
|
||||
else {
|
||||
die "Please specify your --api-key.\n";
|
||||
}
|
||||
}
|
||||
eval {
|
||||
($client, $entry_links) = get_api_client(
|
||||
api_key => $api_key,
|
||||
interval => sub { return; },
|
||||
tries => 1,
|
||||
);
|
||||
};
|
||||
if ( my $e = $EVAL_ERROR ) {
|
||||
die "Sorry, an error occurred while verifying the API key: $e";
|
||||
}
|
||||
elsif ( !$entry_links ) {
|
||||
if ( $client->response->code && $client->response->code == 403 ) {
|
||||
die "Sorry, the API key $api_key is not valid. Please check the API key and try again.\n";
|
||||
}
|
||||
else {
|
||||
my $err = $client->response->message || 'Unknown error';
|
||||
die "Sorry, an error occured while verifying the API key: $err\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Must be able to connect to MySQL to create pt_agent user.
|
||||
$next_step->();
|
||||
eval {
|
||||
$cxn->connect();
|
||||
@@ -8108,56 +8179,130 @@ sub install {
|
||||
. "with sufficient privileges to create MySQL users.\n";
|
||||
}
|
||||
|
||||
# 3. Must have a valid API key.
|
||||
# Check if MySQL is a slave
|
||||
$next_step->();
|
||||
my $api_key = $o->get('api-key');
|
||||
if ( !$api_key ) {
|
||||
print "\n";
|
||||
if ( $interactive || -t STDIN ) {
|
||||
while ( !$api_key ) {
|
||||
print "Enter your API key: ";
|
||||
$api_key = <STDIN>;
|
||||
chomp($api_key) if $api_key;
|
||||
if ( !$api_key || length($api_key) < 32 ) {
|
||||
warn "Invalid API key; it should be at least 32 characters long. Please try again.\n";
|
||||
$api_key = '';
|
||||
}
|
||||
}
|
||||
$next_step->(repeat => 1); # repeat
|
||||
}
|
||||
else {
|
||||
die "Please specify your --api-key.\n";
|
||||
}
|
||||
my $slave = $cxn->dbh->selectrow_hashref("SHOW SLAVE STATUS");
|
||||
if ( $slave ) {
|
||||
$step_result = 'YES, TO MASTER ' . $slave->{master_host} || '?';
|
||||
}
|
||||
my $client;
|
||||
my $entry_links;
|
||||
eval {
|
||||
($client, $entry_links) = get_api_client(
|
||||
api_key => $api_key,
|
||||
interval => sub { return; },
|
||||
tries => 1,
|
||||
);
|
||||
};
|
||||
if ( my $e = $EVAL_ERROR ) {
|
||||
die "Sorry, an error occurred while verifying the API key: $e";
|
||||
}
|
||||
elsif ( !$entry_links ) {
|
||||
if ( $client->response->code && $client->response->code == 403 ) {
|
||||
die "Sorry, the API key $api_key is not valid. Please check the API key and try again.\n";
|
||||
}
|
||||
else {
|
||||
my $err = $client->response->message || 'Unknown error';
|
||||
die "Sorry, an error occured while verifying the API key: $err\n";
|
||||
}
|
||||
else {
|
||||
$step_result = 'NO';
|
||||
}
|
||||
|
||||
# ########################################################################
|
||||
# Do the install
|
||||
# ########################################################################
|
||||
|
||||
# 4. Create/update the pt_agent MySQL user. pt-agent does not and should
|
||||
# not run as a privileged MySQL user.
|
||||
# Create a MySQL user for the agent
|
||||
$next_step->();
|
||||
if ( -f $agent_my_cnf ) {
|
||||
$step_result = "NO, USE EXISTING $agent_my_cnf";
|
||||
}
|
||||
else {
|
||||
if ( !$slave ) { # master
|
||||
create_mysql_user($cxn, $agent_my_cnf);
|
||||
}
|
||||
else { # slave
|
||||
if ( $flags->{force_dangerous_slave_install} ) {
|
||||
create_mysql_user($cxn, $agent_my_cnf);
|
||||
}
|
||||
else {
|
||||
die "Sorry, cannot install the agent because MySQL is a slave "
|
||||
. "and $agent_my_cnf does not exist. It is not safe to "
|
||||
. "write to a slave, so a MySQL user for the agent cannot "
|
||||
. "be created. First install the agent on the master, then "
|
||||
. "copy $agent_my_cnf from the master server to this server. "
|
||||
. "See --install-options for how to force a dangerous slave "
|
||||
. "install.\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Save the API key and defaults file in ~/.pt-agent.conf.
|
||||
$next_step->();
|
||||
eval {
|
||||
write_to_file(
|
||||
data => "api-key=$api_key\ndefaults-file=$agent_my_cnf\n",
|
||||
file => $config_file,
|
||||
);
|
||||
};
|
||||
if ( $EVAL_ERROR ) {
|
||||
die "Sorry, an error occured while initializing $config_file: "
|
||||
. $EVAL_ERROR;
|
||||
}
|
||||
|
||||
# Init --lib and --spool. pt-agent would do this itself, but we'll
|
||||
# do it now in case there are problems.
|
||||
$next_step->();
|
||||
init_lib_dir(
|
||||
lib_dir => $o->get('lib'),
|
||||
);
|
||||
init_spool_dir(
|
||||
spool_dir => $o->get('spool'),
|
||||
);
|
||||
|
||||
# 8. Start the agent, don't run it yet. Normally this forks in
|
||||
# anticipation of run_agent() being called next, but we don't do
|
||||
# this during install; we run the agent manually later.
|
||||
if ( $flags->{offline} ) {
|
||||
$skip++; # Init agent
|
||||
$skip++; # Run agent
|
||||
}
|
||||
else {
|
||||
$next_step->();
|
||||
my $running = eval {
|
||||
start_agent(
|
||||
api_key => $api_key,
|
||||
lib_dir => $o->get('lib'),
|
||||
Cxn => $cxn,
|
||||
client => $client,
|
||||
entry_links => $entry_links,
|
||||
agent_uuid => $o->get('agent-uuid'),
|
||||
daemonize => 0,
|
||||
pid_file => undef,
|
||||
log_file => undef,
|
||||
interval => sub { sleep 2; },
|
||||
tries => 2,
|
||||
);
|
||||
};
|
||||
if ( $EVAL_ERROR ) {
|
||||
die "Sorry, an error occurred while starting the agent: $EVAL_ERROR";
|
||||
}
|
||||
|
||||
# 9. Run the agent daemon. If all the previous worked, the agent
|
||||
# should be able to start without problems. It will get and apply
|
||||
# the default config, then get and apply any services (probably won't
|
||||
# have any yet).
|
||||
$next_step->();
|
||||
my $env = env_vars();
|
||||
my $cmd = "$env $FindBin::Bin/pt-agent --daemonize";
|
||||
my $ret = system($cmd);
|
||||
if ( $ret >> 8 ) {
|
||||
die "Sorry, an error occured while starting pt-agent.\n";
|
||||
}
|
||||
}
|
||||
|
||||
# ########################################################################
|
||||
# Done installing
|
||||
# ########################################################################
|
||||
$next_step->(done => 1);
|
||||
|
||||
if ( $flags->{offline} ) {
|
||||
}
|
||||
else {
|
||||
my $hostname = `hostname`;
|
||||
chomp($hostname);
|
||||
|
||||
print "\nThe agent has been installed and started, but it is not running any services yet. "
|
||||
. "Go to https://cloud.percona.com/agents#$hostname to enable services for this agent.\n\n";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sub create_mysql_user {
|
||||
my ($cxn, $agent_my_cnf) = @_;
|
||||
|
||||
my $random_pass = pseudo_random_password();
|
||||
my $sql = "GRANT SUPER,USAGE ON *.* TO 'pt_agent'\@'localhost' "
|
||||
. "IDENTIFIED BY '$random_pass'";
|
||||
@@ -8170,24 +8315,21 @@ sub install {
|
||||
}
|
||||
$cxn->dbh->disconnect();
|
||||
|
||||
# 5. Save the pt_agent MySQL user info in /etc/percona/agent/my.cnf.
|
||||
# Init $agent_my_cnf
|
||||
# We could set user= and pass= in ~/.pt-agent.conf, but each new agent
|
||||
# has a different MySQL password but shares the same default agent
|
||||
# config, so if we set pass=foo, the next agent would set it to
|
||||
# pass=bar, etc. Instead, every agent sets/uses
|
||||
# defaults-file=/etc/percona/agent/my.cnf in the default config, but
|
||||
# the contents of that file is different for each agent.
|
||||
$next_step->();
|
||||
|
||||
if ( !-d '/etc/percona' ) {
|
||||
_safe_mkdir('/etc/percona');
|
||||
}
|
||||
if ( !-d '/etc/percona/agent' ) {
|
||||
_safe_mkdir('/etc/percona/agent');
|
||||
}
|
||||
my $my_cnf = "[client]
|
||||
user=pt_agent
|
||||
pass=$random_pass
|
||||
";
|
||||
my $my_cnf = "[client]\nuser=pt_agent\npass=$random_pass\n";
|
||||
my $dsn = $cxn->dsn;
|
||||
if ( $dsn->{h} ) {
|
||||
$my_cnf .= "host=$dsn->{h}\n";
|
||||
@@ -8207,79 +8349,7 @@ pass=$random_pass
|
||||
if ( $EVAL_ERROR ) {
|
||||
die "Sorry, an error occured while initializing $agent_my_cnf: "
|
||||
. $EVAL_ERROR;
|
||||
}
|
||||
|
||||
# 6. Save the API key and defaults file in ~/.pt-agent.conf.
|
||||
$next_step->();
|
||||
eval {
|
||||
write_to_file(
|
||||
data =>
|
||||
"api-key=$api_key
|
||||
defaults-file=$agent_my_cnf
|
||||
",
|
||||
file => $config_file,
|
||||
);
|
||||
};
|
||||
if ( $EVAL_ERROR ) {
|
||||
die "Sorry, an error occured while initializing $config_file: "
|
||||
. $EVAL_ERROR;
|
||||
}
|
||||
|
||||
# 7. Init --lib and --spool. pt-agent would do this itself, but we'll
|
||||
# do it now in case there are problems.
|
||||
$next_step->();
|
||||
init_lib_dir(
|
||||
lib_dir => $o->get('lib'),
|
||||
);
|
||||
init_spool_dir(
|
||||
spool_dir => $o->get('spool'),
|
||||
);
|
||||
|
||||
# 8. Start the agent, don't run it yet. Normally this forks in
|
||||
# anticipation of run_agent() being called next, but we don't do
|
||||
# this during install; we run the agent manually later.
|
||||
$next_step->();
|
||||
my $running = eval {
|
||||
start_agent(
|
||||
api_key => $api_key,
|
||||
lib_dir => $o->get('lib'),
|
||||
Cxn => $cxn,
|
||||
client => $client,
|
||||
entry_links => $entry_links,
|
||||
agent_uuid => $o->get('agent-uuid'),
|
||||
daemonize => 0,
|
||||
pid_file => undef,
|
||||
log_file => undef,
|
||||
interval => sub { sleep 2; },
|
||||
tries => 2,
|
||||
);
|
||||
};
|
||||
if ( $EVAL_ERROR ) {
|
||||
die "Sorry, an error occurred while starting the agent: $EVAL_ERROR";
|
||||
}
|
||||
|
||||
# 9. Run the agent daemon. If all the previous worked, the agent
|
||||
# should be able to start without problems. It will get and apply
|
||||
# the default config, then get and apply any services (probably won't
|
||||
# have any yet).
|
||||
$next_step->();
|
||||
my $env = env_vars();
|
||||
my $cmd = "$env $FindBin::Bin/pt-agent --daemonize";
|
||||
my $ret = system($cmd);
|
||||
if ( $ret >> 8 ) {
|
||||
die "Sorry, an error occured while starting pt-agent.\n";
|
||||
}
|
||||
|
||||
# ########################################################################
|
||||
# Done installing
|
||||
# ########################################################################
|
||||
$next_step->(done => 1);
|
||||
|
||||
my $hostname = `hostname`;
|
||||
chomp($hostname);
|
||||
|
||||
print "\nThe agent has been installed and started, but it is not running any services yet. "
|
||||
. "Go to https://cloud.percona.com/agents#$hostname to enable services for this agent.\n\n";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -8733,6 +8803,32 @@ MySQL host.
|
||||
|
||||
Install pt-agent as root.
|
||||
|
||||
=item --install-options
|
||||
|
||||
type: Hash
|
||||
|
||||
Comma-separated list of L<"--install"> options. Options are:
|
||||
|
||||
=over
|
||||
|
||||
=item offline
|
||||
|
||||
Do not verify the API key or start the agent.
|
||||
|
||||
=item force_dangerous_slave_install
|
||||
|
||||
Like the option's name suggests: this forces a dangerous slave install,
|
||||
so you should not use this option unless you are aware of the potential
|
||||
consequences. To install the agent on a slave, C</etc/percona/agent/my.cnf>
|
||||
must exist because it is not safe to create the agent's MySQL user on
|
||||
a slave. The agent should be installed on the master first, then
|
||||
C</etc/percona/agent/my.cnf> copied from the master server to the slave
|
||||
server. Using this option forces the agent to create the agent's MySQL
|
||||
user on the slave. B<WARNING>: writing to a slave is dangerous and could
|
||||
cause replication to crash.
|
||||
|
||||
=back
|
||||
|
||||
=item --interactive
|
||||
|
||||
Run in interactive mode (disables L<"--[no]log-api">).
|
||||
|
Reference in New Issue
Block a user