#!/usr/bin/perl

=head1 NAME

Script to synchronize cluster either as the master node or as a slave node

=head1 SYNOPSIS

  To push out the configuration to the other nodes :
   sync --as-master

  Options :
   --file : use with --as-master to push out only a single file
   --with-fingerbank-db : use with --as-master to push the Fingerbank
                          database during the sync (can be long)

  To sync the configuration from a master server to this server :
   sync --from=<master-ip> --api-user=<master-api-user> --api-password=<master-api-password>

Add files to /usr/local/pf/conf/cluster-files.txt (one per line) to sync additional files during the cluster synchronization.


=head1 DESCRIPTION

Made for keepalived so the pf services can be adjusted depending of the running node

=cut

use strict;
use warnings;

use constant INSTALL_DIR => '/usr/local/pf';
use lib INSTALL_DIR . "/lib";


BEGIN {
  use Log::Log4perl qw(get_logger);
  my $log_conf = q(
  log4perl.rootLogger              = INFO, SCREEN
  log4perl.appender.SCREEN         = Log::Log4perl::Appender::Screen
  log4perl.appender.SCREEN.stderr  = 0
  log4perl.appender.SCREEN.layout  = Log::Log4perl::Layout::PatternLayout
  log4perl.appender.SCREEN.layout.ConversionPattern = %p : %m %n
  );
  Log::Log4perl::init(\$log_conf);
}

use pf::api::jsonrpcclient;
use Getopt::Long;
use pf::api::jsonrpcclient;
use pfconfig::constants;
use pf::file_paths qw(
    $config_version_file
);
use pf::constants::cluster qw(@FILES_TO_SYNC);
use pf::cluster;
use fingerbank::FilePath;
use Pod::Usage;


my ($master_server, $as_master, $api_user, $api_password, $with_fingerbank_db, $file, $help);
GetOptions (
    "from=s"   => \$master_server,
    "api-user=s" => \$api_user,
    "api-password=s" => \$api_password,
    "as-master" => \$as_master,
    "with-fingerbank-db" => \$with_fingerbank_db,
    "file=s" => \$file,
    "help|h" => \$help,
);

if($help){
  pod2usage( -verbose => 1 );
}

unless($master_server || $as_master){
    get_logger->error("You should either set --from or --as-master");
    exit;
}

my @stores = @{pf::cluster::stores_to_sync()};
my @files = @FILES_TO_SYNC;
my @added_files;

my $logger = get_logger();

if (-e '/usr/local/pf/conf/cluster-files.txt') {
    open(my $fh, '<', '/usr/local/pf/conf/cluster-files.txt')
      or get_logger->warn("Couldn't open the list of additionnal files to sync.");

    while (my $row = <$fh>) {
        chomp $row;
        push @files, $row;
        push @added_files, $row;
    }
    close($fh);
}

if($as_master){
    pf::config::cluster::increment_config_version();
    if($file){
        unless($file =~ /^\//){
            get_logger->fatal("File path has to be absolute.");
            exit 1;
        }
        unless(-e $file){
            get_logger->fatal("File does not exist.");
            exit 1;
        }
        get_logger->info("Synching file $file with other nodes in the cluster");
        pf::cluster::sync_files([$file]);
    }
    else {
        get_logger->info("Synching cluster with this node as the configuration master");
        pf::cluster::sync_config_as_master();
        pf::cluster::sync_files(\@added_files);
        if($with_fingerbank_db) {
            pf::cluster::sync_files([$fingerbank::FilePath::UPSTREAM_DB_FILE]);
        }
    }

}

if($master_server){
    get_logger->info("Synching this server from node $master_server");

    my $apiclient = pf::api::jsonrpcclient->new(host => $master_server, proto => 'https', username => $api_user, password => $api_password);

    foreach my $store (@stores) {
        my $cs = $store->new;
        my $config_file = $cs->configFile;
        push @files, $config_file if $config_file;
    }

    push @files, $config_version_file;

    foreach my $file (@files){
        eval {
            get_logger->info("Synching file : $file");
            my %data = ( conf_file => $file );
            my ($result) = $apiclient->call( 'download_configfile', %data );
            open(my $fh, '>', $file);
            print $fh $result;
            close($fh);
            `chown pf.pf $file`;
        };
        if($@){
            get_logger->error("Failed to sync file : $file . $@");
        }
    }


}

=back

=head1 AUTHOR

Inverse inc. <info@inverse.ca>

=head1 COPYRIGHT

Copyright (C) 2005-2021 Inverse inc.

=head1 LICENSE

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
USA.

=cut

1;

# vim: set shiftwidth=4:
# vim: set expandtab:
# vim: set backspace=indent,eol,start:

