3131import os
3232import re
3333import warnings
34- from subprocess import Popen , PIPE
3534from time import time , strptime , mktime , sleep
36- from typing import Tuple , Union , List
35+ from typing import Tuple , Union , List , Dict
3736
3837# pySMART module imports
3938from .attribute import Attribute
39+ from .diagnostics import Diagnostics
4040from .testentry import TestEntry
4141from .smartctl import Smartctl
4242from .utils import smartctl_type , smartctl_isvalid_type , any_in , all_in
@@ -192,9 +192,9 @@ def __init__(self, name: str, interface: str = None, abridged: bool = False, sma
192192 """
193193 **(int):** Estimate progress percantage of the running SMART selftest.
194194 """
195- self .diags = {}
195+ self .diagnostics : Diagnostics = Diagnostics ()
196196 """
197- **(dict of str): ** Contains parsed and processed diagnostic information
197+ **Diagnostics ** Contains parsed and processed diagnostic information
198198 extracted from the SMART information. Currently only populated for
199199 SAS and SCSI devices, since ATA/SATA SMART attributes are manufacturer
200200 proprietary.
@@ -273,6 +273,12 @@ def capacity(self) -> str:
273273 """
274274 return self ._capacity
275275
276+ @property
277+ def diags (self ) -> Dict [str , str ]:
278+ """Gets the old/deprecated version of SCSI/SAS diags atribute.
279+ """
280+ return self .diagnostics .get_classic_format ()
281+
276282 @property
277283 def size_raw (self ) -> str :
278284 """Returns the capacity in the raw smartctl format.
@@ -317,7 +323,7 @@ def __getstate__(self, all_info=True):
317323 'messages' : self .messages ,
318324 'test_capabilities' : self .test_capabilities .copy (),
319325 'tests' : [t .__getstate__ () for t in self .tests ] if self .tests else [],
320- 'diagnostics' : self .diags . copy (),
326+ 'diagnostics' : self .diagnostics . __getstate__ (),
321327 'temperature' : self .temperature ,
322328 'attributes' : [attr .__getstate__ () if attr else None for attr in self .attributes ]
323329 }
@@ -972,69 +978,71 @@ class members, including the SMART attribute table and self-test log.
972978 # the place of similar ATA SMART information
973979 if 'used endurance' in line :
974980 pct = int (line .split (':' )[1 ].strip ()[:- 1 ])
975- self .diags [ ' Life_Left' ] = str ( 100 - pct ) + '%'
981+ self .diagnostics . Life_Left = 100 - pct
976982 if 'Specified cycle count' in line :
977- self .diags ['Start_Stop_Spec' ] = line .split (':' )[1 ].strip ()
978- if self .diags ['Start_Stop_Spec' ] == '0' :
979- self .diags ['Start_Stop_Pct_Left' ] = '-'
983+ self .diagnostics .Start_Stop_Spec = int (
984+ line .split (':' )[1 ].strip ())
980985 if 'Accumulated start-stop cycles' in line :
981- self .diags ['Start_Stop_Cycles' ] = line .split (':' )[1 ].strip ()
982- if 'Start_Stop_Pct_Left' not in self .diags :
983- self .diags ['Start_Stop_Pct_Left' ] = str (int (round (
984- 100 - (int (self .diags ['Start_Stop_Cycles' ]) /
985- int (self .diags ['Start_Stop_Spec' ])), 0 ))) + '%'
986+ self .diagnostics .Start_Stop_Cycles = int (
987+ line .split (':' )[1 ].strip ())
988+ if self .diagnostics .Start_Stop_Spec != 0 :
989+ self .diagnostics .Start_Stop_Pct_Left = int (round (
990+ 100 - (self .diagnostics .Start_Stop_Cycles /
991+ self .diagnostics .Start_Stop_Spec ), 0 ))
986992 if 'Specified load-unload count' in line :
987- self .diags ['Load_Cycle_Spec' ] = line .split (':' )[1 ].strip ()
988- if self .diags ['Load_Cycle_Spec' ] == '0' :
989- self .diags ['Load_Cycle_Pct_Left' ] = '-'
993+ self .diagnostics .Load_Cycle_Spec = int (
994+ line .split (':' )[1 ].strip ())
990995 if 'Accumulated load-unload cycles' in line :
991- self .diags ['Load_Cycle_Count' ] = line .split (':' )[1 ].strip ()
992- if 'Load_Cycle_Pct_Left' not in self .diags :
993- self .diags ['Load_Cycle_Pct_Left' ] = str (int (round (
994- 100 - (int (self .diags ['Load_Cycle_Count' ]) /
995- int (self .diags ['Load_Cycle_Spec' ])), 0 ))) + '%'
996+ self .diagnostics .Load_Cycle_Count = int (
997+ line .split (':' )[1 ].strip ())
998+ if self .diagnostics .Load_Cycle_Spec != 0 :
999+ self .diagnostics .Load_Cycle_Pct_Left = int (round (
1000+ 100 - (self .diagnostics .Load_Cycle_Count /
1001+ self .diagnostics .Load_Cycle_Spec ), 0 ))
9961002 if 'Elements in grown defect list' in line :
997- self .diags [ ' Reallocated_Sector_Ct' ] = line . split ( ':' )[
998- 1 ].strip ()
1003+ self .diagnostics . Reallocated_Sector_Ct = int (
1004+ line . split ( ':' )[ 1 ].strip () )
9991005 if 'read:' in line :
10001006 line_ = ' ' .join (line .split ()).split (' ' )
10011007 if line_ [1 ] == '0' and line_ [2 ] == '0' and line_ [3 ] == '0' and line_ [4 ] == '0' :
1002- self .diags [ ' Corrected_Reads' ] = '0'
1008+ self .diagnostics . Corrected_Reads = 0
10031009 elif line_ [4 ] == '0' :
1004- self .diags [ ' Corrected_Reads' ] = str (
1005- int ( line_ [1 ]) + int (line_ [2 ]) + int (line_ [3 ]) )
1010+ self .diagnostics . Corrected_Reads = int (
1011+ line_ [1 ]) + int (line_ [2 ]) + int (line_ [3 ])
10061012 else :
1007- self .diags [ ' Corrected_Reads' ] = line_ [4 ]
1008- self .diags [ ' Reads_GB' ] = line_ [6 ]
1009- self .diags [ ' Uncorrected_Reads' ] = line_ [7 ]
1013+ self .diagnostics . Corrected_Reads = int ( line_ [4 ])
1014+ self .diagnostics . Reads_GB = float ( line_ [6 ])
1015+ self .diagnostics . Uncorrected_Reads = int ( line_ [7 ])
10101016 if 'write:' in line :
10111017 line_ = ' ' .join (line .split ()).split (' ' )
10121018 if (line_ [1 ] == '0' and line_ [2 ] == '0' and
10131019 line_ [3 ] == '0' and line_ [4 ] == '0' ):
1014- self .diags [ ' Corrected_Writes' ] = '0'
1020+ self .diagnostics . Corrected_Writes = 0
10151021 elif line_ [4 ] == '0' :
1016- self .diags [ ' Corrected_Writes' ] = str (
1017- int ( line_ [1 ]) + int (line_ [2 ]) + int (line_ [3 ]) )
1022+ self .diagnostics . Corrected_Writes = int (
1023+ line_ [1 ]) + int (line_ [2 ]) + int (line_ [3 ])
10181024 else :
1019- self .diags [ ' Corrected_Writes' ] = line_ [4 ]
1020- self .diags [ ' Writes_GB' ] = line_ [6 ]
1021- self .diags [ ' Uncorrected_Writes' ] = line_ [7 ]
1025+ self .diagnostics . Corrected_Writes = int ( line_ [4 ])
1026+ self .diagnostics . Writes_GB = float ( line_ [6 ])
1027+ self .diagnostics . Uncorrected_Writes = int ( line_ [7 ])
10221028 if 'verify:' in line :
10231029 line_ = ' ' .join (line .split ()).split (' ' )
10241030 if (line_ [1 ] == '0' and line_ [2 ] == '0' and
10251031 line_ [3 ] == '0' and line_ [4 ] == '0' ):
1026- self .diags [ ' Corrected_Verifies' ] = '0'
1032+ self .diagnostics . Corrected_Verifies = 0
10271033 elif line_ [4 ] == '0' :
1028- self .diags [ ' Corrected_Verifies' ] = str (
1029- int ( line_ [1 ]) + int (line_ [2 ]) + int (line_ [3 ]) )
1034+ self .diagnostics . Corrected_Verifies = int (
1035+ line_ [1 ]) + int (line_ [2 ]) + int (line_ [3 ])
10301036 else :
1031- self .diags [ ' Corrected_Verifies' ] = line_ [4 ]
1032- self .diags [ ' Verifies_GB' ] = line_ [6 ]
1033- self .diags [ ' Uncorrected_Verifies' ] = line_ [7 ]
1037+ self .diagnostics . Corrected_Verifies = int ( line_ [4 ])
1038+ self .diagnostics . Verifies_GB = float ( line_ [6 ])
1039+ self .diagnostics . Uncorrected_Verifies = int ( line_ [7 ])
10341040 if 'non-medium error count' in line :
1035- self .diags ['Non-Medium_Errors' ] = line .split (':' )[1 ].strip ()
1041+ self .diagnostics .Non_Medium_Errors = int (
1042+ line .split (':' )[1 ].strip ())
10361043 if 'Accumulated power on time' in line :
1037- self .diags ['Power_On_Hours' ] = line .split (':' )[1 ].split (' ' )[1 ]
1044+ self .diagnostics .Power_On_Hours = int (
1045+ line .split (':' )[1 ].split (' ' )[1 ])
10381046 if 'Current Drive Temperature' in line or ('Temperature:' in
10391047 line and interface == 'nvme' ):
10401048 try :
@@ -1061,22 +1069,9 @@ class members, including the SMART attribute table and self-test log.
10611069 # corresponding warnings for non-SCSI disks
10621070 self ._make_smart_warnings ()
10631071 else :
1064- # For SCSI disks, any diagnostic attribute which was not captured
1065- # above gets set to '-' to indicate unsupported/unavailable.
1066- for diag in ['Corrected_Reads' , 'Corrected_Writes' ,
1067- 'Corrected_Verifies' , 'Uncorrected_Reads' ,
1068- 'Uncorrected_Writes' , 'Uncorrected_Verifies' ,
1069- 'Reallocated_Sector_Ct' ,
1070- 'Start_Stop_Spec' , 'Start_Stop_Cycles' ,
1071- 'Load_Cycle_Spec' , 'Load_Cycle_Count' ,
1072- 'Start_Stop_Pct_Left' , 'Load_Cycle_Pct_Left' ,
1073- 'Power_On_Hours' , 'Life_Left' , 'Non-Medium_Errors' ,
1074- 'Reads_GB' , 'Writes_GB' , 'Verifies_GB' ]:
1075- if diag not in self .diags :
1076- self .diags [diag ] = '-'
1077- # If not obtained above, make a direct attempt to extract power on
1072+ # If not obtained Power_On_Hours above, make a direct attempt to extract power on
10781073 # hours from the background scan results log.
1079- if self .diags [ ' Power_On_Hours' ] == '-' :
1074+ if self .diagnostics . Power_On_Hours is None :
10801075 raw , returncode = self .smartctl .generic_call (
10811076 [
10821077 '-d' ,
@@ -1088,8 +1083,8 @@ class members, including the SMART attribute table and self-test log.
10881083
10891084 for line in raw :
10901085 if 'power on time' in line :
1091- self .diags [ ' Power_On_Hours' ] = line . split ( ':' )[
1092- 1 ].split (' ' )[1 ]
1086+ self .diagnostics . Power_On_Hours = int (
1087+ line . split ( ':' )[ 1 ].split (' ' )[1 ])
10931088 # map temperature
10941089 if self .temperature is None :
10951090 # in this case the disk is probably ata
0 commit comments