Add --install-options, handle install on slave, use /etc/percona/agent/my.cnf if it exists.

This commit is contained in:
Daniel Nichter
2013-06-15 17:06:44 -07:00
parent b29c7a8064
commit 0fa7aa4ddb

View File

@@ -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">).