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