#!/usr/bin/perl
#####################################################################
# Function: pg-rex_dbcluster_check
#
#
# 概要:
# PG-REX の Slave 起動時には
# ベースバックアップの取得・展開が必要かどうかを確認する必要がある。
# その確認を自動的に行うツール。
# 
# 特記事項:
# なし
#
# Copyright (c) 2012-2014, 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
              get_ssh_passwd 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 $help_mode = 0;
    my $version_mode = 0;
    my $config_path = "/etc/pg-rex_tools.conf";
    my %config_value;
    my $cib_path = '/var/lib/heartbeat/crm/cib.xml';
    my %cib_value;
    my %node_value;
    my %command_path;
    my $pg_command_user = "postgres";
    my $ssh_pass;
    my $postgresql_conf;
    my $archive_dir;
    my $lock_file;
    my $result;
    my @results;
    my $kill_when_no_data = 0;
    my $operation_num = 1;
	my $myself;

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

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

    # オプション解析
    foreach ( @ARGV ){
        if ( "$_" eq "-" || "$_" eq "--" ){
            $help_mode = 1;
        }
    }
    $result = GetOptions('help'    => \$help_mode,
                         'version' => \$version_mode);
	$myself = $0;
	$myself =~ s/.*\///g;
    if ($help_mode || !$result){
        printlog("VERSION", VERSIONINFO, $myself, VERSIONNUM);
        print "\n";
        printlog("USAGE", DBCHECK_USAGE);
        exit(0);
    }
    if ($version_mode){
        printlog("VERSION", VERSIONINFO, $myself, VERSIONNUM);
        exit(0);
	}

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

    # 環境設定ファイルの読み込み
    %config_value = read_config($config_path);
    $archive_dir = $config_value{'Archive_dir'};

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

    # PostgreSQL のバージョンを確認
    my $pg_version = get_pg_version($command_path{'psql'});

    $result = is_dbcluster_check_support($pg_version);
    if (!$result) {
        printlog("ERROR", DBCHECK_MS0029, $pg_version);
    }

    # ssh 接続の為の情報の取得
    $ssh_pass = get_ssh_passwd($config_value{"Another_D-LAN_IPAddress"}, $config_value{'PEER_NODE_SSH_PASS_MODE'}, $config_value{'PEER_NODE_SSH_PASS_FILE'});

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

    # コマンド実行ノードの 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++);
    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++);
    %cib_value = read_cib($cib_path, $command_path{'psql'}, $config_value{"PG-REX_Primitive_ResourceID"}, $kill_when_no_data, $config_path, $config_value{'ARCHIVE_DELETE_DBCLUSTER_DIR'});

    #  (STEP2) DB クラスタの中のpostgresql.confの有無を確認
    $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++);
    $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 - $pg_command_user -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 - $pg_command_user -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{'time_line_id'} > $another_controldata_value{'time_line_id'}){
        printlog("LOG", DBCHECK_MS0022, $my_controldata_value{'time_line_id'}, $another_controldata_value{'time_line_id'});
        ng_result();
    }

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

    #  (STEP5) 相手ノードのアーカイブから、自身のノードの TLI の時系列履歴ファイルを取得
    my $my_tli_historyfile = sprintf("%08X.history", $my_controldata_value{'time_line_id'});
    my $my_tli_history_path = $archive_dir ."/".$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{'time_line_id'} < $another_controldata_value{'time_line_id'}){

        #  相手ノードの TLI の時系列ファイルを内容を見て確認する
        #    (STEP7) 相手ノードのアーカイブから、相手ノードの TLI の時系列履歴ファイルを取得
        my $another_tli_historyfile = sprintf("%08X.history", $another_controldata_value{'time_line_id'});
        my $another_tli_history_path = $archive_dir ."/".$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{'time_line_id'} 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{'xlog_location'}, $my_controldata_value{'time_line_id'});
        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{'update_time'});
    exit(0);
}


sub ng_result {

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

}

