1 # Copyright (c) 2010 Zmanda, Inc. All Rights Reserved.
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.
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
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
16 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
17 # Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
22 Amanda::Curinfo::Info - Perl extension for representing dump
27 use Amanda::Curinfo::Info;
29 my $info = Amanda::Curinfo::Info->new($infofile);
33 C<Amanda::Curinfo::Info> is the format representation for the curinfo
34 database. It handles the reading and writing of the individual
35 entries, while the entry management is left to C<Amanda::Curinfo>.
36 Further parsing is also dispatched to C<Amanda::Curinfo::History>,
37 C<Amanda::Curinfo::Stats>, and C<Amanda::Curinfo::Perf>.
41 The constructor for a new info object is very simple.
43 my $info = Amanda::Curinfo::Info->new();
45 Will return an empty info object with the necessary fields all blank.
47 Given an existing C<$info> object, for example, as provided by
48 C<Amanda::Curinfo::get_info>, there are other functions present in this
49 library, but they are helper functions to the previously described
50 methods, and not to be used directly.
52 It should also be noted that the reading and writing methods of
53 C<Amanda::Curinfo::Info> are not meant to be used directly, and should be
54 left to L<Amanda::Curinfo>.
56 Reading a previously stored info object is handled with the same
59 my $info = Amanda::Curinfo::Info->new($infofile);
61 Here, C<$info> will contain all the information that was stored in
64 To write the file to a new location, use the following command:
66 $info->write_to_file($infofile);
68 There are also three corresponding container classes that hold data
69 and perform parsing functions. They should only be used when actually
70 writing info file data.
73 Amanda::Curinfo::History->new( $level, $size, $csize, $date, $secs );
75 Amanda::Curinfo::Stats->new( $level, $size, $csize, $secs, $date, $filenum,
78 my $perf = Amanda::Curinfo::Perf->new();
79 $perf->set_rate( $pct1, $pct2, $pct3 );
80 $perf->set_comp( $dbl1, $dbl2, $dbl3 );
82 Note that C<Amanda::Curinfo::Perf> is different. This is because its
83 structure is broken up into two lines in the infofile format, and the
84 length of the C<rate> and C<comp> arrays maybe subject to change in
87 You can also instantiate these objects directly from a
88 properly-formatted line in an infofile:
90 my $history = Amanda::Curinfo::History->from_line($hist_line);
91 my $stats = Amanda::Curinfo::Stats->from_line($stat_line);
93 my $perf = Amanda::Curinfo::Perf->new();
94 $perf->set_rate_from_line($rate_line);
95 $perf->set_comp_from_line($comp_line);
97 Again, creating C<Amanda::Curinfo::Perf> is broken into two calls
98 because its object appears on two lines.
100 Writing these objects back to the info file, however, are all identical:
102 print $infofh $history->to_line();
103 print $infofh $stats->to_line();
104 print $infofh $perf_full->to_line("full");
105 print $infofh $perf_incr->to_line("incr");
107 Additionally, the C<$perf> object accepts a prefix to the line.
111 This package is meant to replace the file reading and writing portions
112 of server-src/infofile.h. If you notice any bugs or compatibility
113 issues, please report them.
117 Paul C. Mantz E<lt>pcmantz@zmanda.comE<gt>
121 my $numdot = qr{[.\d]};
123 package Amanda::Curinfo::Info;
133 my ($class, $infofile) = @_;
137 full => Amanda::Curinfo::Perf->new(),
138 incr => Amanda::Curinfo::Perf->new(),
139 inf => [], # contains Amanda::Curinfo::Stats
140 history => [], # contains Amanda::Curinfo::History
142 consecutive_runs => undef,
146 $self->read_infofile($infofile) if -e $infofile;
153 my ( $self, $level ) = @_;
154 my $inf = $self->{inf};
155 my $date = 0; # Ideally should be set to the epoch, but 0 is fine
157 for ( my $l = 0 ; $l < $level ; $l++ ) {
159 my $this_date = $inf->[$l]->{date};
160 $date = $this_date if ( $this_date > $date );
163 my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
166 my $dumpdate = sprintf(
169 $mon + 1, $mday, $hour, $min, $sec
177 my ( $self, $infofile ) = @_;
179 open my $fh, "<", $infofile or croak "couldn't open $infofile: $!";
181 ## read in the fixed-length data
182 $self->read_infofile_perfs($fh);
184 ## read in the stats data
185 $self->read_infofile_stats($fh);
187 ## read in the history data
188 $self->read_infofile_history($fh);
195 sub read_infofile_perfs
197 my ($self, $fh) = @_;
201 croak "error: malformed infofile header in $self->infofile:$line\n";
204 my $skip_blanks = sub {
206 while ($line eq "") {
207 croak "error: infofile ended prematurely" if eof($fh);
213 # version not paid attention to right now
214 my $line = $skip_blanks->();
215 ($line =~ /^version: ($numdot+)/) ? 1 : $fail->($line);
217 $line = $skip_blanks->();
218 ($line =~ /^command: ($numdot+)/) ? $self->{command} = $1 : $fail->($line);
220 $line = $skip_blanks->();
221 ($line =~ /^full-rate: ($numdot+) ($numdot+) ($numdot+)/)
222 ? $self->{full}->set_rate($1, $2, $3)
225 $line = $skip_blanks->();
226 ($line =~ /^full-comp: ($numdot+) ($numdot+) ($numdot+)/)
227 ? $self->{full}->set_comp($1, $2, $3)
230 $line = $skip_blanks->();
231 ($line =~ /^incr-rate: ($numdot+) ($numdot+) ($numdot+)/)
232 ? $self->{incr}->set_rate($1, $2, $3)
235 $line = $skip_blanks->();
236 ($line =~ /^incr-comp: ($numdot+) ($numdot+) ($numdot+)/)
237 ? $self->{incr}->set_comp($1, $2, $3)
243 sub read_infofile_stats
245 my ( $self, $fh ) = @_;
247 my $inf = $self->{inf};
249 while ( my $line = <$fh> ) {
251 ## try next line if blank
255 } elsif ( $line =~ m{^//} ) {
256 croak "unexpected end of data in stats section (received //)\n";
258 } elsif ( $line =~ m{^history:} ) {
259 croak "history line before end of stats section\n";
261 } elsif ( $line =~ m{^stats:} ) {
263 ## make a new Stats object and push it on to the queue
264 my $stats = Amanda::Curinfo::Stats->from_line($line);
267 } elsif ( $line =~ m{^last_level: (\d+) (\d+)$} ) {
269 $self->{last_level} = $1;
270 $self->{consecutive_runs} = $2;
274 croak "bad line in read_infofile_stats: $line";
281 sub read_infofile_history
283 my ( $self, $fh ) = @_;
285 my $history = $self->{history};
287 while ( my $line = <$fh> ) {
289 if ( $line =~ m{^//} ) {
292 } elsif ( $line =~ m{^history:} ) {
293 my $hist = Amanda::Curinfo::History->from_line($line);
294 push @$history, $hist;
297 croak "bad line found in history section:$line\n";
302 # TODO: make sure there were the right number of history lines
310 my ( $self, $infofile ) = @_;
312 unlink $infofile if -f $infofile;
314 open my $fh, ">", $infofile or die "error: couldn't open $infofile: $!";
318 print $fh "version: 0\n"; # 0 for now, may change in future
319 print $fh "command: $self->{command}\n";
320 print $fh $self->{full}->to_line("full");
321 print $fh $self->{incr}->to_line("incr");
325 foreach my $stat ( @{ $self->{inf} } ) {
326 print $fh $stat->to_line();
328 print $fh "last_level: $self->{last_level} $self->{consecutive_runs}\n";
330 foreach my $hist ( @{ $self->{history} } ) {
331 print $fh $hist->to_line();
344 package Amanda::Curinfo::History;
353 my ( $level, $size, $csize, $date, $secs ) = @_;
363 return bless $self, $class;
368 my ( $class, $line ) = @_;
373 $line =~ m{^history: \s+
375 ($numdot+) \s+ # size
376 ($numdot+) \s+ # csize
377 ($numdot+) \s+ # date
389 croak "bad history line: $line";
392 return bless $self, $class;
399 "history: $self->{level} $self->{size} $self->{csize} $self->{date} $self->{secs}\n";
408 package Amanda::Curinfo::Perf;
425 return bless $self, $class;
430 my ( $self, @rate ) = @_;
431 $self->{rate} = \@rate;
436 my ( $self, @comp ) = @_;
437 $self->{comp} = \@comp;
440 sub set_rate_from_line
442 my ( $self, $line ) = @_;
443 return $self->set_field_from_line( $self, $line, "rate" );
447 sub set_comp_from_line
449 my ( $self, $line ) = @_;
450 return $self->set_field_from_line( $self, $line, "comp" );
454 sub set_field_from_line
456 my ( $self, $line, $field ) = @_;
459 $line =~ m{\w+-$field\: \s+
465 $self->{$field} = [ $1, $2, $3 ];
468 croak "bad perf $field line: $line";
476 my ( $self, $lvl ) = @_;
479 . join( " ", @{ $self->{rate} } ) . "\n"
481 . join( " ", @{ $self->{comp} } ) . "\n";
490 package Amanda::Curinfo::Stats;
499 my ( $level, $size, $csize, $secs, $date, $filenum, $label ) = @_;
517 my ( $class, $line ) = @_;
520 $line =~ m{^stats: \s+
522 ($numdot+) \s+ # size
523 ($numdot+) \s+ # csize
525 ($numdot+) \s+ # date
526 ($numdot+) \s+ # filenum
529 or croak "bad stats line: $line";
540 return bless $self, $class;
547 "stats:", $self->{level}, $self->{size}, $self->{csize},
548 $self->{secs}, $self->{date}, $self->{filenum}, $self->{label} )