I have a perl script in which i want to first check if it possible to ssh into the server before i actually do ssh. So if it possible to ssh, then go ahead and ssh into that server, otherwise handle this exception appropriately.
So i have this script that goes through a textfile with a list of databases, and tries to ssh into each server to perform a bash command that checks if the server is performing MySQL SST or not:
#!/usr/bin/perl -w
use strict;
use warnings;
use 5.010;
use DBI;
use Encode;
use IO::File;
use JSON;
use utf8;
BEGIN {
binmode STDERR, ':utf8';
binmode STDIN, ':utf8';
binmode STDOUT, ':utf8';
}
my $time = localtime();
my $file = '/opt/db-servers/db.txt';
open my $info, $file or die "Could not open $file: $!";
while( my $hostname = <$info>) {
chomp( $hostname );
my $xtrabk_check = `ssh $hostname ps -ef |grep mysql | grep wsrep_sst_xtrabackup-v2`;
my $role_check = `ssh $hostname ps -ef |grep mysql | grep donor`;
my $error_log = `ssh $hostname ps -ef |grep mysql`;
if ( $xtrabk_check ne "" ){
if ( $role_check ne "" ){
my $cmd ="curl -vs -o /dev/null -X POST --data-urlencode 'payload={\"channel\": \"#db-wsrep-status-log\", \"username\": \"db-wsrep-status-log\", \"text\": \"$time: $hostname: --role Donor \n```$error_log```\", \"icon_emoji\": \":scorpion:\"}' 2>&1 /dev/null https://hooks.slack.com/services/GVadasdd/B6LSMF5GV/BIApnzoIldfdsrw343wf";
system($cmd);
}
else {
my $cmd ="curl -vs -o /dev/null -X POST --data-urlencode 'payload={\"channel\": \"#db-wsrep-status-log\", \"username\": \"db-wsrep-status-log\", \"text\": \"$time: $hostname: State transfer in progress, setting sleep higher mysqld \n```$error_log```\", \"icon_emoji\": \":scorpion:\"}' 2>&1 https://hooks.slack.com/services/GVadasdd/B6LSMF5GV/BIApnzoIldfdsrw343wf";
system($cmd);
}
}
}
close $info;
So probably before performing the three lines below, i want to be able to know if that server is down (hence cannot ssh into it) or not (then ssh into it with the commands below):
my $xtrabk_check = `ssh $hostname ps -ef |grep mysql | grep wsrep_sst_xtrabackup-v2`;
my $role_check = `ssh $hostname ps -ef |grep mysql | grep donor`;
my $error_log = `ssh $hostname ps -ef |grep mysql`;
How can i go about achieving this? By the way, the slack URL have been altered so security worries there.
By far the most reliable way to determine whether you can ssh into a server or not is to actually attempt to ssh into it. Just attempt to connect and, if it fails, report the failure and abort any further attempts:
use Try::Tiny;
# Declaring them here so they don't disappear at the end of the try block
my ($xtrabk_check, $role_check, $error_log);
try {
$xtrabk_check = `ssh $hostname ps -ef |grep mysql | grep wsrep_sst_xtrabackup-v2`;
$role_check = `ssh $hostname ps -ef |grep mysql | grep donor`;
$error_log = `ssh $hostname ps -ef |grep mysql`;
} catch {
die "Failed to connect to ssh: $_";
}
Instead of running ssh directly, use any of the Perl modules supporting SSH (for instance, Net::OpenSSH). That would allow you to connect to the remote host and run as many commands as you need over that single connection and it will detect any connection error and report it appropriately.
You can also parallelize the operation using Net::OpenSSH::Parallel.
Related
Perl script is returning incorrect value of zomibie process on Linux box
my $threshold = 5;
$number_of_defuncts = `ps -ef | grep defunct |grep -v grep|wc -l`;
if ( $number_of_defuncts > $threshold )
{
print("number of defunct is [$number_of_defuncts] \n");
}
WHen manually checked via ps command then zombie processes are always zero but using perl script is giving the erroneous output of 7, 8 or similar high number.
(linux only)
$zombie_count = do { local *ARGV; #ARGV=</proc/[0-9]*/stat>; grep /Z[^)]*$/, <> }
Just grepping for defunct in the ps output is broken, because a process may put defunct in its command line just to break your script. More robust (but yet not portable [1]) solutions are ps -eo state | grep Z or ps -eo s | grep Z.
In your case, your perl script is probably creating the extra zombies, which disappear when it terminates. Unlike the shell, perl will not greedily reap its children as soon as they die; it's up to you to wait() for them, either directly or indirectly:
$ perl -e 'my $pid = open my $fh, "echo yup|"; exec "ps", $pid'
PID TTY STAT TIME COMMAND
6840 pts/11 Z+ 0:00 [echo] <defunct>
$ perl -e 'my $pid = open my $fh, "echo yup|"; undef $fh; exec "ps", $pid'
PID TTY STAT TIME COMMAND
$ perl -e 'my $pid = open my $fh, "echo yup|"; wait; exec "ps", $pid'
PID TTY STAT TIME COMMAND
$ perl -e 'my $pid = open FH, "echo yup|"; close FH; exec "ps", $pid'
PID TTY STAT TIME COMMAND
[1] no, ps -ef is not portable, either.
I can currently connect to an SFTP server using a username/password combination:
print "Attempting connection...\n";
open( my $LFTP,'|-', "lftp -u $ftpuser,'$ftppwd' -e open -p $ftpport $ftpserver" ) or die "Cannot open lftp: $!";
print $LFTP <<"END";
ls
END
close($LFTP); # die unless lftp exit code is 0
exit 0;
How can change this code to connect to a different server using an SSH key which is encrypted with a passphrase?
This is what I've tried:
print "Attempting connection...\n";
# $ftppwd is blank now!
open( my $LFTP,'|-', "lftp -u $ftpuser,'$ftppwd' -e 'set sftp:connect-program \"ssh -a -x -i $keypath\"; open $ftpserver;'" ) or die "Cannot open lftp: $!";
print $LFTP <<"END";
ls
END
close($LFTP); # die unless lftp exit code is 0
exit 0;
The output is stuck at:
Attempting connection...
`ls' at 0 [Connecting...]
Thank you in advance for any help..
Add -u option to the open command inside -e argument and use the pass phrase as the password. You should also use url syntax to select the sftp protocol, e.g. sftp://user:phrase#server
I have some a perl code like this to get the pid of the process if it
existed. I wanted to know the return status of pgrep so I can find out if the pid is valid or not. Here is the code:
#!/usr/bin/perl -w
my $pid=`pgrep -n -f \"akonadiserver\"`;
print "$? ";
chomp $pid;
print "Output1: $pid\n";
$pid=`pgrep -n -f \"akonadiserver\"`;
my $evalue=${^CHILD_ERROR_NATIVE};
chomp $pid;
print "Output2: $pid, $evalue\n";
But whenever I run it, I get the following:
0 Output1: 3054
Output2: 3056, 0
another run
0 Output1: 3059
Output2: 3061, 0
But in the system:
$ pgrep -n -f akonadiserver
1862
Several problems seen here:
1) The pid returned is changing all the time, most likely it is matching
the grep process itself.
2) I don't have a way to find out of the pid returned is valid or not. $?
doesn't help.
How do I check the return status and how do I use pgrep properly in perl?
Running `pgrep -n -f akonadiserver` from a Perl script, is equivalent to running sh -c "pgrep -n -f akonadiserver" from the terminal.
From perlop :
qx/STRING/:
A string which is (possibly) interpolated and then executed as a system command with /bin/sh or its equivalent.
The problem is that using the -f option with pgrep will match against the full command line used to invoke the process. This will also include the current pgrep command, since its command line will look like sh -c "pgrep -n -f akonadiserver" which will match the string "akonadiserver". Since you also use the -n flag with pgrep it will only return the newest process that matches "akonadiserver" and this will most likely be the pgrep command itself (sh -c "pgrep -n -f akonadiserver").. So that explains why you get the wrong process id.
It does not however explain why it still works from the terminal window. The reason is that from the terminal window, the pgrep command is run with bash and not sh (as from the Perl script), see this question for more information about the difference: Difference between pgrep in sh and bash. As a result, running pgrep -f from the terminal window will not include the pgrep command itself, and therefore it will work for your case.
To obtain a reliable result from Perl, you should use Proc::ProcessTable:
use feature qw(say);
use strict;
use warnings;
use Proc::ProcessTable;
my $pat = "akonadiserver";
my $pt = Proc::ProcessTable->new();
my #tab;
for my $p ( #{$pt->table} ){
if ($p->cmndline =~ /\Q$pat\E/) {
push #tab, {pid => $p->pid, start => $p->start};
}
}
say $_->{pid} for (sort {$b->{start} <=> $a->{start}} #tab);
It seems to me that pgrep returns the pid of the newest (-n) child process started by the process you are looking for, at least that is what happens on my system.
I have always used ps for such things, how about:
my $pid = (split(" ",`ps -A | grep \"akonadiserver\"`))[0];
instead, or
my #pids = map{ $_ =~ /(\d+)/; $1 }split("\n", `ps -A | grep \"akonadiserver\"` );
print "mutiple instances: #pids\n" if #pids > 1;
print "pid: ".$pids[0]."\n" if #pids == 1;
to get all instances running into an array and then do what you want to with it.
I have a process which when
executed as below:
process_name > file.txt
will redirect all the output to file.txt and also to the console.
when executed like this
process_name >& file.txt&
will redirect the output to file.txt but will not print the output to console returning the pid on the console.
Now i want to execute the second way of running the process inside a perl script!
how could i do this?
currently i am usng the below method:
#!/usr/bin/perl
use strict;
use warnings;
my $res = `bsm_adjust_controller -F -ra -d 'subnetwork=NETSim_STD/bsc=0005' -f /tmp/31102012/Task_31102012_1421_0005.log >& /tmp/31102012/Ctrl_31102012_1421_0005.log&`;
print $res;
when i run the script it gives:
./temp.pl
sh: /tmp/31102012/Ctrl_31102012_1421_0005.log: bad number
I also tried the below method:
#!/usr/bin/perl
use strict;
use warnings;
my $res = "bsm_adjust_controller -F -ra -d 'subnetwork=NETSim_STD/bsc=0005' -f /tmp/31102012/Task_31102012_1421_0005.log >& /tmp/31102012/Ctrl_31102012_1421_0005.log&";
open(CMD, '-|', $res) || die "couldn't run $res: $!\n";
Even the above script throws the same error.
the purpose is to execute the process in background and continue with the next statements without caring what happened to the process and also i donot need the output of the process being run inside to console.
could anybody please help?
well this worked:
#!/usr/bin/perl
use strict;
use warnings;
my $cmd="bsm_adjust_controller -F -ra -d 'subnetwork=NETSim_STD/bsc=0106' -f /tmp/31102012/Task_31102012_1421_0005.log >/tmp/31102012/Ctrl_31102012_1421_0005.log";
open STDERR, ">/dev/null";
print "Redirected stderr to /dev/null\n";
my $pid=open (FH, "-|",$cmd);
if ($pid) { #parent
print "/tmp/31102012/Task_31102012_1421_0005.log";
exit;
}
print "Redirected stderr to /dev/null\n";
close(FH);
$ cat test.pl
my $pid = 5892;
my $not = system("top -H -p $pid -n 1 | grep myprocess | wc -l");
print "not = $not\n";
$ perl test.pl
11
not = 0
$
I want to capture the result i.e. 11 into a variable. How can I do that?
From Perlfaq8:
You're confusing the purpose of system() and backticks (``). system() runs a command and returns exit status information (as a 16 bit value: the low 7 bits are the signal the process died from, if any, and the high 8 bits are the actual exit value). Backticks (``) run a command and return what it sent to STDOUT.
$exit_status = system("mail-users");
$output_string = `ls`;
There are many ways to execute external commands from Perl. The most commons with their meanings are:
system() : you want to execute a command and don't want to capture its output
exec: you don't want to return to the
calling perl script
backticks : you want to capture the
output of the command
open: you want to pipe the command (as
input or output) to your script
Also see How can I capture STDERR from an external command?
The easiest way is to use the `` feature in Perl. This will execute what is inside and return what was printed to stdout:
my $pid = 5892;
my $var = `top -H -p $pid -n 1 | grep myprocess | wc -l`;
print "not = $var\n";
This should do it.
Try using qx{command} rather than backticks. To me, it's a bit better because: you can do SQL with it and not worry about escaping quotes and such. Depending on the editor and screen, my old eyes tend to miss the tiny back ticks, and it shouldn't ever have an issue with being overloaded like using angle brackets versus glob.
Using backtick or qx helps, thanks everybody for the answers. However, I found that if you use backtick or qx, the output contains trailing newline and I need to remove that. So I used chomp.
chomp($host = `hostname`);
chomp($domain = `domainname`);
$fqdn = $host.".".$domain;
More information here:
http://irouble.blogspot.in/2011/04/perl-chomp-backticks.html
Use backticks for system commands, which helps to store their results into Perl variables.
my $pid = 5892;
my $not = ``top -H -p $pid -n 1 | grep myprocess | wc -l`;
print "not = $not\n";
Also for eg. you can use IPC::Run:
use IPC::Run qw(run);
my $pid = 5892;
run [qw(top -H -n 1 -p), $pid],
'|', sub { print grep { /myprocess/ } <STDIN> },
'|', [qw(wc -l)],
'>', \my $out;
print $out;
processes are running without bash subprocess
can be piped to perl subs
very similar to shell