Imported Upstream version 3.3.2
[debian/amanda] / server-src / amoverview.pl
1 #!@PERL@
2 # Copyright (c) 2010-2012 Zmanda Inc.  All Rights Reserved.
3 #
4 # This program is free software; you can redistribute it and/or modify it
5 # under the terms of the GNU General Public License version 2 as published
6 # by the Free Software Foundation.
7 #
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
11 # for more details.
12 #
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
16 #
17 # Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
18 # Sunnyvale, CA 94086, USA, or: http://www.zmanda.com
19
20 use lib '@amperldir@';
21 use strict;
22 use warnings;
23
24 use Amanda::Config qw( :init :getconf );
25 use Amanda::Disklist;
26 use Amanda::DB::Catalog;
27 use FileHandle;
28 use Getopt::Long;
29 use Carp;
30 use POSIX;
31
32 sub Usage {
33     print STDERR <<END;
34 Usage: $0 [--hostwidth width] [--diskwidth width] [--skipmissed]
35           [--last] [--num0] [--togo0] [--verbose] [--config] <config>
36
37 This script generates to standard output an overview of the filesystems
38 dumped over time and the type of dump done on a particular day, such as
39 a full dump, or an incremental, or if the dump failed.
40
41 On larger installations, this script will take a while to run.  In this case,
42 run it with --verbose to see how far along it is.
43 END
44     exit 1;
45 }
46
47 # overrideable defaults
48 my $opt_version;
49 my $opt_config          = undef;
50 my $opt_hostwidth       = 8;
51 my $opt_diskwidth       = 20;
52 my $opt_skipmissed      = 0;
53 my $opt_last            = 0;
54 my $opt_num0            = 0;
55 my $opt_togo0           = 0;
56 my $opt_verbose         = 0;
57
58 GetOptions('version'            => \$opt_version,
59            'config=s'           => \$opt_config,
60            'hostwidth=i'        => \$opt_hostwidth,
61            'diskwidth=i'        => \$opt_diskwidth,
62            'skipmissed'         => \$opt_skipmissed,
63            'last'               => \$opt_last,
64            'num0'               => \$opt_num0,
65            'togo0'              => \$opt_togo0,
66            'verbose'            => \$opt_verbose)
67 or Usage();
68
69 if (defined $opt_version) {
70     print "amoverview-" . $Amanda::Constants::VERSION , "\n";
71     exit 0;
72 }
73
74 unless(defined($opt_config)) {
75     if (@ARGV == 1) {
76         $opt_config = $ARGV[0];
77     } else {
78         Usage();
79     }
80 }
81
82 #Initialize configuration
83 config_init($CONFIG_INIT_EXPLICIT_NAME, $opt_config);
84 my ($cfgerr_level, @cfgerr_errors) = config_errors();
85 if ($cfgerr_level >= $CFGERR_WARNINGS) {
86     config_print_errors();
87     if ($cfgerr_level >= $CFGERR_ERRORS) {
88         die("errors processing config file");
89     }
90 }
91
92 # read disklist
93 $cfgerr_level = Amanda::Disklist::read_disklist();
94 die("Config errors") if ($cfgerr_level >= $CFGERR_WARNINGS);
95
96 my %disks = ();
97 foreach my $dle (Amanda::Disklist::all_disks()) {
98     $disks{$dle->{"host"}->{"hostname"}}{$dle->{"name"}}++;
99 }
100
101 # Get dumps
102 my %dates = ();
103 my %level = ();
104 my ($date, $host, $disk);
105 $opt_verbose and
106     print STDERR "Processing $opt_config dumps\n";
107 foreach my $dump (Amanda::DB::Catalog::sort_dumps(['hostname','diskname','write_timestamp'],Amanda::DB::Catalog::get_dumps())) {
108     $host = $dump->{"hostname"};
109     $disk = $dump->{"diskname"};
110     $date = substr($dump->{"dump_timestamp"},0,8);
111
112     if (defined $disks{$host}{$disk}) {
113         defined($level{$host}{$disk}{$date}) or
114             $level{$host}{$disk}{$date} = '';
115         $level{$host}{$disk}{$date} .= ($dump->{"status"} eq 'OK') ? $dump->{"level"} : 'E';
116         $dates{$date}++;
117     }
118 }
119
120 # Process the status to arrive at a "last" status
121 if ($opt_last) {
122     for $host (sort keys %disks) {
123         for $disk (sort keys %{$disks{$host}}) {
124             $level{$host}{$disk}{"0000LAST"} = '';
125             for $date (sort keys %dates) {
126                 next unless defined($level{$host}{$disk}{$date});
127                 if ($level{$host}{$disk}{$date} eq "E"
128                      && $level{$host}{$disk}{"0000LAST"} =~ /^\d/ ) {
129                     $level{$host}{$disk}{"0000LAST"} .= $level{$host}{$disk}{$date};
130                 } else {
131                     $level{$host}{$disk}{"0000LAST"} = $level{$host}{$disk}{$date};
132                 }
133             }
134         }
135     }
136 }
137
138 # Number of level 0 backups
139 if ($opt_num0) {
140     for $host (sort keys %disks) {
141         for $disk (sort keys %{$disks{$host}}) {
142             $level{$host}{$disk}{'0000NML0'} = 0;
143             for $date (sort keys %dates) {
144                 if (defined($level{$host}{$disk}{$date})
145                         && $level{$host}{$disk}{$date} =~ /0/) {
146                     $level{$host}{$disk}{'0000NML0'} += 1;
147                 }
148             }
149         }
150     }
151 }
152
153 # Runs to the last level 0
154 if ($opt_togo0) {
155     for $host (sort keys %disks) {
156         for $disk (sort keys %{$disks{$host}}) {
157             $level{$host}{$disk}{'0000TOGO'} = 0;
158             my $togo=0;
159             for $date (sort keys %dates) {
160                 if (defined($level{$host}{$disk}{$date})
161                         && $level{$host}{$disk}{$date} =~ /0/) {
162                     $level{$host}{$disk}{'0000TOGO'} = $togo;
163                 }
164                 $togo++;
165             }
166         }
167     }
168 }
169
170 unless ($opt_skipmissed)
171 # touch all the dates just in case whole days were missed.
172 {
173     my ($start, $finish) = 
174         map {
175             my($y,$m,$d) = /(....)(..)(..)/;
176             POSIX::mktime(0,0,0,$d,$m-1,$y-1900);
177         } (sort keys %dates)[0,-1];
178
179     # Special case of only one date
180     if (defined($finish)) {
181         while ($start < $finish) {
182             my @l = localtime $start;
183             $dates{sprintf("%d%02d%02d", 1900+$l[5], $l[4]+1, $l[3])}++;
184             $start += 86400;
185         }
186     }
187 }
188
189 #Add the "last" entry    
190 $dates{"0000LAST"}=1 if ($opt_last);
191
192 #Add the "Number of Level 0s" entry
193 $dates{"0000NML0"}=1 if ($opt_num0);
194
195 #Add the "Runs to go" entry
196 $dates{"0000TOGO"}=1 if ($opt_togo0);
197
198 # make formats
199
200 sub row {
201     my ($host, $disk, @cols) = @_;
202     $host = substr($host, 0, $opt_hostwidth);
203     $disk = substr($disk, 0, $opt_diskwidth);
204     print
205         sprintf("%-0${opt_hostwidth}s %-0${opt_diskwidth}s ", $host, $disk) .
206         join(" ", map(sprintf("%-2s ", $_), @cols)) .
207         "\n";
208 }
209
210 sub fmt_levels {
211     my ($host, $disk, $date) = @_;
212     my $levels = $level{$host}{$disk}{$date};
213
214     # no dumps on this date
215     $levels = '-' unless defined($levels);
216
217     return substr($levels, -2);
218 }
219
220 # header
221 row('',     'date', map((/....(..) .. /x)[0], sort keys %dates));
222 row('host', 'disk', map((/.... .. (..)/x)[0], sort keys %dates));
223
224 # body
225 for $host (sort keys %disks) {
226     for $disk (sort keys %{$disks{$host}}) {
227         row($host, $disk,
228             map(fmt_levels($host, $disk, $_), sort keys %dates));
229     }
230 }