#!/usr/bin/env perl

use strict;
use warnings FATAL => qw( all );

use vars qw( $VERSION );
$VERSION = '0.01';

use DBI;
use Nebulous::Server::SQL;
use URI::file;
use SQL::Interp qw( sql_interp );
use URI;

use Getopt::Long qw( GetOptions :config auto_help auto_version );
use Pod::Usage qw( pod2usage );

my ($host, $state, $xattr, $note, $db, $dbhost, $dbpass, $dbuser, $debug, $volume);

# $db     = $ENV{'NEB_DB'} unless $db;
# $dbhost = $ENV{'NEB_DBHOST'} || 'localhost';
# $dbuser = $ENV{'NEB_USER'} unless $dbuser;
# $dbpass = $ENV{'NEB_PASS'} unless $dbpass;

# my $conffile = "/home/panstarrs/ipp/ippconfig/nebulous.config";
my $conffile = "/home/panstarrs/ippps1/ippconfig/nebulous.config";

if (-e $conffile) {
    # printf STDERR "found config\n";
    open(IN,$conffile);
    while(<IN>) {
	chomp;
	if ($_ =~ /NEB_DB\s+/)  { $db     = (split /\s+/,$_)[2]; next; }
	if ($_ =~ /NEB_DBHOST/) { $dbhost = (split /\s+/,$_)[2]; next; }
	if ($_ =~ /NEB_USER/)   { $dbuser = (split /\s+/,$_)[2]; next; }
	if ($_ =~ /NEB_PASS/)   { $dbpass = (split /\s+/,$_)[2]; next; }
    }
    close(IN);
    # print STDERR  "$db $dbhost $dbuser $dbpass\n";
}

GetOptions(
    'host=s'         => \$host,
    'state=s'        => \$state,
    'xattr=s'        => \$xattr,
    'note=s'         => \$note,
    'db=s'           => \$db,
    'dbhost=s'       => \$dbhost,
    'dbuser=s'       => \$dbuser,
    'dbpass=s'       => \$dbpass,
    'volume'         => \$volume,
    ) || pod2usage( 2 );

# if --host and --state are not supplied, look for positional arguments
my $Narg = @ARGV;

if (($Narg == 1) && (!defined($host)) && (!defined($state))) {
    # neb-host (host) : return volume into for specified host
    $host = shift(@ARGV);
}
if (($Narg == 2) && (!defined($host)) && (!defined($state))) {
    $host = shift(@ARGV);
    $state = shift(@ARGV);
}

pod2usage( -msg => "Unknown option: @ARGV", -exitval => 2 ) if @ARGV;
pod2usage( -msg => "Cannot configure nebulous database", -exitval => 2) unless
    defined $db && defined $dbhost && defined $dbuser && defined $dbpass;

my $dbh = DBI->connect(
    "DBI:mysql:database=$db:host=$dbhost",
    $dbuser,
    $dbpass,
    {
        RaiseError => 1,
        PrintError => 0,
        AutoCommit => 0,
    },
    );

if (defined($host) && defined($state)) {

    # strip dangerous characters
    if (defined($note)) { $note =~ s/'/ /g; $note =~ s/;/ /g; $note =~ s/"/ /g; }
    if (defined($host)) { $host =~ s/'/ /g; $host =~ s/;/ /g; $host =~ s/"/ /g; }

    if (($state ne 'up') && ($state ne 'down') && ($state ne 'repair')) { die "Unknown state '$state'"; }
    
    # for 'up', we want to exclude set volumes with xattr = 3, and possible issue an equivalent of 'repair' for xattr = 3
    # for 'up' and 'repair', we should exclude volumes with xattr = 5
    # for 'down', all volumes are ok
    
    # up:
    # UPDATE volume SET (allocate = 1, available = 1, note = '$note') WHERE (name = '$host') AND (xattr != 3) AND (xattr != 5)
    # UPDATE volume SET (allocate = 0, available = 1, note = '$note') WHERE (name = '$host') AND (xattr = 3)

    # repair:
    # UPDATE volume SET (allocate = 0, available = 1, note = '$note') WHERE (name = '$host') AND (xattr != 5)

    # down:
    # UPDATE volume SET (allocate = 0, available = 0, note = '$note') WHERE (name = '$host')

    # $volume is a boolean which means 'use' the supplied host as the 'name' field
    my $hostWhere;
    if (defined($volume)) {
	$hostWhere = "(name = '$host')";
    } else {
	$hostWhere = "(host = '$host')";
    }

    my ($selections, $constraint);

    if ($state eq 'up')     { 
	$selections = "allocate = 1, available = 1";
	$constraint = "$hostWhere AND (xattr != 3) AND (xattr != 5)";
    }

    if ($state eq 'repair')     { 
	$selections = "allocate = 0, available = 1";
	$constraint = "$hostWhere AND (xattr != 5)";
    }

    if ($state eq 'down')     { 
	$selections = "allocate = 0, available = 0";
	$constraint = "$hostWhere";
    }

    &modify_hosts ($selections, $constraint, $note);

    ## for 'up', make a second call to set hosts with (xattr = 3) to 'repair':
    if ($state eq 'up')     { 
	$selections = "allocate = 0, available = 1";
	$constraint = "$hostWhere AND (xattr = 3)";

	&modify_hosts ($selections, $constraint, $note);
    }
    exit 0;
}

my $sql = Nebulous::Server::SQL->new();
my ($q, @bind);

if (defined($host)) {
    if (defined($xattr)) {
	$q = $sql->get_volumes . " WHERE v.host = '$host' AND v.xattr = $xattr ORDER BY v.host, v.name";
    } else {
	$q = $sql->get_volumes . " WHERE v.host = '$host' ORDER BY v.host, v.name";
    }
} else {
    if (defined($xattr)) {
	$q = $sql->get_volumes . " WHERE v.xattr = $xattr ORDER BY v.host, v.name";
    } else {
	$q = $sql->get_volumes . " ORDER BY v.host, v.name";
    }
}

warn "$q\n" if $debug;
my $query = $dbh->prepare($q);
$query->execute(@bind);
$dbh->commit;

my $format  = "%-15s %-15s %-7s %-8s %-9s %-5s %15s %s\n";
my @columns = qw(host name mounted allocate available xattr last_modified note);
printf($format, @columns);

while (my $row = $query->fetchrow_hashref) {
    unless ((exists($$row{ 'note' })) && (defined($$row{ 'note' }))) {
	$$row{ 'note' } = '-';
    }
    printf($format, @$row{@columns});
}

sub modify_hosts {

    my $selections = $_[0];
    my $constraint = $_[1];

    my $note = $_[2];
    # print "NOTE: $note\n";

    if (defined $note) { $selections = $selections . ", note = '$note'"; }

    {
	my $sqlLine = "UPDATE volume SET $selections WHERE $constraint";
	# print "SQL: $sqlLine\n";
	my $query = $dbh->prepare($sqlLine);
	$query->execute() or die "error from Nebulous database\n";
	$dbh->commit;
    }
    
    { 
	my $sqlLine = "UPDATE mountedvol SET $selections WHERE $constraint";
	# print "SQL: $sqlLine\n";
	my $query = $dbh->prepare($sqlLine);
	$query->execute() or die "error from Nebulous database\n";
	$dbh->commit;
    }
}


__END__



=pod

=head1 NAME

neb-host - simply manage the state of Nebulous storage volumes

=head1 SYNOPSIS

    neb-host [<nebulous host> <nebulous state>]
    [--note '<status note>']
    [--host <nebulous host>] [--state <up|down|repair>]
    [--xattr N]
    [--volume]
    [--dbhost <database host>] [--db <database name>]
    [--dbuser <database user>] [--dbpass <database password>]


=head1 DESCRIPTION

This program manages a Nebulous server's list of known storage volumes.  It 
is designed to provide an easy interface to toggle hosts.  If no options are 
given, the host and state are read directly from the command line.  If nothing
is specified, prints out the table of currently mounted volumes in nebulous.
If the state is not specified, but a host is supplied, the table of values is restricted 
to the specified host.

=head1 OPTIONS

=over 4

=item * --note <status note>

Set a status note for the specified volume.

=item * --host <nebulous host>

Name of the host on which the nebulous volume(s) reside.

=item * --state <up|down|repair>

Set the state of the nebulous volume:
    up     Allocate = 1, Available = 1
    down   Allocate = 0, Available = 0
    repair Allocate = 0, Available = 1

=item * --xattr <N>

Limit the listed volumes to the specified xattr value

=item * --volume

Set a particular volume to the given state.  This is a Boolean option,
and uses the value of --host as the volume name to update.

=item * --db <database name>

Name of database (C<namespace>) to create tables in.

Optional if the appropriate environment variable is set.

=item * --dbhost <database host>

Host name where the database resides.

Defaults to C<localhost>.

=item * --dbuser <database username>

Username to authenticate with.

Optional if the appropriate environment variable is set.

=item * --dbpass <database password>

Password to authenticate with.

Optional if the appropriate environment variable is set.

=back

=head1 ENVIRONMENT

These environment variables may be used in place of the specified command line
options.  All command line option will override the corresponding environment
value.

=over 4

=item * C<NEB_DB>

Equivalent to --db|-d

=item * C<NEB_HOST>

Equivalent to --host

=item * C<NEB_USER>

Equivalent to --user|-u

=item * C<NEB_PASS>

Equivalent to --pass|-p 

=back

=head1 COPYRIGHT

Copyright (C) 2008  Joshua Hoblitt.  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.

The full text of the license can be found in the L<perlgpl> Pod as supplied
with Perl 5.8.1 and later.

=head1 SEE ALSO

L<Nebulous::Server>, L<neb-voladm>, L<neb-voladd>

=cut
