#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
use REST::Client;
use File::Slurp;
use JSON::XS;
use YAML::Syck;
use File::Copy "mv";
use Log::Log4perl qw(:easy);
use Fcntl qw(:flock);
use Capture::Tiny ':all';

$| = 1;

my $log_file       = '/root/initial/initial.log';
my $node_conf      = '/root/initial/conf/node_conf.yaml';
my $regions_conf   = '/root/initial/conf/regions.yaml';
my $billing_setup  = 'undef';
my $hal_local_file = '/root/.hal_local_data';

Log::Log4perl->easy_init(
    {
        level  => $DEBUG,
        file   => ">>$log_file",
        layout => '%d - %p %L-%M: %m%n'
    }
);

my $logger = get_logger("");

open my $initial_lock, '<', $0 or $logger->logdie($!);
flock $initial_lock, LOCK_EX | LOCK_NB or exit;

my $host_groups = eval { LoadFile($node_conf) }    or $logger->logdie( $! . " $node_conf" );
my $regions     = eval { LoadFile($regions_conf) } or $logger->logdie( $! . " $regions_conf" );
my $hal_server_info = hal_info( { method => 'hal_server_info', box => 1 } );
if ( -f $hal_local_file ) {
    $hal_server_info = read_local_hal($hal_local_file);
}

unless ( $hal_server_info->{meta}->{prov_stage} eq 'initial' ) {
    $logger->logdie("Cannot Initial, prov_stage meta field not set to 'initial' in HAL");
}

$ENV{BRAND}     = $host_groups->{default}->{division};
$ENV{TIME_ZONE} = 'UTC';
$ENV{LOCATION}  = $regions->{ $hal_server_info->{region_id} } || 'hou_c1';

unless ( -d "/root/initial/tasks" ) {
    mkdir "/root/initial/tasks";
}

my $server_name = $hal_server_info->{hostname};
my ( $host, $domain ) = split( '\.', $server_name, 2 ) or $logger->logdie($!);
my ( $class, $serv_num ) = $host =~ /(^[a-z\-]+)(\d+)/;
my $primary_ip = $hal_server_info->{ip};

$host_groups->{$domain} = $host_groups->{ $hal_server_info->{meta}->{config_group} } if ( $hal_server_info->{meta}->{config_group} );
my $initial_script = "$host_groups->{$domain}->{$class}->{init_script}" or $logger->logdie("Cannot find config for class $class under $domain");

if ( $host_groups->{$domain}->{default}->{time_zone} ) {
    $ENV{TIME_ZONE} = $host_groups->{$domain}->{default}->{time_zone};
}

my $hg_api_methods = {
    foreman_hostgroup_set => {
        method    => 'henson_update_server',
        hostname  => $server_name,
        hostgroup => $host_groups->{$domain}->{$class}->{'henson_hg'},
    },
    foreman_dc_set => {
        method    => 'henson_ensure_param',
        hostname  => $server_name,
        parameter => 'datacenter',
    },
    zabbix_add_server => {
        method        => 'zabbix_add_server',
        ip            => $primary_ip,
        hostname      => $server_name,
        zabbix_group  => $host_groups->{$domain}->{$class}->{zabbix},
        brand         => $host_groups->{$domain}->{default}->{brand},
        zabbix_server => $host_groups->{$domain}->{default}->{zabbix_server} || 'hou_c1',
    },
    hal_prov_stage_update => {
        method     => 'hal_update_meta',
        server_id  => $hal_server_info->{id},
        meta       => 'prov_stage',
        meta_value => $billing_setup
    },
    server_password_get => {
        method   => 'hg_server_pass',
        hostname => $server_name
    },
};

run_tasks();

sub run_tasks {
    set_password();
    sh_command( qq|hostnamectl set-hostname '$server_name'|,                                  'die' );
    sh_command( qq|$initial_script stage1 '$host_groups->{$domain}->{$class}->{henson_env}'|, 'die' );
    if ( !$hal_server_info->{meta}->{foreman_prov} ) {
        hg_api('foreman_hostgroup_set');
    }
    sleep(5);
    sh_command( qq|$initial_script stage2|, 'die' );
    sh_command( qq|$initial_script stage3|, 'die' );
    sh_command( qq|$initial_script stage4|, 'die' );
    hg_api('hal_prov_stage_update');
}

sub hg_api {
    my ( $task, $quiet ) = @_;
    my $task_done = task_lock_check($task);
    if ( $task_done && $task ne 'hal_server_info' ) {
        return 0;
    }
    my $client = REST::Client->new();
    $client->setHost('https://provision.hostgator.com');
    my $url = 'initial.pl';
    my $uri = $client->buildQuery( $hg_api_methods->{$task} );
    $client->GET( $url . "$uri&box=1" );
    my $response = $client->responseContent();
    eval { $response = decode_json($response) };
    $logger->info( encode_json( $hg_api_methods->{$task} ) );

    if ( $client->responseCode() != 200 || !( ref($response) eq "HASH" && $response->{status} ) ) {
        $logger->logdie( $client->responseContent() );
    }
    elsif ( $response->{status} ne 'success' ) {
        $logger->logdie( $client->responseContent() );
    }
    else {
        if ($quiet) {
            $logger->info("$hg_api_methods->{$task}->{method} => success");
        }
        else {
            $logger->info( $client->responseContent() );
        }
    }
    task_lock_set($task);
    return $response->{data};
}

sub hal_info {
    my ( $query, $quiet ) = @_;
    my $client = REST::Client->new();
    $client->setHost('https://provision.hostgator.com');
    my $url = 'initial.pl';
    my $uri = $client->buildQuery($query);
    $client->GET( $url . $uri );
    my $response = $client->responseContent();
    eval { $response = decode_json($response) };
    $logger->info( encode_json($query) );

    if ( $client->responseCode() != 200 || !( ref($response) eq "HASH" && $response->{status} ) ) {
        $logger->logdie( $client->responseContent() );
    }
    elsif ( $response->{status} ne 'success' ) {
        $logger->logdie( $client->responseContent() );
    }
    else {
        if ($quiet) {
            $logger->info("$query->{method} => success");
        }
        else {
            $logger->info( $client->responseContent() );
        }
    }
    return $response->{data};
}

sub set_dc_param {
    my $task      = ( split( '::', ( ( caller(0) )[3] ) ) )[1];
    my $task_done = task_lock_check($task);
    if ( !$task_done ) {
        my ( $region_id, $dc ) = shift;
        if ( $region_id == 1 ) {
            $dc = 'provo';
        }
        elsif ( $region_id == 3 ) {
            $dc = 'c1';
        }
        elsif ( $region_id == 9 ) {
            $dc = 'br';
        }
        elsif ( $region_id == 14 ) {
            $dc = $regions->{ $hal_server_info->{region_id} };
        }
        $hg_api_methods->{foreman_dc_set}->{value} = $dc;
        hg_api('foreman_dc_set');
        task_lock_set($task);
    }
}

sub set_password {
    my $task      = ( split( '::', ( ( caller(0) )[3] ) ) )[1];
    my $task_done = task_lock_check($task);
    if ( !$task_done ) {
        my $crypted_sha = hg_api( 'server_password_get', 1 );
        if ( !$crypted_sha ) {
            $logger->logdie("$task, didn't get password hash");
        }
        sh_command( qq|usermod -p '$crypted_sha' root|, 'die' );
        task_lock_set($task);
    }
}

sub write_conf {
    my ( $data, $conf_file ) = @_;
    $logger->info("$data $conf_file");
    write_file( $conf_file, { err_mode => 'carp' }, $data ) or $logger->logdie($!);
}

sub sh_command {
    my ( $command, $error ) = @_;
    system("$command >> $log_file");
    if ( $? && $error eq 'die' ) {
        $logger->logdie( "$command ", $? >> 8 );
    }
}

sub hal_local_info {
    return 1;
}

sub task_lock_check {
    my ($task) = @_;
    my $task_lock = "/root/initial/tasks/$task.done";
    if ( -f $task_lock ) {
        return 1;
    }
    else {
        return 0;
    }
}

sub task_lock_set {
    my ($task) = @_;
    my $task_lock = "/root/initial/tasks/$task.done";
    my $task_fh;
    open( $task_fh, '>', $task_lock )
      or $logger->logdie("Unable to open file $task_lock : $!");
}
1
