#!/usr/bin/perl
#####################################################################
# Function: pg-rex_slave_start
#
#
# 概要:
# PG-REX での Slave 起動実行ツール。
# 手順の簡易化を目的として作成している。
# 
# 特記事項:
# なし
#
# Copyright (c) 2012,2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#
#####################################################################
use warnings;
use strict;
use Getopt::Long;
use lib qw(/usr/local/share/pg-rex);
use include::command;
use common qw(pacemaker_online pgrex_failed_action network_running master_running
              slave_running vip_running stonith_running pingd_running diskd_running
              read_config read_cib read_hacf exec_command ssh_exec_command
              scp_exec_command get_pg_version get_pg_command_path check_user printlog);

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

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

main();

1;

sub main{
    my $kill_when_no_data = 0;
    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 $copy_dir = "/tmp";
    my $copy_cib_path = $copy_dir."/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 %command_path;
    my $lock_file = "";
    my $exit_code;
    my $ssh_pass;
    my $starting_resource = "";

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

    ### スクリプト実行準備 ###
    # 実行ユーザの確認
    check_user();

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

    # コマンドに必要な情報の取得
    # 環境設定ファイルの読み込み
    %config_value = read_config($config_path);

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

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

    # PostgreSQL のコマンドパスを取得
    %command_path = get_pg_command_path($config_value{"PGPATH"});

    ### 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 (!master_running($node_value{'another_node'}, $config_value{"PG-REX_Master_ResourceID"}, $config_value{"PG-REX_Primitive_ResourceID"}, $ssh_pass, $config_value{"Another_D-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}}){
        @results = ssh_exec_command($config_value{"Another_D-LAN_IPAddress"}, "root", $ssh_pass, "$IFCONFIG $nic");
        
        # IP address format : <inet addr:<value>>
        $results[0] =~ /inet\saddr:(\S+)/;
        if ($results[0] 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 をコピーし読み込む
    scp_exec_command($config_value{'Another_D-LAN_IPAddress'},"root", $ssh_pass, $cib_path, $copy_dir);
    %master_cib_value = read_cib($copy_cib_path, $command_path{'pg_basebackup'}, $config_value{"PG-REX_Primitive_ResourceID"}, $kill_when_no_data);
    exec_command("$RM -f $copy_cib_path");

    # 起動禁止フラグが存在することを確認
    $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_D-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($command_path{'pg_basebackup'});
        if ($result =~ /^9\.1[^0-9]/){
            exec_command("$SU - postgres -c \" $command_path{'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 \" $command_path{'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($config_value{"Another_D-LAN_IPAddress"}, "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, $starting_resource);
        }

        if (!pacemaker_online($node_value{'my_node'})){
            $starting_resource = "Pacemaker";
            next;
        }

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

        if (!network_running($node_value{'my_node'}, $node_value{'another_node'}, @{$hacf_value{ic_dev}})){
            $starting_resource = "IC-LAN monitor";
            next;
        }

        # PINGD のリソース ID 指定有りの場合、起動確認を行なう
        if ($config_value{"PINGD_ResourceID"} && !pingd_running($node_value{'my_node'}, $config_value{"PINGD_ResourceID"})){
            $starting_resource = "PINGD";
            next;
        }

        # DISKD のリソース ID 指定有りの場合、起動確認を行なう
        if ($config_value{"DISKD_ResourceID"} && !diskd_running($node_value{'my_node'}, $config_value{"DISKD_ResourceID"})){
            $starting_resource = "DISKD";
            next;
        }

        # STONITH 環境有りかつリソース ID 指定有りの場合、起動確認を行なう
        if ($config_value{"STONITH_ResourceID"} && !stonith_running($node_value{'my_node'}, $config_value{"STONITH_ResourceID"})){
            $starting_resource = "STONITH";
            next;
        }

        if (!slave_running($node_value{'my_node'}, $config_value{"PG-REX_Master_ResourceID"}, $config_value{"PG-REX_Primitive_ResourceID"})){
            $starting_resource = "PostgreSQL";
            next;
        }

        # VIP-SLAVE 環境有りかつリソース ID 指定有りの場合、起動確認を行なう
        if ($config_value{"VIP-SLAVE_ResourceID"} && !vip_running($node_value{'my_node'}, $config_value{"VIP-SLAVE_ResourceID"})){
            $starting_resource = "VIP-SLAVE";
            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 \" $command_path{'psql'} -U postgres -l > /dev/null \" `;
    $exit_code = $? >> 8;
    if ($exit_code){
        printlog("LOG", SLAVESTART_MS0005);
        printlog("ERROR", SLAVESTART_MS0026);
    }

    # Master の PostgreSQL の wal sender プロセスの確認
    @results = ssh_exec_command($config_value{"Another_D-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 のレプリケーション接続、同期レプリケーションが開始の確認
    @results = ssh_exec_command($config_value{"Another_D-LAN_IPAddress"}, "root", $ssh_pass, "$SU - postgres -c \" $command_path{'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);
}

