#!/bin/perl -w
use strict;
use Getopt::Long;
use JSON::PP;
use Data::Dumper;
use LWP::UserAgent;
use List::Util qw[max];

use constant OK => 0;
use constant WARNING => 1;
use constant CRITICAL => 2;
use constant UNKNOWN => 3;

use vars qw {
  $verbose
  $exitstatus
};
$exitstatus = 0;

sub
Usage()
{
  print STDERR "usage: $0 -H <hostname|IP address> -U <username> -P <password> [-k|--insecure] [-v] [-h]\n";
  exit(UNKNOWN);
}

# The greatest value of the exit status prevails
sub
ExitStatus()
{
  my ($new_value) = @_;

  $exitstatus = max($exitstatus, $new_value);
}

sub
Help()
{
  print STDERR '
Module for monitoring DELL PowerStore via the API
Author: Lars-Erik Johansson

-h, --help
   You\'re looking at it :)

-v, --verbose
   verbose mode.

-H, --hostname=HOST
   name or IP address of host to check

-U, --username=STRING
   User name
   E.g: op5-monitor

-P, --password=STRING
   Password

-k, --insecure
   Ignore any certificate problems with the server.
   Most appliances will have a self signed certificate
   You will probably need to use this option.
';
  exit(UNKNOWN);
}

# We want undef if the sting variable is not defined.
sub
FixUndef()
{
  my ($str) = @_;

  return($str) if(defined($str));
  return('undef');
}

sub
Main() {
  my $help;
  my $hostname;
  my $username;
  my $password;
  my $insecure;
  Getopt::Long::Configure ("bundling");
  GetOptions("verbose" => \$verbose, "v" => \$verbose,
             "help" => \$help, "h" => \$help,
             "H:s" => \$hostname, "hostname:s" => \$hostname,
             "U:s" => \$username, "username:s" => \$username,
             "P:s" => \$password, "password:s" => \$password,
             "k:s" => \$insecure, "insecure" => \$insecure,
            ) or &Usage();

  # If the user asked for help, show it and then exit
  if(defined($help)) {
    &Help();
  }

  # We need at least hostname, username and password to do anything.
  &Usage unless(defined($hostname) && defined($username) && defined($password));

  # Hostname must consist of letters, numbers dots and dashes.
  unless($hostname =~ /^[a-z0-9.\-]+$/i) {
    print STDERR "Bad character(s) in hostname or IP address ", $hostname, "\n";
    print STDERR "This parameter cabn only contain a-z, 0-9, . (period) and - (dash).\n";
    &Usage();
  }

  # If this is not an IP address, try to look it up as a name.
  # If that fails, we are unable to use this hostname
  if($hostname !~ /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/) {
    unless(gethostbyname($hostname)) {
      print STDERR "Bad hostname or IP address ", $hostname, "\n";
      &Usage();
    }
  }

  my $url = 'https://'.$hostname.'/api/rest/hardware?select=*';

  my $headers = new HTTP::Headers(Content_Base => $url);
  $headers->authorization_basic($username, $password);

  my $ua = new LWP::UserAgent;
  if(defined($insecure)) {
    $ua->ssl_opts(verify_hostname => 0);
  }
  my $request = HTTP::Request->new('GET', $url, $headers);
  my $response = $ua->request($request);

  if(!$response->is_success) {
    print $response->content, "\n";
    exit(UNKNOWN);
  }

  my $json = JSON::PP->new->ascii->pretty->allow_nonref;
  my $ref = $json->decode($response->content);

  my $problems = 0;
  my $problemcomponents = "";
  my $infolines = "";

  # $ref is a pointer and we have to convert that to an array to be able to use for.
  # Each item in the array is a data struct built from the JSON.
  # Also sort the list alphabetically based on type and nummerically based on slot.
  for my $item (sort {$a->{'type'} cmp $b->{'type'} || $a->{'slot'} <=> $b->{'slot'} } @{$ref}) {
    # Skip things we have no health info about.
    next if(!defined($item->{'lifecycle_state'}));

    # Skip emty slots.
    next if($item->{'lifecycle_state'} eq 'Empty');

    # Handle any problems.
    if($item->{'lifecycle_state'} eq 'Healthy') {
    } else {
      $problems++;
      $problemcomponents .= $item->{'type'}.', slot: '.$item->{'slot'}.". ";
      &ExitStatus(CRITICAL);
    }

    # Build a list of component status

    my $type = &FixUndef($item->{'type'});
    my $slot = &FixUndef($item->{'slot'});
    my $lifecycle_state = &FixUndef($item->{'lifecycle_state'});
    my $appliance_id = &FixUndef($item->{'appliance_id'});
    my $serial_number = &FixUndef($item->{'serial_number'});

    $infolines .= sprintf("Type: %-16s slot: %-2s state: %-20s Appliance: %-3s Serial number: %s\n",
                          $type, $slot, $lifecycle_state, $appliance_id, $serial_number);

    # Show very much information in verbose mode.
    if(defined($verbose)) {
      print Dumper $item;
    } else {
    }
  }

  if($problems) {
    print $problems, " problem hardware components ", $problemcomponents, "\n";
  } else {
    print "All hardware is Healthy.\n";
    print $infolines;
  }

  exit($exitstatus);
}

&Main
