话接上篇,继续完成这个perl脚本。花了今天一天的时间,基本定稿如下: ```perl#!/usr/bin/perl -w use Net::Ping::External qw(ping); use Tie::File; use Getopt::Long;
Getopt::Long::Configure (“bundling”); GetOptions( ‘H:s’ => $ct_host, ‘host:s’ => $ct_host, ‘T:i’ => $time, ‘time:i’ => $time, ‘N:i’ => $fork_num, ‘number:i’ => $fork_num, ‘h’ => $help, ‘help’ => $help, );
if( $help ) { print “Usage: ping_check.pl -H 10.168.168.251 -T 30 -N 10\n”; print “ -H/host: The switch ip address to be checked in china telecom;\n”; print “ -T/time: The seconds used for pinging;\n”; print “ -N/number: The number of fork processes to expect the remote hosts;\n”; print “ -h/help: The usage just you see now.\n”; exit 0; }
my $mark_file = ‘/tmp/mark_file’; my @last;
my $result = ping( hostname => “$ct_host”, count => $time * 5, size => ‘128’, timeout => ‘1’, #原生的Net::Ping模块需要自己while来控制sleep; #现在采用的Net::Ping::External是直接调用的外部ping命令,但默认也没有-i参数; #package中sub ping{}里把未定义的@_都给到了%args, #所以只需要在199行(即sub _ping_linux{}中)添加上-i $args{interval}就能用了。 interval => ‘0.2’, ); #用tie将数组@last锁定到文件上——我曾经想过直接锁个变量,但是似乎没有,只能数组或哈希? tie @last, ‘Tie::File’, $mark_file or die $!;
if ( $result && ($last[0] == 1) ) { print “ok\n”; } elsif ( $result && ($last[0] == 0) ) { print “Beginning recovery\n”; ¶llel_manage(“recovery”, “$fork_num”); &sms_alarm(‘CNC recovery peer to intranet’); &email_alarm(‘CNC recovery peer to intranet’); } else { print “Error! Beginning change to template configuration\n”; ¶llel_manage(“change”, “$fork_num”); &sms_alarm(‘CNC change peer to CT’); &email_alarm(‘CNC change peer to CT’); }
$last[0] = $result; untie @last;
sub email_alarm { use Net::SMTP_auth; my $email_message = shift; my $smtp = Net::SMTP_auth->new( Host => ‘smtp.domain.com’, Timeout => ‘30’,
);
$smtp->auth('LOGIN', 'alarm@domain.com', 'password');
$smtp->mail('alarm@domain.com');
$smtp->to( 'netadmin@domain.com' );
$smtp->data();
$smtp->datasend("To: Netadmin\@domain.com\n");
$smtp->datasend("\n");
$smtp->datasend("${email_message}\n");
$smtp->dataend();
$smtp->quit; }
sub sms_alarm { use Net::MySQL; my $sms_message = shift; my %contacts = &get_contacts(); my $mysql = Net::MySQL->new( hostname => ‘10.1.1.45’, database => ‘smsd’, user => ‘smsd’, password => ‘smsd’, ); foreach my $send_number (values %contacts) { $mysql->query( “INSERT INTO outbox (number, text) VALUES ( $send_number, ‘$sms_message’)” ); } $mysql->close; }
sub parallel_manage { #因为Expect模块本身使用了fork();要求运行在主进程中,所以在并发的时候不能采用多线程而得用多进程 use Expect; use Parallel::ForkManager;
my $command = shift || 'id';
my $max_fork = shift || '10';
my @remote_list = ('10.1.1.64',
'10.1.1.50',
'10.1.1.35',
);
my $remote_host;
my %remote_result;
my $pm = Parallel::ForkManager->new( $max_fork, '/tmp/'); #采用Parallel::ForkManager模块时,如果需要子进程返回数据结果给父进程,必须把run_on_finish()放在fork之前 #Parallel::ForkManager模块实际上是采用文件存储的方式进行父子进程的数据通信,所以上面new的时候定义一个临时文件路径
$pm->run_on_finish (
sub { #主要有用的是子进程pid,子进程退出状态,返回数据的引用
my ($pid, $exit_code, $ident, $exit_signal, $core_dump, $reference) = @_;
if (defined($reference)) { #解引用后复制到数组并转存成哈希
my @data = @$reference;
$remote_result{"$data[0]"} = $data[1];
} else {
print qq|No message received from child process $pid!\n|;
}
}
);
foreach $remote_host (@remote_list) {
$pm->start and next;
my @check = ($remote_host, &ssh_expect($remote_host, $command)); #默认参数就是finish(0);需要返回数据时才加上引用
$pm->finish(0, \@check);
}
$pm->wait_all_children;
foreach my $key (sort keys %remote_result) {
print $remote_result{$key},"\n";
} }
sub ssh_expect { my ($host, $shell) = @_; my $exp = Expect->new; my $password = ‘password’; $exp = Expect->spawn(“ssh -l monitor -i /usr/local/monitor/conf/id_rsa -o ConnectTimeout=5 $host”);
$exp->raw_pty(1); #关闭输出,不然expect会把整个session都print出来(实际是到STDERR)
$exp->log_stdout(0);
$exp->expect(2,[
'\$',
sub {
my $self = shift;
$self->send("su -\n");
}
],
[
'\(yes/no\)\?',
sub {
my $self = shift;
$self->send("yes\n");
exp_continue;
}
]
);
$exp->expect(2, [
'Password:',
sub {
my $self = shift;
$self->send("${password}\n");
exp_continue;
}
],
[
'#',
sub {
my $self = shift;
$self->send("${shell}\n");
}
]
);
$exp->send("exit\n") if ($exp->expect(undef,'#')); #expect有before/match/after来返回相应的数据
my $read = $exp->before();
$exp->send("exit\n") if ($exp->expect(undef,'$'));
$exp->soft_close();
return $read; }
sub get_contact {
open my FH, “/usr/local/nagios/etc/objects/contacts.cfg”;
my %hash;
local $/ = ‘define’;
while(