DATA 전문가로 가는 길

[Perl] Net::FTP를 활용한 시스템 체크 본문

Programming/Perl

[Perl] Net::FTP를 활용한 시스템 체크

EstenPark 2009. 5. 14. 17:38

처음에는 Expect를 활용해서 각 서버에 들어가서 체크리스트 스크립트를 수행 시키고 빠져나오는 방식으로 구현 하였습니다. 하지만 거래량이 많아지면서 접속하는 시간과 체크리스트를 수행하는 시간이 길어지면서 타임아웃이 자주 발생되는 것을 보고 조금 다른 생각으로 체크를 진행 하게 되었습니다.


1. 시스템 체크리스트 계획
  • A서버와  B서버는 서로 접근이 안됨
    • 양쪽 모두 인바운드, 아웃바운드가 되지 않음
    • 단, 특정 IP에서는 해당 서버로 모두 접근 됨
  • 서버당 체크리스트
    • 일정 시간에 각 서버에서 크론에 의해 작업을 수행
  • 서버 정보
    • 아이디, PW, 디렉토리 모두 다르거나 같음
  • FTP 활용
    • *.log 파일은 각 각의 서버에 2개가 있을지 1개가 있을지 모름
      • 예를 들면 (data.log, backup.log 등등이 서버에 1개 또는 2개 또는 없을 수도 있음)
  • 로그분석기
    • FTP로 가져온 데이터의 값을 확인 하여 정상 건과 비정상 건을 확인
    • 비정상일 경우 해당 서버명, 결과구분, 내용을 출력
  • 최종 결과 전송
    • 최종 결과를 *.sql로 만들어서 전송 하면 해당 내용을 웹사이트에 기재 됨
2. 소스 확인
 #!/usr/bin/perl

use strict;
use warnings;
use Net::FTP;

# The default password
my $svr_passwd='PASSWD';

# Hardcode the directory and filename to get
my $ftp;
my @result_data;
my $local_dir  = 'C:\\Script\\CheckList\\log\\';
my $local_fn_sql = 'C:\\Script\\CheckList\\CheckList.sql';
my @target_fn  = qw( mass01_result.log mass02_result.log
                                calm01_result.log calm02_result.log backup02_result.log );
                                
my $target_send_fn = 'checklist.sql';
                     
# 203.211.XXX.XXX Check list Format
my %sms_list = (
    'STEP01' => {seq=>'52',  ref=>'정상', ,msg=>''},
    'STEP02' => {seq=>'53',  ref=>'정상', ,msg=>''},
    'STEP03' => {seq=>'54',  ref=>'정상', ,msg=>''},
    'STEP04' => {seq=>'55',  ref=>'정상', ,msg=>''},
    'STEP05' => {seq=>'56',  ref=>'정상', ,msg=>''},
    'STEP06' => {seq=>'57',  ref=>'정상', ,msg=>''},
    'STEP07' => {seq=>'58',  ref=>'정상', ,msg=>''},
    'STEP08' => {seq=>'59',  ref=>'정상', ,msg=>''},
    'STEP09' => {seq=>'60',  ref=>'정상', ,msg=>''},
    'STEP10' => {seq=>'61',  ref=>'정상', ,msg=>''},
    'STEP11' => {seq=>'62',  ref=>'정상', ,msg=>''},
    'STEP12' => {seq=>'63',  ref=>'정상', ,msg=>''},
    'STEP13' => {seq=>'64',  ref=>'정상', ,msg=>''},
    'STEP14' => {seq=>'107', ref=>'정상', ,msg=>''},
    'STEP15' => {seq=>'65',  ref=>'정상', ,msg=>''},
    'STEP16' => {seq=>'66',  ref=>'정상', ,msg=>''},
    'STEP17' => {seq=>'67',  ref=>'정상', ,msg=>''},
    'STEP18' => {seq=>'68',  ref=>'정상', ,msg=>''},
    'STEP19' => {seq=>'69',  ref=>'정상', ,msg=>''},
    'STEP20' => {seq=>'70',  ref=>'정상', ,msg=>''},
);
    

# Data structure using anonymous hash
my %svr_list = (
    '203.211.XXX.XX1' => {id=>'estenpark', pw=>"$svr_passwd",     dir=>'/home/MASS/check_list'},
    '203.211.XXX.XX2' => {id=>'estenpark', pw=>"$svr_passwd",     dir=>'/home/MASS/check_list'},
    '203.211.XXX.XX3' => {id=>'macacy',  pw=>"$svr_passwd",         dir=>'/home/CALM/check_list'},
    '203.211.XXX.XX4' => {id=>'macacy',  pw=>"$svr_passwd",         dir=>'/home/CALM/check_list'},
);

my %svr_send_list = (
    '203.211.XXX.XX5' => {id=>'root', pw=>'password',       dir=>'/b2b/b2badmin/check_list/pay'},
);

# Check the time services (format : 20090510140000)
my @date = localtime(time);
my $now_date = sprintf(    "%04d%02d%02d%02d0000", $date[5] + 1900,
                                               $date[4] +1,
                                               $date[3],
                                               $date[2],
);
                                                

# E-Services of the connection to the server(Perl -> Net::FTP )
foreach my $ip ( sort keys %svr_list ) {
    print "===============================================================\n";
    print " E-Services Service System Check List \n";
    print "-------------------------------------------\n";
    printf "IP : %16s ID : %10s PW : %16s\n",$ip, $svr_list{$ip}->{id}, $svr_list{$ip}->{pw};
    print "-------------------------------------------\n";
    
    # Open the connection to the host
    $ftp = Net::FTP->new($ip, Debug => 0)
           or die "Cannot connect to sum.host.name: $@";
    $ftp->login($svr_list{$ip}->{id}, $svr_list{$ip}->{pw})
                 or die "Cannet Login ", $ftp->message;
    # Transfer file in binary mode
      $ftp->binary;                
      # Attempt to change directory
    $ftp->cwd($svr_list{$ip}->{dir});
    
    # Now get to file and leave
    my @fn_svr = $ftp->ls($svr_list{$ip}->{dir});
    foreach my $fn ( @target_fn ) {
    
    my @fn_ref = grep m@$fn@g, @fn_svr;
    foreach my $result ( @fn_ref ) {
        $ftp->get($result, $local_dir.$fn)
               or die "get failed ", $ftp->message;
      print "FTP Transport Success : $result \n";
      }
    }
    
    $ftp->quit;
    
}
print "===============================================================\n";

# Check the local file
my @ret_data;
foreach my $fn ( @target_fn ) {
    open( my $FH, '<', $local_dir.$fn ) || die "Cannet open $fn.\n";
    my @sys_check = <$FH>;
    
    foreach my $fn_push ( @sys_check ) {
        push (@ret_data, $fn_push );
    }
    
    close$FH;
}


# Host|Step|Flag|Message -> Flag='N' Output
my @ret_err = grep m/\|N\|/g, @ret_data ,"\n";

# Check the Result Service
foreach my $seq ( sort keys %sms_list ) {

    my @sch;
    my @str;
    my @msg;
    my $flag;
    my $chk_ref;
    my $result_seq;
    my $result_ref;
    my $result_msg;
    
    @sch = grep m/($seq)/, @ret_err;

    foreach ( @sch ) {
        chomp;
        # Put an array ofString Cut. Check Message
        @_ = split /\|/, $_;
        push (@msg, $_[0].' '.$_[3]);
      $flag = $_[2];

    }

    # 정상 / 비정상 deimited output
    if ($flag) {
            $chk_ref = '비정상';
        } else {
          $chk_ref = $sms_list{$seq}->{ref};
    }
    # Result scalar values
    $result_seq = $sms_list{$seq}->{seq};
    $result_ref    = $chk_ref;
    $result_msg = sprintf( "%s", join(", ", @msg) );

    # MySql Insert query
    $_ = sprintf "insert into t_chklist_result values('2','%s','%s','%s','%s');\n",
                                $now_date, $result_seq, , $result_ref, $result_msg;
    push (@result_data, $_);
    #print "$_ \n";
    
}

print "===============================================================\n";
print " Results File Transfer START \n";
print "-------------------------------------------\n";
# Transfor to make sql file
open (my $FHW, '>', $local_fn_sql ) || die "Cannet open $local_fn_sql.\n";
    my $result_sql = "insert into t_chklist values('$now_date','PAY','','','','2','',''); \n";
    push (@result_data, $result_sql);
    print $FHW @result_data;
close $FHW;

# Transfor to emergency ( 203.211.XXX.XX5)
foreach my $snd_ip ( sort keys %svr_send_list ) {
    
    $ftp = Net::FTP->new($snd_ip, Debug => 0)
           or die "Cannot connect to sum.host.name: $@";
    $ftp->login($svr_send_list{$snd_ip}->{id},$svr_send_list{$snd_ip}->{pw})
                 or die "Cannet Login ", $ftp->message;
    $ftp->binary;                
    $ftp->cwd($svr_send_list{$snd_ip}->{dir});
    $ftp->put($local_fn_sql, $target_send_fn)
             or die "put failed ", $ftp->message;
    print "FTP Transport Success : $svr_send_list{$snd_ip}->{dir} \n";
    $ftp->quit;
}
print "-------------------------------------------\n";    
print " Results File Transfer END \n";
print "===============================================================\n";
                                                                      

3. 소스 분석

 

 #!/usr/bin/perl

use strict;
use warnings;
use Net::FTP;

# The default password
my $svr_passwd='PASSWD';

# Hardcode the directory and filename to get
my $ftp;
my @result_data;
my $local_dir  = 'C:\\Script\\CheckList\\log\\';
my $local_fn_sql = 'C:\\Script\\CheckList\\CheckList.sql';
my @target_fn  = qw( mass01_result.log mass02_result.log
                                calm01_result.log calm02_result.log backup02_result.log );
                                
my $target_send_fn = 'checklist.sql';
                     
# 203.211.XXX.XXX Check list Format
my %sms_list = (
    'STEP01' => {seq=>'01',  ref=>'정상', ,msg=>''},
    'STEP02' => {seq=>'02',  ref=>'정상', ,msg=>''},
    'STEP03' => {seq=>'03',  ref=>'정상', ,msg=>''},
    'STEP04' => {seq=>'04',  ref=>'정상', ,msg=>''},
    'STEP05' => {seq=>'05',  ref=>'정상', ,msg=>''},
    'STEP06' => {seq=>'06',  ref=>'정상', ,msg=>''},
    'STEP07' => {seq=>'20',  ref=>'정상', ,msg=>''},
    'STEP08' => {seq=>'25',  ref=>'정상', ,msg=>''},
    'STEP09' => {seq=>'15',  ref=>'정상', ,msg=>''},
    'STEP10' => {seq=>'07',  ref=>'정상', ,msg=>''},
    'STEP11' => {seq=>'08',  ref=>'정상', ,msg=>''},
    'STEP12' => {seq=>'09',  ref=>'정상', ,msg=>''},
    'STEP13' => {seq=>'10',  ref=>'정상', ,msg=>''},
    'STEP14' => {seq=>'11', ref=>'정상', ,msg=>''},
    'STEP15' => {seq=>'60',  ref=>'정상', ,msg=>''},
    'STEP16' => {seq=>'61',  ref=>'정상', ,msg=>''},
    'STEP17' => {seq=>'67',  ref=>'정상', ,msg=>''},
    'STEP18' => {seq=>'68',  ref=>'정상', ,msg=>''},
    'STEP19' => {seq=>'51',  ref=>'정상', ,msg=>''},
    'STEP20' => {seq=>'52',  ref=>'정상', ,msg=>''},
);
    

# Data structure using anonymous hash
my %svr_list = (
    '203.211.XXX.XX1' => {id=>'estenpark', pw=>"$svr_passwd",      dir=>'/home/MASS/check_list'},
    '203.211.XXX.XX2' => {id=>'estenpark', pw=>"$svr_passwd",      dir=>'/home/MASS/check_list'},
    '203.211.XXX.XX3' => {id=>'macacy',  pw=>"$svr_passwd",         dir=>'/home/CALM/check_list'},
    '203.211.XXX.XX4' => {id=>'macacy',  pw=>"$svr_passwd",         dir=>'/home/CALM/check_list'},
);

my %svr_send_list = (
    '203.211.XXX.XX5' => {id=>'root', pw=>'password',       dir=>'/b2b/b2badmin/check_list/pay'},
);

# Check the time services (format : 20090510140000)
my @date = localtime(time);
my $now_date = sprintf(    "%04d%02d%02d%02d0000", $date[5] + 1900,
                                                                            $date[4] +1,
                                                                            $date[3],
                                                                            $date[2],
);
  • Net::FTP 모듈 설명
    •   perldoc Net::FTP ( 그 어떤 설명 보다 자세하게 알 수 있습니다.)
  • # 203.211.XXX.XXX Check list Format
    • 나중에 이야기가 나오겠지만 이 부분은 기본적인 포멧을 정의 한 것입니다.
    • 우선 총 체크 할 부분은 20가지라고 세팅 하였습니다.
  • # Data structure using anonymous hash
    • 익명해시를 이용하여 IP, ID, PW 생성
  • # Check the time services (format : 20090510140000)
    • 날짜를 구하기 위한 부분 입니다.
    • 20090514010000 ( DB TYPE에 따라 다릅니다. )
 # E-Services of the connection to the server(Perl -> Net::FTP )
foreach my $ip ( sort keys %svr_list ) {
    print "===============================================================\n";
    print " E-Services Service System Check List \n";
    print "-------------------------------------------\n";
    printf "IP : %16s ID : %10s PW : %16s\n",$ip, $svr_list{$ip}->{id}, $svr_list{$ip}->{pw};
    print "-------------------------------------------\n";
    
    # Open the connection to the host
    $ftp = Net::FTP->new($ip, Debug => 0)
           or die "Cannot connect to sum.host.name: $@";
    $ftp->login($svr_list{$ip}->{id}, $svr_list{$ip}->{pw})
                 or die "Cannet Login ", $ftp->message;
    # Transfer file in binary mode
      $ftp->binary;                
      # Attempt to change directory
    $ftp->cwd($svr_list{$ip}->{dir});
    
    # Now get to file and leave
    my @fn_svr = $ftp->ls($svr_list{$ip}->{dir});
    foreach my $fn ( @target_fn ) {
    
    my @fn_ref = grep m@$fn@g, @fn_svr;
    foreach my $result ( @fn_ref ) {
        $ftp->get($result, $local_dir.$fn)
               or die "get failed ", $ftp->message;
      print "FTP Transport Success : $result \n";
      }
    }
    
    $ftp->quit;
    
}

  • 서버에 접근 하여 FTP로 파일 내려받기
  • 서버에 있는 파일명과 미리 선안 한 파일명을 체크하여 결과에 만족하는 파일만 내려 받음
  •  my @fn_ref = grep m@$fn@g, @fn_svr;
    • map을 이용하면 원하는 결과를 얻을 수 없습니다.  그래서 grep를 사용하여 파일명을 매칭 합니다.

 

 # Check the local file
my @ret_data;
foreach my $fn ( @target_fn ) {
    open( my $FH, '<', $local_dir.$fn ) || die "Cannet open $fn.\n";
    my @sys_check = <$FH>;
    
    foreach my $fn_push ( @sys_check ) {
        push (@ret_data, $fn_push );
    }
    
    close$FH;
}


# Host|Step|Flag|Message -> Flag='N' Output
my @ret_err = grep m/\|N\|/g, @ret_data ,"\n";

# Check the Result Service
foreach my $seq ( sort keys %sms_list ) {

    my @sch;
    my @str;
    my @msg;
    my $flag;
    my $chk_ref;
    my $result_seq;
    my $result_ref;
    my $result_msg;
    
    @sch = grep m/($seq)/, @ret_err;

    foreach ( @sch ) {
        chomp;
        # Put an array ofString Cut. Check Message
        @_ = split /\|/, $_;
        push (@msg, $_[0].' '.$_[3]);
      $flag = $_[2];

    }

    # 정상 / 비정상 deimited output
    if ($flag) {
            $chk_ref = '비정상';
        } else {
          $chk_ref = $sms_list{$seq}->{ref};
    }
    # Result scalar values
    $result_seq = $sms_list{$seq}->{seq};
    $result_ref    = $chk_ref;
    $result_msg = sprintf( "%s", join(", ", @msg) );

    # MySql Insert query
    $_ = sprintf "insert into chklist_result values('2','%s','%s','%s','%s');\n",
                                $now_date, $result_seq, , $result_ref, $result_msg;
    push (@result_data, $_);
    #print "$_ \n";
    
}

  • 내려받은 파일을 분석하는 부분
  • 전체 파일의 내용을 배열에 저장 
    •  my @ret_err = grep m/\|N\|/g, @ret_data ,"\n";
    • ex) calm|step_01|N|cpu 32% 에서 구분자 "|"중 3번째 값이 N인 플래그인 것만 출력
  • 각각의 결과를 MySql 인서트 결과 생성


 print "===============================================================\n";
print " Results File Transfer START \n";
print "-------------------------------------------\n";
# Transfor to make sql file
open (my $FHW, '>', $local_fn_sql ) || die "Cannet open $local_fn_sql.\n";
    my $result_sql = "insert into t_chklist values('$now_date','PAY','','','','2','',''); \n";
    push (@result_data, $result_sql);
    print $FHW @result_data;
close $FHW;

# Transfor to emergency ( 203.211.XXX.XX5)
foreach my $snd_ip ( sort keys %svr_send_list ) {
    
    $ftp = Net::FTP->new($snd_ip, Debug => 0)
           or die "Cannot connect to sum.host.name: $@";
    $ftp->login($svr_send_list{$snd_ip}->{id},$svr_send_list{$snd_ip}->{pw})
                 or die "Cannet Login ", $ftp->message;
    $ftp->binary;                
    $ftp->cwd($svr_send_list{$snd_ip}->{dir});
    $ftp->put($local_fn_sql, $target_send_fn)
             or die "put failed ", $ftp->message;
    print "FTP Transport Success : $svr_send_list{$snd_ip}->{dir} \n";
    $ftp->quit;
}
print "-------------------------------------------\n";    
print " Results File Transfer END \n";
print "===============================================================\n";

  • 최종 결과 전송


4. Tip 
디렉토리(폴더)의 파일을 한번에 읽을 수 없을까요??

a3r0's Blog 

#!/usr/bin/perl
use strict;
use warnings;

my @files = qw/a.txt b.txt c.txt/;
@ARGV = @files;
my $content;
while (<ARGV>) {
$content .= $_;
}

@ARGV와 ARGV와 $ARGV에 대한 설명은
http://p3rl.org/perlvar 보면 나옵니다.

@ARGV 는 Perl내부 특수변수로 명령행 인자에서 받은 리스트를 담는 변수죠
거기에 파일 리스트를 넣고 <>혹은 <ARGV> 로 while에서 돌리면 파일의 끝을
만날때 마다 자동으로 다음파일로 넘어가고 그 시점에서 각파일의 파일 핸들명은
ARGV가 되고 파일 이름은 $ARGV가 됩니다.

================================================================================
http://aero.springnote.com/pages/994336                   (aero님의 노트 )
http://mailman.linuxchix.org/pipermail/courses/2003-November/001368.html                  (grep and map)
http://search.cpan.org/~gbarr/libnet-1.22/Net/FTP.pm           (Net::FTP)
http://cafe.naver.com/perlstudy.cafe                                 (대한민국Perl커뮤니티)
http://cafe.naver.com/ArticleRead.nhn?clubid=18062050&page=1&menuid=12&boardtype=L&articleid=200
================================================================================

 










Comments