mirror of
https://github.com/percona/percona-toolkit.git
synced 2025-09-17 17:27:57 +00:00
Factor get_agent_pid() from stop_agent(). Implement Percona::Agent::Exception classes. Implement --status/agent_status().
This commit is contained in:
345
bin/pt-agent
345
bin/pt-agent
@@ -4731,6 +4731,92 @@ sub _d {
|
|||||||
# End CleanupTask package
|
# End CleanupTask package
|
||||||
# ###########################################################################
|
# ###########################################################################
|
||||||
|
|
||||||
|
# ###########################################################################
|
||||||
|
# Percona::Agent::Exception::*
|
||||||
|
# ###########################################################################
|
||||||
|
|
||||||
|
{
|
||||||
|
package Percona::Agent::Exception::PIDNotFound;
|
||||||
|
|
||||||
|
use Lmo;
|
||||||
|
use overload '""' => \&as_string;
|
||||||
|
|
||||||
|
has 'pid_file' => (
|
||||||
|
is => 'ro',
|
||||||
|
isa => 'Maybe[Str]',
|
||||||
|
required => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
sub as_string {
|
||||||
|
my $self = shift;
|
||||||
|
return sprintf "PID file %s does not exist and no matching "
|
||||||
|
. "process was found in ps", $self->pid_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
no Lmo;
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
package Percona::Agent::Exception::NoPID;
|
||||||
|
|
||||||
|
use Lmo;
|
||||||
|
use overload '""' => \&as_string;
|
||||||
|
|
||||||
|
has 'pid_file' => (
|
||||||
|
is => 'ro',
|
||||||
|
isa => 'Maybe[Str]',
|
||||||
|
required => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
has 'pid_file_is_empty' => (
|
||||||
|
is => 'ro',
|
||||||
|
isa => 'Bool',
|
||||||
|
required => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
sub as_string {
|
||||||
|
my $self = shift;
|
||||||
|
if ( $self->pid_file_is_empty ) {
|
||||||
|
return sprintf "PID file %s is empty", $self->pid_file;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return sprintf "PID file %s does not exist and parsing ps output "
|
||||||
|
. "failed", $self->pid_file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
no Lmo;
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
package Percona::Agent::Exception::PIDNotRunning;
|
||||||
|
|
||||||
|
use Lmo;
|
||||||
|
use overload '""' => \&as_string;
|
||||||
|
|
||||||
|
has 'pid' => (
|
||||||
|
is => 'ro',
|
||||||
|
isa => 'Str',
|
||||||
|
required => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
sub as_string {
|
||||||
|
my $self = shift;
|
||||||
|
return sprintf "PID is not running", $self->pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
no Lmo;
|
||||||
|
1;
|
||||||
|
}
|
||||||
|
|
||||||
|
BEGIN {
|
||||||
|
$INC{'Percona/Agent/Exception/PIDNotFound.pm'} = __FILE__;
|
||||||
|
$INC{'Percona/Agent/Exception/NoPID.pm'} = __FILE__;
|
||||||
|
$INC{'Percona/Agent/Exception/PIDNotRunning.pm'} = __FILE__;
|
||||||
|
}
|
||||||
|
|
||||||
# ###########################################################################
|
# ###########################################################################
|
||||||
# This is a combination of modules and programs in one -- a runnable module.
|
# This is a combination of modules and programs in one -- a runnable module.
|
||||||
# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last
|
# http://www.perl.com/pub/a/2006/07/13/lightning-articles.html?page=last
|
||||||
@@ -4763,6 +4849,9 @@ use Percona::WebAPI::Resource::Agent;
|
|||||||
use Percona::WebAPI::Resource::Config;
|
use Percona::WebAPI::Resource::Config;
|
||||||
use Percona::WebAPI::Resource::Service;
|
use Percona::WebAPI::Resource::Service;
|
||||||
use Percona::WebAPI::Representation;
|
use Percona::WebAPI::Representation;
|
||||||
|
use Percona::Agent::Exception::PIDNotFound;
|
||||||
|
use Percona::Agent::Exception::NoPID;
|
||||||
|
use Percona::Agent::Exception::PIDNotRunning;
|
||||||
|
|
||||||
Percona::Toolkit->import(qw(_d Dumper have_required_args));
|
Percona::Toolkit->import(qw(_d Dumper have_required_args));
|
||||||
Percona::WebAPI::Representation->import(qw(as_json as_config));
|
Percona::WebAPI::Representation->import(qw(as_json as_config));
|
||||||
@@ -4843,7 +4932,11 @@ sub main {
|
|||||||
# --status, --stop, and --reset
|
# --status, --stop, and --reset
|
||||||
# ########################################################################
|
# ########################################################################
|
||||||
if ( $o->get('status') ) {
|
if ( $o->get('status') ) {
|
||||||
agent_status();
|
agent_status(
|
||||||
|
api_key => $o->get('api-key'),
|
||||||
|
pid_file => $o->get('pid'),
|
||||||
|
lib_dir => $o->get('lib'),
|
||||||
|
);
|
||||||
return $exit_status;
|
return $exit_status;
|
||||||
}
|
}
|
||||||
elsif ( $o->get('stop') ) {
|
elsif ( $o->get('stop') ) {
|
||||||
@@ -6698,6 +6791,100 @@ CONTENT
|
|||||||
# ################################## #
|
# ################################## #
|
||||||
|
|
||||||
sub agent_status {
|
sub agent_status {
|
||||||
|
my (%args) = @_;
|
||||||
|
|
||||||
|
have_required_args(\%args, qw(
|
||||||
|
pid_file
|
||||||
|
lib_dir
|
||||||
|
)) or die;
|
||||||
|
my $pid_file = $args{pid_file};
|
||||||
|
my $lib_dir = $args{lib_dir};
|
||||||
|
|
||||||
|
# Optional args
|
||||||
|
my $api_key = $args{api_key};
|
||||||
|
my $crontab_list = defined $args{crontab_list} ? $args{crontab_list}
|
||||||
|
: `crontab -l 2>/dev/null`;
|
||||||
|
my $bin_dir = defined $args{bin_dir} ? $args{bin_dir}
|
||||||
|
: "$FindBin::Bin/";
|
||||||
|
|
||||||
|
# Check if pt-agent is running.
|
||||||
|
my $pid = eval {
|
||||||
|
get_agent_pid(
|
||||||
|
pid_file => $pid_file,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if ( my $e = $EVAL_ERROR ) {
|
||||||
|
if ( !blessed($e) ) {
|
||||||
|
_warn("Sorry, an error occured while getting the pt-agent PID: $e");
|
||||||
|
}
|
||||||
|
elsif ( $e->isa('Percona::Agent::Exception::PIDNotFound') ) {
|
||||||
|
_warn("$e. If pt-agent is still running, use kill -ABRT to force "
|
||||||
|
. "it to stop.");
|
||||||
|
}
|
||||||
|
elsif ( $e->isa('Percona::Agent::Exception::PIDNotRunning') ) {
|
||||||
|
_warn("$e. pt-agent may have stopped unexpectedly or crashed.");
|
||||||
|
}
|
||||||
|
else { # unhandled exception
|
||||||
|
_warn("Sorry, an unknown exception occured while getting "
|
||||||
|
. "the pt-agent PID: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_info("pt-agent is running as PID $pid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( $api_key ) {
|
||||||
|
_info("API key: " . ($api_key || ''));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_warn("No API key is set");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the agent's info.
|
||||||
|
if ( -f "$lib_dir/agent" ) {
|
||||||
|
my $agent = json_decode(slurp("$lib_dir/agent"));
|
||||||
|
foreach my $attrib ( qw(uuid hostname username) ) {
|
||||||
|
_info("Agent $attrib: " . ($agent->{$attrib} || ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_warn("$lib_dir/agent does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse pt-agent lines from crontab to see what's scheduled/running.
|
||||||
|
my %scheduled = map {
|
||||||
|
my $line = $_;
|
||||||
|
my ($service) = $line =~ m/pt-agent (?:--run-service|--send-data) (\S+)/;
|
||||||
|
$service => 1;
|
||||||
|
}
|
||||||
|
grep { $_ =~ m/pt-agent (?:--run-service|--send-data)/ }
|
||||||
|
split("\n", $crontab_list);
|
||||||
|
|
||||||
|
if ( -d "$lib_dir/services" ) {
|
||||||
|
_info("Sevices:");
|
||||||
|
SERVICE:
|
||||||
|
foreach my $service_file ( glob "$lib_dir/services/*" ) {
|
||||||
|
my $service = eval {
|
||||||
|
json_decode(slurp($service_file));
|
||||||
|
};
|
||||||
|
if ( $EVAL_ERROR ) {
|
||||||
|
_warn("$service_file is corrupt");
|
||||||
|
next SERVICE;
|
||||||
|
}
|
||||||
|
next if $service->meta; # only real services
|
||||||
|
if ( $scheduled{$service->name} ) {
|
||||||
|
_info($service->name . " is running");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_warn($service->name . " is not running, check $lib_dir/crontab.err");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_warn("$lib_dir/services does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub stop_agent {
|
sub stop_agent {
|
||||||
@@ -6714,47 +6901,70 @@ sub stop_agent {
|
|||||||
my $bin_dir = defined $args{bin_dir} ? $args{bin_dir}
|
my $bin_dir = defined $args{bin_dir} ? $args{bin_dir}
|
||||||
: "$FindBin::Bin/";
|
: "$FindBin::Bin/";
|
||||||
|
|
||||||
if ( !-f $pid_file ) {
|
# Get the agent's PID and kill it. If the PID file doesn't
|
||||||
_info("PID file $pid_file does not exist; if pt-agent is running, "
|
# exist for some reason, get_agent_pid() will attempt to find
|
||||||
. "use kill -ABRT to force it to stop.");
|
# pt-agent --daemonize in ps. And if pt-agent doesn't respond
|
||||||
|
# to the TERM signal after a short while, we kill it with
|
||||||
|
# the KILL signal.
|
||||||
|
my $pid = eval {
|
||||||
|
get_agent_pid(
|
||||||
|
pid_file => $pid_file,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
if ( my $e = $EVAL_ERROR ) {
|
||||||
|
if ( !blessed($e) ) {
|
||||||
|
_warn("Sorry, an error occured while getting the pt-agent PID: $e");
|
||||||
|
}
|
||||||
|
elsif ( $e->isa('Percona::Agent::Exception::PIDNotFound') ) {
|
||||||
|
_warn("$e. If pt-agent is still running, use kill -ABRT to force "
|
||||||
|
. "it to stop.");
|
||||||
|
}
|
||||||
|
elsif ( $e->isa('Percona::Agent::Exception::PIDNotRunning') ) {
|
||||||
|
_warn("$e. pt-agent may have stopped unexpectedly or crashed.");
|
||||||
|
}
|
||||||
|
else { # unhandled exception
|
||||||
|
_warn("Sorry, an unknown exception occured while getting "
|
||||||
|
. "the pt-agent PID: $e");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
chop(my $pid = slurp($pid_file));
|
_info("Stopping pt-agent...");
|
||||||
_info("pt-agent PID: $pid");
|
kill 15, $pid;
|
||||||
my $alive = kill 0, $pid;
|
my $running;
|
||||||
if ( !$alive ) {
|
for (1..5) {
|
||||||
_warn("pt-agent is running, or not responding to signals. "
|
$running = kill 0, $pid;
|
||||||
. "Verify that pt-agent has stopped, then remove PID file "
|
last if !$running;
|
||||||
. "$pid_file.");
|
sleep 0.5;
|
||||||
}
|
}
|
||||||
else {
|
$running = kill 0, $pid;
|
||||||
_info("Stopping pt-agent...");
|
if ( $running ) {
|
||||||
kill 15, $pid;
|
_warn("pt-agent did not respond to the TERM signal, using "
|
||||||
for (1..5) {
|
. "the KILL signal...");
|
||||||
$alive = kill 0, $pid;
|
kill 9, $pid;
|
||||||
last unless $alive;
|
for (1..3) {
|
||||||
|
$running = kill 0, $pid;
|
||||||
|
last if !$running;
|
||||||
sleep 0.5;
|
sleep 0.5;
|
||||||
}
|
}
|
||||||
$alive = kill 0, $pid;
|
$running = kill 0, $pid;
|
||||||
if ( $alive ) {
|
# Shouldn't happen:
|
||||||
_warn("pt-agent did not respond to the TERM signal, using "
|
_warn("pt-agent did not response to the KILL signal");
|
||||||
. "the KILL signal...");
|
}
|
||||||
kill 9, $pid;
|
else {
|
||||||
for (1..3) {
|
_info("pt-agent has stopped");
|
||||||
$alive = kill 0, $pid;
|
}
|
||||||
last unless $alive;
|
|
||||||
sleep 0.5;
|
# pt-agent should remove its own PID file, but in case it didn't,
|
||||||
}
|
# (e.g we had to kill -9 it), we remove the PID file manually.
|
||||||
$alive = kill 0, $pid;
|
if ( -f $pid_file ) {
|
||||||
# Shouldn't happen:
|
unlink $pid_file
|
||||||
_warn("pt-agent did not response to the KILL signal");
|
or _warn("Cannot remove $pid_file: $OS_ERROR. Remove "
|
||||||
}
|
. "this file manually.");
|
||||||
else {
|
|
||||||
_info("pt-agent has stopped");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Un-schedule all services, i.e. remove them from the user's crontab,
|
||||||
|
# leaving the user's other tasks untouched.
|
||||||
if ( -f "$lib_dir/crontab" ) {
|
if ( -f "$lib_dir/crontab" ) {
|
||||||
_info("Removing all services from crontab...");
|
_info("Removing all services from crontab...");
|
||||||
eval {
|
eval {
|
||||||
@@ -6772,41 +6982,84 @@ sub stop_agent {
|
|||||||
_info("$lib_dir/crontab does not exist, no services to remove from crontab");
|
_info("$lib_dir/crontab does not exist, no services to remove from crontab");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Stop all real services by running their stop-<service> meta-service.
|
||||||
|
# If a real service doesn't have a stop-<service> meta-service, then
|
||||||
|
# presumably nothing needs to be done to stop it other than un-scheduling
|
||||||
|
# it, which we've already done.
|
||||||
if ( -d "$lib_dir/services" ) {
|
if ( -d "$lib_dir/services" ) {
|
||||||
my $env_vars = env_vars();
|
my $env_vars = env_vars();
|
||||||
|
|
||||||
_info("Stopping all services...");
|
|
||||||
SERVICE:
|
SERVICE:
|
||||||
foreach my $file ( glob "$lib_dir/services/stop-*" ) {
|
foreach my $file ( glob "$lib_dir/services/stop-*" ) {
|
||||||
my $service = basename($file);
|
my $service = basename($file);
|
||||||
my $disable_log = "$lib_dir/logs/$service.stop";
|
my $stop_log = "$lib_dir/logs/$service.stop";
|
||||||
my $cmd = ($env_vars ? "$env_vars " : '')
|
my $cmd = ($env_vars ? "$env_vars " : '')
|
||||||
. "${bin_dir}pt-agent --run-service $service"
|
. "${bin_dir}pt-agent --run-service $service"
|
||||||
. " </dev/null"
|
. " </dev/null"
|
||||||
. " >$disable_log 2>&1";
|
. " >$stop_log 2>&1";
|
||||||
_info("Running $cmd");
|
_info("Stopping $service...");
|
||||||
PTDEBUG && _d($cmd);
|
PTDEBUG && _d($cmd);
|
||||||
system($cmd);
|
system($cmd);
|
||||||
my $cmd_exit_status = $CHILD_ERROR >> 8;
|
my $cmd_exit_status = $CHILD_ERROR >> 8;
|
||||||
if ( $cmd_exit_status != 0 ) {
|
if ( $cmd_exit_status != 0 ) {
|
||||||
my $err = slurp($disable_log);
|
my $err = slurp($stop_log);
|
||||||
_warn("Error stopping $service: " . ($err || ''));
|
_warn("Error stopping $service: " . ($err || ''));
|
||||||
next SERVICE;
|
next SERVICE;
|
||||||
}
|
}
|
||||||
unlink $disable_log
|
unlink $stop_log
|
||||||
or _warn("Cannot remove $disable_log: $OS_ERROR");
|
or _warn("Cannot remove $stop_log: $OS_ERROR");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
_info("$lib_dir/services does not exist, no services to disable")
|
_info("$lib_dir/services does not exist, no services to stop")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# TODO: kill --lib/pids/*
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub reset_agent {
|
sub reset_agent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_agent_pid {
|
||||||
|
my (%args) = @_;
|
||||||
|
my $pid_file = $args{pid_file};
|
||||||
|
|
||||||
|
my $pid;
|
||||||
|
if ( -f $pid_file ) {
|
||||||
|
PTDEBUG && _d('Reading PID from', $pid_file);
|
||||||
|
chop($pid = slurp($pid_file));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
my $ps_output = `ps ax | grep 'pt-agent --daemonize | grep -v grep`;
|
||||||
|
PTDEBUG && _d('Reading PID from ps', $ps_output);
|
||||||
|
if ( !$ps_output ) {
|
||||||
|
die Percona::Agent::Exception::PIDNotFound->new(
|
||||||
|
pid_file => $pid_file,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
# Match the first digits, which should be the PID.
|
||||||
|
($pid) =~ $ps_output =~ m/(\d+)/;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !$pid ) {
|
||||||
|
die Percona::Agent::Exception::NoPID->new(
|
||||||
|
pid_file => $pid_file,
|
||||||
|
pid_file_is_empty => -f $pid_file,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
my $running = kill 0, $pid;
|
||||||
|
if ( !$running ) {
|
||||||
|
die Percona::Agent::Exception::PIDNotRunning->new(
|
||||||
|
pid => $pid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pid;
|
||||||
|
}
|
||||||
|
|
||||||
# ############## #
|
# ############## #
|
||||||
# --install subs #
|
# --install subs #
|
||||||
# ############## #
|
# ############## #
|
||||||
|
Reference in New Issue
Block a user