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']
def smartctl_type(interface_type: Optional[str]) -> Optional[str]:
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

SMARTCTL_PATH = None
def all_in(search_in, *searched_items):
 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,

def any_in(search_in, *searched_items):
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

def get_object_properties( obj: Any, deep_copy: bool = True, remove_private: bool = False, recursive: bool = True) -> Optional[Dict[str, Any]]:
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