#!/usr/bin/perl

use strict;
use warnings;
use JSON;
use Getopt::Long;
use File::Which;

my $json   = {};
my $mpath  = undef;
my $help   = 0;
my $pretty = 0;

GetOptions(
  "mpath=s" => \$mpath,
  "help"    => \$help,
  "pretty"  => \$pretty
);

my $multipathd = which('multipathd');

if (not defined $multipathd){
  print 'ZBX_NOTSUPPORTED';
  exit 1;
}

if ($help or not defined $mpath){
  print <<_EOF;
Usage : $0 --mpath=<name of the mpath device> [--pretty]

  * --mpath : the name of the device to check
  * --pretty : output pretty JSON, easier to read for humans
  * --help : display this help

_EOF
  exit 2;
}

$json = {
  mpath              => $mpath,
  size               => 0,
  dm_st              => 'unknown',
  features           => '',
  failures           => 0,
  path_failures      => 0,
  paths_num_total    => 0,
  paths_num_ko       => 0,
  paths_num_active   => 0,
  paths_num_inactive => 0,
  paths_details      => [],
  paths_with_issue   => [],
  errors             => []
};
my @res = qx($multipathd show maps raw format "%n|%N|%S|%f|%t|%x|%0");
if ($? != 0){
  push @{$json->{errors}}, "Failed to run multipathd show maps raw format";
}
foreach (@res){
  chomp;
  next if $_ !~ /^$mpath\|/;
  (undef, $json->{paths_num_total}, $json->{size}, $json->{features},
     $json->{dm_st}, $json->{failures}, $json->{path_failures}) = split(/\s*\|\s*/, $_);
  # Cast to int
  foreach (qw(failures path_failures paths_num_total)){
    $json->{$_} = 0 + $json->{$_};
  }
  # Convert size to bytes
  my $unit = chop $json->{size};
  if ($unit eq 'K'){
    $json->{size} *= 1024;
  } elsif ($unit eq 'M'){
    $json->{size} *= 1024 * 1024;
  } elsif ($unit eq 'G'){
    $json->{size} *= 1024 * 1024 * 1024;
  } elsif ($unit eq 'T'){
    $json->{size} *= 1024 * 1024 * 1024 * 1024;
  } elsif ($unit eq 'P'){
    $json->{size} *= 1024 * 1024 * 1024 * 1024 * 1024;
  }
  # No need to process the other mpath here
  last;
}

# Now check status of every path
@res = qx($multipathd show paths format "%m|%d|%t|%o|%T|%0|%z");
if ($? != 0){
  push @{$json->{errors}}, "Failed to run multipathd show paths format";
}
# Skip header line
shift @res;
foreach (@res){
  chomp;
  next if $_ !~ /^$mpath\|/;
  my (undef, $dev, $dm_st, $dev_st, $chk_st, $failures, $serial) = split(/\s*\|\s*/, $_);
  push @{$json->{paths_details}}, {
    dev      => $dev,
    dm_st    => $dm_st,
    dev_st   => $dev_st,
    chk_st   => $chk_st,
    failures => $failures + 0,
    serial   => $serial
  };
  if ($dm_st eq 'active'){
    $json->{paths_num_active} += 1;
    if ($dev_st ne 'running'){
      $json->{paths_num_ko} += 1;
      push @{$json->{paths_with_issue}}, $dev;
      push @{$json->{errors}}, "dev $dev is not running";
    } elsif ($chk_st ne 'ready' or $failures > 0){
      $json->{paths_num_ko} += 1;
      push @{$json->{paths_with_issue}}, $dev;
      push @{$json->{errors}}, "dev $dev is not active";
    } else {
      $json->{paths_num_ok} += 1;
    }
  } else {
    $json->{paths_num_inactive} += 1;
  }
}
# We want easy usage from zabbix, so turn thos ones to strings
$json->{paths_with_issue} = join(',', @{$json->{paths_with_issue}});
$json->{errors}           = join(',', @{$json->{errors}});

print to_json($json, { pretty => $pretty });
exit 0;