зеркало из https://github.com/microsoft/StorScore.git
232 строки
5.6 KiB
Perl
232 строки
5.6 KiB
Perl
# StorScore
|
|
#
|
|
# Copyright (c) Microsoft Corporation
|
|
#
|
|
# All rights reserved.
|
|
#
|
|
# MIT License
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a
|
|
# copy of this software and associated documentation files (the "Software"),
|
|
# to deal in the Software without restriction, including without limitation
|
|
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
# and/or sell copies of the Software, and to permit persons to whom the
|
|
# Software is furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
# DEALINGS IN THE SOFTWARE.
|
|
|
|
package SmartRunner;
|
|
|
|
use strict;
|
|
use warnings;
|
|
use Moose;
|
|
use File::Temp 'mktemp';
|
|
use Util;
|
|
|
|
has 'physical_drive' => (
|
|
is => 'ro',
|
|
isa => 'Int',
|
|
required => 1
|
|
);
|
|
|
|
has 'device_type' => (
|
|
is => 'rw',
|
|
isa => 'Maybe[Str]',
|
|
writer => '_device_type',
|
|
init_arg => undef,
|
|
default => undef
|
|
);
|
|
|
|
has 'is_functional' => (
|
|
is => 'rw',
|
|
isa => 'Bool',
|
|
writer => '_is_functional',
|
|
init_arg => undef,
|
|
default => 0
|
|
);
|
|
|
|
has 'rotation_rate' => (
|
|
is => 'rw',
|
|
isa => 'Maybe[Str]',
|
|
writer => '_rotation_rate',
|
|
init_arg => undef,
|
|
default => undef
|
|
);
|
|
|
|
has 'sata_version' => (
|
|
is => 'rw',
|
|
isa => 'Maybe[Str]',
|
|
writer => '_sata_version',
|
|
init_arg => undef,
|
|
default => undef
|
|
);
|
|
|
|
sub is_smartctl_present()
|
|
{
|
|
my $missing = execute_task(
|
|
'where smartctl',
|
|
quiet => 1
|
|
);
|
|
|
|
return 0 if $missing;
|
|
return 1;
|
|
}
|
|
|
|
sub collect
|
|
{
|
|
my $self = shift;
|
|
my %args = @_;
|
|
|
|
my $file_name = $args{'file_name'};
|
|
my $output_dir = $args{'output_dir'};
|
|
my $dev_type = $args{'dev_type'} // $self->device_type;
|
|
my $do_identify = $args{'do_identify'};
|
|
|
|
my $cmd = "";
|
|
if( $dev_type =~ /nvme/i )
|
|
{
|
|
$cmd .= "StorageTool.exe ";
|
|
$cmd .= "-HealthInfo ";
|
|
$cmd .= "Disk " . $self->physical_drive . " ";
|
|
}
|
|
else
|
|
{
|
|
return unless( $self->is_smartctl_present() );
|
|
|
|
$cmd .= "smartctl.exe ";
|
|
$cmd .= "-d $dev_type " if defined $dev_type;
|
|
$cmd .= "--identify " if defined $do_identify;
|
|
$cmd .= "-a /dev/pd" . $self->physical_drive . " ";
|
|
$cmd .= "-s on ";
|
|
}
|
|
|
|
if( defined $file_name )
|
|
{
|
|
$file_name = $output_dir . "\\$file_name"
|
|
unless is_absolute_path( $file_name );
|
|
|
|
execute_task( "echo $cmd > $file_name" );
|
|
|
|
$cmd .= ">> $file_name";
|
|
}
|
|
|
|
return execute_task( $cmd );
|
|
}
|
|
|
|
sub detect_device_type()
|
|
{
|
|
my $self = shift;
|
|
|
|
my $file_name =
|
|
mktemp( $ENV{'TEMP'} . "\\smart_devtypeXXXXXX" );
|
|
|
|
my $detected_device_type;
|
|
|
|
foreach my $try_type ( qw( nvme ata sat scsi ) )
|
|
{
|
|
$self->collect(
|
|
file_name => $file_name,
|
|
dev_type => $try_type
|
|
);
|
|
|
|
open my $SMART, "<$file_name"
|
|
or die "Couldn't open SMART output file: $file_name\n";
|
|
|
|
while( my $line = <$SMART> )
|
|
{
|
|
# Previous code looked for "Vendor Specific SMART Attributes"
|
|
# Sometimes this text appears but the counters are garbage.
|
|
# Look for this ubiquitous attribute instead.
|
|
if( $line =~ "9 Power_On_Hours" )
|
|
{
|
|
$detected_device_type = $try_type;
|
|
last;
|
|
}
|
|
elsif( $line =~ /NVME/i )
|
|
{
|
|
$detected_device_type = $try_type;
|
|
last;
|
|
}
|
|
}
|
|
|
|
close $SMART;
|
|
last if( $detected_device_type );
|
|
}
|
|
|
|
unlink $file_name;
|
|
|
|
return $detected_device_type;
|
|
}
|
|
|
|
my @extract_rules =
|
|
(
|
|
{
|
|
match => qr/Rotation Rate:\s+(.+)/,
|
|
store => 'Rotation Rate'
|
|
},
|
|
{
|
|
match => qr/SATA Version is:\s+(.+)/,
|
|
store => 'SATA Version'
|
|
},
|
|
{#ToDo: Integrate this parameter with Sata Version & SmartCtl.exe
|
|
# (This format matches only the output from StorageTool.exe)
|
|
match => qr/Disk #\d+\s+:\s+\[(\S+)\s+\]/,
|
|
store => 'Protocol Version'
|
|
},
|
|
);
|
|
|
|
sub BUILD
|
|
{
|
|
my $self = shift;
|
|
|
|
if( $pretend )
|
|
{
|
|
# Mock up a fake SATA III SSD for pretend mode
|
|
$self->_is_functional( 1 );
|
|
$self->_device_type( 'ata' );
|
|
$self->_rotation_rate( 'Solid State Device' );
|
|
$self->_sata_version( 'current: 6.0 Gb/s' );
|
|
|
|
return;
|
|
}
|
|
|
|
$self->_device_type( $self->detect_device_type() );
|
|
|
|
return unless defined $self->device_type;
|
|
|
|
# If we get here, it looks like everything is working
|
|
$self->_is_functional( 1 );
|
|
|
|
my $file_name = mktemp( $ENV{'TEMP'} . "\\smart_initXXXXXX" );
|
|
|
|
$self->collect( file_name => $file_name );
|
|
|
|
my %stats;
|
|
|
|
open my $FILE, '<', $file_name or die "Error opening $file_name";
|
|
|
|
while( my $line = <$FILE> )
|
|
{
|
|
do_simple_extract( $line, \%stats, \@extract_rules );
|
|
}
|
|
close $FILE;
|
|
|
|
unlink $file_name;
|
|
|
|
$self->_rotation_rate( $stats{'Rotation Rate'} );
|
|
$self->_sata_version( $stats{'SATA Version'} );
|
|
}
|
|
|
|
no Moose;
|
|
__PACKAGE__->meta->make_immutable;
|
|
1;
|