#!/usr/bin/perl
#####################################################################
# Function: pg-rex_slave_start
#
#
# 概要:
# PG-REX での Slave 起動実行ツール。
# 手順の簡易化を目的として作成している。
# 
# 特記事項:
# なし
#
#####################################################################
use warnings;
use strict;
use Getopt::Long;
use lib qw(/usr/local/share/pg-rex);
use common qw(pacemaker_online pgrex_failed_action network_running resource_running
              read_config read_cib read_hacf exec_command ssh_exec_command printlog get_pg_version);

BEGIN {
#    if ($ENV{"LANG"} =~ m/ja/i){
    if (1){
        eval qq{
            use po::ja;
        };
    }
    else{
        eval qq{
            use po::en;
        }
    }
};

$SIG{INT} = sub {
    printlog("LOG", SLAVESTART_MS0001);
};

main();

1;

sub main{
    my $help_mode = 0;
    my $nobasebackup_mode = 0;
    my $force_mode = 0;
    my $operation_num = 1;
    my $key;
    my $value;
    my %node_value;
    my @ic_dev = ();
    my $result;
    my @results;
    my $check;
    my $pg_xlog_dir;
    my $symbolic_link = 0;
    my $timeout = 300;
    my $monitor_time = 10;
    my $wait_time = 0;
    my $process_check;
    my $cib_path = "/var/lib/heartbeat/crm/cib.xml";
    my $hacf_path = "/etc/ha.d/ha.cf";
    my %hacf_value;
    my @master_cib_strings;
    my %master_cib_value;
    my $config_path = "/etc/pg-rex_tools.conf";
    my %config_value;
    my $lock_file = "";
    my $exit_code;
    my $ssh_pass;

    # 標準出力が途中で停止するのを防ぐ為に
    # 標準出力のオートフラッシュを有効化
    $| = 1;

    ### スクリプト実行準備 ###
    # オプション解析
    GetOptions('help'         => \$help_mode,
               'nobasebackup' => \$nobasebackup_mode,
               'force'        => \$force_mode);
    if ($help_mode){
        printlog("USAGE", SLAVESTART_USAGE);
        exit(0);
    }

    # コマンドに必要な情報の取得
    # 環境設定ファイルの読み込み
    open (FILE, $config_path) or printlog("ERROR", SLAVESTART_MS0002);
    %config_value = read_config(<FILE>);
    close (FILE);

    # ssh 接続の為の情報の取得
    printlog("LOG", SLAVESTART_MS0030, $config_value{"Another_S-LAN_IPAddress"});
    `stty -echo`;
    $ssh_pass = <STDIN>;
    chomp $ssh_pass;
    `stty echo`;
    printlog("LOG", SLAVESTART_MS0031);

    # コマンドを実行しているマシンのノード名ともう一台のノード名を取得
    %node_value = get_node($config_value{'Another_S-LAN_IPAddress'}, $ssh_pass);
    
    # ha.cf の読み込み
    open (FILE, $hacf_path) or printlog("ERROR", SLAVESTART_MS0003);
    %hacf_value = read_hacf(<FILE>);
    close (FILE);

    ### Pacemaker 起動の為の準備 ###
    # コマンド実行ノードの Pacemaker の稼働確認
    printlog("LOG", SLAVESTART_MS0004, $operation_num++);
    $result = `service heartbeat status`;
    chomp $result;
    if ($result ne "heartbeat is stopped. No process"){
        printlog("LOG", SLAVESTART_MS0005);
        printlog("ERROR", SLAVESTART_MS0006);
    }
    printlog("LOG", SLAVESTART_MS0007);

    ### 稼働中の Master の存在確認 ###
    printlog("LOG", SLAVESTART_MS0008, $operation_num++);
    if (!resource_running($node_value{'another_node'}, "pg-rex_master", $ssh_pass, $config_value{"Another_S-LAN_IPAddress"})){
        printlog("LOG", SLAVESTART_MS0005);
        printlog("ERROR", SLAVESTART_MS0010);
    }
    printlog("LOG", SLAVESTART_MS0007);

    ### IC-LAN が接続されていることを確認 ###
    printlog("LOG", SLAVESTART_MS0011, $operation_num++);
    foreach my $nic (@{$hacf_value{ic_dev}}){
        $result = exec_command("ifconfig $nic");
        # IP address format : <inet addr:<value>>
        $result =~ /inet\saddr:(\S+)/;
        if ($result eq ""){
            printlog("LOG", SLAVESTART_MS0005);
            printlog("ERROR", SLAVESTART_MS0033);
        }
        `ping -c 5 $1`;
        $exit_code = $? >> 8;
        if ($exit_code){
            printlog("LOG", SLAVESTART_MS0005);
            printlog("ERROR", SLAVESTART_MS0012);
        }
    }
    printlog("LOG", SLAVESTART_MS0007);

    ### 起動禁止フラグの存在確認 ###
    printlog("LOG", SLAVESTART_MS0013, $operation_num++);
    
    # Master マシンにある cib.xml を読み込む
    @results = ssh_exec_command($config_value{"Another_S-LAN_IPAddress"}, "root", $ssh_pass, "cat $cib_path");
    @master_cib_strings = split (/\n/, $results[0]);
    %master_cib_value = read_cib(@master_cib_strings);
 
    # 起動禁止フラグが存在することを確認
    $lock_file = $master_cib_value{"tmpdir"}."/PGSQL.lock";
    if (-e $lock_file){
        printlog("LOG", SLAVESTART_MS0005);
        printlog("ERROR", SLAVESTART_MS0014, $lock_file);
    }

    printlog("LOG", SLAVESTART_MS0007);
    
    
    ### Master からベースバックアップ取得・展開 ###
    # ベースバックアップを取得・展開するかどうかをユーザに確認
    if (!$nobasebackup_mode){
        printlog("LOG", SLAVESTART_MS0015, $operation_num++);

        if (!$force_mode){
            printlog("LOG", SLAVESTART_MS0016);
            $check = <STDIN>;
            chomp $check;
            if ($check =~ m/^n$/i){
                # 以降 --nobasebackup オプション付与時と同じ動作を行う
                $nobasebackup_mode = 1;
                printlog("LOG", SLAVESTART_MS0007);
            }
            elsif ($check !~ m/^y$/i){
                printlog("LOG", SLAVESTART_MS0017);
                exit(0);
            }
        }
    }

    # --nobasebackup オプションが指定されていなかったら、pg_basebackup 、pg_xlog のシンボリックリンク作成を行なう
    if (!$nobasebackup_mode){
        # Slave 側の DB クラスタを削除
        exec_command("su - postgres -c \"rm -rf $master_cib_value{\"pgdata\"} \"");

        # Master の pg_xlog の場所を取得し、Slave の pg_xlog を削除
        # ※readlink コマンドは、引数に指定されたファイルにシンボリックリンクが存在する場合、コマンドの戻り値に"0"を返す
        # ※戻り値が"0"の場合のみ、pg_xlog を削除する
        @results = ssh_exec_command($config_value{"Another_S-LAN_IPAddress"}, "root", $ssh_pass, "readlink $master_cib_value{\"pgdata\"}/pg_xlog ");
        $exit_code = $results[1];
        if ($exit_code != 0 && $exit_code != 1){
            printlog("LOG", SLAVESTART_MS0005);
            printlog("ERROR", SLAVESTART_MS0018);
        }
        elsif (!$exit_code){
            chomp $result;
            # pg_xlog format : ~/pg_xlog
            $results[0] =~ s/\/pg_xlog$//;
            $pg_xlog_dir = $results[0];
            exec_command("su - postgres -c \"rm -rf $pg_xlog_dir/pg_xlog\"");
            $symbolic_link = 1;
        }

        # PostgreSQL のバージョンを確認し、
        # PostgreSQL9.1 と それ以降で pg_basebackup のオプションを変更して実行する
        $result = get_pg_version();
        if ($result =~ /^9\.1[^0-9]/){
            exec_command("su - postgres -c \"pg_basebackup -h $master_cib_value{\"master_ip\"} -U $master_cib_value{\"repuser\"} -D $master_cib_value{\"pgdata\"} -x -P \"");
        }
        else{
            exec_command("su - postgres -c \"pg_basebackup -h $master_cib_value{\"master_ip\"} -U $master_cib_value{\"repuser\"} -D $master_cib_value{\"pgdata\"} -X stream -P \"");
        }

        # pg_xlog のシンボリックリンクを作成
        if ($symbolic_link){
            exec_command("su - postgres -c \"mv $master_cib_value{\"pgdata\"}/pg_xlog $pg_xlog_dir \"");
            exec_command("su - postgres -c \"ln -s $pg_xlog_dir/pg_xlog $master_cib_value{\"pgdata\"}/pg_xlog \"");
        }
        printlog("LOG", SLAVESTART_MS0007);
    }

    ### Master のアーカイブディレクトリと同期 ###
    # su コマンドとリモート先を指定する rsync コマンドを併用するとマシンによっては実行できないため、
    # ここでは root ユーザで実行する
    printlog("LOG", SLAVESTART_MS0019, $operation_num++);
    rsync_exec_command($master_cib_value{"master_ip"}, "root", $ssh_pass, $config_value{"Archive_dir"}."/", $config_value{"Archive_dir"}."/");
    printlog("LOG", SLAVESTART_MS0007);

    ### /var/lib/heartbeat/crm 配下のファイルの削除 ###
    printlog("LOG", SLAVESTART_MS0020, $operation_num++);
    exec_command("rm -f /var/lib/heartbeat/crm/*");
    printlog("LOG", SLAVESTART_MS0007);

    ### Pacemaker 起動 ###
    printlog("LOG", SLAVESTART_MS0021, $operation_num++);
    exec_command("service heartbeat start");
    printlog("LOG", SLAVESTART_MS0007);

    ### Slave の起動確認 ###
    printlog("LOG", SLAVESTART_MS0022, $operation_num++);

    # crm_mon の結果を確認
    while (1){
        sleep $monitor_time;
        $wait_time += $monitor_time;
        if ($wait_time >= $timeout){
            printlog("LOG", SLAVESTART_MS0005);
            printlog("ERROR", SLAVESTART_MS0023, $timeout);
        }

        if (!pacemaker_online($node_value{'my_node'})){
            next;
        }

        if (pgrex_failed_action($node_value{'my_node'})){
            printlog("LOG", SLAVESTART_MS0005);
            printlog("ERROR", SLAVESTART_MS0024);
        }

        if (!resource_running($node_value{'my_node'}, "pg-rex_slave")){
            next;
        }

        # VIP_SLAVE 環境有りの場合のみ起動確認を行なう
        if ($config_value{"VIP_SLAVE"} eq "enable" && !resource_running($node_value{'my_node'}, "vip-slave")){
            next;
        }

        if (!resource_running($node_value{'my_node'}, "pingd")){
            next;
        }

        if (!resource_running($node_value{'my_node'}, "diskd")){
            next;
        }

        # STONITH 環境有りの場合のみ起動確認を行なう
        if ($config_value{"STONITH"} eq "enable" && !resource_running($node_value{'my_node'}, "stonith")){
            next;
        }

        if (!network_running($node_value{'my_node'}, $node_value{'another_node'}, @{$hacf_value{ic_dev}})){
            next;
        }

        # crm_mon の結果が全て揃ったら無限ループを抜ける
        last;
    }

    # PostgreSQL のプロセス確認
    while(1){
        $process_check = exec_command("ps -ef | grep \"postgres \" | grep -v \"grep\" ");
        # postmaster process format : </bin/postgres -D (DB cluster)>
        # crm ファイルに登録してある DB クラスタを含む postmaster プロセスを検索
        # PostgreSQL の postmaster プロセスが存在したら無限ループを抜ける
        if ($process_check =~ /bin\/postgres\s+-D\s+$master_cib_value{"pgdata"}\s+/){
            last;
        }

        if ($wait_time >= $timeout){
            printlog("LOG", SLAVESTART_MS0005);
            printlog("ERROR", SLAVESTART_MS0025, $timeout);
        }
        sleep $monitor_time;
        $wait_time += $monitor_time;
    }

    # PostgreSQL の DB 接続確認
    `su - postgres -c \"psql -U postgres -l > /dev/null \" `;
    $exit_code = $? >> 8;
    if ($exit_code){
        printlog("LOG", SLAVESTART_MS0005);
        printlog("ERROR", SLAVESTART_MS0026);
    }

    # Master の PostgreSQL の wal sender プロセスの確認
    #$result = exec_command("ssh root\@$config_value{\"Another_S-LAN_IPAddress\"} \"ps aux | grep \\\"wal sender \\\" | grep -v \\\"grep\\\" \"");
    @results = ssh_exec_command($config_value{"Another_S-LAN_IPAddress"}, "root", $ssh_pass, "ps aux | grep \"wal sender \" | grep -v \"grep\"");
    # wal sender process format : <postgres: wal sender process <repuser name>>
    if ($results[0] !~ /postgres:\swal\ssender\sprocess\s$master_cib_value{"repuser"}/){
        printlog("LOG", SLAVESTART_MS0005);
        printlog("ERROR", SLAVESTART_MS0027);
    }

    # PostgreSQL のレプリケーション接続、同期レプリケーションが開始の確認
    #$result = exec_command("ssh root\@$config_value{\"Another_S-LAN_IPAddress\"} \"su - postgres -c \\\"psql -t -c \\\\\\\"SELECT state FROM pg_stat_replication \\\\\\\" \\\" \"");
    #chomp $result;
    
    @results = ssh_exec_command($config_value{"Another_S-LAN_IPAddress"}, "root", $ssh_pass, "su - postgres -c \"psql -t -c \\\"SELECT state FROM pg_stat_replication \\\" \"");
    
    # SQL result format : <space><value><space>
    $results[0] =~ s/\s//g;
    if ($results[0] ne "streaming"){
        printlog("LOG", SLAVESTART_MS0005);
        printlog("ERROR", SLAVESTART_MS0028);
    }
    printlog("LOG", SLAVESTART_MS0007);

    printlog("LOG", SLAVESTART_MS0029, $node_value{'my_node'});
    exit(0);
}

