#!/usr/bin/perl

#        Copyright (C) 2012 - 25th-floor - Operating Custom Solutions      
#                        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<B7>
# Place, Suite 330, Boston, MA  02111-1307  USA

use strict; 
use warnings;

use DBI;
use DBD::Pg qw/:pg_types/;
use Getopt::Long qw(:config no_ignore_case);


my %allowed_intervals = (
    m      => 'month',
    month  => 'month',
    months => 'month',
    y      => 'year',
    year   => 'year',
    years  => 'year',
    d      => 'day',
    day    => 'day',
    days   => 'day',
);

# setting default connection options

my %options = (
    db   => 'postgres',
    host => 'localhost',
    port => 5432,
    user => 'postgres',
);

# getting options

GetOptions(
    'd|db|database=s'      => \$options{db},
    'e|expect=s'        => \$options{expect},
    'h|usage|help|?'    => \$options{help},
    'H|hostname|host=s' => \$options{host},
    'P|port=i'          => \$options{port},
    't|tablename=s'     => \$options{tablename},
    'U|user=s'          => \$options{user},
    'V|version'         => \$options{version},
    'i|interval=s'      => \$options{interval},
    'p|password=s'      => \$options{password},
);

usage() if defined $options{help};
version() if defined $options{version};

unless(defined $options{tablename}) {
    printf("UNKNOWN: missing --tablename option or file not found\n");
    exit 3;
}

if (defined $options{expect}){
    if ($options{expect} =~ /^(-{0,1}\d+)$/) {
        $options{expect} = $1;
    } else {
        printf "UNKNOWN: Invalid expected number of intervals\n";
        exit 3;
    }
} else {
    $options{expect} = 1;
}

if (defined $options{interval}){
    unless(defined $allowed_intervals{$options{interval}}) {
	printf("UNKNOWN: unknown setting for --interval: [%s]\n", $options{interval});
        exit 3;
    }
} else { 
    $options{interval} = 'd'; 
}

my $inter = $options{expect} . $options{interval};
my $dbh;
my $ret;

eval {
    $dbh = DBI->connect(
        sprintf('dbi:Pg:dbname=%s;host=%s;port=%s', @options{qw/db host port/}),
        $options{user}, $options{password},
        {
            PrintError => 0,
            RaiseError => 1,
            AutoCommit => 0,
        },
    );

    my $sth = $dbh->prepare("
        SELECT (CASE LENGTH(num)
                   WHEN 8 THEN (TO_DATE(num, 'YYYYMMDD') >= (CURRENT_DATE + :interv::INTERVAL)) 
                   WHEN 6 THEN (TO_DATE(num, 'YYYYMM') >= TO_DATE((CURRENT_DATE + :interv::INTERVAL)::TEXT,'YYYY-MM')) 
                   ELSE        (TO_DATE(num, 'YYYY') >= TO_DATE((CURRENT_DATE + :interv::INTERVAL)::TEXT,'YYYY')) 
                END) AS check_date
        FROM   (SELECT MAX(SUBSTRING((c.oid::pg_catalog.regclass)::TEXT FROM E'[[:digit:]]+\$')) AS num
                FROM   pg_catalog.pg_class c
                JOIN   pg_catalog.pg_inherits i ON (c.oid = i.inhrelid)
                WHERE  i.inhparent = (SELECT oid
                                      FROM   pg_class
                                      WHERE  relname = :table)) AS foo
    ");

    $sth->bind_param(':table', $options{tablename}, { pg_types => PG_NAME });
    $sth->bind_param(':interv', $inter, { pg_types => PG_INTERVAL });
    $sth->execute;
    $sth->bind_columns(\$ret);
    $sth->fetch;
    $sth->finish;
};

if ($@) {
    1 while chomp $@;
    printf "UNKNOWN: error while executing statement, please check your connection options (check STDERR for full error message)!\n";
    warn $@, "\n";

    $dbh->disconnect if defined $dbh and $dbh->ping;
    exit 3;
}

$dbh->disconnect;

if (!defined $ret) {
    printf "UNKNOWN: looks like we could'nt find the table, please check tablename [%s] and other connection options!\n",
        $options{tablename};
    exit 3;
} elsif ($ret) {
    printf "OK: partitions until current date + (%s) exist.\n", $inter;
    exit 0;
} else {
    printf "CRITICAL: partitions until current date + (%s) do not exist!\n", $inter;
    exit 2;
}

sub usage {
    print do { local $/; <DATA> };
    exit 0;
}

sub version {
    print q/check_partitions 1.0

Copyright (C) 2012 25th-floor - Operating Custom Solutions. All rights reserved.
Please proceed to 25th-floor.com for more information.
/;

    exit 0;
}

__DATA__
Usage:
check_partitions -t TABLENAME [OPTIONS]

Short version:          Long version:
+++++++++++++++++++++|++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Help options:
  -h, -?             |  --help, --usage        show this help, then exit
  -v, -V             |  --version              output version & info, then exit

Check options:
  -e  NUMBER         |  --expect=NUMBER        expected number of INTERVALS 
                        (how many 'Intervals' in the future the newest partition
                         should preexist) Range: -32768 to 32767 (default is 1)

  -i  INTERVAL       |  --interval=INTERVAL    Check partitions for an interval
                        (default is 'd') 'm','mon','month','months','d','day',
                        'days' (=day),'y','year','years' (=year) are valid 
  
  -p  PASSWORD       |  --password=PASSWORD sets the password for the connection
                        EXTREMELY INSECURE!!! We suggest using a password file!
  
  -t  TABLENAME      |  --tablename=TABLENAME  name of main partiton whose child
                        partitions get checked  -> this option MUST be set!

Connection options:
  -d, -db  DATABASE  |  --database=DATABASE    databasename to connect to 
                        (default: 'postgres')

  -H  HOSTNAME       |  --host[name]=HOSTNAME  database server host
                        (default: 'localhost')

  -P  PORT           |  --port=PORT            database server port 
                        (default: '5432')

  -U  USER           |  --user=USER            database user name 
                        (default: 'postgres')

Check_partitions is thought as an check if partitions are pre generated (due to 
an explicit date). The plugin only handles partitions whose syntax are like 
table_name_prefix_YYYYMMDD | fooYYYYMM | 123blubbYYYY or just end with a date 
where there are no characters between the digits.

Usage Example:  
   root\@host:./check_partitions -t emp -H localhost -d postgres -e 4 --i days
   OK: partitions until current date + (4days) exist.
   root\@host:

If a more secure handling of the password is needed, please handle the password
with a password file. 
Please visit http://www.postgresql.org/docs/devel/static/libpq-pgpass.html for 
more information to the password file handling.

Visit and contact http://www.25th-floor.com when requesting additional 
information to the plugin.
