# Copyright (C) 2021 Rafael Leira, Naudit HPCN S.L.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License,
# version 2, as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
#
################################################################
"""
This module contains the definition of the `Diagnostics` class/structure, used to
represent different kinds of SMART SCSI health and attributes associated with a `Device`.
Currently it merges the nvme (v2) and scsi diagnostics structures.
"""
import copy
from typing import Dict, Union, Optional
class Diagnostics(object):
"""
Contains all of the information associated with every SMART SCSI/SAS attribute
in a `Device`'s SMART table. This class pretends to provide a better view
of the data recolected from smartctl and its types.
Values set to None reflects that disk does not support such info
"""
def __init__(self):
"""Initialize the structure with every field set to None
"""
# Extra useful fields
self._block_size = 512
"""The block size of the device in Bytes (512 for most disks)"""
# Generic counters
self.Reallocated_Sector_Ct: Optional[int] = None
"""**(int):** Reallocated sector count."""
self.Start_Stop_Spec: Optional[int] = None
self.Start_Stop_Cycles: Optional[int] = None
self.Start_Stop_Pct_Left: Optional[int] = None
"""**(int):** Percent left of the life-time start-stop cycles."""
self.Load_Cycle_Spec: Optional[int] = None
self.Load_Cycle_Count: Optional[int] = None
self.Load_Cycle_Pct_Left: Optional[int] = None
"""**(int):** Percent left of the life-time load cycles."""
self.Power_On_Hours: Optional[int] = None
"""**(int):** Number of hours the device have been powered on."""
self.Life_Left: Optional[int] = None
"""**(int):** Percent left of the whole disk life."""
# Error counters
self.Corrected_Reads: Optional[int] = None
"""**(float):** Total number of read operations that had an error but were corrected."""
self.Corrected_Writes: Optional[int] = None
"""**(float):** Total number of write operations that had an error but were corrected."""
self.Corrected_Verifies: Optional[int] = None
self._Uncorrected_Reads: Optional[int] = None
"""**(float):** Total number of read operations that had an uncorrectable error."""
self._Uncorrected_Writes: Optional[int] = None
"""**(float):** Total number of write operations that had an uncorrectable error."""
self._Uncorrected_Verifies: Optional[int] = None
self._Reads_GB: Optional[float] = None
"""**(float):** Total number of GBs readed in the disk life."""
self._Writes_GB: Optional[float] = None
"""**(float):** Total number of GBs written in the disk life."""
self._Verifies_GB: Optional[float] = None
"""**(float):** Total number of GBs verified in the disk life."""
self._Reads_count: Optional[int] = None
"""**(int):** Total number of blocks readed in the disk life."""
self._Writes_count: Optional[int] = None
"""**(int):** Total number of blocks written in the disk life."""
self._Verifies_count: Optional[int] = None
"""**(int):** Total number of blocks verified in the disk life."""
self.Non_Medium_Errors: Optional[int] = None
"""**(int):** Other errors not caused by this disk."""
# Properties
@property
def Uncorrected_Reads(self):
return self._Uncorrected_Reads
@property
def Uncorrected_Writes(self):
return self._Uncorrected_Writes
@property
def Uncorrected_Verifies(self):
return self._Uncorrected_Verifies
@property
def Reads_GB(self) -> Union[float, None]:
if self._Reads_GB is not None:
return self._Reads_GB
elif self._Reads_count is not None:
return (self._Reads_count * self.block_size) / (1024.0 * 1024 * 1024)
else:
return None
@property
def Writes_GB(self) -> Union[float, None]:
if self._Writes_GB is not None:
return self._Writes_GB
elif self._Writes_count is not None:
return (self._Writes_count * self.block_size) / (1024.0 * 1024 * 1024)
else:
return None
@property
def Verifies_GB(self) -> Union[float, None]:
if self._Verifies_GB is not None:
return self._Verifies_GB
elif self._Verifies_count is not None:
return (self._Verifies_count * self.block_size) / (1024.0 * 1024 * 1024)
else:
return None
@property
def Reads_count(self) -> Union[int, None]:
if self._Reads_count is not None:
return self._Reads_count
elif self._Reads_GB is not None:
return int((self._Reads_GB * 1024 * 1024 * 1024) / self.block_size)
else:
return None
@property
def Writes_count(self) -> Union[int, None]:
if self._Writes_count is not None:
return self._Writes_count
elif self._Writes_GB is not None:
return (int)((self._Writes_GB * 1024 * 1024 * 1024) / self.block_size)
else:
return None
@property
def Verifies_count(self) -> Union[int, None]:
if self._Verifies_count is not None:
return self._Verifies_count
elif self._Verifies_GB is not None:
return (int)((self._Verifies_GB * 1024 * 1024 * 1024) / self.block_size)
else:
return None
@property
def block_size(self) -> int:
return self._block_size
# Methods
def get_classic_format(self) -> Dict[str, str]:
"""This method pretends to generate the previously/depreceted diag dictionary structure
Returns:
Dict[str,str]: the <1.1.0 PySMART diags structure
"""
# Copy all the fields to a new dictionary that are not hidden
ret_dict = {k: v for k, v in vars(
self).items() if not k.startswith('_')}
# Add all the properties
ret_dict.update({k: getattr(self, k) for k, v in vars(
Diagnostics).items() if type(v) is property})
# replace Non_Medium_Errors -> Non-Medium_Errors
ret_dict['Non-Medium_Errors'] = ret_dict['Non_Medium_Errors']
del ret_dict['Non_Medium_Errors']
# replace None with '-'
for value in ret_dict:
if ret_dict[value] is None:
ret_dict[value] = '-'
# ensure everything is a string
for value in ret_dict:
ret_dict[value] = str(ret_dict[value])
# include percent %
percent_values = [
'Life_Left',
'Start_Stop_Pct_Left',
'Load_Cycle_Pct_Left'
]
for pv in percent_values:
if ret_dict[pv] != '-':
ret_dict[pv] = ret_dict[pv] + '%'
return ret_dict
def __getstate__(self, all_info=True):
"""
Allows us to send a pySMART diagnostics object over a serializable
medium which uses json (or the likes of json) payloads
"""
return vars(self)
def __setstate__(self, state):
self.__dict__.update(state)
__all__ = ['Diagnostics']
Anons79 File Manager Version 1.0, Coded By Anons79
Email: [email protected]