#!/usr/bin/perl
#####################################################################
# Function: pg-rex_master_start
#
#
# 概要:
# PG-REX での Master 起動実行ツール。
# 手順の簡易化を目的として作成している。
# 
# 特記事項:
# なし
#
# 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 master_running
              vip_running stonith_running pingd_running diskd_running
              read_config read_cib read_hacf exec_command
              get_pg_version get_pg_command_path get_pm_version 
              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", MASTERSTART_MS0001);
};

main();

1;

sub main{
    my $kill_when_no_data = 0;
    my $help_mode = 0;
    my $crm_path = "";
    my $operation_num = 1;
    my $key;
    my $value;
    my $resource_id;
    my @crm_paramlist = ("tmp_dir", "pgdata", "master_ip", "repuser");
    my %crm_value;
    my $tmp_dir;
    my $dbcluster_dir;
    my $pgsql_data_status;
    my $check;
    my %node_value;
    my $result;
    my @results;
    my $pm_version;
    my $timeout = 300;
    my $monitor_time = 10;
    my $wait_time = 0;
    my $process_check;
    my $cib_path = "/var/lib/heartbeat/crm/cib.xml";
    my %cib_value;
    my $config_path = "/etc/pg-rex_tools.conf";
    my %config_value;
    my %command_path;
    my $hacf_path = "/etc/ha.d/ha.cf";
    my $lock_file = "";
    my $exit_code;
    my $ssh_pass;
    my $starting_resource = "";

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

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

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

    # 引数の確認
    if ($#ARGV >= 0){
        $crm_path = $ARGV[0];
        if (! -e $crm_path){
            printlog("ERROR", MASTERSTART_MS0002);
        }
    }

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

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

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

    # 引数に crm ファイルが指定されている場合は crm ファイルを読み込む
    # そうでない場合は cib.xml ファイルを読み込む
    if ($crm_path ne ""){
        open (FILE, $crm_path) or printlog("ERROR", MASTERSTART_MS0004);

        # PG-REX のバージョンを確認し、その結果に合わせて tmpdir のデフォルト値を設定する
        $result = get_pg_version($command_path{'pg_basebackup'});
        if ($result =~ /^9\.1[^0-9]/){
            $tmp_dir = "/var/lib/pgsql";
        }
        else{
            $tmp_dir = "/var/lib/pgsql/tmp";
        }

        # tmpdir に指定してある値を取得
        while (<FILE>){
            # <PostgreSQLのPrimitiveのリソースID> name format : primitive <resource_id> ocf:heartbeat:pgsql
            if ($_=~ /primitive\s+(.*)\s+ocf:heartbeat:pgsql/){
                $resource_id = $1;
            }
            # <tmp_dir, pgdata, master_ip, repuser のパラメータ取得> name format : $key = $1
            if (defined($resource_id) && $resource_id eq $config_value{"PG-REX_Primitive_ResourceID"}){
                foreach my $key (@crm_paramlist){
                    if ($_ =~ /$key="(.*)"/){
                        $crm_value{$key} = $1;
                        if ($key eq "tmp_dir"){
                            $tmp_dir = $1;
                        }
                    }
                }
            }
        }
        close (FILE);

        foreach my $key (@crm_paramlist) {
            if ($key ne "tmp_dir" && (!exists($crm_value{$key}) || $crm_value{$key} eq '')){
                printlog("ERROR", MASTERSTART_MS0033, $config_value{"PG-REX_Primitive_ResourceID"}, $key);
            }
        }

        $dbcluster_dir = $crm_value{"pgdata"};
    }
    else {
        %cib_value = read_cib($cib_path, $command_path{'pg_basebackup'}, $config_value{"PG-REX_Primitive_ResourceID"}, $kill_when_no_data);
        $tmp_dir = $cib_value{"tmpdir"};
        $dbcluster_dir = $cib_value{"pgdata"};
        $pgsql_data_status = $cib_value{"pgsql_data_status"};
    }

    # コマンドを実行しているマシンのノード名ともう一台のノード名を取得
    %node_value = get_node($config_value{'Another_D-LAN_IPAddress'}, $ssh_pass);

    # ha.cf の設定項目の内、bcast(or ucast or mcast(6))、node の設定値が空文字では無いかの確認
    # (read_hacf() は戻り値 (bcast(or ucast or mcast(6))、node の設定値)が発生するが、ここでは設定値の
    # 空文字では無いかの確認しか行なわない為、戻り値を取得しない)
    read_hacf($hacf_path, "start");

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

    ### 稼働中の Master の存在確認 ###
    printlog("LOG", MASTERSTART_MS0011, $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", MASTERSTART_MS0008);
        printlog("ERROR", MASTERSTART_MS0013);
    }
    printlog("LOG", MASTERSTART_MS0010);

    ### 初回起動以外の場合、Master として稼働することが可能かの確認 ###
    if ($crm_path eq ""){
        printlog("LOG", MASTERSTART_MS0027, $operation_num++);
        if($pgsql_data_status && $pgsql_data_status ne "LATEST" && $pgsql_data_status ne "STREAMING|SYNC"){
            printlog("LOG", MASTERSTART_MS0008);
            printlog("ERROR", MASTERSTART_MS0028, $pgsql_data_status);
        }
        printlog("LOG", MASTERSTART_MS0010);
    }

    ### 起動禁止フラグの存在確認 ###
    printlog("LOG", MASTERSTART_MS0014, $operation_num++);
    $lock_file = $tmp_dir."/PGSQL.lock";
    if (-e $lock_file){
        printlog("LOG", MASTERSTART_MS0008);
        printlog("ERROR", MASTERSTART_MS0015, $lock_file);
    }
    printlog("LOG", MASTERSTART_MS0010);

    # /var/lib/heartbeat/crm 配下のファイルの削除
    if ($crm_path ne "" && -e $cib_path){
        printlog("LOG", MASTERSTART_MS0016);
        $check = <STDIN>;
        chomp $check;
        if ($check !~ m/^y$/i) {
            printlog("LOG", MASTERSTART_MS0017);
            exit(0);
        }
        ### /var/lib/heartbeat/crm 配下のファイルの削除 ###
        printlog("LOG", MASTERSTART_MS0018, $operation_num++);
        exec_command("$RM -f /var/lib/heartbeat/crm/*");
        printlog("LOG", MASTERSTART_MS0010);
    }

    ### Pacemaker 起動 ###
    printlog("LOG", MASTERSTART_MS0019, $operation_num++);
    exec_command("$SERVICE heartbeat start");
    printlog("LOG", MASTERSTART_MS0010);

    ### crm ファイルを引数で指定した場合 ###
    if ($crm_path ne ""){
        printlog("LOG", MASTERSTART_MS0020, $operation_num++);
        # crm ファイルを反映できる状態まで待機
        $starting_resource = "Pacemaker(not reflected crm file)";
        while (1){
            if (pacemaker_online($node_value{'my_node'})){
                last;
            }

            sleep $monitor_time;
            $wait_time += $monitor_time;
            if ($wait_time >= $timeout){
                printlog("LOG", MASTERSTART_MS0008);
                printlog("ERROR", MASTERSTART_MS0021, $timeout, $starting_resource);
            }
        }
        sleep 10;
        
        # crm ファイルを反映
        # Pacemaker のバージョンを確認し、バージョンが 1.0.13 以外だったら
        # crm ファイル反映時のエラーの確認を行なう
        # エラーが発生したら起動処理を終了する
        # Pacemaker のバージョンが 1.0.13 の場合は、crm ファイル反映が
        # 正しく行なわれてもエラーが発生する場合があるので、
        # 反映時のエラーの確認は行なわない
        $pm_version = get_pm_version();
        exec_command("$CRM options sort-elements no");
        $result = `$CRM --force configure load update $crm_path 2>&1`;
        if ($pm_version ne "1.0.13" && $result =~ /ERROR/){
            printlog("LOG", MASTERSTART_MS0008);
            printlog("ERROR", MASTERSTART_MS0032, $crm_path);
        }
        printlog("LOG", MASTERSTART_MS0010);
    }

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

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

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

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

        # 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 (!master_running($node_value{'my_node'}, $config_value{"PG-REX_Master_ResourceID"}, $config_value{"PG-REX_Primitive_ResourceID"})){
            $starting_resource = "PostgreSQL";
            next;
        }

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

        # VIP-REP のリソース ID 指定有りの場合、起動確認を行なう
        if ($config_value{"VIP-REP_ResourceID"} && !vip_running($node_value{'my_node'}, $config_value{"VIP-REP_ResourceID"})){
            $starting_resource = "VIP-REP";
            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+$dbcluster_dir\s+/){
            last;
        }

        if ($wait_time >= $timeout){
            printlog("LOG", MASTERSTART_MS0008);
            printlog("ERROR", MASTERSTART_MS0024, $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", MASTERSTART_MS0008);
        printlog("ERROR", MASTERSTART_MS0025);
    }
    printlog("LOG", MASTERSTART_MS0010);

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

