话接上篇,继续完成这个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”;
&parallel_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”;
&parallel_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’,
# Debug => ‘1’,
);
$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”);
# $ENV{TERM}=”xterm”;
# $exp->exp_internal(1);
$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() { next unless /pager\s+(\d{11})/; my $sms_num = $1; $hash{$1}=$sms_num if /email\s+([a-z\.]+?)\@bj.china.com/; } return %hash; } ```