1 # Copyright (c) 2010-2012 Zmanda, Inc. All Rights Reserved.
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful, but
9 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 # You should have received a copy of the GNU General Public License along
14 # with this program; if not, write to the Free Software Foundation, Inc.,
15 # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
23 Amanda::Curinfo::Info - Perl extension for representing dump
28 use Amanda::Curinfo::Info;
30 my $info = Amanda::Curinfo::Info->new($infofile);
34 C<Amanda::Curinfo::Info> is the format representation for the curinfo
35 database. It handles the reading and writing of the individual
36 entries, while the entry management is left to C<Amanda::Curinfo>.
37 Further parsing is also dispatched to C<Amanda::Curinfo::History>,
38 C<Amanda::Curinfo::Stats>, and C<Amanda::Curinfo::Perf>.
42 The constructor for a new info object is very simple.
44 my $info = Amanda::Curinfo::Info->new();
46 Will return an empty info object with the necessary fields all blank.
48 Given an existing C<$info> object, for example, as provided by
49 C<Amanda::Curinfo::get_info>, there are other functions present in this
50 library, but they are helper functions to the previously described
51 methods, and not to be used directly.
53 It should also be noted that the reading and writing methods of
54 C<Amanda::Curinfo::Info> are not meant to be used directly, and should be
55 left to L<Amanda::Curinfo>.
57 Reading a previously stored info object is handled with the same
60 my $info = Amanda::Curinfo::Info->new($infofile);
62 Here, C<$info> will contain all the information that was stored in
65 To write the file to a new location, use the following command:
67 $info->write_to_file($infofile);
69 There are also three corresponding container classes that hold data
70 and perform parsing functions. They should only be used when actually
71 writing info file data.
74 Amanda::Curinfo::History->new( $level, $size, $csize, $date, $secs );
76 Amanda::Curinfo::Stats->new( $level, $size, $csize, $secs, $date, $filenum,
79 my $perf = Amanda::Curinfo::Perf->new();
80 $perf->set_rate( $pct1, $pct2, $pct3 );
81 $perf->set_comp( $dbl1, $dbl2, $dbl3 );
83 Note that C<Amanda::Curinfo::Perf> is different. This is because its
84 structure is broken up into two lines in the infofile format, and the
85 length of the C<rate> and C<comp> arrays maybe subject to change in
88 You can also instantiate these objects directly from a
89 properly-formatted line in an infofile:
91 my $history = Amanda::Curinfo::History->from_line($hist_line);
92 my $stats = Amanda::Curinfo::Stats->from_line($stat_line);
94 my $perf = Amanda::Curinfo::Perf->new();
95 $perf->set_rate_from_line($rate_line);
96 $perf->set_comp_from_line($comp_line);
98 Again, creating C<Amanda::Curinfo::Perf> is broken into two calls
99 because its object appears on two lines.
101 Writing these objects back to the info file, however, are all identical:
103 print $infofh $history->to_line();
104 print $infofh $stats->to_line();
105 print $infofh $perf_full->to_line("full");
106 print $infofh $perf_incr->to_line("incr");
108 Additionally, the C<$perf> object accepts a prefix to the line.
112 This package is meant to replace the file reading and writing portions
113 of server-src/infofile.h. If you notice any bugs or compatibility
114 issues, please report them.
118 Paul C. Mantz E<lt>pcmantz@zmanda.comE<gt>
122 my $numdot = qr{[.\d]};
124 package Amanda::Curinfo::Info;
134 my ($class, $infofile) = @_;
138 full => Amanda::Curinfo::Perf->new(),
139 incr => Amanda::Curinfo::Perf->new(),
140 inf => [], # contains Amanda::Curinfo::Stats
141 history => [], # contains Amanda::Curinfo::History
143 consecutive_runs => undef,
147 $self->read_infofile($infofile) if -e $infofile;
154 my ( $self, $level ) = @_;
155 my $inf = $self->{inf};
156 my $date = 0; # Ideally should be set to the epoch, but 0 is fine
158 for ( my $l = 0 ; $l < $level ; $l++ ) {
160 my $this_date = $inf->[$l]->{date};
161 $date = $this_date if ( $this_date > $date );
164 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
167 my $dumpdate = sprintf(
170 $mon + 1, $mday, $hour, $min, $sec
178 my ( $self, $infofile ) = @_;
180 open my $fh, "<", $infofile or croak "couldn't open $infofile: $!";
182 ## read in the fixed-length data
183 $self->read_infofile_perfs($fh);
185 ## read in the stats data
186 $self->read_infofile_stats($fh);
188 ## read in the history data
189 $self->read_infofile_history($fh);
196 sub read_infofile_perfs
198 my ($self, $fh) = @_;
202 croak "error: malformed infofile header in $self->infofile:$line\n";
205 my $skip_blanks = sub {
207 while ($line eq "") {
208 croak "error: infofile ended prematurely" if eof($fh);
214 # version not paid attention to right now
215 my $line = $skip_blanks->();
216 ($line =~ /^version: ($numdot+)/) ? 1 : $fail->($line);
218 $line = $skip_blanks->();
219 ($line =~ /^command: ($numdot+)/) ? $self->{command} = $1 : $fail->($line);
221 $line = $skip_blanks->();
222 ($line =~ /^full-rate: ($numdot+) ($numdot+) ($numdot+)/)
223 ? $self->{full}->set_rate($1, $2, $3)
226 $line = $skip_blanks->();
227 ($line =~ /^full-comp: ($numdot+) ($numdot+) ($numdot+)/)
228 ? $self->{full}->set_comp($1, $2, $3)
231 $line = $skip_blanks->();
232 ($line =~ /^incr-rate: ($numdot+) ($numdot+) ($numdot+)/)
233 ? $self->{incr}->set_rate($1, $2, $3)
236 $line = $skip_blanks->();
237 ($line =~ /^incr-comp: ($numdot+) ($numdot+) ($numdot+)/)
238 ? $self->{incr}->set_comp($1, $2, $3)
244 sub read_infofile_stats
246 my ( $self, $fh ) = @_;
248 my $inf = $self->{inf};
250 while ( my $line = <$fh> ) {
252 ## try next line if blank
256 } elsif ( $line =~ m{^//} ) {
257 croak "unexpected end of data in stats section (received //)\n";
259 } elsif ( $line =~ m{^history:} ) {
260 croak "history line before end of stats section\n";
262 } elsif ( $line =~ m{^stats:} ) {
264 ## make a new Stats object and push it on to the queue
265 my $stats = Amanda::Curinfo::Stats->from_line($line);
268 } elsif ( $line =~ m{^last_level: (\d+) (\d+)$} ) {
270 $self->{last_level} = $1;
271 $self->{consecutive_runs} = $2;
275 croak "bad line in read_infofile_stats: $line";
282 sub read_infofile_history
284 my ( $self, $fh ) = @_;
286 my $history = $self->{history};
288 while ( my $line = <$fh> ) {
290 if ( $line =~ m{^//} ) {
293 } elsif ( $line =~ m{^history:} ) {
294 my $hist = Amanda::Curinfo::History->from_line($line);
295 push @$history, $hist;
298 croak "bad line found in history section:$line\n";
303 # TODO: make sure there were the right number of history lines
311 my ( $self, $infofile ) = @_;
313 unlink $infofile if -f $infofile;
315 open my $fh, ">", $infofile or die "error: couldn't open $infofile: $!";
319 print $fh "version: 0\n"; # 0 for now, may change in future
320 print $fh "command: $self->{command}\n";
321 print $fh $self->{full}->to_line("full");
322 print $fh $self->{incr}->to_line("incr");
326 foreach my $stat ( @{ $self->{inf} } ) {
327 print $fh $stat->to_line();
329 print $fh "last_level: $self->{last_level} $self->{consecutive_runs}\n";
331 foreach my $hist ( @{ $self->{history} } ) {
332 print $fh $hist->to_line();
345 package Amanda::Curinfo::History;
354 my ( $level, $size, $csize, $date, $secs ) = @_;
364 return bless $self, $class;
369 my ( $class, $line ) = @_;
374 $line =~ m{^history: \s+
376 ($numdot+) \s+ # size
377 ($numdot+) \s+ # csize
378 ($numdot+) \s+ # date
390 croak "bad history line: $line";
393 return bless $self, $class;
400 "history: $self->{level} $self->{size} $self->{csize} $self->{date} $self->{secs}\n";
409 package Amanda::Curinfo::Perf;
426 return bless $self, $class;
431 my ( $self, @rate ) = @_;
432 $self->{rate} = \@rate;
437 my ( $self, @comp ) = @_;
438 $self->{comp} = \@comp;
441 sub set_rate_from_line
443 my ( $self, $line ) = @_;
444 return $self->set_field_from_line( $self, $line, "rate" );
448 sub set_comp_from_line
450 my ( $self, $line ) = @_;
451 return $self->set_field_from_line( $self, $line, "comp" );
455 sub set_field_from_line
457 my ( $self, $line, $field ) = @_;
460 $line =~ m{\w+-$field\: \s+
466 $self->{$field} = [ $1, $2, $3 ];
469 croak "bad perf $field line: $line";
477 my ( $self, $lvl ) = @_;
480 . join( " ", @{ $self->{rate} } ) . "\n"
482 . join( " ", @{ $self->{comp} } ) . "\n";
491 package Amanda::Curinfo::Stats;
500 my ( $level, $size, $csize, $secs, $date, $filenum, $label ) = @_;
518 my ( $class, $line ) = @_;
521 $line =~ m{^stats: \s+
523 ($numdot+) \s+ # size
524 ($numdot+) \s+ # csize
526 ($numdot+) \s+ # date
527 ($numdot+) \s+ # filenum
530 or croak "bad stats line: $line";
541 return bless $self, $class;
548 "stats:", $self->{level}, $self->{size}, $self->{csize},
549 $self->{secs}, $self->{date}, $self->{filenum}, $self->{label} )