anvil/tools/anvil-sync-shared
Digimer 41cd1e0319 * Several bugs fixed and enhancements;
* DRBD is now configured to a ping-timeout of 3 seconds.
* Created Log->switches() that returnes the command line switches used by Anvil! tool command line calls based on the active log levels / secure logging. Appended this to all invocations of our tools.
* Updated Database->resync_databases() to now only skip 'jobs' and 'variables' tables with less than 10 record differences. All other differences will trigger a resync.
* Created System->_check_anvil_conf() that, as you might guess, checks in anvil.conf exists and created it (using defaults), if not. It also checks to see if the 'admin' group and user exists and creates them, if not.
* Updated anvil-daemon to check anvil.conf on start up and in each loop. Created the function check_journald() that checks (and sets, if needed) that journald logging is persistent.
* Made striker-manage-peers to check_if_configured on the Database->connect() when updating anvil.conf and the target UUID is the local machine. Also created a loop to make the reconnection a lot more robust.

Signed-off-by: Digimer <digimer@alteeve.ca>
2021-05-24 00:09:32 -04:00

1209 lines
43 KiB
Perl
Executable File

#!/usr/bin/perl
#
# This runs on striker dashboards and syncs files under /mnt/shared on all known systems. It reaches out and
# pulls over any files under /mnt/shared/files/ to the same on the local system. It then pushes files out to
# all members of the same Anvil!.
#
# If this is called with a job-uuid, file-specific tasks will be handled, like moving files uploaded over a
# browser or deleting / purging a file.
#
# NOTE: This file is NOT responsible for sync'ing definition files! That is handles in scan-server.
#
# TODO:
# - Handle deleting files by user input, or if a given file that was on an Anvil! has been removed for both
# nodes and DR, where applicable.
# -
use strict;
use warnings;
use Anvil::Tools;
use Data::Dumper;
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0];
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0];
if (($running_directory =~ /^\./) && ($ENV{PWD}))
{
$running_directory =~ s/^\./$ENV{PWD}/;
}
# Turn off buffering so that the pinwheel will display while waiting for the SSH call(s) to complete.
$| = 1;
my $anvil = Anvil::Tools->new();
# Read switches (target ([user@]host[:port]) and the file with the target's password.
$anvil->data->{switches}{'job-uuid'} = "";
$anvil->Get->switches;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0115", variables => { program => $THIS_FILE }});
# Connect to the database(s).
$anvil->Database->connect;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, key => "log_0132"});
# If we don't have a job-uuid, look for one.
if (not $anvil->data->{switches}{'job-uuid'})
{
# Load the job data.
$anvil->data->{switches}{'job-uuid'} = $anvil->Job->get_job_uuid({debug => 2, program => $THIS_FILE});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'} }});
}
# If we still don't have a job-uuit, go into interactive mode.
if ($anvil->data->{switches}{'job-uuid'})
{
# Load the job data.
$anvil->Job->get_job_details({debug => 3});
$anvil->Job->clear({debug => 3});
$anvil->data->{sys}{progress} = 1;
if ($anvil->data->{jobs}{job_name} eq "storage::move_incoming")
{
process_incoming_file($anvil);
}
if ($anvil->data->{jobs}{job_name} eq "storage::pull_file")
{
process_pull_file($anvil);
}
if ($anvil->data->{jobs}{job_name} eq "storage::purge")
{
process_purge_file($anvil);
}
if ($anvil->data->{jobs}{job_name} eq "storage::rename")
{
process_rename_file($anvil);
}
if ($anvil->data->{jobs}{job_name} eq "storage::check_mode")
{
process_file_mode($anvil);
}
}
else
{
# This checks to see if there are any files that need to be pulled, renamed, removed, or mode
# updated.
#run_checks($anvil);
}
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Functions #
#############################################################################################################
### TODO: Left off here. Run periodic checks for files that need to be pulled/removed, mode changed,
### etc. For 3.0, we'll ignore files people add manually though later we'll probably want to
### auto-sync them.
### NOTE: When finding new files, check the size, sleep for 30 seconds, and check again. If a file's
### size changed, skip it, it's likely still being updated.
### NOTE: This isn't finished, and likely won't be for a while.
sub run_checks
{
my ($anvil) = @_;
# First, get a list of files we're expected to have.
$anvil->Database->get_files({debug => 3, include_deleted => 1});
$anvil->Database->get_file_locations({debug => 3});
if ($anvil->Get->host_type eq "striker")
{
# Look for files to add
}
else
{
# Are we an Anvil! member?
my $anvil_uuid = $anvil->Cluster->get_anvil_uuid();
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { anvil_uuid => $anvil_uuid }});
if (not $anvil_uuid)
{
# Nothing to do.
$anvil->nice_exit({exit_code => 0});
}
# What files should we have?
foreach my $file_name (sort {$a cmp $b} keys %{$anvil->data->{files}{file_name}})
{
# TODO: Once per day, calculate the md5sums and compare against the DB.
my $file_uuid = $anvil->data->{files}{file_name}{$file_name}{file_uuid};
my $file_directory = $anvil->data->{files}{file_name}{$file_name}{file_directory};
my $file_size = $anvil->data->{files}{file_name}{$file_name}{file_size};
my $file_type = $anvil->data->{files}{file_name}{$file_name}{file_type};
my $file_path = $file_directory."/".$file_name;
my $file_location_uuid = $anvil->data->{file_locations}{anvil_uuid}{$anvil_uuid}{file_uuid}{$file_uuid}{file_location_uuid};
my $file_location_active = $anvil->data->{file_locations}{file_location_uuid}{$file_location_uuid}{file_location_active};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
file_uuid => $file_uuid,
file_name => $file_name,
file_directory => $file_directory,
file_size => $file_size." (".$anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}).")",
file_type => $file_type,
file_path => $file_path,
file_location_uuid => $file_location_uuid,
file_location_active => $file_location_active,
}});
if (-e $file_path)
{
# File exists, should it?
}
else
{
# File doesn't exist, should it?
}
}
}
return(0);
}
sub process_file_mode
{
my ($anvil) = @_;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0205",
});
# We have to include deleted because purges will already have the file flagged as deleted.
$anvil->Database->get_files({debug => 3, include_deleted => 1});
$anvil->Database->get_file_locations({debug => 3});
my $file_uuid = ($anvil->data->{jobs}{job_data} =~ /file_uuid=(.*)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }});
if (not $file_uuid)
{
# Can't do anything, file wasn't parsed.
$anvil->Job->update_progress({
progress => 100,
message => "error_0185,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0185", variables => { job_uuid => $anvil->data->{switches}{'job-uuid'} }});
$anvil->nice_exit({exit_code => 1});
}
elsif (not exists $anvil->data->{files}{file_uuid}{$file_uuid})
{
# File UUID doesn't appear to be valid.
$anvil->Job->update_progress({
progress => 100,
message => "error_0186,!!file_uuid!".$file_uuid."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0186", variables => { file_uuid => $file_uuid }});
$anvil->nice_exit({exit_code => 1});
}
my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name};
my $file_directory = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory};
my $file_path = $file_directory."/".$file_name;
my $file_type = $anvil->data->{files}{file_uuid}{$file_uuid}{file_type};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:file_name' => $file_name,
's2:file_directory' => $file_directory,
's3:file_path' => $file_path,
's4:file_type' => $file_type,
}});
# From here, we'll unlink if it exists. If it doesn't exist, we still exit sucessfully.
if (-e $file_path)
{
# Change the mode, if need it.
my $new_mode = "a-x";
if ($file_type eq "script")
{
$new_mode = "a+x";
}
$anvil->Storage->change_mode({
path => $file_path,
mode => $new_mode,
});
$anvil->Job->update_progress({
progress => 100,
message => "job_0145,!!file_path!".$file_path."!!,!!new_mode!".$new_mode."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "job_0145", variables => {
file_path => $file_path,
new_mode => $new_mode,
}});
$anvil->nice_exit({exit_code => 0});
}
else
{
# Doesn't exist, nothing to do.
$anvil->Job->update_progress({
progress => 100,
message => "job_0146,!!file_path!".$file_path."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "job_0146", variables => { file_path => $file_path }});
$anvil->nice_exit({exit_code => 0});
}
return(0);
}
sub process_rename_file
{
my ($anvil) = @_;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0204",
});
$anvil->Database->get_files({debug => 3});
$anvil->Database->get_file_locations({debug => 3});
my $file_uuid = "";
my $old_name = "";
my $new_name = "";
foreach my $line (split/\n/, $anvil->data->{jobs}{job_data})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { line => $line }});
if ($line =~ /file_uuid=(.*)$/)
{
$file_uuid = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }});
}
if ($line =~ /old_name=(.*)$/)
{
$old_name = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { old_name => $old_name }});
}
if ($line =~ /new_name=(.*)$/)
{
$new_name = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { new_name => $new_name }});
}
}
if (not $file_uuid)
{
# Can't do anything, file wasn't parsed.
$anvil->Job->update_progress({
progress => 100,
message => "error_0181,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0181", variables => { job_uuid => $anvil->data->{switches}{'job-uuid'} }});
$anvil->nice_exit({exit_code => 1});
}
elsif (not exists $anvil->data->{files}{file_uuid}{$file_uuid})
{
# File UUID doesn't appear to be valid.
$anvil->Job->update_progress({
progress => 100,
message => "error_0182,!!file_uuid!".$file_uuid."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0182", variables => { file_uuid => $file_uuid }});
$anvil->nice_exit({exit_code => 1});
}
my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name};
my $file_directory = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory};
my $old_file_path = $file_directory."/".$old_name;
my $new_file_path = $file_directory."/".$new_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:file_name' => $file_name,
's2:file_directory' => $file_directory,
's3:old_file_path' => $old_file_path,
's4:new_file_path' => $new_file_path,
}});
if (not $new_name)
{
# Can't do anything, file wasn't parsed.
$anvil->Job->update_progress({
progress => 100,
message => "error_0183,!!file_name!".$new_file_path."!!,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0183", variables => {
file_name => $new_file_path,
job_uuid => $anvil->data->{switches}{'job-uuid'},
}});
$anvil->nice_exit({exit_code => 1});
}
# If the file doesn't exist, there's nothing to do (and that's OK).
if (-e $old_file_path)
{
# Move the file.
$anvil->Job->update_progress({
progress => 50,
message => "job_0141,!!old_file!".$old_file_path."!!,!!new_file!".$new_file_path."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "job_0141", variables => {
old_file => $old_file_path,
new_file => $new_file_path,
}});
$anvil->Storage->move_file({
source_file => $old_file_path,
target_file => $new_file_path,
});
if ((not -e $old_file_path) && (-e $new_file_path))
{
# Success!
$anvil->Job->update_progress({
progress => 100,
message => "job_0142",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "job_0142"});
$anvil->nice_exit({exit_code => 0});
}
elsif ((-e $old_file_path) && (not -e $new_file_path))
{
# Move failed
$anvil->Job->update_progress({
progress => 1,
message => "error_0184",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0184"});
sleep 60;
$anvil->nice_exit({exit_code => 1});
}
}
else
{
# Nothing to do
$anvil->Job->update_progress({
progress => 100,
message => "job_0140,!!file_path!".$old_file_path."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "job_0140", variables => { file_path => $old_file_path }});
$anvil->nice_exit({exit_code => 0});
}
return(0);
}
sub process_purge_file
{
my ($anvil) = @_;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0203",
});
# We have to include deleted because purges will already have the file flagged as deleted.
$anvil->Database->get_files({debug => 3, include_deleted => 1});
$anvil->Database->get_file_locations({debug => 3});
my $file_uuid = ($anvil->data->{jobs}{job_data} =~ /file_uuid=(.*)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }});
if (not $file_uuid)
{
# Can't do anything, file wasn't parsed.
$anvil->Job->update_progress({
progress => 100,
message => "error_0178,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0178", variables => { job_uuid => $anvil->data->{switches}{'job-uuid'} }});
$anvil->nice_exit({exit_code => 1});
}
elsif (not exists $anvil->data->{files}{file_uuid}{$file_uuid})
{
# File UUID doesn't appear to be valid.
$anvil->Job->update_progress({
progress => 100,
message => "error_0179,!!file_uuid!".$file_uuid."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0179", variables => { file_uuid => $file_uuid }});
$anvil->nice_exit({exit_code => 1});
}
my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name};
my $file_directory = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory};
my $file_path = $file_directory."/".$file_name;
my $file_type = $anvil->data->{files}{file_uuid}{$file_uuid}{file_type};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:file_name' => $file_name,
's2:file_directory' => $file_directory,
's3:file_path' => $file_path,
's4:file_type' => $file_type,
}});
# From here, we'll unlink if it exists. If it doesn't exist, we still exit sucessfully.
if (-e $file_path)
{
# Remove it
unlink $file_path;
if (-e $file_path)
{
# Failed.
my $error = $!;
$anvil->Job->update_progress({
progress => 100,
message => "error_0180,!!file_path!".$file_path."!!,!!error!".$error."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0180", variables => {
file_path => $file_path,
error => $error,
}});
$anvil->nice_exit({exit_code => 1});
}
else
{
# Success!
$anvil->Job->update_progress({
progress => 100,
message => "job_0134,!!file_path!".$file_path."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "job_0134", variables => { file_path => $file_path }});
$anvil->nice_exit({exit_code => 0});
}
}
else
{
# Doesn't exist, nothing to do.
$anvil->Job->update_progress({
progress => 100,
message => "job_0135,!!file_path!".$file_path."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "job_0135", variables => { file_path => $file_path }});
$anvil->nice_exit({exit_code => 0});
}
return(0);
}
sub process_incoming_file
{
my ($anvil) = @_;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0191",
});
my $file = ($anvil->data->{jobs}{job_data} =~ /file=(.*)$/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file => $file }});
if (not $file)
{
# Can't do anything, file wasn't parsed.
$anvil->Job->update_progress({
progress => 100,
message => "error_0170,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0170", variables => { job_uuid => $anvil->data->{switches}{'job-uuid'} }});
$anvil->nice_exit({exit_code => 1});
}
elsif (not -e $file)
{
# Can't do anything, file doesn't exist
$anvil->Job->update_progress({
progress => 100,
message => "error_0171,!!file!".$file."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0171", variables => { file => $file }});
$anvil->nice_exit({exit_code => 1});
}
# Move it over to files.
$anvil->data->{sys}{progress} = 10;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0192,!!file!".$file."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0192", variables => { file => $file }});
$anvil->Storage->move_file({
debug => 2,
overwrite => 1,
source_file => $file,
target_file => $anvil->data->{path}{directories}{shared}{files}."/",
});
my $file_name = ($file =~ /\/.*\/(.*?)$/)[0];
my $target_file = $anvil->data->{path}{directories}{shared}{files}."/".$file_name;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
file_name => $file_name,
target_file => $target_file,
}});
if (not -e $target_file)
{
# Failed to move.
$anvil->Job->update_progress({
progress => 100,
message => "error_0172,!!file!".$file."!!,!!target_directory!".$anvil->data->{path}{directories}{shared}{files}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0171", variables => { file => $file }});
$anvil->nice_exit({exit_code => 1});
}
# Change the owner as it'll be apache, which won't be a valid users on anvil members.
$anvil->Storage->change_owner({
debug => 2,
path => $target_file,
user => "root",
group => "root",
});
# Calculate the md5sum.
$anvil->data->{sys}{progress} = 20;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0193",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0193"});
my ($string, $return_code) = $anvil->System->call({shell_call => $anvil->data->{path}{exe}{md5sum}." ".$target_file});
my $md5sum = ($string =~ /^(.*?)\s/)[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
string => $string,
md5sum => $md5sum,
return_code => $return_code,
}});
# Store the file details!
$anvil->data->{sys}{progress} = 30;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0194,!!md5sum!".$md5sum."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0194", variables => { md5sum => $md5sum }});
$anvil->Storage->get_file_stats({
debug => 2,
file_path => $target_file,
});
my $file_mimetype = $anvil->data->{file_stat}{$target_file}{mimetype};
my $file_size = $anvil->data->{file_stat}{$target_file}{size};
my $file_mtime = $anvil->data->{file_stat}{$target_file}{modified_time};
my $executable = -x $target_file ? 1 : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
file_mimetype => $file_mimetype,
file_size => $file_size." (".$anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}).")",
file_mtime => $file_mtime,
executable => $executable,
}});
# This is the file's type/purpose. The expected values are 'iso', 'rpm', 'script', 'disk-image', or
# 'other'. If set to 'DELETED', the file will be removed from disk.
my $file_type = "other";
if ($file_mimetype =~ /cd-image/)
{
$file_type = "iso";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_type => $file_type }});
}
# This will need to be expanded over time
elsif (($executable) or ($file_mimetype =~ /perl/) or ($file_mimetype =~ /python/))
{
$file_type = "script";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_type => $file_type }});
# Change the mode to be executable
$anvil->Storage->change_mode({
debug => 2,
path => $target_file,
mode => "0755",
});
}
elsif ($file_mimetype =~ /raw-disk-image/)
{
$file_type = "image";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_type => $file_type }});
}
my $file_uuid = $anvil->Database->insert_or_update_files({
debug => 2,
file_name => $file_name,
file_directory => $anvil->data->{path}{directories}{shared}{files},
file_size => $file_size,
file_md5sum => $md5sum,
file_type => $file_type,
file_mtime => $file_mtime,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }});
# Now copy this to our peers.
foreach my $host_uuid (sort {$a cmp $b} keys %{$anvil->data->{database}})
{
# Periodically, autovivication causes and empty key to appear.
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }});
next if ((not $host_uuid) or (not $anvil->Validate->uuid({uuid => $host_uuid})));
next if $host_uuid eq $anvil->Get->host_uuid;
my $host = $anvil->data->{database}{$host_uuid}{host};
my $password = $anvil->data->{database}{$host_uuid}{password};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host => $host,
password => $anvil->Log->is_secure($password),
}});
my $striker_name = $anvil->Get->host_name_from_uuid({host_uuid => $host_uuid});
my $say_host = $striker_name." (".$host.")";
# Rsync the file.
$anvil->data->{sys}{progress} += 10;
$anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0195,!!host!".$say_host."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0195", variables => { host => $say_host }});
$anvil->Storage->rsync({
debug => 2,
source => $target_file,
destination => "root\@".$host.":".$anvil->data->{path}{directories}{shared}{files}."/",
try_again => 1,
});
}
# Tell other Anvil! systems to download this file.
$anvil->Database->get_anvils({debug => 2});
foreach my $anvil_name (sort {$a cmp $b} keys %{$anvil->data->{anvils}{anvil_name}})
{
my $anvil_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_uuid};
my $anvil_node1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node1_host_uuid};
my $anvil_node2_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_node2_host_uuid};
my $anvil_dr1_host_uuid = $anvil->data->{anvils}{anvil_name}{$anvil_name}{anvil_dr1_host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
anvil_uuid => $anvil_uuid,
anvil_node1_host_uuid => $anvil_node1_host_uuid,
anvil_node2_host_uuid => $anvil_node2_host_uuid,
anvil_dr1_host_uuid => $anvil_dr1_host_uuid,
}});
$anvil->data->{sys}{progress} += 5;
$anvil->data->{sys}{progress} = 90 if $anvil->data->{sys}{progress} > 90;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0196,!!anvil_name!".$anvil_name."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0196", variables => { anvil_name => $anvil_name }});
my $file_location_uuid = $anvil->Database->insert_or_update_file_locations({
debug => 2,
file_location_file_uuid => $file_uuid,
file_location_anvil_uuid => $anvil_uuid,
file_location_active => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_location_uuid => $file_location_uuid }});
# Register a job to call anvil-sync-shared
foreach my $host_uuid ($anvil_node1_host_uuid, $anvil_node2_host_uuid, $anvil_dr1_host_uuid)
{
next if not $host_uuid;
my ($job_uuid) = $anvil->Database->insert_or_update_jobs({
file => $THIS_FILE,
line => __LINE__,
job_command => $anvil->data->{path}{exe}{'anvil-sync-shared'}.$anvil->Log->switches,
job_data => "file_uuid=".$file_uuid,
job_name => "storage::pull_file",
job_title => "job_0132",
job_description => "job_0133",
job_progress => 0,
job_host_uuid => $host_uuid,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { job_uuid => $job_uuid }});
}
}
# Done!
$anvil->Job->update_progress({
progress => 100,
message => "message_0197",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0197"});
return(0);
}
# This pulls files from dashboards onto the running host.
sub process_pull_file
{
my ($anvil) = @_;
$anvil->Job->update_progress({
progress => $anvil->data->{sys}{progress},
message => "message_0198",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0198" });
# Collect some data
$anvil->Database->get_anvils({debug => 3});
$anvil->Database->get_files({debug => 3});
$anvil->Database->get_file_locations({debug => 3});
my $file_uuid = ($anvil->data->{jobs}{job_data} =~ /file_uuid=(.*)$/)[0];
my $anvil_uuid = $anvil->Cluster->get_anvil_uuid({debug => 2});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
anvil_uuid => $anvil_uuid,
file_uuid => $file_uuid,
}});
if (not $file_uuid)
{
# Can't do anything, file wasn't parsed.
$anvil->Job->update_progress({
progress => 100,
message => "error_0173,!!job_uuid!".$anvil->data->{switches}{'job-uuid'}."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0173", variables => { job_uuid => $anvil->data->{switches}{'job-uuid'} }});
$anvil->nice_exit({exit_code => 1});
}
elsif (not exists $anvil->data->{files}{file_uuid}{$file_uuid})
{
# File UUID doesn't appear to be valid.
$anvil->Job->update_progress({
progress => 100,
message => "error_0174,!!file_uuid!".$file_uuid."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0174", variables => { file_uuid => $file_uuid }});
$anvil->nice_exit({exit_code => 1});
}
my $file_name = $anvil->data->{files}{file_uuid}{$file_uuid}{file_name};
my $file_directory = $anvil->data->{files}{file_uuid}{$file_uuid}{file_directory};
my $file_path = $file_directory."/".$file_name;
my $file_size = $anvil->data->{files}{file_uuid}{$file_uuid}{file_size};
my $file_md5sum = $anvil->data->{files}{file_uuid}{$file_uuid}{file_md5sum};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
's1:file_name' => $file_name,
's2:file_directory' => $file_directory,
's3:file_path' => $file_path,
's4:file_size' => $file_size." (".$anvil->Convert->bytes_to_human_readable({"bytes" => $file_size}).")",
's5:file_md5sum' => $file_md5sum,
}});
if (not $anvil_uuid)
{
# Uhhh...
$anvil->Job->update_progress({
progress => 100,
message => "error_0175,!!file!".$file_path."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0175", variables => { file => $file_path }});
$anvil->nice_exit({exit_code => 1});
}
# How many Strikers are up and have the file we're looking for?
$anvil->data->{target_strikers} = [];
foreach my $host_uuid (keys %{$anvil->data->{database}})
{
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { host_uuid => $host_uuid }});
my $host_name = $anvil->Get->host_name_from_uuid({debug => 2, host_uuid => $host_uuid});
my $target = $anvil->data->{database}{$host_uuid}{host};
my $password = $anvil->data->{database}{$host_uuid}{password};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_name => $host_name,
target => $target,
password => $anvil->Log->is_secure($password),
}});
# If the file exists, the return code is '0'. If the file isn't found, '1' is returned.
# When found, the size in bytes followed by the file name is returned.
my $shell_call = $anvil->data->{path}{exe}{wc}." -c ".$file_path;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { shell_call => $shell_call }});
my ($output, $error, $return_code) = $anvil->Remote->call({
debug => 3,
shell_call => $shell_call,
target => $target,
password => $password,
remote_user => "root",
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
output => $output,
error => $error,
output => $output,
}});
if ($output =~ /^(\d+)\s+$file_path$/)
{
my $size_on_peer = $1;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
size_on_peer => $size_on_peer." (".$anvil->Convert->bytes_to_human_readable({"bytes" => $size_on_peer}).")"
}});
# For now, we only do a size check as md5sums can take a long time.
if ($size_on_peer eq $file_size)
{
# We can pull from this striker!
push @{$anvil->data->{target_strikers}}, {
host_name => $host_name,
host_uuid => $host_uuid,
target => $target,
password => $password,
};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_name => $host_name,
host_uuid => $host_uuid,
target => $target,
password => $anvil->Log->is_secure($password),
}});
}
else
{
# The file doesn't exist or we couldn't contact the Striker, so we'll skip
# it.
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "log_0576", variables => {
file_path => $file_path,
host_name => $host_name,
}});
}
}
}
my $anvil_node1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node1_host_uuid};
my $anvil_node2_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_node2_host_uuid};
my $anvil_dr1_host_uuid = $anvil->data->{anvils}{anvil_uuid}{$anvil_uuid}{anvil_dr1_host_uuid};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
anvil_node1_host_uuid => $anvil_node1_host_uuid,
anvil_node2_host_uuid => $anvil_node2_host_uuid,
anvil_dr1_host_uuid => $anvil_dr1_host_uuid,
}});
# Where we pull from will depend on which machine we are and how many strikers we have. If we have
# one Anvil!, node 1 and 2 download at the same time, and DR waits. If there are two strikers, Each
# node will download from a different striker (if possible) and DR waits. If there are 3 or more
# Strikers, DR does not wait, and downloads from a different striker than the striker's nodes use.
my $i_am = "node1";
if ($anvil->Get->host_uuid eq $anvil_node2_host_uuid)
{
$i_am = "node2";
}
elsif ($anvil->Get->host_uuid eq $anvil_dr1_host_uuid)
{
$i_am = "dr1";
# As we're DR, we'll likely be pinging the nodes to seee if they're up when we wait for them
# to finish jobs. As such, load their IPs into memory.
$anvil->Network->load_ips({debug => 2, host_uuid => $anvil_node1_host_uuid});
$anvil->Network->load_ips({debug => 2, host_uuid => $anvil_node2_host_uuid});
}
my $striker_count = @{$anvil->data->{target_strikers}};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
i_am => $i_am,
striker_count => $striker_count,
}});
if (not $striker_count)
{
# No available Strikers.
$anvil->Job->update_progress({
progress => 1,
message => "warning_0072,!!file_path!".$file_path."!!",
job_status => "failed",
});
sleep 60;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "warning_0072", variables => { file_path => $file_path }});
$anvil->nice_exit({exit_code => 2});
}
my $use = 0;
my $dr_wait = 1;
if ($striker_count >= 3)
{
$dr_wait = 0;
if ($i_am eq "node1") { $use = 0; }
elsif ($i_am eq "node2") { $use = 1; }
elsif ($i_am eq "dr1") { $use = 2; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'use' => $use,
dr_wait => $dr_wait,
}});
}
elsif ($striker_count == 2)
{
# Two strikers, each node will use the other Striker, DR waits for both to be done.
if ($i_am eq "node1") { $use = 0; }
elsif ($i_am eq "node2") { $use = 1; }
elsif ($i_am eq "dr1") { $use = 1; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'use' => $use,
dr_wait => $dr_wait,
}});
}
elsif ($striker_count == 1)
{
# Only 1 Striker
if ($i_am eq "node1") { $use = 0; }
elsif ($i_am eq "node2") { $use = 0; }
elsif ($i_am eq "dr1") { $use = 0; }
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'use' => $use,
dr_wait => $dr_wait,
}});
}
# If I'm DR and need to wait, look for jobs on node1 and node2 and wait until both are done (or is
# offline).
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
i_am => $i_am,
dr_wait => $dr_wait,
}});
if (($i_am eq "dr1") && ($dr_wait))
{
my $node1_job_uuid = "";
my $node1_online = 1;
my $node2_job_uuid = "";
my $node2_online = 1;
my $node1_waiting = 1;
my $node2_waiting = 1;
my $query = "
SELECT
job_uuid,
job_host_uuid
FROM
jobs
WHERE
job_name = 'storage::pull_file'
AND
(
job_host_uuid = ".$anvil->Database->quote($anvil_node1_host_uuid)."
OR
job_host_uuid = ".$anvil->Database->quote($anvil_node2_host_uuid)."
)
;";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $results = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__});
my $count = @{$results};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
results => $results,
count => $count,
}});
foreach my $row (@{$results})
{
my $job_uuid = $row->[0];
my $job_host_uuid = $row->[1];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
job_uuid => $job_uuid,
job_host_uuid => $job_host_uuid,
}});
if ($job_host_uuid eq $anvil_node1_host_uuid)
{
$node1_job_uuid = $job_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node1_job_uuid => $node1_job_uuid }});
}
if ($job_host_uuid eq $anvil_node2_host_uuid)
{
$node2_job_uuid = $job_uuid;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node2_job_uuid => $node2_job_uuid }});
}
}
my $waiting = 1;
while($waiting)
{
if ($node1_waiting)
{
if (not $node1_job_uuid)
{
$node1_waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node1_waiting => $node1_waiting }});
}
else
{
$node1_waiting = wait_on_host($anvil, $anvil_node1_host_uuid, $node1_job_uuid);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node1_waiting => $node1_waiting }});
}
}
if ($node2_waiting)
{
if (not $node2_job_uuid)
{
$node2_waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node2_waiting => $node2_waiting }});
}
else
{
$node2_waiting = wait_on_host($anvil, $anvil_node2_host_uuid, $node2_job_uuid);
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { node2_waiting => $node2_waiting }});
}
}
if ((not $node1_waiting) && (not $node2_waiting))
{
# We can proceed.
$waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
}
else
{
# Sleep for a bit.
my $sleep_time = 30;
my $wait_until = $anvil->Get->date_and_time({offset => $sleep_time});
$anvil->Job->update_progress({
progress => 1,
message => "message_0199,!!strikers!".$striker_count."!!,!!node1_waiting!".$node1_waiting."!!,!!node2_waiting!".$node2_waiting."!!,!!wait_until!".$wait_until."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0199", variables => {
strikers => $striker_count,
node1_waiting => $node1_waiting,
node2_waiting => $node2_waiting,
wait_until => $wait_until,
}});
sleep $sleep_time;
}
}
}
# Now proceed with the download!
my $target_host_name = $anvil->data->{target_strikers}->[$use]->{host_name};
my $target_host_uuid = $anvil->data->{target_strikers}->[$use]->{host_uuid};
my $target_host = $anvil->data->{target_strikers}->[$use]->{target};
my $target_password = $anvil->data->{target_strikers}->[$use]->{password};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
target_host_name => $target_host_name,
target_host_uuid => $target_host_uuid,
target_host => $target_host,
target_password => $anvil->Log->is_secure($target_password),
}});
# Rsync the file.
my $say_source_file = "root\@".$target_host.":".$file_path;
my $target_directory = $file_directory."/";
$anvil->Job->update_progress({
progress => 50,
message => "message_0200,!!source_file!".$say_source_file."!!,!!target_directory!".$target_directory."!!",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0200", variables => {
source_file => $say_source_file,
target_directory => $target_directory,
}});
$anvil->Storage->rsync({
debug => 2,
source => $say_source_file,
destination => $target_directory,
try_again => 1,
});
if (-e $file_path)
{
# Calculate the md5sum.
$anvil->Job->update_progress({
progress => 75,
message => "message_0201",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0201"});
my $local_md5sum = $anvil->Get->md5sum({file => $file_path});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { local_md5sum => $local_md5sum }});
if ($file_md5sum eq $local_md5sum)
{
$anvil->Job->update_progress({
progress => 100,
message => "message_0202",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 1, key => "message_0202"});
$anvil->nice_exit({exit_code => 0});
}
else
{
# Unlink the file. The perioding sync call can try again later.
unlink $file_path;
$anvil->Job->update_progress({
progress => 1,
message => "error_0176,!!local_md5sum!".$local_md5sum."!!,!!file_md5sum!".$file_md5sum."!!",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0176", variables => {
local_md5sum => $local_md5sum,
file_md5sum => $file_md5sum,
}});
sleep 60;
$anvil->nice_exit({exit_code => 1});
}
}
else
{
# Failed...
$anvil->Job->update_progress({
progress => 1,
message => "error_0177",
job_status => "failed",
});
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => 'err', key => "error_0177" });
sleep 60;
$anvil->nice_exit({exit_code => 1});
}
return(0);
}
# This takes a host and job UUID and determines if we're still waiting on the target.
sub wait_on_host
{
my ($anvil, $host_uuid, $job_uuid) = @_;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
host_uuid => $host_uuid." (".$anvil->Get->host_name_from_uuid({host_uuid => $host_uuid}).")",
job_uuid => $job_uuid,
}});
my $waiting = 1;
# Look up the job progress.
my $query = "SELECT job_progress FROM jobs WHERE job_uuid = ".$anvil->Database->quote($job_uuid).";";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }});
my $progress = $anvil->Database->query({query => $query, source => $THIS_FILE, line => __LINE__})->[0]->[0];
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { progress => $progress }});
if ($progress == 100)
{
$waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
}
else
{
# Can I ping the node?
my $pinged = 0;
foreach my $interface (sort {$a cmp $b} keys %{$anvil->data->{network}{$host_uuid}{interface}})
{
my $target_ip = $anvil->data->{network}{$host_uuid}{interface}{$interface}{ip};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
interface => $interface,
target_ip => $target_ip,
}});
($pinged, my $average_time) = $anvil->Network->ping({
ping => $target_ip,
count => 1,
});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { pinged => $pinged }});
last if $pinged;
}
if (not $pinged)
{
# Stop waiting, it looks to be offline.
$waiting = 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
}
}
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { waiting => $waiting }});
return($waiting)
}