pySMART.utils
This module contains generic utilities and configuration information for use
by the other submodules of the pySMART
package.
1# Copyright (C) 2014 Marc Herndon 2# 3# This program is free software; you can redistribute it and/or 4# modify it under the terms of the GNU General Public License, 5# version 2, as published by the Free Software Foundation. 6# 7# This program is distributed in the hope that it will be useful, 8# but WITHOUT ANY WARRANTY; without even the implied warranty of 9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10# GNU General Public License for more details. 11# 12# You should have received a copy of the GNU General Public License 13# along with this program; if not, write to the Free Software 14# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 15# MA 02110-1301, USA. 16# 17################################################################ 18""" 19This module contains generic utilities and configuration information for use 20by the other submodules of the `pySMART` package. 21""" 22 23import copy 24import io 25import logging 26import logging.handlers 27import os 28import traceback 29from typing import Dict, Any, Optional 30from shutil import which 31 32_srcfile = __file__ 33TRACE = logging.DEBUG - 5 34 35 36class TraceLogger(logging.Logger): 37 def __init__(self, name): 38 logging.Logger.__init__(self, name) 39 logging.addLevelName(TRACE, 'TRACE') 40 return 41 42 def trace(self, msg, *args, **kwargs): 43 self.log(TRACE, msg, *args, **kwargs) 44 45 def findCaller(self, stack_info=False, stacklevel=1): 46 """ 47 Overload built-in findCaller method 48 to omit not only logging/__init__.py but also the current file 49 """ 50 f = logging.currentframe() 51 # On some versions of IronPython, currentframe() returns None if 52 # IronPython isn't run with -X:Frames. 53 if f is not None: 54 f = f.f_back 55 orig_f = f 56 while f and stacklevel > 1: 57 f = f.f_back 58 stacklevel -= 1 59 if not f: 60 f = orig_f 61 rv = "(unknown file)", 0, "(unknown function)", None 62 while hasattr(f, "f_code"): 63 co = f.f_code 64 filename = os.path.normcase(co.co_filename) 65 if filename in (logging._srcfile, _srcfile): 66 f = f.f_back 67 continue 68 sinfo = None 69 if stack_info: 70 sio = io.StringIO() 71 sio.write('Stack (most recent call last):\n') 72 traceback.print_stack(f, file=sio) 73 sinfo = sio.getvalue() 74 if sinfo[-1] == '\n': 75 sinfo = sinfo[:-1] 76 sio.close() 77 rv = (co.co_filename, f.f_lineno, co.co_name, sinfo) 78 break 79 return rv 80 81 82def configure_trace_logging(): 83 if getattr(logging.handlers.logging.getLoggerClass(), 'trace', None) is None: 84 logging.setLoggerClass(TraceLogger) 85 86def get_trace_logger(name:str='pySMART') -> TraceLogger: 87 configure_trace_logging() 88 return logging.getLogger(name) 89 90def log_trace(msg: str, *args, **kwargs): 91 try: 92 get_trace_logger().trace(msg, *args, **kwargs) 93 except Exception as e: 94 get_trace_logger().debug(f"Exception while logging trace info: {e}") 95 get_trace_logger().debug(msg, *args, **kwargs) 96 97 98def any_in(search_in, *searched_items): 99 """ 100 return True if any of searched_items is in search_in otherwise False. 101 raise 102 """ 103 assert len(searched_items) > 0 104 return any(map(lambda one: one in search_in, searched_items)) 105 106 107def all_in(search_in, *searched_items): 108 """ 109 return True if all of searched_items are in search_in otherwise False 110 does not care about duplicates in searched_items potentially evaluates all of them, 111 """ 112 assert len(searched_items) > 0 113 return all(map(lambda one: one in search_in, searched_items)) 114 115 116smartctl_type_dict = { 117 'ata': 'ata', 118 'csmi': 'ata', 119 'nvme': 'nvme', 120 'sas': 'scsi', 121 'sat': 'sat', 122 'sata': 'ata', 123 'scsi': 'scsi', 124 'atacam': 'atacam' 125} 126""" 127**(dict of str):** Contains actual interface types (ie: sas, csmi) as keys and 128the corresponding smartctl interface type (ie: scsi, ata) as values. 129""" 130 131SMARTCTL_PATH = which('smartctl') 132 133 134def smartctl_isvalid_type(interface_type: str) -> bool: 135 """Tests if the interface_type is supported 136 137 Args: 138 interface_type (str): An internal interface_type 139 140 Returns: 141 bool: True if the type is supported, false z 142 """ 143 if interface_type in smartctl_type_dict: 144 return True 145 elif 'megaraid,' in interface_type: 146 return True 147 else: 148 return False 149 150 151def smartctl_type(interface_type: Optional[str]) -> Optional[str]: 152 """This method basically searchs on smartctl_type_dict to convert from internal 153 smartctl interface type to an understable type for smartctl. However, further 154 transforms may be performed for some special interfaces 155 156 Args: 157 interface_type (str): An internal representation of an smartctl interface type 158 159 Returns: 160 str: Returns the corresponding smartctl interface_type that matches with the internal interface representation. 161 In case it is not supported, None would be returned 162 """ 163 if interface_type is None: 164 return None 165 166 if interface_type in smartctl_type_dict: 167 return smartctl_type_dict[interface_type] 168 elif 'megaraid,' in interface_type: 169 return interface_type 170 else: 171 return None 172 173 174def get_object_properties(obj: Any, deep_copy: bool = True, remove_private: bool = False, recursive: bool = True) -> Optional[Dict[str, Any]]: 175 if obj is None: 176 return None 177 178 if not hasattr(obj, '__dict__'): 179 return obj 180 181 prop_names = dir(obj) 182 183 if deep_copy: 184 ret = copy.deepcopy(vars(obj)) 185 else: 186 ret = copy.copy(vars(obj)) 187 188 available_types = ['dict', 'str', 'int', 'float', 'list', 'NoneType'] 189 recursion_types = ['object', 190 'NvmeError', 'NvmeSelfTest', 'NvmeAttributes', 191 'AtaAttributes', 192 'SCSIAttributes', 'Diagnostics', 193 'Attribute', 'TestEntry', 194 ] 195 196 for prop_name in prop_names: 197 prop_val = getattr(obj, prop_name) 198 prop_val_type_name = type(prop_val).__name__ 199 200 if (prop_name[0] != '_'): 201 # Get properties from objects 202 if (prop_val_type_name in available_types) and (prop_name not in ret): 203 # Check if prop_val has __getstate__ method, if so, call it 204 # if hasattr(prop_val, '__getstate__'): 205 # prop_val_state = prop_val.__getstate__() 206 # if prop_val_state is not None: 207 # ret[prop_name] = prop_val 208 # continue # Do not do recursion 209 210 ret[prop_name] = prop_val 211 212 # Do recursion 213 if recursive: 214 if prop_val_type_name in recursion_types: 215 ret[prop_name] = get_object_properties( 216 prop_val, deep_copy, remove_private, recursive) 217 elif prop_val_type_name == 'list': 218 ret[prop_name] = [] 219 for item in prop_val: 220 if type(item).__name__ in recursion_types: 221 ret[prop_name].append(get_object_properties( 222 item, deep_copy, remove_private, recursive)) 223 else: 224 ret[prop_name].append(item) 225 elif prop_val_type_name == 'dict': 226 ret[prop_name] = {} 227 for key, value in prop_val.items(): 228 if type(value).__name__ in recursion_types: 229 ret[prop_name][key] = get_object_properties( 230 value, deep_copy, remove_private, recursive) 231 else: 232 ret[prop_name][key] = value 233 234 if remove_private: 235 for key in ret.keys(): 236 if key[0] == '_': 237 del ret[key] 238 239 return ret 240 241 242__all__ = ['smartctl_type', 'SMARTCTL_PATH', 243 'all_in', 'any_in', 'get_object_properties']
152def smartctl_type(interface_type: Optional[str]) -> Optional[str]: 153 """This method basically searchs on smartctl_type_dict to convert from internal 154 smartctl interface type to an understable type for smartctl. However, further 155 transforms may be performed for some special interfaces 156 157 Args: 158 interface_type (str): An internal representation of an smartctl interface type 159 160 Returns: 161 str: Returns the corresponding smartctl interface_type that matches with the internal interface representation. 162 In case it is not supported, None would be returned 163 """ 164 if interface_type is None: 165 return None 166 167 if interface_type in smartctl_type_dict: 168 return smartctl_type_dict[interface_type] 169 elif 'megaraid,' in interface_type: 170 return interface_type 171 else: 172 return None
This method basically searchs on smartctl_type_dict to convert from internal smartctl interface type to an understable type for smartctl. However, further transforms may be performed for some special interfaces
Args: interface_type (str): An internal representation of an smartctl interface type
Returns: str: Returns the corresponding smartctl interface_type that matches with the internal interface representation. In case it is not supported, None would be returned
108def all_in(search_in, *searched_items): 109 """ 110 return True if all of searched_items are in search_in otherwise False 111 does not care about duplicates in searched_items potentially evaluates all of them, 112 """ 113 assert len(searched_items) > 0 114 return all(map(lambda one: one in search_in, searched_items))
return True if all of searched_items are in search_in otherwise False does not care about duplicates in searched_items potentially evaluates all of them,
99def any_in(search_in, *searched_items): 100 """ 101 return True if any of searched_items is in search_in otherwise False. 102 raise 103 """ 104 assert len(searched_items) > 0 105 return any(map(lambda one: one in search_in, searched_items))
return True if any of searched_items is in search_in otherwise False. raise
175def get_object_properties(obj: Any, deep_copy: bool = True, remove_private: bool = False, recursive: bool = True) -> Optional[Dict[str, Any]]: 176 if obj is None: 177 return None 178 179 if not hasattr(obj, '__dict__'): 180 return obj 181 182 prop_names = dir(obj) 183 184 if deep_copy: 185 ret = copy.deepcopy(vars(obj)) 186 else: 187 ret = copy.copy(vars(obj)) 188 189 available_types = ['dict', 'str', 'int', 'float', 'list', 'NoneType'] 190 recursion_types = ['object', 191 'NvmeError', 'NvmeSelfTest', 'NvmeAttributes', 192 'AtaAttributes', 193 'SCSIAttributes', 'Diagnostics', 194 'Attribute', 'TestEntry', 195 ] 196 197 for prop_name in prop_names: 198 prop_val = getattr(obj, prop_name) 199 prop_val_type_name = type(prop_val).__name__ 200 201 if (prop_name[0] != '_'): 202 # Get properties from objects 203 if (prop_val_type_name in available_types) and (prop_name not in ret): 204 # Check if prop_val has __getstate__ method, if so, call it 205 # if hasattr(prop_val, '__getstate__'): 206 # prop_val_state = prop_val.__getstate__() 207 # if prop_val_state is not None: 208 # ret[prop_name] = prop_val 209 # continue # Do not do recursion 210 211 ret[prop_name] = prop_val 212 213 # Do recursion 214 if recursive: 215 if prop_val_type_name in recursion_types: 216 ret[prop_name] = get_object_properties( 217 prop_val, deep_copy, remove_private, recursive) 218 elif prop_val_type_name == 'list': 219 ret[prop_name] = [] 220 for item in prop_val: 221 if type(item).__name__ in recursion_types: 222 ret[prop_name].append(get_object_properties( 223 item, deep_copy, remove_private, recursive)) 224 else: 225 ret[prop_name].append(item) 226 elif prop_val_type_name == 'dict': 227 ret[prop_name] = {} 228 for key, value in prop_val.items(): 229 if type(value).__name__ in recursion_types: 230 ret[prop_name][key] = get_object_properties( 231 value, deep_copy, remove_private, recursive) 232 else: 233 ret[prop_name][key] = value 234 235 if remove_private: 236 for key in ret.keys(): 237 if key[0] == '_': 238 del ret[key] 239 240 return ret