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