BackupPC verification helper
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

187 lines
6.4 KiB

  1. #============================================================= -*-perl-*-
  2. #
  3. # BackupPC::CGI::Check package
  4. #
  5. # DESCRIPTION
  6. #
  7. # This module implements the Check action for the CGI interface.
  8. #
  9. # AUTHOR
  10. # Heuze Florent <heuzef@firewall-services.com>
  11. #
  12. #========================================================================
  13. #
  14. # Released November 2019 - firewall-services.com
  15. #
  16. #========================================================================
  17. package BackupPC::CGI::Check;
  18. use strict;
  19. use lib "/usr/share/BackupPC/lib";
  20. use BackupPC::Lib;
  21. use BackupPC::CGI::Lib qw(:all);
  22. use Statistics::Descriptive;
  23. sub action
  24. {
  25. # Init
  26. my($str, $strGood, $header);
  27. GetStatusInfo("hosts info");
  28. my $Privileged = CheckPermission();
  29. my $bpc = BackupPC::Lib->new();
  30. # Start loop
  31. foreach my $host ( GetUserHosts(1) ) {
  32. my($incrAge, $reasonHilite, $frequency, $idBackup, $lastAge, $lastAgeColor, $tempState, $tempReason, $lastXferErrors, $lastXferErrorsColor, $ifErrors, $sizeConsistency, $sizeConsistencyColor);
  33. my($shortErr);
  34. my @Backups = $bpc->BackupInfoRead($host);
  35. $bpc->ConfigRead($host);
  36. %Conf = $bpc->Conf();
  37. next if ( $Conf{XferMethod} eq "archive" );
  38. next if ( !$Privileged && !CheckPermission($host) );
  39. # Get frequency for this host
  40. if ( $Conf{IncrPeriod} < $Conf{FullPeriod} ) {
  41. $frequency = $Conf{IncrPeriod};
  42. } else {
  43. $frequency = $Conf{FullPeriod};
  44. }
  45. # ID
  46. my $idBackup = $Backups[@Backups-1]->{num} if ( @Backups );
  47. # Age
  48. my $lastBackup = ( $Backups[-1]->{type} =~ m/^full|incr$/ ) ? -1 : -2;
  49. $lastAge = sprintf("%.1f", (time - $Backups[$lastBackup]->{startTime}) / (24 * 3600));
  50. # Color for age
  51. if ( $lastAge < $frequency ) {
  52. $lastAgeColor = "MediumSeaGreen";
  53. } else {
  54. $lastAgeColor = "Tomato";
  55. }
  56. # Color and link for errors
  57. $lastXferErrors = $Backups[@Backups-1]->{xferErrs} if ( @Backups );
  58. if ( $lastXferErrors == 0 ) {
  59. $lastXferErrorsColor = "MediumSeaGreen";
  60. $ifErrors = "";
  61. } else {
  62. $lastXferErrorsColor = "Tomato";
  63. my $browseErrors = "?action=view&type=XferErr&num=$idBackup&host=$host";
  64. $ifErrors = "| <a href=\"$browseErrors\" target=\"_blank\"><strong>Read me !</strong></a>";
  65. }
  66. # Colors statuts
  67. $reasonHilite = $Conf{CgiStatusHilightColor}{$Status{$host}{reason}} || $Conf{CgiStatusHilightColor}{$Status{$host}{state}};
  68. $reasonHilite = " bgcolor=\"$reasonHilite\"" if ( $reasonHilite ne "" );
  69. # Check Size Consistency
  70. my $new_size = 0;
  71. my $new_size_avg = 0;
  72. my $new_size_q1 = 0;
  73. my $new_size_q3 = 0;
  74. my $sizes = new Statistics::Descriptive::Full;
  75. foreach my $backup ( @Backups ) {
  76. my $idBackup = $Backups[@Backups-1]->{num} if ( @Backups );
  77. # Skip partial or active backups
  78. next if ( $backup->{type} !~ m/^full|incr$/ );
  79. # Push all the sizes in our data set to compute avg sizes
  80. # Exclude backup N°0 as it'll always have much more new data than normal backups
  81. $sizes->add_data($backup->{sizeNew}) unless ( $backup->{num} == 0 );
  82. # Ignore the last backup if it's not full or incr (which means it's either partial or active)
  83. my $i = ( $Backups[-1]->{type} =~ m/^full|incr$/ ) ? -1 : -2;
  84. $new_size = $Backups[$i]->{sizeNew};
  85. $new_size_avg = int $sizes->mean;
  86. $new_size_q1 = eval { int $sizes->quantile(1) } || 0;
  87. $new_size_q3 = eval { int $sizes->quantile(3) } || 0;
  88. }
  89. # Using a mathematical formula to calculate the consistency of the average size, for new files, on all backups
  90. my $toobig = 0;
  91. my $toosmall = 0;
  92. my $sizeConsistencyColor = "Tomato";
  93. my $sizeConsistency = "<strong>ANOMALOUS</strong>";
  94. # TOO BIG ? If the size is 6 times higher than usual :
  95. if ( $new_size > ($new_size_q3 + ($new_size_q3 - $new_size_q1) ) * 1.5 and $new_size > $new_size_avg * 6 ) {
  96. $toobig = 1;
  97. }
  98. # TOO SMALL ? If the size is 3 times lower than usual :
  99. if ( $new_size < ($new_size_q1 - ($new_size_q3 - $new_size_q1) ) * 1.5 and $new_size < $new_size_avg / 3 ) {
  100. $toosmall = 1;
  101. }
  102. # Get result
  103. if ( not $idBackup > 4) {
  104. $sizeConsistencyColor = "Gray";
  105. $sizeConsistency = "Not enough backups";
  106. }
  107. elsif ( not $toobig and not $toosmall and not $idBackup < 4) {
  108. $sizeConsistencyColor = "MediumSeaGreen";
  109. $sizeConsistency = "Normal";
  110. }
  111. # Get URL for explore file
  112. my $browseFile = "?action=browse&host=$host";
  113. # Show summary
  114. $str .= <<EOF;
  115. <tr$reasonHilite>
  116. <td class="border"><a href="$browseFile" target="_blank">$host ($idBackup)</a></td>
  117. <td align="center" class="border" style="color:$lastAgeColor;">$lastAge <em>(Freq: $frequency)</em></td>
  118. <td align="center" class="border" style="color:$lastXferErrorsColor;">$lastXferErrors $ifErrors</td>
  119. <td align="center" class="border" style="color:$sizeConsistencyColor;">$sizeConsistency</td>
  120. </tr>
  121. EOF
  122. }
  123. # End loop
  124. # Time set
  125. my $now = timeStamp2(time);
  126. my $DUmaxTime = timeStamp2($Info{DUDailyMaxTime});
  127. my $DUInodemaxTime = timeStamp2($Info{DUInodeDailyMaxTime});
  128. # Show header
  129. $header = <<EOF;
  130. \${h1(qq{BackupPC: Check})}
  131. <p>This check was generated at <strong>\$now</strong>.</p>
  132. <p>File system pool size usage (\$DUmaxTime) :</p>
  133. <div style="background-color:#f1f1f1!important">
  134. <div style="color:#fff!important;background-color:#2196F3!important; text-align:center; width:\$Info{DUDailyMax}%">\$Info{DUDailyMax}%</div>
  135. </div>
  136. <p>File system inode size usage (\$DUInodemaxTime) :</p>
  137. <div style="background-color:#f1f1f1!important">
  138. <div style="color:#fff!important;background-color:#2196F3!important; text-align:center; width:\$Info{DUInodeDailyMax}%">\$Info{DUInodeDailyMax}%</div>
  139. </div>
  140. \${h2("Backups summary")}
  141. <table class="sortable" id="host_summary_backups" border cellpadding="3" cellspacing="1">
  142. <tr class="tableheader">
  143. <td>Host (Explore files)</td>
  144. <td>Last backup in days</td>
  145. <td>Errors</td>
  146. <td>Size Consistency</td>
  147. </tr>
  148. \$str
  149. </table>
  150. EOF
  151. # Show page
  152. my $content = eval ("qq{$header}");
  153. Header("BackupPC: Check", $content);
  154. Trailer();
  155. }
  156. 1;