#!/usr/bin/perl
#####################################################################
# Function: pg-rex_dbcluster_check
#
#
# 概要:
# PG-REX の Slave 起動時には
# ベースバックアップの取得・展開が必要かどうかを確認する必要がある。
# その確認を自動的に行うツール。
# 
# 特記事項:
# なし
#
# Copyright (c) 2012,2013, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
#
#####################################################################
use warnings;
use strict;
use Getopt::Long;
use File::Compare;
use lib qw(/usr/local/share/pg-rex);
use include::command;
use common qw(master_running read_config read_cib exec_command
              ssh_exec_command scp_exec_command get_xlogfile_name
              get_controldata_value get_node 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", DBCHECK_MS0001);
};

main();

1;

sub main{
    my $kill_when_no_data = 0;
    my $result;
    my @results;
    my $ssh_pass;
    my $help_mode = 0;

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

    # 実行ユーザの確認
    check_user();

    # オプション解析
    GetOptions('help' => \$help_mode);

    if ($help_mode){
        printlog("USAGE", DBCHECK_USAGE);
        exit(0);
    }

    my $operation_num = 1;

    ### 動作環境の確認 ###
    printlog("LOG", DBCHECK_MS0002);

    # 環境設定ファイルの読み込み
    my $config_path = "/etc/pg-rex_tools.conf";
    my %config_value = read_config($config_path);
    my $archivedir = $config_value{'Archive_dir'};

    # ssh 接続の為の情報の取得
    printlog("LOG", DBCHECK_MS0029, $config_value{'Another_D-LAN_IPAddress'});
    `$STTY -echo`;
    $ssh_pass = <STDIN>;
    chomp $ssh_pass;
    `$STTY echo`;
    printlog("LOG", DBCHECK_MS0030);
    
    # コマンドを実行しているマシンのノード名ともう一台のノード名を取得
    printlog("LOG", DBCHECK_MS0003, $operation_num++);
    my %node_value = get_node($config_value{'Another_D-LAN_IPAddress'}, $ssh_pass);
    printlog("LOG", DBCHECK_MS0005);

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

    # コマンド実行ノードの Pacemaker の稼働確認
    printlog("LOG", DBCHECK_MS0006, $operation_num++);
    $result = `$SERVICE heartbeat status`;
    chomp $result;
    if ($result ne "heartbeat is stopped. No process"){
        printlog("LOG", DBCHECK_MS0007);
        printlog("ERROR", DBCHECK_MS0008);
    }
    printlog("LOG", DBCHECK_MS0005);

    printlog("LOG", DBCHECK_MS0009, $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", DBCHECK_MS0007);
        printlog("ERROR", DBCHECK_MS0011);
    }
    printlog("LOG", DBCHECK_MS0005);

    ### 以降確認を行う ###
    printlog("LOG", DBCHECK_MS0012);
    $operation_num = 1;

    ### 確認1「初回起動時の場合」 ###
    printlog("LOG", DBCHECK_MS0013, $operation_num++);
    my $cib_path = '/var/lib/heartbeat/crm/cib.xml';
    if (! -f $cib_path){
        printlog("LOG", DBCHECK_MS0014, $cib_path);
        ng_result();
    }
    printlog("LOG", DBCHECK_MS0005);

    ### 確認2「 DB クラスタが存在しない場合」 ###
    #  (STEP1) cib.xml ファイル読み込み
    printlog("LOG", DBCHECK_MS0015, $operation_num++);
    my %cib_value = read_cib($cib_path, $command_path{'pg_basebackup'}, $config_value{"PG-REX_Primitive_ResourceID"}, $kill_when_no_data);

    #  (STEP2) DB クラスタの中のpostgresql.confの有無を確認
    my $postgresql_conf = $cib_value{'pgdata'}."/postgresql.conf";
    if (! -f $postgresql_conf){
        printlog("LOG", DBCHECK_MS0017, $postgresql_conf);
        ng_result();
    }
    printlog("LOG", DBCHECK_MS0005);

    ### 確認3「起動禁止フラグが存在する場合」 ###
    printlog("LOG", DBCHECK_MS0018, $operation_num++);
    my $lock_file = $cib_value{'tmpdir'}."/PGSQL.lock";
    if (-f $lock_file){
        printlog("LOG", DBCHECK_MS0019, $lock_file);
        ng_result();
    }
    printlog("LOG", DBCHECK_MS0005);

    ### 確認4「 DB クラスタが非常に古い場合」 ###
    # ※確認せず※

    ### 確認5「相手ノードで稼働中の Master が、ベースバックアップから起動されていた場合」 ###
    printlog("LOG", DBCHECK_MS0020, $operation_num++);

    #  (STEP1) 両系の pg_controldata からデータベース識別子・ TLI ・XLOG の位置を取得
    #          pg_controldata は "C" ロケールにしてから実行
    $result = exec_command("$SU - postgres -c \"export LANG=C; $command_path{'pg_controldata'} $cib_value{'pgdata'}\"");
    my @my_controldata_strings = split(/\n/, $result);
    my %my_controldata_value = get_controldata_value(@my_controldata_strings);

    @results = ssh_exec_command($config_value{'Another_D-LAN_IPAddress'}, "root", $ssh_pass, "$SU - postgres -c \"export LANG=C; $command_path{'pg_controldata'} $cib_value{'pgdata'}\"");
    my @another_controldata_strings = split(/\n/, $results[0]);
    my %another_controldata_value = get_controldata_value(@another_controldata_strings);

    #  (STEP2) データベース識別子を比較
    if ($my_controldata_value{'database'} ne $another_controldata_value{'database'}){
        printlog("LOG", DBCHECK_MS0021, $my_controldata_value{'database'}, $another_controldata_value{'database'});
        ng_result();
    }

    #  (STEP3) TLI を比較
    if ($my_controldata_value{'timelineid'} > $another_controldata_value{'timelineid'}){
        printlog("LOG", DBCHECK_MS0022, $my_controldata_value{'timelineid'}, $another_controldata_value{'timelineid'});
        ng_result();
    }

    #  (STEP4) XLOG の位置を比較
    if ($my_controldata_value{'xloglocation'} gt $another_controldata_value{'xloglocation'}){
        printlog("LOG", DBCHECK_MS0023, $my_controldata_value{'xloglocation'}, $another_controldata_value{'xloglocation'});
        ng_result();
    }

    #  (STEP5) 相手ノードのアーカイブから、自身のノードの TLI の時系列履歴ファイルを取得
    my $my_tli_historyfile = sprintf("%08X.history", $my_controldata_value{'timelineid'});
    my $my_tli_history_path = $archivedir ."/".$my_tli_historyfile;
    my $copy_dir = "/tmp/";
    scp_exec_command($config_value{'Another_D-LAN_IPAddress'},"root", $ssh_pass, $my_tli_history_path, $copy_dir);

    #  (STEP6) 自身ノードの TLI の時系列履歴ファイルを比較
    my $copy_tli_historyfile = $copy_dir.$my_tli_historyfile;
    if (compare($my_tli_history_path, $copy_tli_historyfile)){
        printlog("LOG", DBCHECK_MS0024, $my_tli_history_path);
        ng_result();
    }
    #  (STEP6後処理) /tmp 配下にコピーした時系列履歴ファイルの削除
    exec_command("$RM -f $copy_tli_historyfile");

    #  STEP7 以降は STEP4 で両系の TLI が同じでないときに確認する
    if ($my_controldata_value{'timelineid'} < $another_controldata_value{'timelineid'}){

        #  相手ノードの TLI の時系列ファイルを内容を見て確認する
        #    (STEP7) 相手ノードのアーカイブから、相手ノードの TLI の時系列履歴ファイルを取得
        my $another_tli_historyfile = sprintf("%08X.history", $another_controldata_value{'timelineid'});
        my $another_tli_history_path = $archivedir ."/".$another_tli_historyfile;
        @results = ssh_exec_command($config_value{'Another_D-LAN_IPAddress'}, "root", $ssh_pass, "$CAT $another_tli_history_path");

        #    (STEP8) 自身ノードの TLI が過去に通っていることを確認
        my $my_tli_promote_xloglocation = '';
        my @another_til_history_strings = split(/\n/, $results[0]);
        foreach my $line (@another_til_history_strings){
            if ($line =~ /^(\d+)\s+([0-9A-F]+)/){
                if ($my_controldata_value{'timelineid'} eq $1){
                    $my_tli_promote_xloglocation = $2;
                }
            }
        }
        if (!$my_tli_promote_xloglocation){
            printlog("LOG", DBCHECK_MS0025, $another_tli_historyfile);
            ng_result();
        }

        #    (STEP9) 自身ノードの XLOG の位置よりも先に promote されていないことを確認
        #    ※ファイル名で比較するため正しい位置まで比較できない※
        my $my_xloglocation = get_xlogfile_name($my_controldata_value{'xloglocation'}, $my_controldata_value{'timelineid'});
        if ($my_xloglocation gt $my_tli_promote_xloglocation){
            printlog("LOG", DBCHECK_MS0026, $another_tli_historyfile);
            ng_result();
        }

    }
    printlog("LOG", DBCHECK_MS0005);

    # ベースバックアップ取得不要を返却し、スクリプトを終了する
    printlog("LOG", DBCHECK_MS0027, $my_controldata_value{'updatetime'});
    exit(0);
}


sub ng_result {

    printlog("LOG", DBCHECK_MS0007);
    printlog("ERROR", DBCHECK_MS0028);

}

