You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
418 lines
14 KiB
418 lines
14 KiB
#!/usr/bin/perl |
|
# |
|
# This handles moving around and managing files on Anvil! nodes, DR hosts and Striker dashboards. |
|
# |
|
# When this is called (periodically by the daemon of after an upload / ISO generation); |
|
# - 1. Check 'incoming/' for files. For any found, generate an md5sum and see if the file name and sum match |
|
# anything in the database from any host; |
|
# - If so, update/add an entry in 'file_locations' |
|
# - If not, create a new entry in 'files' and then add the first entry in 'file_locations' |
|
# - 2. Check 'file_locations' for any files on this system, and verify they exist still. |
|
# - If not, check the other files for one with a matching md5sum. If found, handle as a rename. |
|
# - If not found at all, search for the file according to the search rules and copy to here if found. |
|
# - If not found anywhere, remove it from 'file_locations' and send an alert. |
|
# - If found, check the size. If it differs, recalculate the md5sum. |
|
# - 3. If called with '--rename --file <filename> --to <newname>', rename the file and update 'files'. |
|
# - 4. If called with '--delete', remove from 'file_locations' and then remove from the local storage. If |
|
# also used with '--everywhere', then all copies on all systems we know about will be deleted. This is |
|
# done by registering a job against all known hosts. As such, if this is called and the target file |
|
# doesn't exist, it just clears the job and then exits. |
|
# - 5. If called with '--is-script=[0|1]', mark as 'script' in the 'files' table and set/remove the executable bit. |
|
# |
|
# Exit codes; |
|
# 0 = Normal exit or md5sum of this program changed and it exited to reload. |
|
# 1 = No databases available. |
|
# 2 = Another process that is still running has picked up this job. |
|
# 3 = No filename specified when needed by another switch. |
|
# 4 = Request to change the file name but the new name wasn't given. |
|
# 5 = The file specified was not found. |
|
# 6 = The file to delete is not under '/mnt/shared/'. |
|
# |
|
# TODO: |
|
# - |
|
# |
|
# NOTE: |
|
# - |
|
# |
|
|
|
use strict; |
|
use warnings; |
|
use Anvil::Tools; |
|
use Data::Dumper; |
|
|
|
|
|
# Disable buffering |
|
$| = 1; |
|
|
|
my $THIS_FILE = ($0 =~ /^.*\/(.*)$/)[0]; |
|
my $running_directory = ($0 =~ /^(.*?)\/$THIS_FILE$/)[0]; |
|
if (($running_directory =~ /^\./) && ($ENV{PWD})) |
|
{ |
|
$running_directory =~ s/^\./$ENV{PWD}/; |
|
} |
|
|
|
my $anvil = Anvil::Tools->new(); |
|
|
|
$anvil->data->{switches}{'job-uuid'} = ""; |
|
$anvil->data->{switches}{'rename'} = ""; |
|
$anvil->data->{switches}{'is-script'} = ""; |
|
$anvil->data->{switches}{file} = ""; |
|
$anvil->data->{switches}{to} = ""; |
|
$anvil->data->{switches}{'delete'} = ""; |
|
$anvil->data->{switches}{everywhere} = ""; |
|
$anvil->Get->switches; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
"switches::job-uuid" => $anvil->data->{switches}{'job-uuid'}, |
|
"switches::rename" => $anvil->data->{switches}{'rename'}, |
|
"switches::is-script" => $anvil->data->{switches}{'is-script'}, |
|
"switches::file" => $anvil->data->{switches}{file}, |
|
"switches::to" => $anvil->data->{switches}{to}, |
|
"switches::delete" => $anvil->data->{switches}{'delete'}, |
|
"switches::everywhere" => $anvil->data->{switches}{everywhere}, |
|
}}); |
|
|
|
# Connect or die |
|
$anvil->Database->connect; |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 2, secure => 0, key => "log_0132"}); |
|
if (not $anvil->data->{sys}{database}{connections}) |
|
{ |
|
# No databases, exit. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0003"}); |
|
$anvil->nice_exit({exit_code => 1}); |
|
} |
|
|
|
# If we have a job_uuid, pick it up. |
|
if ($anvil->data->{switches}{'job-uuid'}) |
|
{ |
|
my $return = $anvil->Job->get_job_details({check => 1, job_uuid => $anvil->data->{switches}{'job-uuid'}}); |
|
if ($return == 1) |
|
{ |
|
# It's not a UUID. |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { 'return' => $return }}); |
|
$anvil->nice_exit({code => 2}); |
|
} |
|
if ($return == 2) |
|
{ |
|
# This job is being handled by another process that is still alive. |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { 'return' => $return }}); |
|
$anvil->nice_exit({code => 3}); |
|
} |
|
|
|
# If there's a progress, clear it. |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 1, list => { 'job::job_progress' => $anvil->data->{job}{job_progress} }}); |
|
if ($anvil->data->{job}{job_progress}) |
|
{ |
|
$anvil->Job->update_progress({ |
|
progress => 1, |
|
message => "clear", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
} |
|
} |
|
|
|
# What are we doing? |
|
if ($anvil->data->{switches}{'rename'}) |
|
{ |
|
handle_rename($anvil); |
|
} |
|
elsif ($anvil->data->{switches}{'delete'})) |
|
{ |
|
handle_delete($anvil); |
|
} |
|
elsif ($anvil->data->{switches}{'is-script'})) |
|
{ |
|
handle_script($anvil); |
|
} |
|
|
|
|
|
# We're done |
|
$anvil->nice_exit({exit_code => 0}); |
|
|
|
|
|
############################################################################################################# |
|
# Private functions. # |
|
############################################################################################################# |
|
|
|
# This gets the file_uuid for a given file name and/or md5sum. If the file isn't found, an empty string is |
|
# returned. |
|
sub get_file_uuid |
|
{ |
|
my ($anvil, $file_md5sum, $file_name) = @_; |
|
$file_md5sum = "" if not defined $file_md5sum; |
|
$file_name = "" if not defined $file_name; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
file_md5sum => $file_md5sum, |
|
file_name => $file_name, |
|
}}); |
|
|
|
### TODO: At some point, we'll need to deal with the possibility that the same file name with |
|
### different md5sums might exist in the database. |
|
# If I have the md5sum, search using that. If I have the filename only, then we'll fall back to that. |
|
my $query = " |
|
SELECT |
|
file_uuid |
|
FROM |
|
files |
|
WHERE |
|
"; |
|
if ($file_md5sum) |
|
{ |
|
$query .= " file_md5sum = ".$anvil->data->{sys}{database}{use_handle}->quote($file_md5sum)."\n"; |
|
} |
|
elsif ($file_name) |
|
{ |
|
$query .= " file_name = ".$anvil->data->{sys}{database}{use_handle}->quote($file_name)."\n"; |
|
} |
|
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, |
|
}}); |
|
if (not $count) |
|
{ |
|
# File wasn't found in the database |
|
return(""); |
|
} |
|
my $file_uuid = $results->[0]->[0]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
file_uuid => $file_uuid, |
|
}}); |
|
|
|
return($file_uuid); |
|
} |
|
|
|
# This handles toggling a file to marked or unmarked as a script. |
|
sub handle_script |
|
{ |
|
my ($anvil) = @_; |
|
|
|
if (not $anvil->data->{switches}{file}) |
|
{ |
|
# Um... |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0055"}); |
|
if ($anvil->data->{jobs}{'job-uuid'}) |
|
$anvil->Job->update_progress({ |
|
progress => 0, |
|
message => "error_0055", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
$anvil->nice_exit({exit_code => 3}); |
|
} |
|
|
|
# Find the file_uuid. |
|
my ($file_uuid) = get_file_uuid($anvil, "", $anvil->data->{switches}{file}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { file_uuid => $file_uuid }}); |
|
|
|
# Toggle the executable bits. |
|
if ($anvil->data->{switches}{'is-script'}) |
|
{ |
|
# Is it already executable? |
|
if (-x $anvil->data->{switches}{file}) |
|
{ |
|
# Switch it on |
|
$anvil->Storage->change_mode({target => $anvil->data->{switches}{file}, mode => "a+x"}); |
|
} |
|
else |
|
{ |
|
# Already a script. |
|
} |
|
} |
|
else |
|
{ |
|
# Is it executable? |
|
if (-x $anvil->data->{switches}{file}) |
|
{ |
|
# Switch it off. |
|
$anvil->Storage->change_mode({target => $anvil->data->{switches}{file}, mode => "a-x"}); |
|
} |
|
else |
|
{ |
|
# Already not a script. |
|
} |
|
} |
|
|
|
# If we have a file UUID, update the 'file_type |
|
if ($file_uuid) |
|
{ |
|
# Load the details. |
|
my $query = " |
|
SELECT |
|
file_name, |
|
file_size, |
|
file_md5sum, |
|
file_type |
|
FROM |
|
files |
|
WHERE |
|
file_uuid = ".$anvil->data->{sys}{database}{use_handle}->quote($file_uuid)." |
|
;"; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { query => $query }}); |
|
|
|
my $results = $anvil->Database->query({query => $query, source => $file ? $file." -> ".$THIS_FILE : $THIS_FILE, line => $line ? $line." -> ".__LINE__ : __LINE__}); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { results => $results }}); |
|
my $file_name = $results->[0]->[0]; |
|
my $file_size = $results->[0]->[1]; |
|
my $file_md5sum = $results->[0]->[2]; |
|
my $file_type = $results->[0]->[3]; |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { |
|
file_name => $file_name, |
|
file_size => $file_size, |
|
file_md5sum => $file_md5sum, |
|
file_type => $file_type, |
|
}}); |
|
if (($file_type eq "script") && (not $anvil->data->{switches}{'is-script'})) |
|
{ |
|
# Figure out what the file type is and update. |
|
my $mimetype = mimetype($full_path); |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { mimetype => $mimetype }}); |
|
|
|
my $say_mimetype = "other"; |
|
if ($mimetype =~ /cd-image/) |
|
{ |
|
$say_mimetype = "iso"; |
|
} |
|
elsif ($mimetype =~ /rpm$/) |
|
{ |
|
$say_mimetype = "rpm"; |
|
} |
|
elsif ($mimetype =~ /disk-image/) |
|
{ |
|
$say_mimetype = "disk-image"; |
|
} |
|
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => { say_mimetype => $say_mimetype }}); |
|
$anvil->Database->insert_or_update_files({ |
|
file_uuid => $file_uuid, |
|
file_name => $anvil->data->{switches}{file}, |
|
file_size => $file_size, |
|
file_md5sum => $file_md5sum, |
|
file_type => $say_mimetype, |
|
}); |
|
} |
|
elsif ($file_type ne "script") && ($anvil->data->{switches}{'is-script'})) |
|
{ |
|
# Change the file tpye to "script" |
|
$anvil->Database->insert_or_update_files({ |
|
file_uuid => $file_uuid, |
|
file_name => $anvil->data->{switches}{file}, |
|
file_size => $file_size, |
|
file_md5sum => $file_md5sum, |
|
file_type => "script", |
|
}); |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
# This handles deleting a file. If the requested deletion target doesn't exist, we'll just clear the |
|
# database. If that doesn't exist either, we still don't error. This is to handle broad requests to delete a |
|
# file everywhere. If we're asked to delete it everywhere, then we'll register a job against all hosts. |
|
sub handle_delete |
|
{ |
|
my ($anvil) = @_; |
|
|
|
if (not $anvil->data->{switches}{file}) |
|
{ |
|
# Um... |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0052"}); |
|
if ($anvil->data->{jobs}{'job-uuid'}) |
|
$anvil->Job->update_progress({ |
|
progress => 0, |
|
message => "error_0052", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
$anvil->nice_exit({exit_code => 3}); |
|
} |
|
elsif ($anvil->data->{switches}{file} !~ /^\/mnt\/shared\//) |
|
{ |
|
# We don't do that here... |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0053", variables => { file => $anvil->data->{switches}{file} }}); |
|
$anvil->Job->update_progress({ |
|
progress => 0, |
|
message => "error_0053,!!file!".$anvil->data->{switches}{file}."!!", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
$anvil->nice_exit({exit_code => 6}); |
|
} |
|
|
|
# Does the file exist? |
|
if (-e $anvil->data->{switches}{file}) |
|
{ |
|
# Delete it. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0263", variables => { file => $anvil->data->{switches}{file} }}); |
|
|
|
unlink $anvil->data->{switches}{file}; |
|
|
|
# Sleep and verify |
|
sleep 1; |
|
if (-e $anvil->data->{switches}{file}) |
|
{ |
|
# Failed to delete... |
|
# TODO: |
|
} |
|
else |
|
{ |
|
# Deleted successfully. Remove from 'file_locations' |
|
### TODO: Find the file_uuid, then if found, see if we have and entry in file_location and then delete it. |
|
} |
|
} |
|
|
|
return(0); |
|
} |
|
|
|
# This handles renaming files. |
|
sub handle_rename |
|
{ |
|
my ($anvil) = @_; |
|
|
|
# Do we have the current file name and the new one? |
|
if (not $anvil->data->{switches}{file}) |
|
{ |
|
# Um... |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0049"}); |
|
$anvil->Job->update_progress({ |
|
progress => 0, |
|
message => "error_0049", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
$anvil->nice_exit({exit_code => 3}); |
|
} |
|
elsif (not $anvil->data->{switches}{to}) |
|
{ |
|
# We need a target |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0050", variables => { file => $anvil->data->{switches}{file} }}); |
|
$anvil->Job->update_progress({ |
|
progress => 0, |
|
message => "error_0050,!!file!".$anvil->data->{switches}{file}."!!", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
$anvil->nice_exit({exit_code => 4}); |
|
} |
|
elsif (not -f $anvil->data->{switches}{file}) |
|
{ |
|
# The file to rename doesn't exist. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0051", variables => { file => $anvil->data->{switches}{file} }}); |
|
$anvil->Job->update_progress({ |
|
progress => 0, |
|
message => "error_0051,!!file!".$anvil->data->{switches}{file}."!!", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
$anvil->nice_exit({exit_code => 5}); |
|
} |
|
elsif (-e $anvil->data->{switches}{to}) |
|
{ |
|
# There's already a file (or directory or something) with that name. |
|
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 0, key => "error_0052", variables => { file => $anvil->data->{switches}{file}, to => $anvil->data->{switches}{to} }}); |
|
$anvil->Job->update_progress({ |
|
progress => 0, |
|
message => "error_0052,!!file!".$anvil->data->{switches}{file}."!!,!!to!".$anvil->data->{switches}{to}."!!", |
|
job_uuid => $anvil->data->{jobs}{'job-uuid'}, |
|
}); |
|
$anvil->nice_exit({exit_code => 6}); |
|
} |
|
|
|
### TODO: Left off here |
|
|
|
return(0); |
|
}
|
|
|