| 23 | rodolico | 1 | #! /usr/bin/perl -w
 | 
        
           |  |  | 2 |   | 
        
           | 32 | rodolico | 3 | # Version 0.9 20160713 RWR
 | 
        
           |  |  | 4 | # Added ability to have individual owners receive a summary report
 | 
        
           | 93 | rodolico | 5 | #
 | 
        
           |  |  | 6 | # Version 1.0 20191020 RWR
 | 
        
           |  |  | 7 | # Major change to query used for report. Instead of start time of backup,
 | 
        
           |  |  | 8 | # report will look for date report was added to CAMP. This keeps reports
 | 
        
           |  |  | 9 | # of backups which take extra time from showing up AFTER they are complete.
 | 
        
           |  |  | 10 | # For example, if a backup starts on a Tuesday and takes 36 hours to complete
 | 
        
           |  |  | 11 | # it will be reported as missing on Wed, but will be correctly reported as
 | 
        
           |  |  | 12 | # completed on Thu.
 | 
        
           | 32 | rodolico | 13 |   | 
        
           | 23 | rodolico | 14 | use DBI;
 | 
        
           |  |  | 15 | use Cwd 'abs_path';
 | 
        
           |  |  | 16 | #use Data::Dumper;
 | 
        
           |  |  | 17 | use File::Basename;
 | 
        
           |  |  | 18 | use YAML::XS;
 | 
        
           |  |  | 19 |   | 
        
           |  |  | 20 |   | 
        
           | 25 | rodolico | 21 |   | 
        
           | 93 | rodolico | 22 | my $VERSION = '1.0';
 | 
        
           | 23 | rodolico | 23 | my $MY_DIRECTORY = abs_path(dirname(__FILE__) );
 | 
        
           |  |  | 24 | my $message; # the message to send
 | 
        
           | 27 | rodolico | 25 | # query to find all reports in last 24 hours. Assumes the script
 | 
        
           |  |  | 26 | # runs at the same time each day
 | 
        
           | 25 | rodolico | 27 | my $query = qq|select 
 | 
        
           | 32 | rodolico | 28 |                   backups.responsible_party 'mailto',
 | 
        
           | 25 | rodolico | 29 |                   backups_run.backups_run_id 'ID',
 | 
        
           |  |  | 30 |                   device.device_id 'Device ID',
 | 
        
           |  |  | 31 |                   device.name 'Source',
 | 
        
           | 93 | rodolico | 32 |                   backups_run.start_time 'Start Time',
 | 
        
           | 25 | rodolico | 33 |                   timediff( backups_run.end_time,backups_run.start_time ) 'Run Time',
 | 
        
           |  |  | 34 |                   backups_run.transferred_count 'Files',
 | 
        
           |  |  | 35 |                   round(backups_run.transferred_size/1024/1024,2) 'Size (M)',
 | 
        
           |  |  | 36 |                   backups_run.files_deleted 'Deleted',
 | 
        
           |  |  | 37 |                   backups_run.skipped 'Skipped',
 | 
        
           |  |  | 38 |                   round( backups_run.data_sent/1024/1024,2) 'Traffic (M)'
 | 
        
           |  |  | 39 |                from
 | 
        
           |  |  | 40 |                   backups_run
 | 
        
           |  |  | 41 |                   join backups using (backups_id)
 | 
        
           |  |  | 42 |                   join device using (device_id)
 | 
        
           |  |  | 43 |                where
 | 
        
           | 93 | rodolico | 44 |                   backups_run.added_date > date_sub( now(), interval 24 hour)
 | 
        
           | 27 | rodolico | 45 |                order by Source
 | 
        
           | 25 | rodolico | 46 |                |;
 | 
        
           |  |  | 47 |   | 
        
           | 23 | rodolico | 48 |   | 
        
           | 27 | rodolico | 49 | # Find number of backups in the past 24 hours.                  
 | 
        
           | 25 | rodolico | 50 | my $count = qq/select count(*) 'Count' 
 | 
        
           |  |  | 51 |                from backups_run 
 | 
        
           | 27 | rodolico | 52 |                where
 | 
        
           |  |  | 53 |                   backups_run.start_time > date_sub( now(), interval 24 hour)/;
 | 
        
           | 23 | rodolico | 54 |   | 
        
           | 27 | rodolico | 55 | # looks for backups which did NOT happen in last 24 hours, but did
 | 
        
           |  |  | 56 | # happen in last 7 days, ie backups that did not make it.
 | 
        
           | 25 | rodolico | 57 | my $missing = qq/select 
 | 
        
           |  |  | 58 |                   device.device_id ID,
 | 
        
           |  |  | 59 |                   device.name name
 | 
        
           | 93 | rodolico | 60 |                from 
 | 
        
           | 25 | rodolico | 61 |                   ( 
 | 
        
           |  |  | 62 |                      select distinct( backups_id ) 
 | 
        
           |  |  | 63 |                      from 
 | 
        
           |  |  | 64 |                         backups_run 
 | 
        
           |  |  | 65 |                      where 
 | 
        
           | 27 | rodolico | 66 |                         (datediff( now(),added_date) < 7 ) 
 | 
        
           | 25 | rodolico | 67 |                   ) last_five
 | 
        
           |  |  | 68 |                   left join
 | 
        
           |  |  | 69 |                   (
 | 
        
           |  |  | 70 |                      select 
 | 
        
           |  |  | 71 |                         distinct( backups_id ) 
 | 
        
           |  |  | 72 |                      from 
 | 
        
           |  |  | 73 |                         backups_run
 | 
        
           |  |  | 74 |                      where 
 | 
        
           | 93 | rodolico | 75 |                         backups_run.added_date > date_sub( now(), interval 24 hour)
 | 
        
           | 25 | rodolico | 76 |                   ) today using (backups_id)
 | 
        
           |  |  | 77 |                   join 
 | 
        
           |  |  | 78 |                      backups using (backups_id)
 | 
        
           |  |  | 79 |                   join 
 | 
        
           |  |  | 80 |                      device using (device_id)
 | 
        
           |  |  | 81 |                where 
 | 
        
           |  |  | 82 |                   today.backups_id is null
 | 
        
           | 93 | rodolico | 83 |                /;
 | 
        
           | 25 | rodolico | 84 |   | 
        
           |  |  | 85 |   | 
        
           | 23 | rodolico | 86 | # following are used to find the configuration file
 | 
        
           | 25 | rodolico | 87 | my $confFileName = "rsbackupRead.conf.yaml";
 | 
        
           | 23 | rodolico | 88 | my @searchPaths = ( '/etc/camp', '/opt/camp', '/opt/camp/sysinfo', $MY_DIRECTORY );
 | 
        
           |  |  | 89 |   | 
        
           |  |  | 90 |   | 
        
           |  |  | 91 | sub loadConfig {
 | 
        
           |  |  | 92 |    my ( $confFileName, @searchPaths ) = @_;
 | 
        
           |  |  | 93 |    my $configuration;
 | 
        
           |  |  | 94 |    for ( $i = 0; $i < @searchPaths; $i++ ) {
 | 
        
           |  |  | 95 |       $filename = $searchPaths[$i] . '/' . $confFileName;
 | 
        
           |  |  | 96 |       if ( -e $filename ) {
 | 
        
           |  |  | 97 |          #print "Found $filename\n";
 | 
        
           |  |  | 98 |          open CONF, "<$filename" or warn "Could not read $filename: $!\n";
 | 
        
           |  |  | 99 |          $configuration = Load( join( '', <CONF> ) );
 | 
        
           |  |  | 100 |          close CONF;
 | 
        
           |  |  | 101 |          last; # exit out of the loop; we don't try to load it more than once
 | 
        
           |  |  | 102 |       } # if
 | 
        
           |  |  | 103 |    } # foreach
 | 
        
           |  |  | 104 |    return $configuration;
 | 
        
           |  |  | 105 | } # sub loadConfig
 | 
        
           |  |  | 106 |   | 
        
           |  |  | 107 | sub sendReport {
 | 
        
           |  |  | 108 |    my ($parameters,$report) = @_;
 | 
        
           |  |  | 109 |    my %CLIParams ;
 | 
        
           |  |  | 110 |    $CLIParams{'-f'}  = qq/$$parameters{'mailFrom'}/    if $$parameters{'mailFrom'};
 | 
        
           |  |  | 111 |    $CLIParams{'-t'}  = qq/$$parameters{'mailTo'}/      if $$parameters{'mailTo'};
 | 
        
           |  |  | 112 |    $CLIParams{'-u'}  = qq/$$parameters{'mailSubject'}/ if $$parameters{'mailSubject'};
 | 
        
           |  |  | 113 |    $CLIParams{'-s'}  = qq/$$parameters{'mailServer'}/  if $$parameters{'mailServer'};
 | 
        
           |  |  | 114 |    $CLIParams{'-xu'} = qq/$$parameters{'smtpUser'}/    if $$parameters{'smtpUser'};
 | 
        
           |  |  | 115 |    $CLIParams{'-xp'} = qq/$$parameters{'smtpPass'}/    if $$parameters{'smtpPass'};
 | 
        
           |  |  | 116 |    $CLIParams{'-cc'} = qq/$$parameters{'mailCC'}/      if $$parameters{'mailCC'};
 | 
        
           |  |  | 117 |    $CLIParams{'-bcc'}= qq/$$parameters{'mailBCC'}/     if $$parameters{'mailBCC'};
 | 
        
           |  |  | 118 |    $CLIParams{'-l'}  = qq/$$parameters{'logFile'}/     if $$parameters{'logFile'};
 | 
        
           |  |  | 119 |    $CLIParams{'-a'}  = qq/$$parameters{'attachment'}/  if $$parameters{'attachment'};
 | 
        
           |  |  | 120 |    $CLIParams{'-o tls='} = qq/$$parameters{'tls'}/     if $$parameters{'tls'};
 | 
        
           |  |  | 121 |   | 
        
           |  |  | 122 |    $commandLine = qq/$$parameters{'emailScript'}/;
 | 
        
           |  |  | 123 |    die "Could not find executable $commandLine in sendEmailScript\n" unless -x $commandLine;
 | 
        
           |  |  | 124 |    $commandLine .= ' -q'; # make it act quietly
 | 
        
           |  |  | 125 |    foreach my $key ( keys %CLIParams ) {
 | 
        
           |  |  | 126 |       # depending on whether the key ends in an = or not, we will or will not use a space
 | 
        
           |  |  | 127 |       # between the key and the parameter
 | 
        
           |  |  | 128 |       $commandLine .= $key =~ m/=$/ ? " $key'$CLIParams{$key}'" : " $key '$CLIParams{$key}'";
 | 
        
           |  |  | 129 |    }
 | 
        
           |  |  | 130 |    $commandLine .= ' ' . $$parameters{'otherCLParams'} if $$parameters{'otherCLParams'};
 | 
        
           |  |  | 131 |    open SENDMAIL, "|$commandLine" or die "Could not open [$commandLine]: $!\n";
 | 
        
           |  |  | 132 |    print SENDMAIL $report;
 | 
        
           |  |  | 133 |    close SENDMAIL;
 | 
        
           |  |  | 134 | } # sendReport
 | 
        
           |  |  | 135 |   | 
        
           | 73 | rodolico | 136 | sub sendViaSendmail {
 | 
        
           |  |  | 137 |    my ( $parameters, $report ) = @_;
 | 
        
           |  |  | 138 |    my @header;
 | 
        
           |  |  | 139 |    push @header, 'To: ' . qq/$$parameters{'mailTo'}/;
 | 
        
           |  |  | 140 |    push @header, 'From: ' . qq/$$parameters{'mailFrom'}/;
 | 
        
           |  |  | 141 |    push @header, 'Subject: ' . qq/$$parameters{'mailSubject'}/;
 | 
        
           |  |  | 142 |    $report = join( "\n", @header ) . "\n\n" . $report . "\n.\n";
 | 
        
           | 75 | rodolico | 143 |    open SENDMAIL, "|/usr/sbin/sendmail $$parameters{mailTo}" or die "could not open sendmail: $!\n";
 | 
        
           | 73 | rodolico | 144 |    print SENDMAIL $report;
 | 
        
           |  |  | 145 |    close SENDMAIL;
 | 
        
           |  |  | 146 | } 
 | 
        
           |  |  | 147 |   | 
        
           |  |  | 148 |   | 
        
           |  |  | 149 |   | 
        
           |  |  | 150 |   | 
        
           | 32 | rodolico | 151 | sub individualReport {
 | 
        
           |  |  | 152 |    my $data = shift;
 | 
        
           |  |  | 153 |    my @report;
 | 
        
           |  |  | 154 |   | 
        
           |  |  | 155 |    for my $key ( 'Device ID', 'Run Time', 'Files', 'Size (M)', 'Deleted', 'Skipped', 'Traffic (M)' ) {
 | 
        
           |  |  | 156 |       push @report, "$key: " . $$data{$key} unless $key eq 'mailto';
 | 
        
           |  |  | 157 |    }
 | 
        
           |  |  | 158 |    return join( "\n", @report );
 | 
        
           |  |  | 159 | }
 | 
        
           |  |  | 160 |   | 
        
           | 73 | rodolico | 161 |   | 
        
           |  |  | 162 |   | 
        
           | 23 | rodolico | 163 | ########################################################################
 | 
        
           |  |  | 164 | # Main Program
 | 
        
           |  |  | 165 | ########################################################################
 | 
        
           |  |  | 166 |   | 
        
           |  |  | 167 | # Read anything passed on STDIN to prepend to e-mail
 | 
        
           |  |  | 168 | while ( my $line = <> ) {
 | 
        
           |  |  | 169 |    $message .= $line;
 | 
        
           |  |  | 170 | }
 | 
        
           |  |  | 171 |   | 
        
           |  |  | 172 | my $configuration = &loadConfig( $confFileName, @searchPaths );
 | 
        
           |  |  | 173 | die "Could not find configuration file $confFileName in " . join( ', ', @searchPaths) . "\n" unless $configuration;
 | 
        
           |  |  | 174 |   | 
        
           |  |  | 175 | my $dbh = DBI->connect('DBI:mysql:' . $$configuration{'database'}{'database'},
 | 
        
           |  |  | 176 |                        $$configuration{'database'}{'databaseUsername'}, 
 | 
        
           |  |  | 177 |                        $$configuration{'database'}{'databasePassword'} )
 | 
        
           |  |  | 178 |          || die "Could not connect to database: $DBI::errstr";
 | 
        
           |  |  | 179 |   | 
        
           |  |  | 180 | my $sth = $dbh->prepare( $count );
 | 
        
           |  |  | 181 | $sth->execute();
 | 
        
           |  |  | 182 | my $results = $sth->fetchrow_hashref();
 | 
        
           |  |  | 183 | $message .= "$results->{Count} reports in last 24 hours\n";
 | 
        
           |  |  | 184 | $sth->finish();
 | 
        
           |  |  | 185 |   | 
        
           | 25 | rodolico | 186 | $results = $dbh->selectall_hashref( $missing, 'ID' );
 | 
        
           |  |  | 187 | if ( %$results ) {
 | 
        
           |  |  | 188 |    $message .= '='x40 . "\nWARNING, no backup reports for the following machines\n" . '='x40 . "\n";
 | 
        
           |  |  | 189 |    foreach my $device ( keys %$results ) {
 | 
        
           |  |  | 190 |       $message .= sprintf( "%6d\t%s\n", $device, $results->{$device}->{name} );
 | 
        
           |  |  | 191 |    }
 | 
        
           |  |  | 192 |    $message .= '='x40 . "\n\n";
 | 
        
           |  |  | 193 | }
 | 
        
           |  |  | 194 |   | 
        
           | 32 | rodolico | 195 | # we're going to reuse the mail configuration for the individual reports
 | 
        
           |  |  | 196 | # if any, so let's save the mailTo and mailSubject
 | 
        
           |  |  | 197 | my $saveMailTo = $$configuration{'sendReport'}->{'mailTo'};
 | 
        
           |  |  | 198 | my $saveSubject = $$configuration{'sendReport'}->{'mailSubject'};
 | 
        
           |  |  | 199 |   | 
        
           | 23 | rodolico | 200 | $results = $dbh->selectall_hashref( $query, 'ID' );
 | 
        
           | 93 | rodolico | 201 | $message .= 'Device  Device Name                               Files Size (M) Delete   Skip     xfer  Start Time           Run Time' . "\n" . '='x40 . "\n";
 | 
        
           | 79 | rodolico | 202 | # following sorts the results by the name ('source'} field
 | 
        
           |  |  | 203 | foreach my $id ( sort { $results->{$a}->{'Source'} cmp $results->{$b}->{'Source'} } keys %$results ) {
 | 
        
           | 93 | rodolico | 204 |    $message .= sprintf( "%7u %-40s %6u %8.2f %6u %6u %8.2f %20s %9s\n",
 | 
        
           | 25 | rodolico | 205 |                   $results->{$id}->{'Device ID'} , 
 | 
        
           |  |  | 206 |                   $results->{$id}->{'Source'} , 
 | 
        
           |  |  | 207 |                   $results->{$id}->{'Files'} ,
 | 
        
           |  |  | 208 |                   $results->{$id}->{'Size (M)'} ,
 | 
        
           |  |  | 209 |                   $results->{$id}->{'Deleted'} ,
 | 
        
           |  |  | 210 |                   $results->{$id}->{'Skipped'} ,
 | 
        
           | 93 | rodolico | 211 |                   $results->{$id}->{'Traffic (M)'},
 | 
        
           |  |  | 212 |                   $results->{$id}->{'Start Time'},
 | 
        
           |  |  | 213 |                   $results->{$id}->{'Run Time'}
 | 
        
           | 25 | rodolico | 214 |                );
 | 
        
           | 32 | rodolico | 215 |    if ( $results->{$id}->{'mailto'} ) {
 | 
        
           |  |  | 216 |       $$configuration{'sendReport'}->{'mailTo'} = $results->{$id}->{'mailto'};
 | 
        
           |  |  | 217 |       $$configuration{'sendReport'}->{'mailSubject'} = 'Backup Report for ' . $results->{$id}->{'Source'};
 | 
        
           | 73 | rodolico | 218 |       if ( $$configuration{'sendReport'}{'emailScript'} ) {
 | 
        
           |  |  | 219 |          &sendReport( $$configuration{'sendReport'}, &individualReport( $results->{$id} ) );
 | 
        
           |  |  | 220 |       } else {
 | 
        
           |  |  | 221 |          &sendViaSendmail( $$configuration{'sendReport'}, &individualReport( $results->{$id} ) );
 | 
        
           |  |  | 222 |       }
 | 
        
           | 32 | rodolico | 223 |    }
 | 
        
           |  |  | 224 |   | 
        
           | 23 | rodolico | 225 | }
 | 
        
           |  |  | 226 | $dbh->disconnect();
 | 
        
           | 32 | rodolico | 227 |   | 
        
           |  |  | 228 | # reset mailTo and MailSubject to original values
 | 
        
           |  |  | 229 | $$configuration{'sendReport'}->{'mailTo'} = $saveMailTo;
 | 
        
           |  |  | 230 | $$configuration{'sendReport'}->{'mailSubject'} = $saveSubject;
 | 
        
           |  |  | 231 |   | 
        
           |  |  | 232 |   | 
        
           |  |  | 233 | #print $message;
 | 
        
           | 73 | rodolico | 234 | if ( $$configuration{'sendReport'}{'emailScript'} ) {
 | 
        
           |  |  | 235 |    &sendReport( $$configuration{'sendReport'}, $message );
 | 
        
           |  |  | 236 | } else {
 | 
        
           |  |  | 237 |    &sendViaSendmail( $$configuration{'sendReport'}, $message );
 | 
        
           |  |  | 238 | }
 | 
        
           |  |  | 239 |   | 
        
           | 23 | rodolico | 240 | 1;
 |