Blob Blame History Raw
#!@PERL@
# Copyright (c) 2010-2012 Zmanda Inc.  All Rights Reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Contact information: Carbonite Inc., 756 N Pastoria Ave
# Sunnyvale, CA 94086, USA, or: http://www.zmanda.com

use lib '@amperldir@';
use strict;
use warnings;

use Amanda::Config qw( :init :getconf );
use Amanda::Disklist;
use Amanda::DB::Catalog;
use FileHandle;
use Getopt::Long;
use Carp;
use POSIX;

sub Usage {
    print STDERR <<END;
Usage: $0 [--hostwidth width] [--diskwidth width] [--skipmissed]
	  [--last] [--num0] [--togo0] [--verbose] [--config] <config>

This script generates to standard output an overview of the filesystems
dumped over time and the type of dump done on a particular day, such as
a full dump, or an incremental, or if the dump failed.

On larger installations, this script will take a while to run.  In this case,
run it with --verbose to see how far along it is.
END
    exit 1;
}

# overrideable defaults
my $opt_version;
my $opt_config		= undef;
my $opt_hostwidth	= 8;
my $opt_diskwidth	= 20;
my $opt_skipmissed	= 0;
my $opt_last		= 0;
my $opt_num0		= 0;
my $opt_togo0		= 0;
my $opt_verbose		= 0;

GetOptions('version'            => \$opt_version,
	   'config=s'		=> \$opt_config,
	   'hostwidth=i'	=> \$opt_hostwidth,
	   'diskwidth=i'	=> \$opt_diskwidth,
	   'skipmissed'		=> \$opt_skipmissed,
	   'last'		=> \$opt_last,
	   'num0'		=> \$opt_num0,
	   'togo0'		=> \$opt_togo0,
	   'verbose'		=> \$opt_verbose)
or Usage();

if (defined $opt_version) {
    print "amoverview-" . $Amanda::Constants::VERSION , "\n";
    exit 0;
}

unless(defined($opt_config)) {
    if (@ARGV == 1) {
	$opt_config = $ARGV[0];
    } else {
	Usage();
    }
}

#Initialize configuration
config_init_with_global($CONFIG_INIT_EXPLICIT_NAME, $opt_config);
my ($cfgerr_level, @cfgerr_errors) = config_errors();
if ($cfgerr_level >= $CFGERR_WARNINGS) {
    config_print_errors();
    if ($cfgerr_level >= $CFGERR_ERRORS) {
        die("errors processing config file");
    }
}

# read disklist
$cfgerr_level = Amanda::Disklist::read_disklist();
die("Config errors") if ($cfgerr_level >= $CFGERR_WARNINGS);

my %disks = ();
foreach my $dle (Amanda::Disklist::all_disks()) {
    $disks{$dle->{"host"}->{"hostname"}}{$dle->{"name"}}++;
}

# Get dumps
my %dates = ();
my %level = ();
my ($date, $host, $disk);
$opt_verbose and
    print STDERR "Processing $opt_config dumps\n";
foreach my $dump (Amanda::DB::Catalog::sort_dumps(['hostname','diskname','write_timestamp'],Amanda::DB::Catalog::get_dumps())) {
    $host = $dump->{"hostname"};
    $disk = $dump->{"diskname"};
    $date = substr($dump->{"dump_timestamp"},0,8);

    if (defined $disks{$host}{$disk}) {
        defined($level{$host}{$disk}{$date}) or
            $level{$host}{$disk}{$date} = '';
        $level{$host}{$disk}{$date} .= ($dump->{"status"} eq 'OK') ? $dump->{"level"} : 'E';
        $dates{$date}++;
    }
}

# Process the status to arrive at a "last" status
if ($opt_last) {
    for $host (sort keys %disks) {
        for $disk (sort keys %{$disks{$host}}) {
	    $level{$host}{$disk}{"0000LAST"} = '';
	    for $date (sort keys %dates) {
	        next unless defined($level{$host}{$disk}{$date});
	        if ($level{$host}{$disk}{$date} eq "E"
		     && $level{$host}{$disk}{"0000LAST"} =~ /^\d/ ) {
		    $level{$host}{$disk}{"0000LAST"} .= $level{$host}{$disk}{$date};
	        } else {
		    $level{$host}{$disk}{"0000LAST"} = $level{$host}{$disk}{$date};
	        }
	    }
        }
    }
}

# Number of level 0 backups
if ($opt_num0) {
    for $host (sort keys %disks) {
        for $disk (sort keys %{$disks{$host}}) {
            $level{$host}{$disk}{'0000NML0'} = 0;
            for $date (sort keys %dates) {
                if (defined($level{$host}{$disk}{$date})
			&& $level{$host}{$disk}{$date} =~ /0/) {
                    $level{$host}{$disk}{'0000NML0'} += 1;
                }
            }
        }
    }
}

# Runs to the last level 0
if ($opt_togo0) {
    for $host (sort keys %disks) {
        for $disk (sort keys %{$disks{$host}}) {
            $level{$host}{$disk}{'0000TOGO'} = 0;
            my $togo=0;
            for $date (sort keys %dates) {
                if (defined($level{$host}{$disk}{$date})
                	&& $level{$host}{$disk}{$date} =~ /0/) {
                    $level{$host}{$disk}{'0000TOGO'} = $togo;
                }
                $togo++;
            }
        }
    }
}

unless ($opt_skipmissed)
# touch all the dates just in case whole days were missed.
{
    my ($start, $finish) = 
	map {
	    my($y,$m,$d) = /(....)(..)(..)/;
	    POSIX::mktime(0,0,0,$d,$m-1,$y-1900);
	} (sort keys %dates)[0,-1];

    # Special case of only one date
    if (defined($finish)) {
	while ($start < $finish) {
	    my @l = localtime $start;
	    $dates{sprintf("%d%02d%02d", 1900+$l[5], $l[4]+1, $l[3])}++;
	    $start += 86400;
	}
    }
}

#Add the "last" entry    
$dates{"0000LAST"}=1 if ($opt_last);

#Add the "Number of Level 0s" entry
$dates{"0000NML0"}=1 if ($opt_num0);

#Add the "Runs to go" entry
$dates{"0000TOGO"}=1 if ($opt_togo0);

# make formats

sub row {
    my ($host, $disk, @cols) = @_;
    $host = substr($host, 0, $opt_hostwidth);
    $disk = substr($disk, 0, $opt_diskwidth);
    print
	sprintf("%-0${opt_hostwidth}s %-0${opt_diskwidth}s ", $host, $disk) .
	join(" ", map(sprintf("%-2s ", $_), @cols)) .
	"\n";
}

sub fmt_levels {
    my ($host, $disk, $date) = @_;
    my $levels = $level{$host}{$disk}{$date};

    # no dumps on this date
    $levels = '-' unless defined($levels);

    return substr($levels, -2);
}

# header
row('',     'date', map((/....(..) .. /x)[0], sort keys %dates));
row('host', 'disk', map((/.... .. (..)/x)[0], sort keys %dates));

# body
for $host (sort keys %disks) {
    for $disk (sort keys %{$disks{$host}}) {
	row($host, $disk,
	    map(fmt_levels($host, $disk, $_), sort keys %dates));
    }
}