# Inventory plugin
package Easy::Plugin::Inventory;

use strict;
use Easy::Item;
use Easy::Helpers;
use FileHandle;
use CSL::DBs;
use CSL::Pg;
#use CSL::PlRPC_Krb5;
use DBI;
use Class::Phrasebook::SQL;

use Data::Dumper;

use CSL::Nagios2;
use Nagios::Config;
use CSL::Sushi;
use Sushi::BackupInfo;

sub get_query_flags
{
    return (
	    '-b' => \&do_backups,
	    '-i' => \&do_invnum,
	    '-s' => \&do_serialnum,
	    '-r' => \&do_room, 
	    '-l' => \&do_lock,
	    '-k' => \&do_keyring,
	    '-m' => \&do_machine,
	    '-mh' => \&do_machine_hard,
	    '-o' => \&do_order,
	   );
}

sub get_query_regexps
{
    return ('[CSUcsu][cC\d]\d{5}' => \&do_invnum,
	    '\d{6}' => \&do_order,
	    '!!DEFAULT!!' => \&do_machine);
}

sub get_usage
{
    return (
	    '-b' => 'Backup information for host',
	    '-i <invent number>' => "Parts by inventory (C, S, U) numbers",
	    '-s <serial number>' => "Parts by serial numbers",
	    '-r <room>' => "Machines in a room", 
	    '-l <"lock">' => "Machines in a \"lock\" (2360 only)",
	    '-k <room>' => "Keyrings in a room",
	    '-m <machine>' => "Soft match machine (default)",
	    '-mh <machine>' => "Hard match machine",
	    '-o <order number>' => "Parts by ponum",
	   );
}

sub do_machine
{
    my @machines = @_;

    #&do_machine_hard(map { Easy::Helpers::fqdn($_) } @machines);
    &do_machine_hard(@machines);
}

sub do_machine_hard
{
    my @machines = @_;

    # connect to the database, load the queries
    my ($dbh, $pb) = &_db_connect;

    # Loop through machines
    my @ret;
    foreach my $machine (@machines)
    {
    	# Get the general attributes: cpuname, room and the like
    	my $query = $dbh->prepare($pb->get("machine-hard"));
    	$query->execute($machine);
    	my $attributes = $query->fetchrow_hashref;
	
    	# If it's not in the database, try the FQDN, unless we have that...
    	unless ($attributes)
    	{
            my $mach2 = Easy::Helpers::fqdn($machine);
            if ($mach2 ne $machine) {
                push @machines, Easy::Helpers::fqdn($mach2);
                next;
            }
            # we shouldn't be here unless we already had the fqdn
    	    push @ret, new Easy::Item title => $machine,
    	                              text => "Not found";
    	    next;
    	}

#  
# added from nagios data - craft Mon Apr 25 15:39:29 CDT 2005
#
        $attributes->{'monitor type'} = Nagios::Config->LoadFreshData($machine)->{$machine}->{Data}->{Monitor}->{type};
        if ($attributes->{'monitor type'} eq 'unsup') {
            $attributes->{'monitor type'} = '';
        }

        ($attributes->{'monitor type'}) || ($attributes->{'monitor type'} = "<undef>");

    	# Try and get the network that the host lives on, assuming
    	# it has a valid ip address in DNS
    	my $ip = Easy::Helpers::resolveIPAddress($machine);
    	if($ip)
    	  {
    	    # Okay, we have a valid IP, so set that, and lookup the network's name
    	    $attributes->{'ip address'} = $ip;
    
    	    # Lookup the network
    	    my $network = Easy::Helpers::lookupHostNetwork($ip, $dbh);
    	    $attributes->{'network'} = $network if $network;
    	  }
    
    	# Do a little renaming
	$attributes->{'prime user'} = $attributes->{prime_user};
	$attributes->{notes} = $attributes->{mnotes};
	$attributes->{dumps} = $attributes->{bupolicy};

	# Fill in billtype
	$attributes->{billtype} = (undef,
				   "full",
				   "minimal",
				   "none",
				   "infrastructure",
				   "instructional",
				   "not billable",
				   "cluster")[$attributes->{billtype}];

	# Boolify condor
	$attributes->{condor} = $attributes->{condor} ? "yes" : "no";
	
	# Get a list of authorized administrators
	my $auth_query = $dbh->prepare($pb->get("auth"));
	$auth_query->execute($machine, $machine, $machine);
	my $authorized = join(", ", map { $_->[0] } @{$auth_query->fetchall_arrayref});
	$attributes->{authorized} = $authorized;

	# Get a list of parts
	my $parts_query = $dbh->prepare($pb->get("parts"));
	$parts_query->execute($machine);
	my $parts = $parts_query->fetchall_arrayref;

	# Order attributes
	my $attributes_order = ['cpuname',
				'prime user',
				'notes',
				'authorized',
				'dumps',
				'room',
				'lock',
				'os',
				'billtype',
				'condor',
				'monitor type',
                                'status',
				'network',
				'ip address'
	    			       ];

    # See if it's a printer
    my $foundprinter;
    my $foundcpu;
    my $lanierprinter = 0;
    my $printer_part;
    foreach my $part (@$parts) {
        # if we find a CPU part, assume it is a computer and stop looping.
	if ($part->[3] eq "CPU") {
            $foundcpu = 1;
            last;
        }
        if ($part->[3] eq "PRI") {
            $foundprinter = 1;
            $printer_part = $part;
	    if ($part->[4] =~ /lanier/i || $part->[4] =~ /ricoh/i) {
		# Ricoh and Lanier are the same company
	    	$lanierprinter = 1;
	    }
        }
    }
    if (($foundprinter) && (!$foundcpu)) {
	(my $printer = $machine) =~ s/\..*//;
    	if ($lanierprinter) {
	    # Pagecount for Lanier devices
	    my $count = 0;
	    # do NOT test for open failure...it will detect failure 
	    # even though pipe will open successfully  :(
	    #open (COUNT, "/usr/bin/ssh -n print.cs.wisc.edu /usr/bin/snmpget -c CSL -v 1 $printer .1.3.6.1.2.1.43.10.2.1.4.1.1 |");
	    # running local until new printer gateway is up and running
	    open (COUNT, "/usr/bin/snmpget -c CSL -v 1 $printer .1.3.6.1.2.1.43.10.2.1.4.1.1 |");
	    while (<COUNT>) {
	    	chomp;
		if (/Counter32:/) {
		    $count = $_;
		    $count =~ s/.*Counter32: //;
		}
	    }
	    close(COUNT);
	    $attributes->{'page count'} = $count;
	} else {
	    # Pagecount for other printers
	    my $fh = new FileHandle "/s/std/bin/lpq -P$printer\@calvin -L|";
	    my @counts = map { chomp; s/\D+(\d+).*/$1/; $_} grep(/pagecounter/, <$fh>);
	    $attributes->{'page count'} = $counts[-1];
	}

	    # Toner type
	    # Since it now uses postgres, we can use the existing connection
	    my $query = $dbh->prepare("select type from printer_accounting.toner_types where model = ?");
	    $query->execute($printer_part->[0]);
	    ($attributes->{'toner type'}) = $query->fetchrow;
	    $query->finish;

	    # Adjust attributes
	    $attributes_order = ['cpuname',
				 'toner type',
				 'page count',
				 'room',
				 'lock',
				 'billtype'];
	}
	    
	# Return the result
	push @ret, new Easy::Item title => $machine,  
	                          attributes => $attributes, 
	                          attributes_order => $attributes_order,
	                          headings => ['modelnum',
					       'invnum',
					       'serialnum',
					       'type',
					       'mdesc'], 
	                          list => $parts;
    }

    return @ret;
}

sub do_invnum
{
    my @parts = @_;

    # Connect to the database
    my ($dbh, $pb) = &_db_connect;

    # Loop throught parts
    my @ret;
    foreach my $part (@parts)
    {
	# Get general attributes
	$part = uc($part);
	my $query = $dbh->prepare($pb->get("part"));
	$query->execute($part);
	my $attributes = $query->fetchrow_hashref;
	
	# Fail if it's no there
	unless ($attributes)
	{
	    push @ret, new Easy::Item title => $part,
	                              text => "Not found";
	    next;
	}

	# Reformat warrenty, purchase, cost and vendor
	$attributes->{warrentytill} =~ s/\s+\S+$//;
	$attributes->{purchasedate} =~ s/\s+\S+$//;
	$attributes->{vendor} =~ s/(\S)(\S+)/$1 . lc($2)/ge;
	$attributes->{cost} = "\$" . $attributes->{cost};

	# A little renaming
	$attributes->{warranty} = $attributes->{warrentytill};
	$attributes->{purchase} = $attributes->{purchasedate};

	# Do modelnum, invnum, serialnum, type and mdesc as a list
	my $headings = ["modelnum",
			"invnum",
			"serialnum",
			"type",
			"mdesc"];
	my %attributes = %$attributes;
	my $list = [ [ @attributes{@$headings} ] ];
	delete @attributes{@$headings};
	$attributes = \%attributes;

	# Return the result
	push @ret, new Easy::Item title => $part,
	                          attributes => $attributes,
	                          attributes_order => ['cpuname',
						       'vendor',
						       'warranty',
						       'purchase',
						       'room',
						       'ponum',
						       'cost'],
	                          headings => $headings,
	                          list => $list;
    }

    return @ret;
}

sub do_serialnum
{
    my @parts = @_;
    
    # Connect to the database
    my ($dbh, $pb) = &_db_connect;

    # Loop over parts
    my @ret;
    foreach my $part (@parts)
    {
	# Get general attributes
	$part = uc($part);
	my $query = $dbh->prepare($pb->get("serial"));
	$query->execute($part);
	my $attributes = $query->fetchrow_hashref;
	
	# Fail if it's not there
	unless ($attributes)
	{
	    push @ret, new Easy::Item title => $part,
                                      text => "Not found";
	    next;
	}

	# Reformat warrenty, purchase and vendor
	$attributes->{warrentytill} =~ s/\s+\S+$//;
	$attributes->{purchasedate} =~ s/\s+\S+$//;
	$attributes->{vendor} =~ s/(\S)(\S+)/$1 . lc($2)/ge;
	$attributes->{cost} = "\$" . $attributes->{cost};

	# A little renaming
	$attributes->{warranty} = $attributes->{warrentytill};
	$attributes->{purchase} = $attributes->{purchasedate};

	# Do modelnum, invnum, serialnum, type and mdesc as a list
	my $headings = ["modelnum",
			"invnum",
			"serialnum",
			"type",
			"mdesc"];
	my %attributes = %$attributes;
	my $list = [ [ @attributes{@$headings} ] ];
	delete @attributes{@$headings};
	$attributes = \%attributes;

	# Return the result
	push @ret, new Easy::Item title => $part,
	                          attributes => $attributes,
	                          attributes_order => ['cpuname',
						       'vendor',
						       'warranty',
						       'purchase',
						       'room',
						       'ponum',
						       'cost'],
	                          headings => $headings,
	                          list => $list;
    }

    return @ret;
}

sub do_room
{
    my @rooms = @_;
    
    # Connect to the database
    my ($dbh, $pb) = &_db_connect;

    # Loop over rooms
    my @ret;
    foreach my $room (@rooms)
    {
	# Get a list of machines
	my $query = $dbh->prepare($pb->get("room"));
	$query->execute($room);
	
	# Get the CPU mdesc if there's no mnote
	my @machines;
	while (my $row = $query->fetchrow_hashref)
	{
	    unless ($row->{mnotes})
	    {
		my $part_query = $dbh->prepare($pb->get("parts"));
		$part_query->execute($row->{cpuname});
		while (my $part = $part_query->fetchrow_hashref)
		{
		    $row->{mnotes} = $part->{mdesc} unless $row->{mnotes};
		    next unless $part->{type} eq "CPU";
		    $row->{mnotes} = $part->{mdesc};
		}
	    }
	    
	    push @machines, [$row->{cpuname},
			     $row->{lock},
			     $row->{mnotes}];
	}

	# Return the result
	push @ret, new Easy::Item title => $room, 
	                          headings => ["machine",
					       "lock",
					       "desc"],
	                          list => \@machines;
    }

    return @ret;
}

sub do_keyring
{
    my (@rooms) = @_;

    # Connect to and configure proxy
    my ($dbh, $pb) = &_db_connect;

    # Loop over rooms
    my @ret;
    foreach my $room (@rooms)
    {
	my $query = $dbh->prepare($pb->get("keyring"));
	$query->execute($room);

	my %rings = map { $_->[0] =~ s/(.{2}).*/$1/; 
			  $_->[0] => 1 } @{$query->fetchall_arrayref};
	
	my $text = "Keyrings: " . join(", ", keys %rings);
	
	push @ret, new Easy::Item title => $room,
	                          text => $text;
    }

    return @ret;
}

sub do_lock
{
    my (@locks) = @_;
    
    # Connect to the database
    my ($dbh, $pb) = &_db_connect;

    # Loop over locks
    my @ret;
    foreach my $lock (@locks)
    {
	# Get a list of machines
	my $query = $dbh->prepare("select cpuname, lock, mnotes from machines where room = '2360' and lower(lock) like ? order by upper(lock), cpuname");
	$query->execute(lc($lock) . "%");

	# Get the CPU mdesc if there's no mnote
	my @machines;
	while (my $row = $query->fetchrow_hashref)
	{
	    unless ($row->{mnotes})
	    {
		my $part_query = $dbh->prepare($pb->get("parts"));
		$part_query->execute($row->{cpuname});
		while (my $part = $part_query->fetchrow_hashref)
		{
		    $row->{mnotes} = $part->{mdesc} unless $row->{mnotes};
		    next unless $part->{type} eq "CPU";
		    $row->{mnotes} = $part->{mdesc};
		    last;
		}
		$part_query->finish;
	    }
	    
	    push @machines, [$row->{cpuname},
			     $row->{lock},
			     $row->{mnotes}];
	}

	# Return the result
	push @ret, new Easy::Item title => $lock,
                	          headings => ["machine", "lock", "desc"],
	                          list => \@machines;
    }

    return @ret;
}

sub do_order
{
    my @ponums = @_;

    # Connect to the database
    my ($dbh, $pb) = &_db_connect;

    my @ret;
    foreach my $ponum (@ponums)
    {
	my $query = $dbh->prepare("select p.cpuname, m.modelnum, p.invnum, m.type, m.mdesc from parts p, models m where p.modelnum = m.modelnum and p.ponum = ? order by p.invnum");
	$query->execute($ponum);

	my $parts = $query->fetchall_arrayref;

	unless (@$parts)
	{
	    push @ret, new Easy::Item title => $ponum,
                                      text => "Not found";
	    next;
	}

	push @ret, new Easy::Item title => $ponum,
                	          headings => ["cpuname", "modelnum", 
					       "invnum", "type",
					       "mdesc"],
	                          list => $parts;
    }

    return @ret;	
}

sub do_backups
{
  my @machines = @_;

  my (@ret, $backups, $attributes, $host);

  my $headings = [
		  'Atom Type',
		  'Element',
		  'Description',
		 ];

  foreach my $machine (@machines) {
    $host = Easy::Helpers::fqdn($machine);
    $host = $machine unless defined $host;
    $backups = Sushi::BackupInfo->getBackups ($machine);
    if (!defined $backups) {
      push @ret, new Easy::Item title => $machine,
	                        text => "Not found";
      next;
    }

    # Convert array of hashrefs to array of arrays.
    $attributes = [];
    push @$attributes, map {[ $_->{type},
			      $_->{element},
			      $_->{description},
			    ]} @$backups;

    push @ret, new Easy::Item title => $host,
                              attributes => { cpuname => $host },
                              attributes_order => ['cpuname'],
			      headings => $headings,
			      list => $attributes;
  }
  @ret;
}

# Set up the database connection and phrasebook here
# in one place...instead of many times up there
sub _db_connect {
  my $dbh = DBI->connect($CSL::DBs::csdb)
    or die "Could not connect to CSDB: $!";
  $dbh->do("set search_path to csdb");
  $dbh->{ChopBlanks} = 1;

  # I will have to make this a CSL-ism somehow
  my $pb = new Class::Phrasebook::SQL(undef, "/s/lab/common/perl5/Easy/easy.pbk")
    or die "Could not open SQL Phrasebook: $!";
  $pb->load("easy_queries")
    or die "Could not load queries, check your Phrasebook XML";

  return ($dbh, $pb);
}

1;

