* Created tools/anvil-download-file which will handle downloading, aborting and reporting status of downloads.

* Started working on Convert->time().
* Changed anvil-manage-files skip /mnt/shared/temp when looking for files to add to the database.

Signed-off-by: Digimer <digimer@alteeve.ca>
main
Digimer 6 years ago
parent beb1197c1b
commit b1326e1b4e
  1. 2
      Anvil/Tools.pm
  2. 138
      Anvil/Tools/Convert.pm
  3. 2
      Anvil/Tools/Words.pm
  4. 1
      rpm/SPECS/anvil.spec
  5. 18
      share/words.xml
  6. 244
      tools/anvil-download-file
  7. 22
      tools/anvil-manage-files

@ -962,6 +962,7 @@ sub _set_paths
definitions => "/mnt/shared/definitions", definitions => "/mnt/shared/definitions",
files => "/mnt/shared/files", files => "/mnt/shared/files",
incoming => "/mnt/shared/incoming", incoming => "/mnt/shared/incoming",
temp => "/mnt/shared/temp",
}, },
skins => "/var/www/html/skins", skins => "/var/www/html/skins",
syslinux => "/usr/share/syslinux", syslinux => "/usr/share/syslinux",
@ -1037,6 +1038,7 @@ sub _set_paths
usermod => "/usr/sbin/usermod", usermod => "/usr/sbin/usermod",
uuidgen => "/usr/bin/uuidgen", uuidgen => "/usr/bin/uuidgen",
virsh => "/usr/bin/virsh", virsh => "/usr/bin/virsh",
wget => "",
}, },
json => { json => {
files => "files.json", files => "files.json",

@ -973,6 +973,144 @@ sub round
return ($rounded_number); return ($rounded_number);
} }
=head2 time
This takes a number of seconds and converts it into a human readable string. Returns C<< #!error!# >> is an error is encountered.
Parameters;
=head3 time (required)
This is the time, in seconds, to convert.
=head3 long (optional, default 0)
If set to C<< 1 >>, the long suffixes will be used instead of the default C<< w/d/h/m/s >> format.
B<< Note >>: The suffixes are translatable in both short (default) and long formats. See the C<< suffix_0002 >> through C<< suffix_0011 >> string keys.
=cut
sub time
{
my $self = shift;
my $parameter = shift;
my $anvil = $self->parent;
my $debug = defined $parameter->{debug} ? $parameter->{debug} : 3;
my $time = $parameter->{'time'} ? $parameter->{'time'} : 0;
my $long = $parameter->{long} ? $parameter->{long} : 0;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
'time' => $time,
long => $long,
}});
if (not $time)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0020", variables => { method => "Storage->update_file()", parameter => "time" }});
return("#!error!#");
}
# Remote commas and verify we're left with a number.
my $time =~ s/,//g;
if ($time =~ /^\d+\.\d+$/)
{
# Round the time
$time = $anvil->Convert->round({number => $time});
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { 'time' => $time }});
}
if ($time =~ /\D/)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, priority => "err", key => "log_0294", variables => { 'time' => $time }});
return("#!error!#");
}
# The suffix used for each unit of time will depend on the requested suffix type.
my $suffix_seconds = $long ? " #!string!suffix_0002!#" : " #!string!suffix_0007!#";
my $suffix_minutes = $long ? " #!string!suffix_0003!#" : " #!string!suffix_0008!#";
my $suffix_hours = $long ? " #!string!suffix_0004!#" : " #!string!suffix_0009!#";
my $suffix_days = $long ? " #!string!suffix_0005!#" : " #!string!suffix_0010!#";
my $suffix_weeks = $long ? " #!string!suffix_0006!#" : " #!string!suffix_0011!#";
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
suffix_seconds => $suffix_seconds,
suffix_minutes => $suffix_minutes,
suffix_hours => $suffix_hours,
suffix_days => $suffix_days,
suffix_weeks => $suffix_weeks,
}});
my $say_time = "";
my $seconds = $time % 60;
my $minutes = ($time - $seconds) / 60;
my $remaining_minutes = $minutes % 60;
my $hours = ($minutes - $remaining_minutes) / 60;
my $remaining_hours = $hours % 24;
my $days = ($hours - $remaining_hours) / 24;
my $remaining_days = $days % 7;
my $weeks = ($days - $remaining_days) / 7;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => {
's1:weeks' => $weeks,
's2:remaining_days' => $remaining_days,
's3:days' => $days,
's4:remaining_hours' => $remaining_hours,
's5:hours' => $hours,
's6:remaining_minutes' => $remaining_minutes,
's7:minutes' => $minutes,
's8:seconds' => $seconds,
}});
### TODO: Left off here.
if ($seconds < 1)
{
$say_time = "0".$suffix_seconds;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
else
{
$say_time = sprintf("%01d", $seconds).$suffix_seconds;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => $debug, list => { say_time => $say_time }});
}
if ($remaining_minutes > 0)
{
$say_time =~ s/ sec.$/$suffix_seconds/;
$say_time = sprintf("%01d", $remaining_minutes).$suffix_minutes." $say_time";
}
elsif (($hours > 0) or ($days > 0) or ($weeks > 0))
{
$say_time = "0".$suffix_minutes." ".$say_time;
}
if ($remaining_hours > 0)
{
$say_time = sprintf("%01d", $remaining_hours)."$suffix_hours $say_time";
}
elsif (($days > 0) or ($weeks > 0))
{
$say_time = "0".$suffix_hours." ".$say_time;
}
if ($days > 0)
{
$say_time = sprintf("%01d", $remaining_days).$suffix_days." ".$say_time;
}
elsif ($weeks > 0)
{
$say_time = "0".$suffix_days." ".$say_time;
}
if ($weeks > 0)
{
$weeks = $an->Readable->comma($weeks);
$say_time = $weeks.$suffix_weeks." ".$say_time;
}
# Return an already-translated string
$say_time = $an->String->_process_string({
string => $say_time,
language => $an->default_language,
hash => $an->data,
variables => {},
});
return($say_time);
}
# =head3 # =head3
# #
# Private Functions; # Private Functions;

@ -443,7 +443,7 @@ sub read
=head2 string =head2 string
This method takes a string key and returns the string in the requested language. If not key is passed, the language key in 'defaults::languages::output' is used. A hash reference containing variables can be provided to inject values into a string. This method takes a string key and returns the string in the requested language. If no key is passed, the language key in 'defaults::languages::output' is used. A hash reference containing variables can be provided to inject values into a string.
If the requested string is not found, 'C<< #!not_found!# >>' is returned. If the requested string is not found, 'C<< #!not_found!# >>' is returned.

@ -62,6 +62,7 @@ Requires: postgresql-plperl
Requires: rsync Requires: rsync
Requires: screen Requires: screen
Requires: vim Requires: vim
Requires: wget
# iptables-services conflicts with firewalld # iptables-services conflicts with firewalld
Conflicts: iptables-services Conflicts: iptables-services
# We handle interface naming # We handle interface naming

@ -592,6 +592,14 @@ We will keep looking.</key>
<key name="log_0283">Done.</key> <key name="log_0283">Done.</key>
<key name="log_0284">[ Error ] - Failed to remove the file: [#!variable!file!#]! Please check the permissions or for SELinux denials.</key> <key name="log_0284">[ Error ] - Failed to remove the file: [#!variable!file!#]! Please check the permissions or for SELinux denials.</key>
<key name="log_0285">As requested by another machine, we will now delete the file: [#!variable!file!#].</key> <key name="log_0285">As requested by another machine, we will now delete the file: [#!variable!file!#].</key>
<key name="log_0286">[ Error ] - The URL: [#!variable!url!#] to download appears to be invalid.</key>
<key name="log_0287">[ Error ] - The requested URL: [#!variable!url!#] was not found on the remote server.</key>
<key name="log_0288">[ Error ] - The requested URL: [#!variable!url!#] does not resolve to a known domain.</key>
<key name="log_0289">[ Error ] - The requested URL: [#!variable!url!#] failed because the remote host refused the connection.</key>
<key name="log_0290">[ Error ] - The requested URL: [#!variable!url!#] failed because there is no route to that host.</key>
<key name="log_0292">[ Error ] - The requested URL: [#!variable!url!#] failed because the network is unreachable.</key>
<key name="log_0293">[ Error ] - The requested URL: [#!variable!url!#] failed for an unknown reason.</key>
<key name="log_0294"><![CDATA[[ Error ] - The method Convert->time() was passed the 'time' of: [#!variable!time!#] which does not appear to be a whole number.]]></key>
<!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. --> <!-- Test words. Do NOT change unless you update 't/Words.t' or tests will needlessly fail. -->
<key name="t_0000">Test</key> <key name="t_0000">Test</key>
@ -754,6 +762,16 @@ Here we will inject 't_0006', which injects 't_0001' which has a variable: [#!st
<!-- These are generally units and appended to numbers --> <!-- These are generally units and appended to numbers -->
<key name="suffix_0001">#!variable!number!#/sec</key> <key name="suffix_0001">#!variable!number!#/sec</key>
<key name="suffix_0002">s</key> <!-- Short suffix for 'seconds'. -->
<key name="suffix_0003">m</key> <!-- Short suffix for 'minutes'. -->
<key name="suffix_0004">h</key> <!-- Short suffix for 'hours'. -->
<key name="suffix_0005">d</key> <!-- Short suffix for 'days'. -->
<key name="suffix_0006">w</key> <!-- Short suffix for 'weeks'. -->
<key name="suffix_0007">Seconds</key> <!-- Long suffix for 'seconds'. -->
<key name="suffix_0008">Minutes</key> <!-- Long suffix for 'minutes'. -->
<key name="suffix_0009">Hours</key> <!-- Long suffix for 'hours'. -->
<key name="suffix_0010">Days</key> <!-- Long suffix for 'days'. -->
<key name="suffix_0011">Weeks</key> <!-- Long suffix for 'days'. -->
<!-- Strings used by jobs --> <!-- Strings used by jobs -->
<key name="job_0001">Configure Network</key> <key name="job_0001">Configure Network</key>

@ -0,0 +1,244 @@
#!/usr/bin/perl
#
# This takes a URL (ftp, http or https) and downloads the file. If it is called without --url, it shows the
# progress of any other instances currently downloading files.
#
# Return codes:
# 0 = Normal exit.
# 1 = URL not found.
# 2 = The requested URL was not found on the remote server.
# 3 = The requested URL does not resolve to a known domain.
# 4 = The requested URL failed because the remote host refused the connection.
# 5 = The requested URL failed because there is no route to that host.
# 6 = Abort requested, but UUID or PID not passed
# 7 = The requested URL failed because the network is unreachable.
#
#
# 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->Log->level({set => 2});
$anvil->Log->secure({set => 1});
$anvil->data->{switches}{abort} = "";
$anvil->data->{switches}{'job-uuid'} = "";
$anvil->data->{switches}{'save-to'} = ""; # /mnt/shared/files by default
$anvil->data->{switches}{url} = "";
$anvil->Get->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
'switches::abort' => $anvil->data->{switches}{abort},
'switches::job-uuid' => $anvil->data->{switches}{'job-uuid'},
'switches::save-to' => $anvil->data->{switches}{'save-to'},
'switches::url' => $anvil->data->{switches}{url},
}});
if ($anvil->data->{switches}{abort})
{
# Kill the other download
abort_download($anvil);
}
elsif ($anvil->data->{switches}{url})
{
# Try to download the file
download_file($anvil);
}
else
{
# Show the status of any downloading, finished, failed or aborted downloads.
show_status($anvil);
}
# We're done
$anvil->nice_exit({exit_code => 0});
#############################################################################################################
# Private functions. #
#############################################################################################################
sub abort_download
{
my ($anvil) = @_;
my $failed = 0;
my $url = $anvil->data->{switches}{url};
my $file_name = ($url =~ /^.*\/(.*)$/)[0];
my $temp_file = $anvil->data->{path}{directories}{shared}{temp}."/".$file;
my $save_to = $anvil->data->{switches}{'save-to'} ? $anvil->data->{switches}{'save-to'} : $anvil->data->{path}{directories}{shared}{files};
my $out_file = $save_to."/".$file;
$save_to =~ s/\/\///g;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
url => $url,
file_name => $file_name,
temp_file => $temp_file,
save_to => $save_to,
}});
# Is this a supported protocol?
if (($url !~ /^ftp\:\/\//) && ($url !~ /^http\:\/\//) && ($url !~ /^https\:\/\//))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0286", variables => { url => $url }});
$anvil->nice_exit({exit_code => 1});
}
### NOTE: We don't use System->call because we need to track the output in real time.
# Try to download it.
my $bytes_downloaded = 0;
my $downloaded = 0; # Bytes
my $percent = 0;
my $rate = 0; # Bytes/sec
my $time_left = 0; # Seconds
my $running_time = 0;
my $shell_call = $anvil->data->{path}{exe}{wget}." --continue --progress=dot:binary ".$url." --output-document ".$temp_file;
open (my $file_handle, $shell_call." 2>&1 |") or $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 0, secure => $secure, priority => "err", key => "log_0014", variables => { shell_call => $shell_call, error => $! }});
while(<$file_handle>)
{
chomp;
my $line = $anvil->Words->clean_spaces({ string => $_ });;
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, level => 3, key => "log_0017", variables => { line => $line }});
# Check for problems
if (($line =~ /404/) && ($line =~ /Not Found/i))
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0287", variables => { url => $url }});
$failed = 2;
}
elsif ($line =~ /Name or service not known/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0288", variables => { url => $url }});
$failed = 3;
}
elsif ($line =~ /Connection refused/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0289", variables => { url => $url }});
$failed = 4;
}
elsif ($line =~ /route to host/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0290", variables => { url => $url }});
$failed = 5;
}
elsif ($line =~ /Network is unreachable/i)
{
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0291", variables => { url => $url }});
$failed = 7;
}
elsif ($line =~ /^(\d+)K .*? (\d+)% (.*?) (\d+.*)$/)
{
$downloaded = $1;
$percent = $2;
$rate = $3;
$time_left = $4;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 2, list => {
downloaded => $url,
percent => $percent,
rate => $rate,
time_left => $time_left,
}});
### NOTE: According to: http://savannah.gnu.org/bugs/index.php?22765, wget uses base-2.
# Convert
$bytes_downloaded = $downloaded * 1024;
my $say_downloaded = $anvil->Convert->bytes_to_human_readable({'bytes' => $bytes_downloaded});
my $say_percent = $percent."%";
my $byte_rate = $anvil->Convert->human_readable_to_bytes({size => $rate, base2 => 1});
my $say_rate = $anvil->Convert->bytes_to_human_readable({'bytes' => $byte_rate})."/s";
$running_time = time - $unix_start;
my $say_running_time = $an->Readable->time({'time' => $running_time, process => 1});
# Time left is a bit more complicated
my $days = 0;
my $hours = 0;
my $minutes = 0;
my $seconds = 0;
if ($time_left =~ /(\d+)d/)
{
$days = $1;
#print "$THIS_FILE ".__LINE__."; == days: [$days]\n";
}
if ($time_left =~ /(\d+)h/)
{
$hours = $1;
#print "$THIS_FILE ".__LINE__."; == hours: [$hours]\n";
}
if ($time_left =~ /(\d+)m/)
{
$minutes = $1;
#print "$THIS_FILE ".__LINE__."; == minutes: [$minutes]\n";
}
if ($time_left =~ /(\d+)s/)
{
$seconds = $1;
#print "$THIS_FILE ".__LINE__."; == seconds: [$seconds]\n";
}
my $seconds_left = (($days * 86400) + ($hours * 3600) + ($minutes * 60) + $seconds);
my $say_time_left = $an->Readable->time({'time' => $seconds_left, suffix => "long", process => 1});
$running_time = 1 if not $running_time;
$average_rate = int($bytes_downloaded / $running_time);
my $say_average_rate = $anvil->Convert->bytes_to_human_readable({'bytes' => $average_rate})."/s";
#print "$THIS_FILE ".__LINE__."; downloaded: [$downloaded], bytes_downloaded: [$bytes_downloaded], say_downloaded: [$say_downloaded], percent: [$percent], rate: [$rate], byte_rate: [$byte_rate], say_rate: [$say_rate], time_left: [$time_left]\n";
if (time > $next_report)
{
#print "$THIS_FILE ".__LINE__."; say_downloaded: [$say_downloaded], percent: [$percent], say_rate: [$say_rate], running_time: [$running_time], say_running_time: [$say_running_time], seconds_left: [$seconds_left], say_time_left: [$say_time_left]\n";
#print "$file; Downloaded: [$say_downloaded]/[$say_percent], Rate/Avg: [$say_rate]/[$say_average_rate], Running: [$say_running_time], Left: [$say_time_left]\n";
#print "$THIS_FILE ".__LINE__."; bytes_downloaded=$bytes_downloaded, percent=$percent, current_rate=$byte_rate, average_rate=$average_rate, seconds_running=$running_time, seconds_left=$seconds_left, out_file=$out_file\n";
$next_report += $report_interval;
my $shell_call = $progress_file;
$an->Log->entry({log_level => 3, message_key => "an_variables_0001", message_variables => {
name1 => "shell_call", value1 => $shell_call,
}, file => $THIS_FILE, line => __LINE__});
open (my $file_handle, ">$shell_call") or $an->Alert->error({title_key => "an_0003", message_key => "error_title_0015", message_variables => { shell_call => $shell_call, error => $! }, code => 2, file => $THIS_FILE, line => __LINE__});
print $file_handle "uuid=$uuid bytes_downloaded=$bytes_downloaded percent=$percent current_rate=$byte_rate average_rate=$average_rate seconds_running=$running_time seconds_left=$seconds_left url=$url out_file=$out_file\n";
close $file_handle;
}
}
}
close $file_handle;
return(0);
}
sub download_file
{
my ($anvil) = @_;
return(0);
}
# Show the status of any downloading, finished, failed or aborted downloads.
sub show_status
{
my ($anvil) = @_;
return(0);
}

@ -54,22 +54,24 @@ my $anvil = Anvil::Tools->new();
$anvil->Log->level({set => 2}); $anvil->Log->level({set => 2});
$anvil->Log->secure({set => 1}); $anvil->Log->secure({set => 1});
$anvil->data->{switches}{'delete'} = "";
$anvil->data->{switches}{download} = "";
$anvil->data->{switches}{everywhere} = "";
$anvil->data->{switches}{file} = "";
$anvil->data->{switches}{'is-script'} = "";
$anvil->data->{switches}{'job-uuid'} = ""; $anvil->data->{switches}{'job-uuid'} = "";
$anvil->data->{switches}{'rename'} = ""; $anvil->data->{switches}{'rename'} = "";
$anvil->data->{switches}{'is-script'} = "";
$anvil->data->{switches}{file} = "";
$anvil->data->{switches}{to} = ""; $anvil->data->{switches}{to} = "";
$anvil->data->{switches}{'delete'} = "";
$anvil->data->{switches}{everywhere} = "";
$anvil->Get->switches; $anvil->Get->switches;
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
"switches::delete" => $anvil->data->{switches}{'delete'},
"switches::download" => $anvil->data->{switches}{download},
"switches::everywhere" => $anvil->data->{switches}{everywhere},
"switches::file" => $anvil->data->{switches}{file},
"switches::is-script" => $anvil->data->{switches}{'is-script'},
"switches::job-uuid" => $anvil->data->{switches}{'job-uuid'}, "switches::job-uuid" => $anvil->data->{switches}{'job-uuid'},
"switches::rename" => $anvil->data->{switches}{'rename'}, "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::to" => $anvil->data->{switches}{to},
"switches::delete" => $anvil->data->{switches}{'delete'},
"switches::everywhere" => $anvil->data->{switches}{everywhere},
}}); }});
# Connect or die # Connect or die
@ -582,8 +584,12 @@ sub check_incoming
$anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0264"}); $anvil->Log->entry({source => $THIS_FILE, line => __LINE__, 'print' => 1, level => 1, key => "log_0264"});
foreach my $full_path (sort {$a cmp $b} keys %{$anvil->data->{scan}{directories}}) foreach my $full_path (sort {$a cmp $b} keys %{$anvil->data->{scan}{directories}})
{ {
# Skip this if it's under '/mnt/shared/temp' (that's used for files being downloaded, etc)
next if $full_path =~ /^\/mnt\/shared\/temp\//;
# Skip if this isn't a file. # Skip if this isn't a file.
my $file_type = $anvil->data->{scan}{directories}{$full_path}{type}; my $file_type = $anvil->data->{scan}{directories}{$full_path}{type};
$anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => { $anvil->Log->variables({source => $THIS_FILE, line => __LINE__, level => 3, list => {
full_path => $full_path, full_path => $full_path,
file_type => $file_type, file_type => $file_type,

Loading…
Cancel
Save