Browse Source

Initial commit of Disk::SMART

Paul Trost 11 years ago
parent
commit
00b6d76b22
13 changed files with 370 additions and 0 deletions
  1. 5 0
      Changes
  2. 10 0
      MANIFEST
  3. 20 0
      Makefile.PL
  4. 39 0
      README
  5. 12 0
      ignore.txt
  6. 158 0
      lib/Disk/SMART.pm
  7. 9 0
      t/00-load.t
  8. 7 0
      t/01-instantiation.t
  9. 11 0
      t/02-get_functions.t
  10. 56 0
      t/boilerplate.t
  11. 13 0
      t/manifest.t
  12. 18 0
      t/pod-coverage.t
  13. 12 0
      t/pod.t

+ 5 - 0
Changes

@@ -0,0 +1,5 @@
+Revision history for Disk-SMART
+
+0.01    Date/time
+        First version, released on an unsuspecting world.
+

+ 10 - 0
MANIFEST

@@ -0,0 +1,10 @@
+Changes
+lib/Disk/SMART.pm
+Makefile.PL
+MANIFEST			This list of files
+README
+README.md
+t/00-load.t
+t/manifest.t
+t/pod-coverage.t
+t/pod.t

+ 20 - 0
Makefile.PL

@@ -0,0 +1,20 @@
+use 5.006;
+use strict;
+use warnings;
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+    NAME                => 'Disk::SMART',
+    AUTHOR              => q{Paul Trost <paul.trost@trostfamily.org>},
+    VERSION_FROM        => 'lib/Disk/SMART.pm',
+    ABSTRACT_FROM       => 'lib/Disk/SMART.pm',
+    ($ExtUtils::MakeMaker::VERSION ge 6.3002
+      ? ('LICENSE'=> 'perl')
+      : ()),
+    PL_FILES            => {},
+    PREREQ_PM => {
+        'Test::More' => 0,
+    },
+    dist                => { COMPRESS => 'gzip -9f', SUFFIX => 'gz', },
+    clean               => { FILES => 'Disk-SMART-*' },
+);

+ 39 - 0
README

@@ -0,0 +1,39 @@
+Disk-SMART
+
+This module requires that smartctl be accessed as root. If you do not wish to install the module as root then you will need to make smartctl SETUID as root with "chmod u+s <path/to/smartctl>"
+
+To install this module, run the following commands:
+
+	perl Makefile.PL
+	make
+	sudo make test
+	sudo make install
+
+SUPPORT AND DOCUMENTATION
+
+After installing, you can find documentation for this module with the
+perldoc command.
+
+    perldoc Disk::SMART
+
+You can also look for information at:
+
+    RT, CPAN's request tracker (report bugs here)
+        http://rt.cpan.org/NoAuth/Bugs.html?Dist=Disk-SMART
+
+    AnnoCPAN, Annotated CPAN documentation
+        http://annocpan.org/dist/Disk-SMART
+
+    CPAN Ratings
+        http://cpanratings.perl.org/d/Disk-SMART
+
+    Search CPAN
+        http://search.cpan.org/dist/Disk-SMART/
+
+
+LICENSE AND COPYRIGHT
+
+Copyright 2014 Paul Trost
+This script is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License v2, or at your option any later version.
+<http://gnu.org/licenses/gpl.html>
+

+ 12 - 0
ignore.txt

@@ -0,0 +1,12 @@
+blib*
+Makefile
+Makefile.old
+Build
+Build.bat
+_build*
+pm_to_blib*
+*.tar.gz
+.lwpcookies
+cover_db
+pod2htm*.tmp
+Disk-SMART-*

+ 158 - 0
lib/Disk/SMART.pm

@@ -0,0 +1,158 @@
+package Disk::SMART;
+
+use warnings;
+use strict;
+use Carp;
+use Math::Round;
+
+=head1 NAME
+
+Disk::SMART - Provides an interface to smartctl
+
+=head1 VERSION
+
+Version 0.01
+
+=cut
+
+our $VERSION = '0.01';
+
+=head1 SYNOPSIS
+
+Disk::SMART is an object ooriented module that provides an interface to get SMART disk info from a device as well as initiate testing.
+
+    use Disk::SMART;
+
+    my $smart = Disk::SMART->new('/dev/sda');
+
+
+=cut
+
+=head1 METHODS
+
+=head2 B<new (DEVICE)>
+
+Instantiates the Disk::SMART object
+
+C<DEVICE> - Device identifier of SSD / Hard Drive
+
+    my $smart = Disk::SMART->new( 'dev/sda', '/dev/sdb' );
+
+=cut
+
+sub new {
+    my ( $class, @devices ) = @_;
+    confess "Valid device identifier not supplied to constructor.\n" if ( !@devices );
+
+    my $smartctl = '/usr/sbin/smartctl';
+    if ( !-f $smartctl ) {
+        confess "smartctl binary was not found on your system, are you running as root?\n";
+    }
+
+    my $self = bless {}, $class;
+
+    foreach my $device (@devices) {
+        $self->{'devices'}->{$device}->{'SMART_OUTPUT'} = qx($smartctl -a $device);
+    }
+
+    return $self;
+}
+
+=head1 Getting information from smartctl
+
+=cut
+
+=head2 B<get_disk_temp (DEVICE)>
+
+Returns an array with the temperature of the device in Celsius and Farenheit, or N/A.
+
+C<DEVICE> - Device identifier of SSD / Hard Drive
+
+    my ($temp_c, $temp_f) = $smart->get_disk_temp('/dev/sda');
+
+=cut
+
+sub get_disk_temp {
+    my ( $self, $device ) = @_;
+    my $smart_output = $self->{'devices'}->{$device}->{'SMART_OUTPUT'};
+    my ($temp_c) = $smart_output =~ /(Temperature_Celsius.*\n)/;
+
+    if ( defined $temp_c ) {
+        chomp $temp_c;
+        $temp_c =~ s/ //g;
+        $temp_c =~ s/.*-//;
+        $temp_c =~ s/\(.*\)//;
+    }
+
+    if ( !$temp_c || $smart_output =~ qr/S.M.A.R.T. not available/x ) {
+        return 'N/A';
+    }
+    else {
+        my $temp_f = round( ( $temp_c * 9 ) / 5 + 32 );
+        return ( $temp_c, $temp_f );
+    }
+    return undef;
+}
+
+=head2 B<get_disk_health (DEVICE)>
+
+Returns the health of the disk. Output is "PASSED", "FAILED", or "N/A".
+
+C<DEVICE> - Device identifier of SSD / Hard Drive
+
+    my $disk_health = $smart->get_disk_health('/dev/sda');
+
+=cut
+
+sub get_disk_health {
+    my ( $self, $device ) = @_;
+    my $smart_output = $self->{'devices'}->{$device}->{'SMART_OUTPUT'};
+    my ($health) = $smart_output =~ /(SMART overall-health self-assessment.*\n)/;
+
+    if ( defined $health and $health =~ /PASSED|FAILED/x ) {
+        $health =~ s/.*: //;
+        chomp $health;
+        return $health;
+    }
+    else {
+        return 'N/A';
+    }
+}
+
+=head2 B<get_disk_model (DEVICE)>
+
+Returns the model of the device. Output is "<device>: <model>" or "N/A". eg. "/dev/sda: ST3250410AS"
+
+C<DEVICE> - Device identifier of SSD / Hard Drive
+
+    my $disk_model = $smart->get_disk_model('/dev/sda');
+
+=cut
+
+sub get_disk_model {
+    my ( $self, $device ) = @_;
+    my $smart_output = $self->{'devices'}->{$device}->{'SMART_OUTPUT'};
+    my ($model) = $smart_output =~ /(Device\ Model.*\n)/;
+
+    if ( defined $model ) {
+        $model =~ s/.*:\ //;
+        $model =~ s/^\s+|\s+$//g;    #trim beginning and ending whitepace
+    }
+    return ($model) ? "$device: $model" : "$device: N/A";
+}
+
+1;
+
+__END__
+
+=head1 AUTHOR
+
+ Paul Trost <paul.trost@trostfamily.org>
+
+=head1 LICENSE AND COPYRIGHT
+
+ Copyright 2014.
+ This script is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License v2, or at your option any later version.
+ <http://gnu.org/licenses/gpl.html>
+
+=cut

+ 9 - 0
t/00-load.t

@@ -0,0 +1,9 @@
+#!perl -T
+
+use Test::More tests => 1;
+
+BEGIN {
+    use_ok( 'Disk::SMART' ) || print "Bail out!\n";
+}
+
+diag( "Testing Disk::SMART $Disk::SMART::VERSION, Perl $], $^X" );

+ 7 - 0
t/01-instantiation.t

@@ -0,0 +1,7 @@
+use warnings;
+use strict;
+use Test::More 'tests' => 1;
+use Disk::SMART;
+
+my $smart = Disk::SMART->new('/dev/sda');
+pass('instantiation of object successful') if (defined($smart));

+ 11 - 0
t/02-get_functions.t

@@ -0,0 +1,11 @@
+use warnings;
+use strict;
+use Test::More 'tests' => 3;
+use Disk::SMART;
+
+my $disk = '/dev/sda';
+my $smart = Disk::SMART->new($disk);
+
+ok( $smart->get_disk_temp($disk), 'get_disk_temp() returns drive temperature or N/A');
+like( $smart->get_disk_health('/dev/sda'), qr/PASSED|FAILED|N\/A/, 'get_disk_health() returns health status or N/A');
+like( $smart->get_disk_model('/dev/sda'), qr/(\w+: \w+)|N\/A/, 'get_disk_model() returned disk model or N/A');

+ 56 - 0
t/boilerplate.t

@@ -0,0 +1,56 @@
+#!perl -T
+
+use 5.006;
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+sub not_in_file_ok {
+    my ($filename, %regex) = @_;
+    open( my $fh, '<', $filename )
+        or die "couldn't open $filename for reading: $!";
+
+    my %violated;
+
+    while (my $line = <$fh>) {
+        while (my ($desc, $regex) = each %regex) {
+            if ($line =~ $regex) {
+                push @{$violated{$desc}||=[]}, $.;
+            }
+        }
+    }
+
+    if (%violated) {
+        fail("$filename contains boilerplate text");
+        diag "$_ appears on lines @{$violated{$_}}" for keys %violated;
+    } else {
+        pass("$filename contains no boilerplate text");
+    }
+}
+
+sub module_boilerplate_ok {
+    my ($module) = @_;
+    not_in_file_ok($module =>
+        'the great new $MODULENAME'   => qr/ - The great new /,
+        'boilerplate description'     => qr/Quick summary of what the module/,
+        'stub function definition'    => qr/function[12]/,
+    );
+}
+
+TODO: {
+  local $TODO = "Need to replace the boilerplate text";
+
+  not_in_file_ok(README =>
+    "The README is used..."       => qr/The README is used/,
+    "'version information here'"  => qr/to provide version information/,
+  );
+
+  not_in_file_ok(Changes =>
+    "placeholder date/time"       => qr(Date/time)
+  );
+
+  module_boilerplate_ok('lib/Disk/SMART.pm');
+
+
+}
+

+ 13 - 0
t/manifest.t

@@ -0,0 +1,13 @@
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+unless ( $ENV{RELEASE_TESTING} ) {
+    plan( skip_all => "Author tests not required for installation" );
+}
+
+eval "use Test::CheckManifest 0.9";
+plan skip_all => "Test::CheckManifest 0.9 required" if $@;
+ok_manifest();

+ 18 - 0
t/pod-coverage.t

@@ -0,0 +1,18 @@
+use strict;
+use warnings;
+use Test::More;
+
+# Ensure a recent version of Test::Pod::Coverage
+my $min_tpc = 1.08;
+eval "use Test::Pod::Coverage $min_tpc";
+plan skip_all => "Test::Pod::Coverage $min_tpc required for testing POD coverage"
+    if $@;
+
+# Test::Pod::Coverage doesn't require a minimum Pod::Coverage version,
+# but older versions don't recognize some common documentation styles
+my $min_pc = 0.18;
+eval "use Pod::Coverage $min_pc";
+plan skip_all => "Pod::Coverage $min_pc required for testing POD coverage"
+    if $@;
+
+all_pod_coverage_ok();

+ 12 - 0
t/pod.t

@@ -0,0 +1,12 @@
+#!perl -T
+
+use strict;
+use warnings;
+use Test::More;
+
+# Ensure a recent version of Test::Pod
+my $min_tp = 1.22;
+eval "use Test::Pod $min_tp";
+plan skip_all => "Test::Pod $min_tp required for testing POD" if $@;
+
+all_pod_files_ok();