#!/usr/bin/perl -w

###########################################################################
#
# Baracus build and boot management framework
#
# Copyright (C) 2010 Novell, Inc, 404 Wyman Street, Waltham, MA 02451, USA.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the Artistic License 2.0, as published
# by the Perl Foundation, or the GNU General Public License 2.0
# as published by the Free Software Foundation; your choice.
#
# 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.  Both the Artistic
# Licesnse and the GPL License referenced have clauses with more details.
#
# You should have received a copy of the licenses mentioned
# along with this program; if not, write to:
#
# FSF, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110, USA.
# The Perl Foundation, 6832 Mulderstraat, Grand Ledge, MI 48837, USA.
#
###########################################################################

use strict;
use warnings;

use Getopt::Long qw( :config pass_through );
use Pod::Usage;

use lib "/usr/share/baracus/perl";

use BaracusPower qw( :subs );
use BaracusDB;
use BaracusSql;
use BaracusCore qw( :subs );
use BaracusAux  qw( :subs );

=pod

=head1 NAME

B<bapower> - baracus tool for BMC power management

=head1 SYNOPSIS
 
B<bapower> E<lt>commandE<gt> [options]

Where E<lt>commandE<gt> is

  off    --mac <mac> | --hostname <host>  power off hardware.
  on     --mac <mac> | --hostname <host>  power on hardware.
  cycle  --mac <mac> | --hostname <host>  power cycle hardware.
  status --mac <mac> | --hostname <host>  power status.
  remove --mac <mac> | --hostname <host>  Remove BMC entry

  add    --mac <mac> [--hostname <host>] --bmcaddr=<bmc ip> --ctype <BMC type> --login <username> --passwd <password>
         Add BMC entry

  list   [ --mac <mac> | --hostname <host> ] List BMC entries with optional filter

  help   This 'Usage' summary message.
  man    Detailed man page.

Use 'man' or 'help <command>' for more details.

=head1 DESCRIPTION

This tool allows for power management via plugable BMC control modules

=head1 OPTIONS

=over 4

=item -v --verbose  Be verbose with output

=back

=cut

our $LASTERROR="";

my $man      = 0;
my $help     = 0;
my $debug    = 0;
my $verbose  = 0;
my $quiet    = 0;

my %cmds = (
            'on'     => \&on,
            'off'    => \&off,
            'cycle'  => \&cycle,
            'status' => \&status,
            'add'    => \&add_stub,
            'remove' => \&remove_stub,
            'list'   => \&list_stub,
            'help'   => \&myhelp,
            'man'    => \&myman,
            );

GetOptions(
           'debug'          => \$debug,
           'quiet'          => \$quiet,
           'help|?'         => \$help,
           'man'            => \$man,
           'verbose'        => \$verbose,
           );

&myman()  if $man;
&myhelp() if $help;
&myhelp() unless ( scalar @ARGV );
if ($ARGV[0] eq "man"  ) { &myman(); }
if ($ARGV[0] eq "help" ) { shift @ARGV; &myhelp(@ARGV); }

my $dbname = "baracus";
my $dbrole = $dbname;

my $uid = BaracusDB::su_user( $dbrole );
die BaracusDB::errstr unless ( defined $uid );

my $dbh = BaracusDB::connect_db( $dbname, $dbrole );
die BaracusDB::errstr unless( $dbh );

my $status = &mymain(@ARGV);

die BaracusDB::errstr unless BaracusDB::disconnect_db( $dbh );

print $LASTERROR if $status;

exit $status;

die "DOES NOT EXECUTE";

###############################################################################
#
# subroutines
#

sub mymain
{
    my $command = shift;

    my $deviceid = "";
    my $bmc = {};

    $bmc->{'dbh'} = $dbh;

    printf "Executing $command with \"@_\".\n" if $debug;

    $command = lc $command;
    return 1 unless &mycheck_command( $command );

    if ( $command eq "list" ) {
        # bypass real mac hostname checks to support filtering
        return $cmds{list}( $bmc, @_ );
    }

    ($deviceid, @_) = &arg_handler( $bmc, $command, @_ );
    unless (defined $deviceid) {
        $LASTERROR = "invalid --mac or --hostname usage\n";
        return 1;
    }
    # other arg_handler error conditions return 1
    return 1 if ( $deviceid eq "1" );

    if ( $command =~ m/^(on|off|cycle|status)$/ ) {
        my $href = &get_bmc($bmc, $deviceid);
        unless ( $href ) {
            $LASTERROR= "Unable to find entry for device with id: $deviceid\n";
            return 1;
        }
        $cmds{ $command }( $href );
    } else {
        $cmds{ $command }( $bmc, @_ );
    }

}

sub myhelp() {
    my $command = shift;

    unless ( defined $command ) {
        pod2usage( -verboase => 0,
                   -exitstatus => 0 );
    }

    $command = lc $command;
    &mycheck_command( $command );

    my $execname = $0;
    $execname = $1 if ( $0 =~ m|^.*/([^/].+)| );

    pod2usage( -msg        => "$execname $command ...\n",
               -verbose    => 99,
               -sections   => "COMMANDS/${command}.*",
               -exitstatus => 0 );
}

sub myman
{
    pod2usage( -verbose    => 2,
               -sections   => "NAME|SYNOPSIS|DESCRIPTION|OPTIONS|COMMANDS",
               -noperldoc  => 1,
               -exitstatus => 0 );
}

sub arg_handler() {
    my $bmc = shift;
    my $command = shift;

    my $deviceid = 0;

    @ARGV = @_;

    GetOptions(
               'mac=s'      => \$bmc->{'mac'},
               'hostname=s' => \$bmc->{'hostname'},
              );

    #                              =~ m/^(on|off|cycle|status|remove|list)/
    if ( scalar @ARGV and $command !~ m/^(add)$/ ) {
        printf "\nUnknown arg(s): %s\n\n", join(" ", @ARGV);
        &myhelp();
    }
    unless ( $command eq "add" ) {
        if ( ($bmc->{'mac'}) and ($bmc->{'hostname'}) ) {
            $LASTERROR = "Please use either --mac or --hostname\n";
            return 1;
        }
    }
    unless ( $bmc->{'mac'} or $bmc->{'hostname'} ) {
        $LASTERROR = "Require an id. Use --mac or --hostname\n";
        return 1;
    }

    if ($bmc->{'mac'}) {
        # check_mac exits on invalid mac string
        $bmc->{'mac'} = &check_mac( $bmc->{'mac'} );
        $deviceid = $bmc->{'mac'};
    }
    if ($bmc->{'hostname'}) {
        if ( &check_hostname( $bmc->{'hostname'} ) == 0  ) {
            $deviceid = $bmc->{'hostname'};
        } else {
            $LASTERROR = "Hostname provided is not valid: $bmc->{hostname}\n";
            return 1;
        }
    }

    return ( $deviceid, @ARGV );
}

sub mycheck_command
{
    my $command = shift;

    my $cmd_list = join ', ', (sort keys %cmds);
    unless ( defined $command ) {
        print "Requires <command> (e.g. $cmd_list)\n";
        &myhelp();
    }

    unless ( defined $cmds{ $command } ) {
        $LASTERROR = sprintf "\nInvalid <command> '$command' please use:  $cmd_list\n";
        return 0;
    }
}

sub remove_stub() {

    my $bmcref = shift;

    my $status = &remove( $bmcref );

    return $status;

}

sub add_stub() {

    my $bmcref = shift;

    @ARGV = @_;
    GetOptions(
               'ctype=s'       => \$bmcref->{'ctype'},
               'login=s'       => \$bmcref->{'login'},
               'passwd=s'      => \$bmcref->{'passwd'},
               'bmcaddr=s'     => \$bmcref->{'bmcaddr'},
               'node=s'        => \$bmcref->{'node'},
               'other=s'       => \$bmcref->{'other'},
               );

    if ( scalar @ARGV ) {
        $LASTERROR = sprintf "\nUnknown arg(s): %s\n", join(" ", @ARGV);
        return 1;
    }
    &add( $bmcref );
}

sub list_stub() {

    my $bmcref = shift;

    my $filter = "";

    @ARGV = @_;
    GetOptions(
               'filter=s'  => $filter,
               );

    if ( scalar @ARGV ) {
        $LASTERROR = sprintf "\nUnknown arg(s): %s\n", join(" ", @ARGV);
        return 1;
    }

    my $opts = { 'LASTERROR' => "", 'debug' => $debug };
    my $sth = &list_start_data( $opts, $dbh, 'power', $filter );

    unless( defined $sth ) {
        # bapower needs to get in line with the other commands
        # for main, help, and opts/debug handling
        $LASTERROR = $opts->{LASTERROR};
        return 1;
    }

    unless ($quiet) {
    print "--------------------------------------------------------------------------------------\n";
    printf "%-8s %-16s %-18s %-18s %-16s %-16s ", "ctype", "bmcaddr", "mac", "hostname", "node", "other";
    print "\n";
    print "--------------------------------------------------------------------------------------\n";
    }

    my $dbref;
    while ( $dbref = &list_next_data( $sth ) ) {
        $dbref->{'node'} =~ s/NULL//g;
        $dbref->{'other'} =~ s/NULL//g;
        printf "%-8s %-16s %-18s %-18s %-16s %-16s ",
            "$dbref->{'ctype'}", "$dbref->{'bmcaddr'}", "$dbref->{'mac'}",
                "$dbref->{'hostname'}", "$dbref->{'node'}", "$dbref->{'other'}";
        print "\n";
    }

    return 0;

}

die "ABSOLUTELY DOES NOT EXECUTE";

__END__
