#!/usr/bin/perl use strict; use warnings; use JSON; use Getopt::Long; use File::Which; my $dev = undef; my $type = 'auto'; my $what = 'json'; my $pretty = 0; GetOptions( 'device=s' => \$dev, 'type=s' => \$type, 'what=s' => \$what, 'pretty' => \$pretty ); if (not defined $dev or $dev !~ m|^/dev/\w+(/\w+)*$|){ print "Invalid --device\n"; exit 1; } elsif ($what !~ m/^\w+$/){ print "Invalid --what\n"; exit 1; } elsif ($type !~ m/^\w+\+*\w+(,\w+)*$/){ print "Invalid --type\n"; exit 1; } my $json = { temperature_celsius => 25, power_on_hours => 0, power_cycle_count => 0, reallocated_sector_count => 0, current_pending_sector => 0, offline_uncorrectable => 0, percent_lifetime_remain => 100, firmware_version => 0 }; my $smartctl = which('smartctl'); sub print_out { if ($what eq 'json'){ print to_json($json, { pretty => $pretty }); exit 0; } elsif (defined $json->{$what}){ print $json->{$what} . "\n"; exit 0; } else { print "ZBX_NOTSUPPORTED\n"; exit 1; } } sub get_smart_attr { my $smart = shift; my $attr = shift; if (defined $smart->{ata_smart_attributes}->{table}){ foreach (@{$smart->{ata_smart_attributes}->{table}}){ if ($_->{name} eq $attr){ return $_; } } } return undef; } if (not defined $smartctl){ $what = 'error'; print_out(); } my $data = from_json(qx($smartctl -a $dev -d $type --json=c)); if (defined $data->{temperature}->{current}){ $json->{temperature_celsius} = $data->{temperature}->{current}; } if (defined $data->{power_on_time}->{hours}){ $json->{power_on_hours} = $data->{power_on_time}->{hours}; } if (defined $data->{power_cycle_count}){ $json->{power_cycle_count} = $data->{power_cycle_count}; } if (defined $data->{firmware_version}){ $json->{firmware_version} = $data->{firmware_version}; } my ($pending, $realloc, $offline, $remain); if ($pending = get_smart_attr($data, 'Current_Pending_Sector')){ $json->{current_pending_sector} = $pending->{raw}->{value}; } if ($realloc = get_smart_attr($data, 'Reallocated_Sector_Ct') || get_smart_attr($data, 'Reallocated_Event_Count')){ $json->{reallocated_sector_count} = $realloc->{raw}->{value}; } elsif (defined $data->{nvme_smart_health_information_log}->{media_errors}){ # NMVe can report media error, so report it as reallocated sectors $json->{reallocated_sector_count} = $data->{nvme_smart_health_information_log}->{media_errors}; } if ($offline = get_smart_attr($data, 'Offline_Uncorrectable')){ $json->{offline_uncorrectable} = $offline->{raw}->{value}; } if ($remain = get_smart_attr($data, 'Percent_Lifetime_Remain') || get_smart_attr($data, 'SSD_Life_Left')){ $json->{percent_lifetime_remain} = $remain->{value}; } elsif ($remain = get_smart_attr($data, 'Wear_Leveling_Count')) { $json->{percent_lifetime_remain} = $remain->{value}; } elsif (defined $data->{nvme_smart_health_information_log}->{percentage_used}){ # NMVe sometime report the estimated life used, instead of the remaining $json->{percent_lifetime_remain} = 100 - $data->{nvme_smart_health_information_log}->{percentage_used}; } print_out();