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

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

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

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