46b6ad20fee84e69e6cd2806738edd643f46de3b
[debian/amanda] / perl / Amanda / Report / postscript.pm
1 # Copyright (c) 2010 Zmanda, Inc.  All Rights Reserved.
2 #
3 # This program is free software; you can redistribute it and/or modify it
4 # under the terms of the GNU General Public License version 2 as published
5 # by the Free Software Foundation.
6 #
7 # This program is distributed in the hope that it will be useful, but
8 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
9 # or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
10 # for more details.
11 #
12 # You should have received a copy of the GNU General Public License along
13 # with this program; if not, write to the Free Software Foundation, Inc.,
14 # 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
15 #
16 # Contact information: Zmanda Inc., 465 S Mathlida Ave, Suite 300
17 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
18
19 package Amanda::Report::postscript;
20
21 =head1 NAME
22
23 Amanda::Report::postscript -- postscript output for amreport
24
25 =head1 DESCRIPTION
26
27 This package implements the postscript output for amreport.  See amreport(8)
28 for more information.
29
30 =cut
31
32 use strict;
33 use warnings;
34
35 use Amanda::Constants;
36 use Amanda::Config qw( :getconf config_dir_relative );
37 use Amanda::Debug qw( debug );
38 use Amanda::Util;
39
40 sub new
41 {
42     my $class = shift;
43     my ($report, $config_name, $logfname) = @_;
44
45     my $self = bless {
46         report => $report,
47         config_name => $config_name,
48         logfname => $logfname,
49     }, $class;
50
51     # get some other parameters we'll need
52     my $ttyp = getconf($CNF_TAPETYPE);
53     my $tt = lookup_tapetype($ttyp) if $ttyp;
54     my ($tapelen, $marksize, $template_filename);
55
56     if ($ttyp && $tt) {
57
58         # append null string to get the right context
59         $tapelen = "" . tapetype_getconf($tt, $TAPETYPE_LENGTH);
60         $marksize = "" . tapetype_getconf($tt, $TAPETYPE_FILEMARK);
61         $template_filename = "" . tapetype_getconf($tt, $TAPETYPE_LBL_TEMPL);
62     }
63
64     # these values should never be zero, so assign defaults
65     $self->{'tapelen'} = $tapelen || 100 * 1024 * 1024;
66     $self->{'marksize'} = $marksize || 1 * 1024 * 1024;
67
68     # TODO: this should be a shared method somewhere
69     my $timestamp = $report->get_timestamp();
70     my ($year, $month, $day) = ($timestamp =~ m/^(\d\d\d\d)(\d\d)(\d\d)/);
71     my $date  = POSIX::strftime('%B %e, %Y', 0, 0, 0, $day, $month - 1, $year - 1900);
72     $date =~ s/  / /g; # get rid of intervening space
73     $self->{'datestr'} = $date;
74
75     # get the template
76     $self->{'template'} = $self->_get_template($template_filename);
77
78     return $self;
79 }
80
81 sub write_report
82 {
83     my $self = shift;
84     my ($fh) = @_;
85
86     my $tape_labels = $self->{'report'}->get_program_info("taper", "tape_labels", []);
87     my $tapes = $self->{'report'}->get_program_info("taper", "tapes", {});
88
89     for my $label (@$tape_labels) {
90         my $tape = $tapes->{$label};
91         $self->_write_report_tape($fh, $label, $tape);
92     }
93 }
94
95 sub _write_report_tape
96 {
97     my $self = shift;
98     my ($fh, $label, $tape) = @_;
99
100     # function to quote string literals
101     sub psstr {
102         my ($str) = @_;
103         $str =~ s/([()\\])/\\$1/g;
104         return "($str)";
105     }
106
107     ## include the template once for each tape (might be overkill, but oh well)
108     print $fh $self->{'template'};
109
110     ## header stuff
111     print $fh psstr($self->{'datestr'}), " DrawDate\n\n";
112     print $fh psstr("Amanda Version $Amanda::Constants::VERSION"), " DrawVers\n";
113     print $fh psstr($label), " DrawTitle\n";
114
115     ## pre-calculate everything
116
117     # make a list of the first part in each dumpfile, and at the same
118     # time count the origsize and outsize of every file beginning on this
119     # tape, and separate sums only of the compressed dumps
120     my @first_parts;
121     my $total_outsize = 0;
122     my $total_origsize = 0;
123     my $comp_outsize = 0;
124     my $comp_origsize = 0;
125     foreach my $dle ($self->{'report'}->get_dles()) {
126         my ($host, $disk) = @$dle;
127         my $dle_info = $self->{'report'}->get_dle_info($host, $disk);
128
129         # run once for each try for this DLE
130         foreach my $try (@{$dle_info->{'tries'}}) {
131
132             next unless exists $try->{taper};
133             my $taper = $try->{taper};
134
135             my $parts = $taper->{parts};
136             next unless @$parts > 0;
137
138             my $first_part = $parts->[0];
139             next unless $first_part->{label} eq $label;
140
141             my $filenum = $first_part->{file};
142
143             # sum the part sizes on this label to get the outsize.  Note that
144             # the postscript output does not contain a row for each part, but
145             # for each part..
146             my $outsize = 0;
147             for my $part (@$parts) {
148                 next unless $part->{'label'} eq $label;
149                 $outsize += $part->{'kb'};
150             }
151
152             # Get origsize for this try.
153             my $origsize = 0;
154             my $level = -1;
155
156             # TODO: this is complex and should probably be in a parent-class method
157             if (exists $try->{dumper} and ($try->{dumper}{status} ne 'fail')) {
158                 my $try_dumper = $try->{dumper};
159                 $level    = $try_dumper->{level};
160                 $origsize = $try_dumper->{orig_kb};
161             } else {    # we already know a taper run exists in this try
162                 $level = $taper->{level};
163                 $origsize = $taper->{orig_kb} if $taper->{orig_kb};
164             }
165
166             $total_outsize += $outsize;
167             $total_origsize += $origsize;
168
169             if ($outsize != $origsize) {
170                 $comp_outsize += $outsize;
171                 $comp_origsize += $origsize;
172             }
173
174             push @first_parts, [$host, $disk, $level, $filenum, $origsize, $outsize];
175         }
176     }
177     # count filemarks in the tapeused assessment
178     my $tapeused = $tape->{'kb'};
179     $tapeused += $self->{'marksize'} * (1 + $tape->{'files'});
180
181     # sort @first_parts by filenum
182     @first_parts = sort { $a->[3] <=> $b->[3] } @first_parts;
183
184     ## output
185
186     print $fh psstr(sprintf('Total Size:        %6.1f MB', $tape->{kb} / 1024)),
187             " DrawStat\n";
188     print $fh psstr(sprintf('Tape Used (%%)       %4s %%',
189                                 $self->divzero($tapeused * 100, $self->{'tapelen'}))),
190             " DrawStat\n";
191     print $fh psstr(sprintf('Number of files:  %5s', $tape->{'files'})),
192             " DrawStat\n";
193     print $fh psstr(sprintf('Filesystems Taped: %4d', $tape->{dle})),
194             " DrawStat\n";
195
196     my $header = ["-", $label, "-", 0, 32, 32];
197     for my $ff ($header, @first_parts) {
198         my ($host, $name, $level, $filenum, $origsize, $outsize) = @$ff;
199         print $fh join(" ",
200                 psstr($host),
201                 psstr($name),
202                 psstr($level),
203                 psstr(sprintf("%3d", $filenum)),
204                 psstr(sprintf("%8s", $origsize || "")),
205                 psstr(sprintf("%8s", $outsize || "")),
206                 "DrawHost\n");
207     }
208
209     print $fh "\nshowpage\n";
210 }
211
212
213 # copy the user's configured template file into $fh
214 sub _get_template {
215     my $self = shift;
216     my ($filename) = @_;
217
218     $filename = config_dir_relative($filename) if $filename;
219
220     if (!$filename || !-r $filename) {
221         debug("could not open template file '$filename'");
222         return undef;
223     }
224
225     return Amanda::Util::slurp($filename);
226 }
227
228 # TODO: this should be a common function somewhere
229 sub divzero
230 {
231     my $self = shift;
232     my ( $a, $b ) = @_;
233     my $q;
234     return
235         ( $b == 0 )              ? "-- "
236       : ( ($q = $a / $b) > 99999.95 ) ? "#####"
237       : ( $q > 999.95 ) ? sprintf( "%5.0f", $q )
238       :                   sprintf( "%5.1f", $q );
239 }
240
241 1;