From c020337ca444116990afc9558df0441c95704ed2 Mon Sep 17 00:00:00 2001 From: David Schneider Date: Tue, 29 Apr 2025 17:40:49 +0200 Subject: [PATCH 1/6] bangadm for cmdline reporting, additional system status, small improvements --- BaNG | 26 + BaNGadm | 157 +++- lib/BaNG/BackupServer.pm | 14 +- lib/BaNG/Config.pm | 112 ++- lib/BaNG/Converter.pm | 4 +- lib/BaNG/Reporting.pm | 677 ++++++++++++++++-- lib/BaNG/TM_rsync.pm | 13 +- .../Auth/Extensible/Provider/LDAPiam.pm | 113 +++ views/dashboard-error_report.tt | 6 +- views/host-bkpreport.tt | 6 +- views/report-xymon.tt | 2 +- views/reporting-jobs.tt | 6 +- views/reporting-task_jobs.tt | 6 +- views/reporting-tasks.tt | 6 +- 14 files changed, 1038 insertions(+), 110 deletions(-) create mode 100644 lib/Dancer/Plugin/Auth/Extensible/Provider/LDAPiam.pm diff --git a/BaNG b/BaNG index 3029de3..662ab30 100755 --- a/BaNG +++ b/BaNG @@ -70,6 +70,32 @@ if ( $serverconfig{bkpmode} eq "rsync") { reorder_queue_by_priority( $taskid, $host_arg, $group_arg ); + # add all queued jobs to db, in order of their priority. + # this allows distinction between still queued, running + # and finished jobs. + foreach my $bkpjob (@queue) { + my $exclsubfolders = $bkpjob->{'exclsubfolders'} || 0; + + # append custom string to path written to DB + # if we are excluding subfolders + my $bkpfrompath = $exclsubfolders + ? $bkpjob->{path}."/FILESONLY" + : $bkpjob->{path}; + + $bkpfrompath =~ s/'//g; + $bkpfrompath =~ s/\/\//\//g; + + bangstat_report_queue_backupjob( + $bkpjob->{taskid}, + $bkpjob->{jobid}, + $bkpjob->{host}, + $bkpjob->{group}, + $bkpfrompath, + $bkpjob->{srcfolder}, + targetpath($bkpjob->{host}, $bkpjob->{group}) + ); + } + run_rsync_threads($host_arg, $group_arg, $nthreads_arg, $dryrun_arg, $cron); logit( $taskid, $host_arg, $group_arg, "Task $taskid finished!" ); diff --git a/BaNGadm b/BaNGadm index 054a107..da6cbd8 100755 --- a/BaNGadm +++ b/BaNGadm @@ -1,7 +1,9 @@ #!/usr/bin/env perl use strict; use warnings; + use Cwd qw( abs_path ); +use Data::Dumper; use File::Basename; use Getopt::Long qw( :config no_auto_abbrev ); use POSIX qw( strftime ); @@ -12,6 +14,7 @@ use BaNG::Config; use BaNG::BackupServer; use BaNG::Wipe; use BaNG::Reporting; +use BaNG::Statistics; my $version = '0.1'; my $add_arg; @@ -33,6 +36,15 @@ my $prefix_arg; my $showgroups; my $get_oob_snapshots_arg; +my $showrep_recentbackups_arg; +my $showrep_recenttasks_arg; +my $showrep_jobstatus_arg; +# host_arg and group_arg already defined above! +my $taskid_arg; +my $jobid_arg; +my $jobstatus_arg; +my $lastxhours_arg; + ################################# # Main # @@ -276,6 +288,122 @@ if ($tidy_arg) { } +if ($showrep_recentbackups_arg) { + printf("Recent Backups\n"); + printf("\n"); + + my %RecentBackups = bangstat_recentbackups($host_arg); + + my $unified_backups = []; + + foreach my $hostgroupkey (sort(keys(%RecentBackups))) { + foreach my $backup (@{$RecentBackups{$hostgroupkey}}) { + if (!defined($backup->{'JobID'})) { + $backup->{'JobID'} = '-'; + } + + push(@$unified_backups, $backup); + } + } + + my $formats = [ + { 'colkey' => 'BkpHost', 'title' => 'Host', 'align' => 'l', 'maxlen' => 10 }, + { 'colkey' => 'BkpGroup', 'title' => 'Group', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'TaskID', 'title' => 'TaskID', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'JobID', 'title' => 'JobID', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'Cron', 'title' => 'Cron', 'align' => 'r', 'maxlen' => 4 }, + { 'colkey' => 'isThread', 'title' => 'Thr', 'align' => 'r', 'maxlen' => 3 }, + { 'colkey' => 'ErrStatus', 'title' => 'ErrSt', 'align' => 'r', 'maxlen' => 5 }, + { 'colkey' => 'JobStatus', 'title' => 'JobSt', 'align' => 'r', 'maxlen' => 5 }, + { 'colkey' => 'Jobs', 'title' => 'Jobs', 'align' => 'r', 'maxlen' => 4 }, + { 'colkey' => 'Starttime', 'title' => 'Start', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'Runtime', 'title' => 'Runtime', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesCreated', 'title' => 'FilesCr', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesDel', 'title' => 'FilesDel', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesTrans', 'title' => 'FilesTrans', 'align' => 'r', 'maxlen' => 10 }, + { 'colkey' => 'SizeTrans', 'title' => 'SizeTrans', 'align' => 'r', 'maxlen' => 9 }, + { 'colkey' => 'TotFileSize', 'title' => 'TotFileSize', 'align' => 'r', 'maxlen' => 11 }, + { 'colkey' => 'NumOfFiles', 'title' => 'NumFiles', 'align' => 'r', 'maxlen' => 8 } + ]; + + print_formatted_table($unified_backups, $formats); + printf("\n"); +} + +if ($showrep_recenttasks_arg) { + printf("Recent Tasks\n"); + printf("\n"); + + my $RecentTasks = bangstat_recent_tasks(); + + my $formats = [ + { 'colkey' => 'TaskID', 'title' => 'TaskID', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'Taskname', 'title' => 'Taskname', 'align' => 'l', 'maxlen' => 30 }, + { 'colkey' => 'Description', 'title' => 'Desc', 'align' => 'l', 'maxlen' => 10 }, + { 'colkey' => 'Cron', 'title' => 'Cron', 'align' => 'r', 'maxlen' => 4 }, + { 'colkey' => 'isThread', 'title' => 'Thr', 'align' => 'r', 'maxlen' => 3 }, + { 'colkey' => 'ErrStatus', 'title' => 'ErrSt', 'align' => 'r', 'maxlen' => 5 }, + { 'colkey' => 'JobStatus', 'title' => 'JobSt', 'align' => 'r', 'maxlen' => 5 }, + { 'colkey' => 'Jobs', 'title' => 'Jobs', 'align' => 'r', 'maxlen' => 4 }, + { 'colkey' => 'BkpToHost', 'title' => 'Host', 'align' => 'l', 'maxlen' => 10 }, + { 'colkey' => 'Starttime', 'title' => 'Start', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'Runtime', 'title' => 'Runtime', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesCreated', 'title' => 'FilesCr', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesDel', 'title' => 'FilesDel', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesTrans', 'title' => 'FilesTrans', 'align' => 'r', 'maxlen' => 10 }, + { 'colkey' => 'SizeTrans', 'title' => 'SizeTrans', 'align' => 'r', 'maxlen' => 9 }, + { 'colkey' => 'TotFileSize', 'title' => 'TotFileSize', 'align' => 'r', 'maxlen' => 11 }, + { 'colkey' => 'NumOfFiles', 'title' => 'NumFiles', 'align' => 'r', 'maxlen' => 8 } + ]; + + print_formatted_table($RecentTasks->{'Data'}, $formats); + printf("\n"); +} + +if ($showrep_jobstatus_arg) { + + printf("Job Status Report (last %s hours)\n", defined($lastxhours_arg) ? $lastxhours_arg : 24); + printf("\n"); + + my $jobstatus = string_to_jobstatus($jobstatus_arg); + + my $backups_found = bangstat_jobs_by_jobstatus( + $taskid_arg, + $host_arg, + $group_arg, + $jobid_arg, + $jobstatus, + $lastxhours_arg + ); + + foreach my $backup (@$backups_found) { + $backup->{'JobStatus'} = jobstatus_to_string($backup->{'JobStatus'}); + } + + my $formats = [ + { 'colkey' => 'TaskID', 'title' => 'TaskID', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'BkpHost', 'title' => 'Host', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'BkpGroup', 'title' => 'Group', 'align' => 'r', 'maxlen' => 20 }, + { 'colkey' => 'JobID', 'title' => 'JobID', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'JobStatus', 'title' => 'JobStatus', 'align' => 'r', 'maxlen' => 12 }, + { 'colkey' => 'ErrStatus', 'title' => 'ErrSt', 'align' => 'r', 'maxlen' => 10 }, + { 'colkey' => 'Starttime', 'title' => 'Start', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'Stoptime', 'title' => 'Stop', 'align' => 'l', 'maxlen' => 20 }, + { 'colkey' => 'Runtime', 'title' => 'Runtime', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'isThread', 'title' => 'Thr', 'align' => 'r', 'maxlen' => 3 }, + { 'colkey' => 'NumOfFiles', 'title' => 'NumFiles', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesTrans', 'title' => 'FilesTrans', 'align' => 'r', 'maxlen' => 10 }, + { 'colkey' => 'FilesCreated', 'title' => 'FilesCr', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'FilesDel', 'title' => 'FilesDel', 'align' => 'r', 'maxlen' => 8 }, + { 'colkey' => 'TotFileSize', 'title' => 'TotFileSize', 'align' => 'r', 'maxlen' => 11 }, + { 'colkey' => 'SizeTrans', 'title' => 'SizeTrans', 'align' => 'r', 'maxlen' => 9 }, + { 'colkey' => 'BkpFromPath', 'title' => 'BkpFromPath', 'align' => 'l', 'maxlen' => 30 } + ]; + + print_formatted_table($backups_found, $formats); + printf("\n"); +} + ################################# # Command line arguments # @@ -301,15 +429,28 @@ sub parse_command_options { 'p|prefix=s' => \$prefix_arg, 'showgroups' => \$showgroups, 'setprop=s' => \$setprop, - 'get_oob_snapshots:i' => \$get_oob_snapshots_arg, + 'taskid=s' => \$taskid_arg, + 'jobid=s' => \$jobid_arg, + 'jobstatus=s' => \$jobstatus_arg, + 'lastxhours=s'=> \$lastxhours_arg, + 'get_oob_snapshots:i' => \$get_oob_snapshots_arg, + 'showrep-recentbackups' => \$showrep_recentbackups_arg, + 'showrep-recenttasks' => \$showrep_recenttasks_arg, + 'showrep-jobstatus' => \$showrep_jobstatus_arg + ) or usage('Invalid commmand line options.'); + usage('Do not use together!') if ( $croncreate && $showgroups ); usage('Check the arguments!') unless ( ( $add_arg || $delete_arg ) && (( $host_arg && $group_arg ) || $group_arg ) || ( $initial_arg && ( $host_arg && $group_arg ) ) || ( $initial_arg && ( $host_arg && $group_arg && ( $prefix_arg || $dryrun_arg ))) || ( $croncreate || $croncheck || $failed_arg || $showgroups || $tidy_arg || $dump_arg ) || - ( $host_arg && $group_arg && $setprop ) || (defined $get_oob_snapshots_arg) + ( $host_arg && $group_arg && $setprop ) || + ( $showrep_recentbackups_arg && $host_arg ) || + ( $showrep_recenttasks_arg ) || + ( $showrep_jobstatus_arg ) || + (defined $get_oob_snapshots_arg) ); usage('The option --get_oob_snapshots can only take the values 1 for oob-only or 2 to include failed backups.') if ($get_oob_snapshots_arg && ($get_oob_snapshots_arg >=3 || $get_oob_snapshots_arg <0)); @@ -371,6 +512,18 @@ sub usage { $command --showgroups # show available groups $command --help # show this help message $command --version # show version number and help + + $command --showrep-recentbackups # show recent backups report (requires --host=...) + $command --showrep-recenttasks # show recent tasks report + $command --showrep-jobstatus # show job status report + # if no filter args are specified only --lastxhours is set to 24. + # the following filters are supported: + # --taskid= + # --host= + # --group= + # --jobid= + # --jobstatus=(QUEUED|STARTED|PROCESSED|FINISHED) + # --lastxhours= Optional Arguments: diff --git a/lib/BaNG/BackupServer.pm b/lib/BaNG/BackupServer.pm index 0f77024..e811983 100644 --- a/lib/BaNG/BackupServer.pm +++ b/lib/BaNG/BackupServer.pm @@ -146,7 +146,7 @@ sub check_client_connection { my $state = 0; my $msg = 'Host offline'; - my $p = Net::Ping->new( 'tcp', 2 ); + my $p = Net::Ping->new( 'icmp', 2 ); eval { if ( $p->ping($host) ) { @@ -269,8 +269,8 @@ sub pre_queue_checks { $endstamp = $startstamp; $jobid = create_timeid( $taskid, $host, $group ); logit( $taskid, $host, $group, "Error: host $host is offline" ); - bangstat_start_backupjob( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, - $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, targetpath( $host, $group ), '0', '-1', '' ) unless $noreport; + bangstat_report_pre_queue_error( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, + $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, targetpath( $host, $group ), ERRSTATUS_NOERR, JOBSTATUS_FAILED_OFFLINE, '' ) unless $noreport; return 0; } @@ -283,8 +283,8 @@ sub pre_queue_checks { $endstamp = $startstamp; $jobid = create_timeid( $taskid, $host, $group ); logit( $taskid, $host, $group, "Error: ". $hosts{"$host-$group"}->{hostconfig}->{BKP_RSYNC_RSHELL} ." on host $host not working" ); - bangstat_start_backupjob( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, - $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, targetpath( $host, $group ), '0', '-2', '' ) unless $noreport; + bangstat_report_pre_queue_error( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, + $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, targetpath( $host, $group ), ERRSTATUS_NOERR, JOBSTATUS_FAILED_RSHELL, '' ) unless $noreport; mail_report( $taskid, $host, $group, () ) if $serverconfig{report_to}; return 0; } @@ -294,8 +294,8 @@ sub pre_queue_checks { $endstamp = $startstamp; $jobid = create_timeid( $taskid, $host, $group ); logit( $taskid, $host, $group, "RSYNC command " . ($serverconfig{path_rsync} || "") . " not found!" ); - bangstat_start_backupjob( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, - $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, targetpath( $host, $group ), '0', '-5', '' ) unless $noreport; + bangstat_report_pre_queue_error( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, + $hosts{"$host-$group"}->{hostconfig}->{BKP_SOURCE_FOLDER}, targetpath( $host, $group ), ERRSTATUS_NOERR, JOBSTATUS_FAILED_RSYNCAPP, '' ) unless $noreport; return 0; } diff --git a/lib/BaNG/Config.pm b/lib/BaNG/Config.pm index c42c5ca..c123225 100644 --- a/lib/BaNG/Config.pm +++ b/lib/BaNG/Config.pm @@ -9,9 +9,44 @@ use File::Find::Rule; use POSIX qw( strftime ); use YAML::Tiny qw( LoadFile DumpFile ); use Text::Diff; - use Exporter 'import'; + +################################################### +##TBD: due to introduction of new job status +# JOBSTATUS_QUEUED, the numeric values of +# the existing positive job statuses is +# incremented by one, which might lead to +# problems in external components evaluating +# the bangstat database, or in BaNG-Web. +# One might have to adjust the values in +# the latter code and also migrate the +# jobstatus values in existing bangstat +# database tables which contain rows generated +# before this change was introduced. +##NB: View Templates in views/*.tt updated. +# +################################################### + +use constant ERRSTATUS_NOERR => 0; + +use constant JOBSTATUS_QUEUED => 0; # (new) - "job was placed to queue" +use constant JOBSTATUS_STARTED => 1; # prev value: 0 "specific rsync process started" +use constant JOBSTATUS_PROCESSED => 2; # prev value: 1, "specific rsync process done" +use constant JOBSTATUS_FINISHED => 3; # prev value: 2, "all rsync processes done" + +use constant JOBSTATUS_FAILED_OFFLINE => -1; # storage server unreachable from bang-server +use constant JOBSTATUS_FAILED_RSHELL => -2; # remote shell error (rsh, ssh) +use constant JOBSTATUS_FAILED_RSYNCAPP => -5; # rsync utility not found + our @EXPORT = qw( + ERRSTATUS_NOERR + JOBSTATUS_QUEUED + JOBSTATUS_STARTED + JOBSTATUS_PROCESSED + JOBSTATUS_FINISHED + JOBSTATUS_FAILED_OFFLINE + JOBSTATUS_FAILED_RSHELL + JOBSTATUS_FAILED_RSYNCAPP %hosts %groups %servers @@ -38,6 +73,8 @@ our @EXPORT = qw( targetpath list_groups list_groupmembers + jobstatus_to_string + string_to_jobstatus ); our %hosts; @@ -79,6 +116,10 @@ sub get_serverconfig { }; } + if (!defined($servers{$servername})) { + die("Missing serverconfig file %s_defaults.yaml\n", $servername); + } + # copy info about localhost to separate hash for easier retrieval foreach my $key ( keys %{$servers{$servername}{serverconfig}} ) { $serverconfig{$key} = $servers{$servername}{serverconfig}->{$key}; @@ -459,6 +500,75 @@ sub list_groupmembers { return \@groupmembers; } +sub jobstatus_to_string { + my ($jobstatus) = @_; + + my $jobstatus_str = 'UNKNOWN'; + + if (defined($jobstatus)) { + if ($jobstatus =~ m/^\d+$/) { + if ($jobstatus == JOBSTATUS_QUEUED) { + $jobstatus_str = 'QUEUED'; + + } elsif ($jobstatus == JOBSTATUS_STARTED) { + $jobstatus_str = 'STARTED'; + + } elsif ($jobstatus == JOBSTATUS_PROCESSED) { + $jobstatus_str = 'PROCESSED'; + + } elsif ($jobstatus == JOBSTATUS_FINISHED) { + $jobstatus_str = 'FINISHED'; + + } elsif ($jobstatus == JOBSTATUS_FAILED_OFFLINE) { + $jobstatus_str = 'ERR_OFFLINE'; + + } elsif ($jobstatus == JOBSTATUS_FAILED_RSHELL) { + $jobstatus_str = 'ERR_RSHELL'; + + } elsif ($jobstatus == JOBSTATUS_FAILED_RSYNCAPP) { + $jobstatus_str = 'ERR_RSYNC'; + } + } + } + + return $jobstatus_str; +} + +sub string_to_jobstatus { + my ($string) = @_; + + my $jobstatus = undef; + + if (defined($string)) { + if ($string eq 'QUEUED') { + $jobstatus = JOBSTATUS_QUEUED; + + } elsif ($string eq 'STARTED') { + $jobstatus = JOBSTATUS_STARTED; + + } elsif ($string eq 'PROCESSED') { + $jobstatus = JOBSTATUS_PROCESSED; + + } elsif ($string eq 'FINISHED') { + $jobstatus = JOBSTATUS_FINISHED; + + } elsif ($string eq 'ERR_OFFLINE') { + $jobstatus = JOBSTATUS_FAILED_OFFLINE; + + } elsif ($string eq 'ERR_RSHELL') { + $jobstatus = JOBSTATUS_FAILED_RSHELL; + + } elsif ($string eq 'ERR_RSYNC') { + $jobstatus = JOBSTATUS_FAILED_RSYNCAPP; + + } else { + printf(STDERR "invalid jobstatus argument specified.\n"); + } + } + + return $jobstatus; +} + sub _read_host_configfile { my ( $host, $group ) = @_; diff --git a/lib/BaNG/Converter.pm b/lib/BaNG/Converter.pm index ca7d274..14cdd6d 100644 --- a/lib/BaNG/Converter.pm +++ b/lib/BaNG/Converter.pm @@ -22,9 +22,9 @@ sub num2human { foreach my $unit ( '', qw(k M G T P) ) { if ( $num < $base ) { if ( $num < 10 && $num > 0 ) { - return sprintf( '%.2f %s', $num, $unit ); # print small values with 1 decimal + return sprintf( '%.2f%s', $num, $unit ? ' '.$unit : ''); # print small values with 1 decimal } else { - return sprintf( '%.1f %s', $num, $unit ); # print larger values without decimals + return sprintf( '%.1f%s', $num, $unit ? ' '.$unit : ''); # print larger values without decimals } } $num = $num / $base; diff --git a/lib/BaNG/Reporting.pm b/lib/BaNG/Reporting.pm index dc8b601..cfb4487 100644 --- a/lib/BaNG/Reporting.pm +++ b/lib/BaNG/Reporting.pm @@ -23,12 +23,15 @@ our @EXPORT = qw( bangstat_recentbackups_job_details bangstat_recent_tasks bangstat_last_transfer - bangstat_task_jobs bangstat_task_delete - bangstat_start_backupjob + bangstat_task_jobs + bangstat_jobs_by_jobstatus + bangstat_report_pre_queue_error + bangstat_report_queue_backupjob + bangstat_report_start_backupjob + bangstat_report_update_backupjob + bangstat_report_finish_backupjob bangstat_set_taskmeta - bangstat_update_backupjob - bangstat_finish_backupjob send_xymon_report mail_report xymon_report @@ -37,6 +40,7 @@ our @EXPORT = qw( read_global_log delete_logfiles error404 + print_formatted_table ); our %serverconfig; @@ -449,6 +453,135 @@ sub bangstat_task_jobs { return \%TaskJobs; } +sub bangstat_jobs_by_jobstatus { + my $taskid = shift(); + my $host = shift(); + my $group = shift(); + my $jobid = shift(); + my $jobstatus = shift(); + my $lastXhours = shift(); + + $lastXhours ||= 24; + + my $conn = bangstat_db_connect( $serverconfig{config_bangstat} ); + return '' unless $conn; + + my @where_parts = (); + my @where_params = (); + + if (defined($taskid)) { + push(@where_parts, 'TaskID = ?'); + push(@where_params, $taskid); + } + + if (defined($host)) { + push(@where_parts, 'BkpFromHost = ?'); + push(@where_params, $host); + } + + if (defined($group)) { + push(@where_parts, 'BkpGroup = ?'); + push(@where_params, $group); + } + + if (defined($jobid)) { + push(@where_parts, 'JobID = ?'); + push(@where_params, $jobid); + } + + if (defined($jobstatus)) { + push(@where_parts, 'JobStatus = ?'); + push(@where_params, $jobstatus); + } + + if (defined($lastXhours)) { + # NB: we use "TimeStamp" instead of Start, because Start is NULL + # when a job is in QUEUED status (Start is only set later, in + # bangstat_report_start_backupjob() when the job really starts). + push(@where_parts, 'TimeStamp > date_sub(NOW(), INTERVAL ? HOUR)'); + push(@where_params, $lastXhours); + } + + my $where_stmt = ''; + + if (@where_parts) { + $where_stmt = 'WHERE '.join(' AND ', @where_parts).' '; + } + + my $sql = + 'SELECT '. + 'ID, '. + 'TaskID, '. + 'BkpFromHost, '. + 'BkpGroup, '. + 'JobID, '. + 'JobStatus, '. + 'ErrStatus, '. + 'Start, '. + 'Stop, '. + 'isThread, '. + 'NumOfFiles, '. + 'NumOfFilesTrans, '. + 'NumOfFilesCreated, '. + 'NumOfFilesDel, '. + 'TotFileSize, '. + 'TotFileSizeTrans, '. + 'BkpFromPath '. + 'FROM '. + 'statistic '. + $where_stmt. + 'ORDER BY '. + 'ID'; + + my $sth = $bangstat_dbh->prepare($sql); + + if (@where_params) { + for (my $i = 0; $i < @where_params; $i++) { + $sth->bind_param($i + 1, $where_params[$i]); + } + } + + $sth->execute(); + + if ($sth->err()) { + printf("SQL ERROR (%s): %s\n", $sth->err(), $sth->errstr()); + } + + my $backups_found = []; + while ( my $dbrow = $sth->fetchrow_hashref() ) { + my $Runtime = $dbrow->{'Runtime'} ? $dbrow->{'Runtime'} / 60 : '-'; + my $Start = defined($dbrow->{'Start'}) ? $dbrow->{'Start'} : '-'; + my $Stop = defined($dbrow->{'Stop'}) ? $dbrow->{'Stop'} : '-'; + + push( + @$backups_found, + { + 'TaskID' => $dbrow->{'TaskID'}, + 'BkpHost' => $dbrow->{'BkpFromHost'}, + 'BkpGroup' => $dbrow->{'BkpGroup'} || '-', + 'JobID' => $dbrow->{'JobID'}, + 'JobStatus' => $dbrow->{'JobStatus'}, + 'ErrStatus' => $dbrow->{'ErrStatus'}, + 'Starttime' => $Start, + 'Stoptime' => $Stop, + 'Runtime' => time2human($Runtime), + 'isThread' => $dbrow->{'isThread'}, + 'NumOfFiles' => num2human($dbrow->{'NumOfFiles'}), + 'FilesTrans' => num2human($dbrow->{'NumOfFilesTrans'}), + 'FilesCreated' => num2human($dbrow->{'NumOfFilesCreated'}), + 'FilesDel' => num2human($dbrow->{'NumOfFilesDel'}), + 'TotFileSize' => num2human($dbrow->{'TotFileSize'},1024), + 'SizeTrans' => num2human($dbrow->{'TotFileSizeTrans'},1024), + 'BkpFromPath' => $dbrow->{'BkpFromPath'} + } + ); + } + + $sth->finish(); + + return $backups_found; +} + sub send_xymon_report { my ($report) = @_; @@ -471,22 +604,34 @@ sub send_xymon_report { return 1; } -sub bangstat_start_backupjob { +sub bangstat_report_pre_queue_error { my ( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $path, $srcfolder, $targetpath, $errcode, $jobstatus, @outlines ) = @_; if ( $serverconfig{db_support} ) { $path =~ s/'//g; # rm quotes to avoid errors in sql syntax - my $isSubfolderThread = $hosts{"$host-$group"}->{hostconfig}->{BKP_THREAD_SUBFOLDERS} ? 'true' : 'NULL'; + my $isSubfolderThread = $hosts{"$host-$group"}->{hostconfig}->{BKP_THREAD_SUBFOLDERS} ? 1 : undef; + + # We don't set a start time, since the job is just queued + # and not yet started. + my $sql = + 'INSERT INTO statistic ('. + 'TaskID, '. + 'JobID, '. + 'Start, '. + 'Stop, '. + 'BkpFromHost, '. + 'BkpGroup, '. + 'BkpFromPath, '. + 'BkpFromPathRoot, '. + 'BkpToHost, '. + 'BkpToPath, '. + 'isThread, '. + 'ErrStatus, '. + 'JobStatus '. + ') '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - my $sql = qq( - INSERT INTO statistic ( - TaskID, JobID, BkpFromHost, BkpGroup, BkpFromPath, BkpFromPathRoot, BkpToHost, BkpToPath, - isThread, ErrStatus, JobStatus, Start - ) VALUES ( - '$taskid', '$jobid', '$host', '$group', '$path', '$srcfolder', '$servername', '$targetpath', - $isSubfolderThread , '$errcode', '$jobstatus', FROM_UNIXTIME('$startstamp') - ) - ); logit( $taskid, $host, $group, "DB Report SQL command: $sql" ) if ( $serverconfig{verboselevel} >= 2 ); my $conn = bangstat_db_connect( $serverconfig{config_bangstat} ); @@ -496,39 +641,121 @@ sub bangstat_start_backupjob { } my $sth = $bangstat_dbh->prepare($sql); + + $sth->bind_param( 1, $taskid); + $sth->bind_param( 2, $jobid); + $sth->bind_param( 3, $startstamp); + $sth->bind_param( 4, $endstamp); + $sth->bind_param( 5, $host); + $sth->bind_param( 6, $group); + $sth->bind_param( 7, $path); + $sth->bind_param( 8, $srcfolder); + $sth->bind_param( 9, $servername); + $sth->bind_param(10, $targetpath); + $sth->bind_param(11, $isSubfolderThread); + $sth->bind_param(12, $errcode); + $sth->bind_param(13, $jobstatus); + $sth->execute() unless $serverconfig{dryrun}; + + if ($sth->err()) { + printf("SQL ERROR (%s): %s\n", $sth->err(), $sth->errstr()); + } + $sth->finish(); $bangstat_dbh->disconnect; - logit( $taskid, $host, $group, "Bangstat start_backup sent." ); + logit( $taskid, $host, $group, "Bangstat queue_backup sent." ); } else { - logit( $taskid, $host, $group, "bangstat_start_backup no sent - no DB-Support!" ); + logit( $taskid, $host, $group, "bangstat_queue_backup not sent - no DB-Support!" ); } return 1; } -sub bangstat_set_taskmeta { - my ( $taskid, $host, $group, $cron, $override ) = @_; +sub bangstat_report_queue_backupjob { + my ( $taskid, $jobid, $host, $group, $path, $srcfolder, $targetpath) = @_; if ( $serverconfig{db_support} ) { - $host ||= 'BULK'; - $group ||= '*'; + $path =~ s/'//g; # rm quotes to avoid errors in sql syntax + my $isSubfolderThread = $hosts{"$host-$group"}->{hostconfig}->{BKP_THREAD_SUBFOLDERS} ? 1 : undef; + + # We don't set a start time, since the job is just queued + # and not yet started. + my $sql = + 'INSERT INTO statistic ('. + 'TaskID, '. + 'JobID, '. + 'BkpFromHost, '. + 'BkpGroup, '. + 'BkpFromPath, '. + 'BkpFromPathRoot, '. + 'BkpToHost, '. + 'BkpToPath, '. + 'isThread, '. + 'ErrStatus, '. + 'JobStatus '. + ') '. + 'VALUES '. + '(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'; - my $taskName = $host ."_". $group; - my $description = $override || "" ; + logit( $taskid, $host, $group, "DB Report SQL command: $sql" ) if ( $serverconfig{verboselevel} >= 2 ); - $description = get_taskmeta($host, $group) unless $override; + my $conn = bangstat_db_connect( $serverconfig{config_bangstat} ); + if ( !$conn ) { + logit( $taskid, $host, $group, "ERROR: Could not connect to DB to send bangstat report." ); + return 1; + } - print "TaskID: $taskid Taskname: $taskName Description: $description Cron: $cron\n" if $serverconfig{verbose}; + my $sth = $bangstat_dbh->prepare($sql); - my $sql = qq( - INSERT INTO statistic_task_meta ( - TaskID, TaskName, Description, Cron - ) VALUES ( - '$taskid', '$taskName', '$description', '$cron') - ); + $sth->bind_param( 1, $taskid); + $sth->bind_param( 2, $jobid); + $sth->bind_param( 3, $host); + $sth->bind_param( 4, $group); + $sth->bind_param( 5, $path); + $sth->bind_param( 6, $srcfolder); + $sth->bind_param( 7, $servername); + $sth->bind_param( 8, $targetpath); + $sth->bind_param( 9, $isSubfolderThread); + $sth->bind_param(10, ERRSTATUS_NOERR); + $sth->bind_param(11, JOBSTATUS_QUEUED); - logit( $taskid, $host, $group, "DB Report SQL command: $sql" ) if ( $serverconfig{verboselevel} >= 2 ); + $sth->execute() unless $serverconfig{dryrun}; + + if ($sth->err()) { + printf("SQL ERROR (%s): %s\n", $sth->err(), $sth->errstr()); + } + + $sth->finish(); + $bangstat_dbh->disconnect; + + logit( $taskid, $host, $group, "Bangstat queue_backup sent." ); + } else { + logit( $taskid, $host, $group, "bangstat_queue_backup not sent - no DB-Support!" ); + } + return 1; +} + +sub bangstat_report_start_backupjob { + my ( $taskid, $jobid, $host, $group, $startstamp, $endstamp, $path, $srcfolder, $targetpath, $errcode, $jobstatus, @outlines ) = @_; + + if ( $serverconfig{db_support} ) { + $path =~ s/'//g; # rm quotes to avoid errors in sql syntax + my $isSubfolderThread = $hosts{"$host-$group"}->{hostconfig}->{BKP_THREAD_SUBFOLDERS} ? 1 : undef; + + my $sql = + 'UPDATE '. + 'statistic '. + 'SET '. + 'ErrStatus = ?, '. + 'JobStatus = ?, '. + 'Start = FROM_UNIXTIME(?) '. + 'WHERE '. + 'TaskID = ? AND '. + 'JobID = ? AND '. + 'BkpFromHost = ? AND '. + 'BkpGroup = ? AND '. + 'BkpFromPath = ?'; my $conn = bangstat_db_connect( $serverconfig{config_bangstat} ); if ( !$conn ) { @@ -537,19 +764,33 @@ sub bangstat_set_taskmeta { } my $sth = $bangstat_dbh->prepare($sql); + + $sth->bind_param(1, $errcode); + $sth->bind_param(2, $jobstatus); + $sth->bind_param(3, $startstamp); + $sth->bind_param(4, $taskid); + $sth->bind_param(5, $jobid); + $sth->bind_param(6, $host); + $sth->bind_param(7, $group); + $sth->bind_param(8, $path); + $sth->execute() unless $serverconfig{dryrun}; + + if ($sth->err()) { + printf("SQL ERROR (%s): %s\n", $sth->err(), $sth->errstr()); + } + $sth->finish(); $bangstat_dbh->disconnect; - logit( $taskid, $host, $group, "Bangstat task_meta sent." ); + logit( $taskid, $host, $group, "Bangstat start_backup sent." ); } else { - logit( $taskid, $host, $group, "bangstat_set_taskmeta no sent - no DB-Support!" ); + logit( $taskid, $host, $group, "bangstat_start_backup not sent - no DB-Support!" ); } - return 1; } -sub bangstat_update_backupjob { +sub bangstat_report_update_backupjob { my ( $taskid, $jobid, $host, $group, $endstamp, $path, $targetpath, $errcode, $jobstatus, @outlines ) = @_; if ( $serverconfig{db_support} ) { @@ -590,30 +831,32 @@ sub bangstat_update_backupjob { $path =~ s/'//g; # rm quotes to avoid errors in sql syntax - my $SQL = qq( - UPDATE statistic - SET JobStatus = '$jobstatus', - ErrStatus = '$errcode', - Stop = FROM_UNIXTIME('$endstamp'), - NumOfFiles = $log_values{NumOfFiles}, - NumOfFilesTrans = $log_values{NumOfFilesTrans}, - NumOfFilesCreated = $log_values{NumOfFilesCreated}, - NumOfFilesDel = $log_values{NumOfFilesDel}, - TotFileSize = $log_values{TotFileSize}, - TotFileSizeTrans = $log_values{TotFileSizeTrans}, - LitData = $log_values{LitData}, - MatchData = $log_values{MatchData}, - FileListSize = $log_values{FileListSize}, - FileListGenTime = $log_values{FileListGenTime}, - FileListTransTime = $log_values{FileListTransTime}, - TotBytesSent = $log_values{TotBytesSent}, - TotBytesRcv = $log_values{TotBytesRcv} - WHERE TaskID = '$taskid' - AND JobID = '$jobid' - AND BkpFromHost = '$host' - AND BkpGroup = '$group' - AND BkpFromPath = '$path'; - ); + my $sql = + 'UPDATE '. + 'statistic '. + 'SET '. + 'ErrStatus = ?, '. + 'JobStatus = ?, '. + 'Stop = FROM_UNIXTIME(?), '. + 'NumOfFiles = ?, '. + 'NumOfFilesTrans = ?, '. + 'NumOfFilesCreated = ?, '. + 'NumOfFilesDel = ?, '. + 'TotFileSize = ?, '. + 'TotFileSizeTrans = ?, '. + 'LitData = ?, '. + 'MatchData = ?, '. + 'FileListSize = ?, '. + 'FileListGenTime = ?, '. + 'FileListTransTime = ?, '. + 'TotBytesSent = ?, '. + 'TotBytesRcv = ? '. + 'WHERE '. + 'TaskID = ? AND '. + 'JobID = ? AND '. + 'BkpFromHost = ? AND '. + 'BkpGroup = ? AND '. + 'BkpFromPath = ?'; my $conn = bangstat_db_connect( $serverconfig{config_bangstat} ); if ( !$conn ) { @@ -621,32 +864,60 @@ sub bangstat_update_backupjob { return 1; } - my $sth = $bangstat_dbh->prepare($SQL); + my $sth = $bangstat_dbh->prepare($sql); + + $sth->bind_param( 1, $errcode); + $sth->bind_param( 2, $jobstatus); + $sth->bind_param( 3, $endstamp); + $sth->bind_param( 4, $log_values{NumOfFiles}); + $sth->bind_param( 5, $log_values{NumOfFilesTrans}); + $sth->bind_param( 6, $log_values{NumOfFilesCreated}); + $sth->bind_param( 7, $log_values{NumOfFilesDel}); + $sth->bind_param( 8, $log_values{TotFileSize}); + $sth->bind_param( 9, $log_values{TotFileSizeTrans}); + $sth->bind_param(10, $log_values{LitData}); + $sth->bind_param(11, $log_values{MatchData}); + $sth->bind_param(12, $log_values{FileListSize}); + $sth->bind_param(13, $log_values{FileListGenTime}); + $sth->bind_param(14, $log_values{FileListTransTime}); + $sth->bind_param(15, $log_values{TotBytesSent}); + $sth->bind_param(16, $log_values{TotBytesRcv}); + $sth->bind_param(17, $taskid); + $sth->bind_param(18, $jobid); + $sth->bind_param(19, $host); + $sth->bind_param(20, $group); + $sth->bind_param(21, $path); + $sth->execute() unless $serverconfig{dryrun}; + + if ($sth->err()) { + printf("SQL ERROR (%s): %s\n", $sth->err(), $sth->errstr()); + } + $sth->finish(); $bangstat_dbh->disconnect; - $SQL =~ s/;.*/;/sg; - logit( $taskid, $host, $group, "Set jobstatus SQL command: $SQL" ) if ( $serverconfig{verbose} && $serverconfig{verboselevel} >= 2 ); logit( $taskid, $host, $group, "Set jobstatus to $jobstatus for host $host group $group jobid $jobid" ); } else { - logit( $taskid, $host, $group, "bangstat_update_backupjob no sent - no DB-Support!" ); + logit( $taskid, $host, $group, "bangstat_update_backupjob not sent - no DB-Support!" ); } return 1; } -sub bangstat_finish_backupjob { +sub bangstat_report_finish_backupjob { my ( $taskid, $jobid, $host, $group, $jobstatus ) = @_; if ( $serverconfig{db_support} ) { - my $SQL = qq( - UPDATE statistic - SET JobStatus = '$jobstatus' - WHERE BkpFromHost = '$host' - AND BkpGroup = '$group' - AND JobID = '$jobid'; - ); + my $sql = + 'UPDATE '. + 'statistic '. + 'SET '. + 'JobStatus = ? '. + 'WHERE '. + 'BkpFromHost = ? AND '. + 'BkpGroup = ? AND '. + 'JobID = ?'; my $conn = bangstat_db_connect( $serverconfig{config_bangstat} ); if ( !$conn ) { @@ -654,15 +925,66 @@ sub bangstat_finish_backupjob { return 1; } - my $sth = $bangstat_dbh->prepare($SQL); + my $sth = $bangstat_dbh->prepare($sql); + + $sth->bind_param(1, $jobstatus); + $sth->bind_param(2, $host); + $sth->bind_param(3, $group); + $sth->bind_param(4, $jobid); + $sth->execute() unless $serverconfig{dryrun}; + + if ($sth->err()) { + printf("SQL ERROR (%s): %s\n", $sth->err(), $sth->errstr()); + } + $sth->finish(); - $SQL =~ s/;.*/;/sg; - logit( $taskid, $host, $group, "Set jobstatus SQL command: $SQL" ) if ( $serverconfig{verbose} && $serverconfig{verboselevel} >= 2 ); logit( $taskid, $host, $group, "Set jobstatus to $jobstatus for host $host group $group jobid $jobid" ); } else { - logit( $taskid, $host, $group, "bangstat_finish_backupjob no sent - no DB-Support!" ); + logit( $taskid, $host, $group, "bangstat_finish_backupjob not sent - no DB-Support!" ); + } + + return 1; +} + +sub bangstat_set_taskmeta { + my ( $taskid, $host, $group, $cron, $override ) = @_; + + if ( $serverconfig{db_support} ) { + $host ||= 'BULK'; + $group ||= '*'; + + my $taskName = $host ."_". $group; + my $description = $override || "" ; + + $description = get_taskmeta($host, $group) unless $override; + + print "TaskID: $taskid Taskname: $taskName Description: $description Cron: $cron\n" if $serverconfig{verbose}; + + my $sql = qq( + INSERT INTO statistic_task_meta ( + TaskID, TaskName, Description, Cron + ) VALUES ( + '$taskid', '$taskName', '$description', '$cron') + ); + + logit( $taskid, $host, $group, "DB Report SQL command: $sql" ) if ( $serverconfig{verboselevel} >= 2 ); + + my $conn = bangstat_db_connect( $serverconfig{config_bangstat} ); + if ( !$conn ) { + logit( $taskid, $host, $group, "ERROR: Could not connect to DB to send bangstat report." ); + return 1; + } + + my $sth = $bangstat_dbh->prepare($sql); + $sth->execute() unless $serverconfig{dryrun}; + $sth->finish(); + $bangstat_dbh->disconnect; + + logit( $taskid, $host, $group, "Bangstat task_meta sent." ); + } else { + logit( $taskid, $host, $group, "bangstat_set_taskmeta not sent - no DB-Support!" ); } return 1; @@ -966,4 +1288,207 @@ sub error404 { )->throw; } +############################################################################## +# # +# print_formatted_table($data, $formats) # +# # +# prints a dynamically formatted, data-aligned table to # +# STDOUT. the sub will compute the required minimal and # +# maximal column widths from table data ($data) and for- # +# matting constraints ($formats). the overall length of # +# the table isn't limited, so displaying a larger table # +# requires a fairly large terminal. # +# # +# params: # +# # +# $data: # +# the table body data, in the following format: # +# a reference to an array of hashreferences. # +# each array item (hashref) represents a row. # +# each hashref points to a hash containing # +# key-value pairs for each column on that row. # +# # +# example: # +# # +# my $data = [ # +# { 'colA' => '12345', 'colB' => 'Task one' }, # +# { 'colA' => '67890', 'colB' => 'Task two' }, # +# { 'colA' => '13579', 'colB' => 'Task three' } # +# ]; # +# # +# $formats: # +# table metadata, like custom table column titles, # +# and column formatting information. this is an array # +# of hash references. it *must* contain the following # +# key-value pairs: # +# - colkey: a column name that matches a column key # +# in $data. only columns listed in $formats # +# will actually be printed to STDOUT. note # +# that colkey must exist for each row in $data. # +# it is however allowed to omit displaying # +# some columns existing in $data by just not # +# mentioning them in $formats. also note that # +# the order of the array referenced by # +# $formats actually defines the column output # +# order (and not the natural column order in # +# $data!). # +# - title: a custom column title (text). should not # +# exceed "maxlen" characters (see below). # +# - align: 'l' for left text alignment in the output # +# of the referenced row's body data. 'r' for # +# right text alignment. # +# - maxlen: maximum length (width) of the referenced # +# column. table cell strings that are longer # +# than maxlen characters will be truncated to # +# maxlen characters. note that the "title" # +# attribute should have a string length that # +# is lower or equal to maxlen. # +# example: # +# # +# my $formats = [ # +# { # +# 'colkey' => 'colA', # +# 'title' => 'Task ID', # +# 'align' => 'r', # +# 'maxlen' => 8 # +# }, # +# { # +# 'colkey' => 'colB', # +# 'title' => 'Desc', # +# 'align' => 'l', # +# 'maxlen' => 12 # +# } # +# ]; # +# # +############################################################################## +sub print_formatted_table { + my ($data, $formats) = @_; + + # compute minimal column lengths for each + # column, respecting maxlen. + my $col_minlengths = {}; + + for (my $i = 0; $i < @$formats; $i++) { + my $colkey = $formats->[$i]{'colkey'}; + my $colmaxlen = $formats->[$i]{'maxlen'}; + my $coltitle = $formats->[$i]{'title'}; + + my $colminlen = length($coltitle); + + foreach my $row (@$data) { + my $cellstr = defined($row->{$colkey}) + ? $row->{$colkey} + : ''; + + my $cellstrlen = length($cellstr); + + # cap the cell value length at colmaxlen + if ($cellstrlen > $colmaxlen) { + $cellstrlen = $colmaxlen; + } + + # update minimum column length + # if longer cell value found + if ($cellstrlen > $colminlen) { + $colminlen = $cellstrlen; + } + } + + # minimum column length should never + # exceed maxlen + if ($colminlen > $colmaxlen) { + $colminlen = $colmaxlen; + } + + $col_minlengths->{$colkey} = $colminlen; + } + + # create printf format strings for each column + my $printf_formats = {}; + + for (my $i = 0; $i < @$formats; $i++) { + my $colkey = $formats->[$i]{'colkey'}; + my $align = $formats->[$i]{'align'}; + + my $colminlength = $col_minlengths->{$colkey}; + + if ($align eq 'r') { + $printf_formats->{$colkey} = sprintf( + '%%%ss', + $colminlength + ); + + } elsif ($align eq 'l') { + $printf_formats->{$colkey} = sprintf( + '%%-%ss', + $colminlength + ); + + } else { + printf( + STDERR + "invalid alignment specifier: %s for column %s.\n", + $align, + $colkey + ); + + # fall back to left alignment + $printf_formats->{$colkey} = "%${colminlength}s"; + } + } + + # output table column header + for (my $i = 0; $i < @$formats; $i++) { + my $colkey = $formats->[$i]{'colkey'}; + my $coltitle = $formats->[$i]{'title'}; + my $colminlength = $col_minlengths->{$colkey}; + + # truncate column title to colminlength, if longer. + # note: colminlength might be tttt + + if (length($coltitle) > $colminlength) { + $coltitle = substr($coltitle, 0, $colminlength); + } + + my $varspace = $i < @$formats - 1 ? ' ' : ''; + printf($printf_formats->{$colkey}.$varspace, $coltitle); + } + + printf("\n"); + + # output table column header horizontal rules + for (my $i = 0; $i < @$formats; $i++) { + my $colkey = $formats->[$i]{'colkey'}; + my $colminlength = $col_minlengths->{$colkey}; + + my $varspace = $i < @$formats - 1 ? ' ' : ''; + printf(('-' x $colminlength).$varspace); + } + + printf("\n"); + + # output table body + for (my $i = 0; $i < @$data; $i++) { + for (my $j = 0; $j < @$formats; $j++) { + my $colkey = $formats->[$j]{'colkey'}; + + my $value = exists($data->[$i]{$colkey}) + ? $data->[$i]{$colkey} + : ''; + + my $colminlength = $col_minlengths->{$colkey}; + + # truncate if needed + if (length($value) > $colminlength) { + $value = substr($value, 0, $colminlength); + } + + my $varspace = $j < @$formats - 1 ? ' ' : ''; + printf($printf_formats->{$colkey}.$varspace, $value); + } + + printf("\n"); + } +} + 1; diff --git a/lib/BaNG/TM_rsync.pm b/lib/BaNG/TM_rsync.pm index ad155f5..d172806 100644 --- a/lib/BaNG/TM_rsync.pm +++ b/lib/BaNG/TM_rsync.pm @@ -132,8 +132,8 @@ sub _execute_rsync { $bkpfrompath =~ s/'//g; $bkpfrompath =~ s/\/\//\//g; - # report rsync start to DB, jobstatus => 0 - bangstat_start_backupjob( $taskid, $jobid, $host, $group, $startstamp, '', $bkpfrompath, $srcfolder, targetpath( $host, $group ), '0', '0', '' ) unless $noreport; + # report rsync start to DB, jobstatus => JOBSTATUS_STARTED + bangstat_report_start_backupjob( $taskid, $jobid, $host, $group, $startstamp, '', $bkpfrompath, $srcfolder, targetpath( $host, $group ), ERRSTATUS_NOERR, JOBSTATUS_STARTED, '' ) unless $noreport; my $taskset = ''; if ( $hosts{"$host-$group"}->{hostconfig}->{TASKSET_OPTIONS} ) { @@ -221,8 +221,8 @@ sub _execute_rsync { logit( $taskid, $host, $group, "Rsync successful for host $host group $group path $path" ); } - #report finished rsync to DB, jobstatus => 1 - bangstat_update_backupjob( $taskid, $jobid, $host, $group, $endstamp, $bkpfrompath, targetpath( $host, $group ), $errcode, '1', @outlines ) unless $noreport; + #report finished rsync to DB, jobstatus => JOBSTATUS_PROCESSED + bangstat_report_update_backupjob( $taskid, $jobid, $host, $group, $endstamp, $bkpfrompath, targetpath( $host, $group ), $errcode, JOBSTATUS_PROCESSED, @outlines ) unless $noreport; return $errcode; } @@ -378,6 +378,7 @@ sub _eval_rsync_generic_exclude_cmd { $globstr =~ s{(.)} { $patmap{$1} || "\Q$1" }ge; return '^' . $globstr . '$'; } + sub _queue_remote_subfolders { my ( $taskid, $jobid, $host, $group, $bkptimestamp, $dosnapshot, $srcfolder, $dryrun, $cron, $noreport ) = @_; my $hostconfig = $hosts{"$host-$group"}->{hostconfig}; @@ -557,8 +558,8 @@ sub _finish_rsync_backupjob { } } - # report finished job to DB, jobstatus => 2 - bangstat_finish_backupjob( $taskid, $jobid, $host, $group, '2' ) unless $noreport; + # report finished job to DB, jobstatus => JOBSTATUS_FINISHED + bangstat_report_finish_backupjob( $taskid, $jobid, $host, $group, JOBSTATUS_FINISHED) unless $noreport; my %RecentBackups = bangstat_recentbackups($host); unless ($noreport) { diff --git a/lib/Dancer/Plugin/Auth/Extensible/Provider/LDAPiam.pm b/lib/Dancer/Plugin/Auth/Extensible/Provider/LDAPiam.pm new file mode 100644 index 0000000..33082b9 --- /dev/null +++ b/lib/Dancer/Plugin/Auth/Extensible/Provider/LDAPiam.pm @@ -0,0 +1,113 @@ +package Dancer::Plugin::Auth::Extensible::Provider::LDAPiam; + +use 5.010; +use strict; +use warnings; +use Net::LDAP; + +my $config = Dancer::Config::setting('plugins')->{'Auth::Extensible'}->{'realms'}->{'ldap'}; + +sub new { + my ($class, $realm_settings) = @_; + + my $self = { + realm_settings => $realm_settings, + }; + + return bless $self => $class; +} + +sub realm_settings { + shift->{realm_settings} || {}; +} + +sub authenticate_user { + my ($self, $username, $password) = @_; + + my $ldap = Net::LDAP->new($config->{server}, scheme => 'ldaps'); + my $bind = $ldap->bind( + "cn=$username,".$config->{user_base_dn}, + 'password' => $password + ); + + return 0 unless $bind; + + $ldap->unbind; + $ldap->disconnect; + + my $authenticated = 0; + $authenticated = 1 if( $bind->code == 0 && not $bind->is_error ); + + return $authenticated; +} + +sub get_user_details { + my ($self, $username) = @_; + + my $ldap = Net::LDAP->new($config->{server}, scheme => 'ldaps'); + my $bind = $ldap->bind(); + + my $ldap_result = $ldap->search( + base => $config->{user_base_dn}, + filter => "(cn=$username)", + attrs => ['cn'], + ); + + $ldap->unbind; + $ldap->disconnect; + + return {} unless $ldap_result->count == 1; + + my $user_object = ($ldap_result->entries)[0]; + + my $user_details = { + dn => $user_object->dn(), + cn => ($user_object->get_value('cn'))[0], + }; + + return $user_details; +} + +sub get_user_roles { + my ($self, $username) = @_; + + if (!open(F, '<', $config->{proxy_user_pwfile})) { + printf( + STDERR + "ERROR: cannot open proxy_user_pwfile (%s)\n", + $config->{proxy_user_pwfile} + ); + + return []; + } + + my $proxy_user_pw = ; + chomp($proxy_user_pw); + close(F); + + my $ldap = Net::LDAP->new($config->{server}, scheme => 'ldaps'); + my $bind = $ldap->bind($config->{proxy_user_dn}, password => $proxy_user_pw); + + my $ldap_result = $ldap->search( + base => $config->{group_base_dn}, + filter => $config->{group_filter}, + attrs => ['cn', 'memberUid'], + ); + + $ldap->unbind; + $ldap->disconnect; + + my $user_groups = []; + foreach my $group ($ldap_result->entries) { + my $has_members = $group->get_value('memberUid'); + my @group_members = @{ $group->get_value( 'memberUid', asref => 1 ) } if $has_members; + if ( grep { $_ eq $username } @group_members ) { + my $groupname = ($group->get_value('cn'))[0]; + push( @$user_groups, $groupname ); + } + } + + return [ sort(@$user_groups) ]; +} + +1; diff --git a/views/dashboard-error_report.tt b/views/dashboard-error_report.tt index 7ebe518..d6c511a 100644 --- a/views/dashboard-error_report.tt +++ b/views/dashboard-error_report.tt @@ -21,12 +21,12 @@ Total Backup Jobs last 24H: <% RecentBackups24h.Data.size %> <%- FOREACH bkp IN RecentBackups24h.Data.nsort('TaskID').reverse %> - <% NEXT IF ( bkp.JobStatus == 2 AND bkp.ErrStatus == 0 ) OR ( bkp.JobStatus == -1 AND bkp.BkpGroup == 'mac-workstation' ) OR ( bkp.JobStatus == 2 AND ( bkp.ErrStatus == 24 OR bkp.ErrStatus == "0,24" ) ) %> + <% NEXT IF ( bkp.JobStatus == 3 AND bkp.ErrStatus == 0 ) OR ( bkp.JobStatus == -1 AND bkp.BkpGroup == 'mac-workstation' ) OR ( bkp.JobStatus == 3 AND ( bkp.ErrStatus == 24 OR bkp.ErrStatus == "0,24" ) ) %> <%- errTxt = '' %> <% bkp.JobID.substr(12) %> - class="hook" title="Job done"> - <% ELSIF bkp.JobStatus == 1 %>class="hook_box" title="Rsync done"> + class="hook" title="Job done"> + <% ELSIF bkp.JobStatus == 2 %>class="hook_box" title="Rsync done"> <% ELSIF bkp.JobStatus == -1 %>class="offline pointer" data-href='https://<% xymon_server %>/xymon-cgi/svcstatus.sh?HOST=<% bkp.BkpHost %>&SERVICE=conn' title="Host offline"> <% ELSIF bkp.JobStatus == -2 %>class="notallow" title="Remote Shell not working"> <% ELSIF bkp.JobStatus == -5 %>class="notallow" title="Rsync command not found"> diff --git a/views/host-bkpreport.tt b/views/host-bkpreport.tt index a01d6f5..f1fb717 100644 --- a/views/host-bkpreport.tt +++ b/views/host-bkpreport.tt @@ -39,9 +39,9 @@ <% bkp.TaskID.substr(12) %> <% IF bkp.Cron == 1 %>CRON<% ELSIF bkp.Cron == 0 %>CLI<% END %> - class="hook3" title="forked Job done"> - <% ELSIF bkp.JobStatus == 2 %>class="hook" title="Job done"> - <% ELSIF bkp.JobStatus == 1 %>class="hook_box" title="Rsync done"> + class="hook3" title="forked Job done"> + <% ELSIF bkp.JobStatus == 3 %>class="hook" title="Job done"> + <% ELSIF bkp.JobStatus == 2 %>class="hook_box" title="Rsync done"> <% ELSIF bkp.JobStatus == -1 %>class="offline pointer" data-href='https://<% xymon_server %>/xymon-cgi/svcstatus.sh?HOST=<% bkp.BkpHost %>&SERVICE=conn' title="Host offline"> <% ELSIF bkp.JobStatus == -2 %>class="notallow" title="Remote Shell not working"> <% ELSIF bkp.ErrStatus == 99 %>> diff --git a/views/report-xymon.tt b/views/report-xymon.tt index 3454f87..54c9e74 100644 --- a/views/report-xymon.tt +++ b/views/report-xymon.tt @@ -33,7 +33,7 @@ <% ELSIF bkp.JobStatus == -5 %> <% xymoncolor = 'red' %> <% titlemsg = 'Rsync command not found!' %> - <% ELSIF bkp.JobStatus == 0 || bkp.JobStatus == 1 %> + <% ELSIF bkp.JobStatus == 1 || bkp.JobStatus == 2 %> <% xymoncolor = 'running' %> <% titlemsg = 'Backup currently running...' %> <% ELSIF bkp.ErrStatus == 0 %> diff --git a/views/reporting-jobs.tt b/views/reporting-jobs.tt index b3d41bf..3a16f2f 100644 --- a/views/reporting-jobs.tt +++ b/views/reporting-jobs.tt @@ -28,9 +28,9 @@ Total Backup Jobs last 24H: <% RecentBackupsLast.Data.size %> <% bkp.TaskID.substr(12) %> <% IF bkp.Jobs >= 2 %><% bkp.JobID.substr(12) %> <% ELSE %><% bkp.JobID.substr(12) %><% END%> - = 2 %>class="hook3" title="forked Job done"> - <% ELSIF bkp.JobStatus == 2 %>class="hook" title="Job done"> - <% ELSIF bkp.JobStatus == 1 %>class="hook_box" title="Rsync done"> + = 2 %>class="hook3" title="forked Job done"> + <% ELSIF bkp.JobStatus == 3 %>class="hook" title="Job done"> + <% ELSIF bkp.JobStatus == 2 %>class="hook_box" title="Rsync done"> <% ELSIF bkp.JobStatus == -1 %>class="offline pointer" data-href='https://<% xymon_server %>/xymon-cgi/svcstatus.sh?HOST=<% bkp.BkpHost %>&SERVICE=conn' title="Host offline"> <% ELSIF bkp.JobStatus == -2 %>class="notallow" title="Remote Shell not working"> <% ELSIF bkp.JobStatus == -5 %>class="notallow" title="Rsync command not found"> diff --git a/views/reporting-task_jobs.tt b/views/reporting-task_jobs.tt index f838c39..7d005e0 100644 --- a/views/reporting-task_jobs.tt +++ b/views/reporting-task_jobs.tt @@ -31,9 +31,9 @@ Total Jobs: <% jobs.Data.size %> <% IF job.Jobs >= 2 %><% job.JobID.substr(12) %> <% ELSE %><% job.JobID.substr(12) %><% END%> - =2 %>class="hook3" title="forked Job done"> - <% ELSIF job.JobStatus == 2 %>class="hook" title="Job done"> - <% ELSIF job.JobStatus == 1 %>class="hook_box" title="Rsync done"> + =2 %>class="hook3" title="forked Job done"> + <% ELSIF job.JobStatus == 3 %>class="hook" title="Job done"> + <% ELSIF job.JobStatus == 2 %>class="hook_box" title="Rsync done"> <% ELSIF job.JobStatus == -1 %>class="offline pointer" data-href='https://<% xymon_server %>/xymon-cgi/svcstatus.sh?HOST=<% job.jobHost %>&SERVICE=conn' title="Host offline"> <% ELSIF bkp.JobStatus == -2 %>class="notallow" title="Remote Shell not working"> <% ELSIF bkp.JobStatus == -5 %>class="notallow" title="Rsync command not found"> diff --git a/views/reporting-tasks.tt b/views/reporting-tasks.tt index dba63a7..10fadc7 100644 --- a/views/reporting-tasks.tt +++ b/views/reporting-tasks.tt @@ -29,9 +29,9 @@ Total Backup Tasks last 24H: <% RecentTasks.Data.size %> <% bkp.Taskname %> <% bkp.Description %> <% IF bkp.Cron == 1 %>CRON<% ELSIF bkp.Cron == 0 %>CLI<% END %> - =2 %>class="hook3" title="forked Job done"> - <% ELSIF bkp.JobStatus == 2 %>class="hook" title="Job done"> - <% ELSIF bkp.JobStatus == 1 %>class="hook_box" title="Rsync done"> + =2 %>class="hook3" title="forked Job done"> + <% ELSIF bkp.JobStatus == 3 %>class="hook" title="Job done"> + <% ELSIF bkp.JobStatus == 2 %>class="hook_box" title="Rsync done"> <% ELSIF bkp.JobStatus == -1 %>class="offline pointer" data-href='https://<% xymon_server %>/xymon-cgi/svcstatus.sh?HOST=<% bkp.BkpHost %>&SERVICE=conn' title="Host offline"> <% ELSIF bkp.JobStatus == -2 %>class="notallow" title="Remote Shell not working"> <% ELSIF bkp.JobStatus == -5 %>class="notallow" title="Rsync command not found"> From 798fdbf1d6924930917b6b7ecf00d2919c607dbe Mon Sep 17 00:00:00 2001 From: David Schneider Date: Thu, 24 Jul 2025 14:22:49 +0200 Subject: [PATCH 2/6] added d-phys patch for reporting mail sender addr --- docs/Installation.markdown | 2 +- etc.example/defaults_servers.yaml | 2 ++ lib/BaNG/Reporting.pm | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/Installation.markdown b/docs/Installation.markdown index acc666f..a695374 100644 --- a/docs/Installation.markdown +++ b/docs/Installation.markdown @@ -40,7 +40,7 @@ mv etc/servers/bangserver_defaults.yaml etc/servers/`hostname -s`_defaults.yaml Fields you typically want to change in the config files: - * `defaults_servers.yaml`: `report_to` + * `defaults_servers.yaml`: `report_from` and `report_to` * `defaults_hosts.yaml`: `BKP_TARGET_HOST` and `BKP_TARGET_PATH` Make sure the `BKP_TARGET_PATH` folder where backups should be stored exists diff --git a/etc.example/defaults_servers.yaml b/etc.example/defaults_servers.yaml index 57dbe1c..89c195f 100644 --- a/etc.example/defaults_servers.yaml +++ b/etc.example/defaults_servers.yaml @@ -32,6 +32,7 @@ path_taskset: path_timeout: timeout_duration: # +report_from: root@example.com report_to: admin@example.com xymon_server: # @@ -85,6 +86,7 @@ HELP: path_rsync: path_dar: + report_from: email-addresses used as report sender report_to: email-addresses for reporting (comma separated) xymon_server: hostname of xymon server diff --git a/lib/BaNG/Reporting.pm b/lib/BaNG/Reporting.pm index cfb4487..ef0559f 100644 --- a/lib/BaNG/Reporting.pm +++ b/lib/BaNG/Reporting.pm @@ -1010,7 +1010,7 @@ sub mail_report { ); my $mail_msg = MIME::Lite->new( - From => 'root@phys.ethz.ch', + From => $serverconfig{report_from} || 'root', To => $serverconfig{report_to}, Type => 'multipart/alternative', Subject => "Backup report of ($host-$group): $status", From 1603c088ca5c9d549becd59094a304a2e1c9a60d Mon Sep 17 00:00:00 2001 From: David Schneider Date: Mon, 11 Aug 2025 11:21:01 +0200 Subject: [PATCH 3/6] added BKP_SKIP_FILESONLY_JOB --- lib/BaNG/TM_rsync.pm | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/BaNG/TM_rsync.pm b/lib/BaNG/TM_rsync.pm index d172806..f0178ef 100644 --- a/lib/BaNG/TM_rsync.pm +++ b/lib/BaNG/TM_rsync.pm @@ -453,25 +453,28 @@ sub _queue_remote_subfolders { print "- $remotesubfolder/\n" if $serverconfig{verbose}; } close $fhExcludeFile unless $serverconfig{dryrun}; + + if (!$hosts{"$host-$group"}->{hostconfig}->{BKP_SKIP_FILESONLY_JOB}) { - # add bkp job for files only - my $bkpjob = { - taskid => $taskid, - jobid => $jobid, - host => $host, - group => $group, - path => ":'${srcfolder}'", - bkptimestamp => $bkptimestamp, - srcfolder => ":$srcfolder", - dosnapshot => 1, - dryrun => $dryrun, - cron => $cron, - noreport => $noreport, - exclsubfolders => 1, - }; - - logit( $taskid, $host, $group, "DOSNAPSHOT == $dosnapshot pushing job for $srcfolder FILESONLY for host $host group $group" ); - push( @queue, $bkpjob ); + # add bkp job for files only + my $bkpjob = { + taskid => $taskid, + jobid => $jobid, + host => $host, + group => $group, + path => ":'${srcfolder}'", + bkptimestamp => $bkptimestamp, + srcfolder => ":$srcfolder", + dosnapshot => 1, + dryrun => $dryrun, + cron => $cron, + noreport => $noreport, + exclsubfolders => 1, + }; + + logit( $taskid, $host, $group, "DOSNAPSHOT == $dosnapshot pushing job for $srcfolder FILESONLY for host $host group $group" ); + push( @queue, $bkpjob ); + } return 1; } From 82349ff138106196caf879b2abd1b6b6c67b8528 Mon Sep 17 00:00:00 2001 From: Claude Becker Date: Fri, 7 Feb 2025 08:26:19 +0100 Subject: [PATCH 4/6] clarify config docs --- docs/Configuration.markdown | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/Configuration.markdown b/docs/Configuration.markdown index 55756b5..0667327 100644 --- a/docs/Configuration.markdown +++ b/docs/Configuration.markdown @@ -5,17 +5,15 @@ BaNG Configuration Config files ------------ -All configuration files are stored in the local `etc/` folder. +All configuration files are stored in the local `etc/` folder. The settings are designed in a sort of hierarchy, where more specific settings can be used to override more general ones. The default settings apply to all, but can be overridden by group- and host-specific settings. - * `defaults_servers.yaml` : common default settings for servers - * `defaults_hosts.yaml` : common default settings for hosts - * `servers/_defaults.yaml` : server-specific settings - * `groups/.yaml` : group-specific settings - * `hosts/_.yaml` : host-specific settings + * `defaults_hosts.yaml` : common default settings for all hosts backed up by BaNG + * `defaults_servers.yaml` : common default settings for all BaNG backup servers + * `servers/_defaults.yaml` : settings specific to a given BaNG backup server + * `groups/.yaml` : common settings for a given group of hosts backed up by BaNG + * `hosts/_.yaml` : settings specific to a given host and group pair * `excludes/excludelist_` : rsync exclude list for given group or host -The default settings apply to all, but can be overridden by server-, group- and host-specific settings. - Add a new host -------------- From f775b6b19e14c165b9e31151bd6a25502a472312 Mon Sep 17 00:00:00 2001 From: Claude Becker Date: Fri, 7 Feb 2025 08:46:41 +0100 Subject: [PATCH 5/6] clean trailing white-space --- lib/BaNG/BackupServer.pm | 10 +++++----- lib/BaNG/TM_lftp.pm | 26 +++++++++++++------------- views/dashboard-oob_status.tt | 6 +++--- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/BaNG/BackupServer.pm b/lib/BaNG/BackupServer.pm index e811983..868cbb4 100644 --- a/lib/BaNG/BackupServer.pm +++ b/lib/BaNG/BackupServer.pm @@ -157,7 +157,7 @@ sub check_client_connection { $msg = 'Host not pingable because behind a Gateway-Host'; } $p->close(); - 1; + 1; } or do { my $theerror = $@; $msg = "Exception caught in Net::Ping - $theerror"; @@ -209,10 +209,10 @@ sub get_backup_folders { $REGEX = ".*_failed" if $folder_type == 1; # show all *_failed folders $REGEX = "\\([0-9\./_]*\\|.*_failed\$\\)" if $folder_type == 2; # show all folders, except "current" folder $REGEX = "[0-9]+\.[0-9]+\.[0-9]+_[0-9]+[-_.]+.*" if ($folder_type == 3 || $folder_type == 4); # get all oob - + if ( $server eq $servername ) { @backup_folders = `find $bkpdir -mindepth 1 -maxdepth 1 -type d -regex '${bkpdir}/$REGEX' 2>/dev/null`; - @backup_folders = grep(!/.*_failed$/,@backup_folders) if ($folder_type == 3); # filter out failed if we don't want them + @backup_folders = grep(!/.*_failed$/,@backup_folders) if ($folder_type == 3); # filter out failed if we don't want them } else { @backup_folders = remote_command( $server, "$servers{$server}{serverconfig}{remote_app_folder}/bang_getBackupFolders", $bkpdir ); } @@ -704,7 +704,7 @@ sub get_oob_snapshot_dirs { elsif ($get_oob_snapshots_mode == 2) { $find_mode = 4; } - + foreach my $hostgroup (sort keys %$hosts ) { unless ( $hosts{$hostgroup}->{hostconfig}->{BKP_TARGET_HOST} ne $servername ) { $oob_dirs{$hostgroup}= [get_backup_folders($hosts{$hostgroup}->{hostname},$hosts{$hostgroup}->{group}, $find_mode)]; @@ -714,7 +714,7 @@ sub get_oob_snapshot_dirs { #} } return %oob_dirs; - + } 1; diff --git a/lib/BaNG/TM_lftp.pm b/lib/BaNG/TM_lftp.pm index bd9c639..3a26046 100644 --- a/lib/BaNG/TM_lftp.pm +++ b/lib/BaNG/TM_lftp.pm @@ -54,8 +54,8 @@ sub queue_lftp_backup { excludes => $excludes, ); push( @$queueref, \%bkpjob ); - - + + logit( $taskid, $host, $group, "End of queueing backup of host $host group $group" ); return $queueref; } @@ -66,8 +66,8 @@ sub run_lftp_threads { my $rQ = Thread::Queue->new; my $nthreads; my @threads; - - + + foreach my $j (@$queue) { foreach my $srcdir (@{$j->{srcdirs}}) { @@ -94,31 +94,31 @@ sub run_lftp_threads { path => $srcdir, excludes => $$j{excludes}, parallel => $parallel, rQ => $rQ); - - + + $Q->enqueue(\%threadargs); my $t = threads->create( \&_do_lftp, $Q); $t->detach(); push(@threads,$t->tid); - + } - + } for(@threads) { my $finished_thread = $rQ->dequeue(); print "Finished waiting for thread $finished_thread.\n"; - } - - + } + + return 0; } sub _lftp_parse_exclude_file { my ($theargs) = @_; my $excludes = %$theargs{excludes}; - + my @excludeslist; my $lftp_excludes; if (-e $excludes) { @@ -169,7 +169,7 @@ sub _do_lftp { my $lftp_cmd = $lftp_bin . " $lftp_srcproto$lftp_srchost " . $lftp_script; logit( $taskid, $host, $group, "LFTP Command: $lftp_cmd" ); - logit( $taskid, $host, $group, "Executing lftp for host $host group $group path $srcpath." ); + logit( $taskid, $host, $group, "Executing lftp for host $host group $group path $srcpath." ); local (*HIS_IN, *HIS_OUT, *HIS_ERR); if (defined $dryrun && $dryrun == 1) { $lftp_cmd = "echo Would run: $lftp_cmd"; diff --git a/views/dashboard-oob_status.tt b/views/dashboard-oob_status.tt index d405887..9069ffc 100644 --- a/views/dashboard-oob_status.tt +++ b/views/dashboard-oob_status.tt @@ -15,9 +15,9 @@ <% hosts.$hostname.hostname %> <% hosts.$hostname.group %> - <% FOREACH oob_directory IN oobsd.$hostname %> - <%- oob_directory.trim -%>, - <% END %> + <% FOREACH oob_directory IN oobsd.$hostname %> + <%- oob_directory.trim -%>, + <% END %> <% END %> From 07903ed48892785d247cf9c12e0f5ed9ab4f5439 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Thu, 20 Feb 2025 14:00:33 +0100 Subject: [PATCH 6/6] Fix log message Execute and log the same command! --- lib/BaNG/TM_rsync.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/BaNG/TM_rsync.pm b/lib/BaNG/TM_rsync.pm index f0178ef..36d6bae 100644 --- a/lib/BaNG/TM_rsync.pm +++ b/lib/BaNG/TM_rsync.pm @@ -184,7 +184,7 @@ sub _execute_rsync { logit( $taskid, $host, $group, "Apply subfolder excludelist: $rsync_generic_exclude" ); } - logit( $taskid, $host, $group, "Rsync Command: $rsync_cmd $rsync_options$path $rsync_target" ); + logit( $taskid, $host, $group, "Rsync Command: $rsync_cmd $rsync_generic_exclude $rsync_options$path $rsync_target" ); logit( $taskid, $host, $group, "Executing rsync for host $host group $group path $path" ); local ( *HIS_IN, *HIS_OUT, *HIS_ERR );