]> git.proxmox.com Git - pve-manager.git/commitdiff
Fix #352: Limit the length of backup logs for mails
authorDominic Jäger <d.jaeger@proxmox.com>
Tue, 21 May 2019 11:16:13 +0000 (13:16 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Tue, 19 Nov 2019 14:02:50 +0000 (15:02 +0100)
When creating a backup the log part can make the mail too big to be
transferred. To ensure delivery, two measures are taken:
1. Always omit the status lines
2. Omit the whole log part if a mail becomes (too) big

Additionally, add a check for missing log files.

Co-developed-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Signed-off-by: Dominic Jäger <d.jaeger@proxmox.com>
PVE/VZDump.pm
test/Makefile
test/mail_test.pl [new file with mode: 0755]

index 22003a65fbf2bc320928ec5032855f522e539cd4..7ba54eff470869a0c9a0747c08f02479a3b32060 100644 (file)
@@ -194,6 +194,7 @@ sub read_vzdump_defaults {
     return $res;
 }
 
+use constant MAX_MAIL_SIZE => 1024*1024;
 sub sendmail {
     my ($self, $tasklist, $totaltime, $err, $detail_pre, $detail_post) = @_;
 
@@ -255,23 +256,30 @@ sub sendmail {
        }
     }
 
-    $text .= "\nDetailed backup logs:\n\n";
-    $text .= "$cmdline\n\n";
+    my $text_log_part;
+    $text_log_part .= "\nDetailed backup logs:\n\n";
+    $text_log_part .= "$cmdline\n\n";
 
-    $text .= $detail_pre . "\n" if defined($detail_pre);
+    $text_log_part .= $detail_pre . "\n" if defined($detail_pre);
     foreach my $task (@$tasklist) {
        my $vmid = $task->{vmid};
        my $log = $task->{tmplog};
        if (!$log) {
-           $text .= "$vmid: no log available\n\n";
+           $text_log_part .= "$vmid: no log available\n\n";
            next;
        }
-       open (TMP, "$log");
-       while (my $line = <TMP>) { $text .= encode8bit ("$vmid: $line"); }
+       if (open (TMP, "$log")) {
+           while (my $line = <TMP>) {
+               next if $line =~ /^status: \d+/; # not useful in mails
+               $text_log_part .= encode8bit ("$vmid: $line");
+           }
+       } else {
+           $text_log_part .= "$vmid: Could not open log file\n\n";
+       }
        close (TMP);
-       $text .= "\n";
+       $text_log_part .= "\n";
     }
-    $text .= $detail_post if defined($detail_post);
+    $text_log_part .= $detail_post if defined($detail_post);
 
     # html part
     my $html = "<html><body>\n";
@@ -304,35 +312,54 @@ sub sendmail {
     $html .= sprintf ("<tr><td align=left colspan=3>TOTAL<td>%s<td>%s<td></tr>",
  format_time ($totaltime), format_size ($ssize));
 
-    $html .= "</table><br><br>\n";
-    $html .= "Detailed backup logs:<br /><br />\n";
-    $html .= "<pre>\n";
-    $html .= escape_html($cmdline) . "\n\n";
+    $html .= "\n</table><br><br>\n";
+    my $html_log_part;
+    $html_log_part .= "Detailed backup logs:<br /><br />\n";
+    $html_log_part .= "<pre>\n";
+    $html_log_part .= escape_html($cmdline) . "\n\n";
 
-    $html .= escape_html($detail_pre) . "\n" if defined($detail_pre);
+    $html_log_part .= escape_html($detail_pre) . "\n" if defined($detail_pre);
     foreach my $task (@$tasklist) {
        my $vmid = $task->{vmid};
        my $log = $task->{tmplog};
        if (!$log) {
-           $html .= "$vmid: no log available\n\n";
+           $html_log_part .= "$vmid: no log available\n\n";
            next;
        }
-       open (TMP, "$log");
-       while (my $line = <TMP>) {
-           if ($line =~ m/^\S+\s\d+\s+\d+:\d+:\d+\s+(ERROR|WARN):/) {
-               $html .= encode8bit ("$vmid: <font color=red>".
-                                      escape_html ($line) . "</font>");
-           } else {
-               $html .= encode8bit ("$vmid: " . escape_html ($line));
+       if (open (TMP, "$log")) {
+           while (my $line = <TMP>) {
+               next if $line =~ /^status: \d+/; # not useful in mails
+               if ($line =~ m/^\S+\s\d+\s+\d+:\d+:\d+\s+(ERROR|WARN):/) {
+                   $html_log_part .= encode8bit ("$vmid: <font color=red>".
+                       escape_html ($line) . "</font>");
+               } else {
+                   $html_log_part .= encode8bit ("$vmid: " . escape_html ($line));
+               }
            }
+       } else {
+           $html_log_part .= "$vmid: Could not open log file\n\n";
        }
        close (TMP);
-       $html .= "\n";
+       $html_log_part .= "\n";
     }
-    $html .= escape_html($detail_post) if defined($detail_post);
-    $html .= "</pre></body></html>\n";
+    $html_log_part .= escape_html($detail_post) if defined($detail_post);
+    $html_log_part .= "</pre>";
+    my $html_end .= "\n</body></html>\n";
     # end html part
 
+    if (length($text) + length($text_log_part) +
+       length($html) + length($html_log_part) < MAX_MAIL_SIZE)
+    {
+       $html .= $html_log_part;
+       $text .= $text_log_part;
+    } else {
+       my $msg = "Log output was too long to be sent by mail. ".
+           "See Task History for details!\n";
+       $text .= $msg;
+       $html .= "<p>$msg</p>";
+       $html .= $html_end;
+    }
+
     my $subject = "vzdump backup status ($hostname) : $stat";
 
     my $dcconf = PVE::Cluster::cfs_read_file('datacenter.cfg');
index cd9348384e2b5309ef992fc2bd5c9fd83e763ba5..a3f104b46fd0181b61a8891df93b2897b023ce94 100644 (file)
@@ -12,7 +12,7 @@ check:
        ./replication_test4.pl
        ./replication_test5.pl
        ./replication_test6.pl
-
+       ./mail_test.pl
 .PHONY: install
 install:
 
diff --git a/test/mail_test.pl b/test/mail_test.pl
new file mode 100755 (executable)
index 0000000..58bfaaf
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use PVE::VZDump;
+use Test::More tests => 5;
+use Test::MockModule;
+
+my $STATUS = qr/.*status.*/;
+my $NO_LOGFILE = qr/.*Could not open log file.*/;
+my $LOG_TOO_LONG = qr/.*Log output was too long.*/;
+my $TEST_FILE_PATH       = '/tmp/mail_test';
+my $TEST_FILE_WRONG_PATH = '/tmp/mail_test_wrong';
+sub prepare_mail_with_status {
+    open(TEST_FILE, '>', $TEST_FILE_PATH); # Removes previous content
+    print TEST_FILE "start of log file\n";
+    print TEST_FILE "status: 0\% this should not be in the mail\n";
+    print TEST_FILE "status: 55\% this should not be in the mail\n";
+    print TEST_FILE "status: 100\% this should not be in the mail\n";
+    print TEST_FILE "end of log file\n";
+    close(TEST_FILE);
+}
+sub prepare_long_mail {
+    open(TEST_FILE, '>', $TEST_FILE_PATH); # Removes previous content
+    # 0.5 MB * 2 parts + the overview tables gives more than 1 MB mail
+    print TEST_FILE "a" x (1024*1024/2);
+    close(TEST_FILE);
+}
+{
+    my $result_text;
+    my $result_html;
+    my $mock_module = Test::MockModule->new('PVE::Tools');
+    $mock_module->mock('sendmail', sub {
+       my (undef, undef, $text, $html, undef, undef) = @_;
+       $result_text = $text;
+       $result_html = $html;
+    });
+    my $MAILTO = ['test_address@proxmox.com'];
+    my $OPTS->{mailto} = $MAILTO;
+    my $SELF->{opts} = $OPTS;
+    $SELF->{cmdline} = 'test_command_on_cli';
+    my $task;
+    $task->{state} = 'ok';
+    $task->{vmid} = '1';
+    {
+       $result_text = undef;
+       $result_html = undef;
+       $task->{tmplog} = $TEST_FILE_WRONG_PATH;
+       my $tasklist = [$task];
+       PVE::VZDump::sendmail($SELF, $tasklist, 0, undef, undef, undef);
+       like($result_text, $NO_LOGFILE, "Missing logfile is detected");
+    }
+    {
+       $result_text = undef;
+       $result_html = undef;
+       $task->{tmplog} = $TEST_FILE_PATH;
+       my $tasklist = [$task];
+       prepare_mail_with_status();
+       PVE::VZDump::sendmail($SELF, $tasklist, 0, undef, undef, undef);
+       unlike($result_text, $STATUS, "Status are not in text part of mails");
+       unlike($result_html, $STATUS, "Status are not in HTML part of mails");
+       unlink $TEST_FILE_PATH;
+    }
+    {
+       $result_text = undef;
+       $result_html = undef;
+       $task->{tmplog} = $TEST_FILE_PATH;
+       my $tasklist = [$task];
+       prepare_long_mail();
+       PVE::VZDump::sendmail($SELF, $tasklist, 0, undef, undef, undef);
+       like($result_text, $LOG_TOO_LONG, "Text part of mails gets shortened");
+       like($result_html, $LOG_TOO_LONG, "HTML part of mails gets shortened");
+       unlink $TEST_FILE_PATH;
+    }
+}
\ No newline at end of file