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
 86
 87def any_in(search_in, *searched_items):
 88    """
 89    return True if any of searched_items is in search_in otherwise False.
 90    raise
 91    """
 92    assert len(searched_items) > 0
 93    return any(map(lambda one: one in search_in, searched_items))
 94
 95
 96def all_in(search_in, *searched_items):
 97    """
 98    return True if all of searched_items are in search_in otherwise False
 99    does not care about duplicates in searched_items potentially evaluates all of them,
100    """
101    assert len(searched_items) > 0
102    return all(map(lambda one: one in search_in, searched_items))
103
104
105smartctl_type_dict = {
106    'ata': 'ata',
107    'csmi': 'ata',
108    'nvme': 'nvme',
109    'sas': 'scsi',
110    'sat': 'sat',
111    'sata': 'ata',
112    'scsi': 'scsi',
113    'atacam': 'atacam'
114}
115"""
116**(dict of str):** Contains actual interface types (ie: sas, csmi) as keys and
117the corresponding smartctl interface type (ie: scsi, ata) as values.
118"""
119
120SMARTCTL_PATH = which('smartctl')
121
122
123def smartctl_isvalid_type(interface_type: str) -> bool:
124    """Tests if the interface_type is supported
125
126    Args:
127        interface_type (str): An internal interface_type
128
129    Returns:
130        bool: True if the type is supported, false z
131    """
132    if interface_type in smartctl_type_dict:
133        return True
134    elif 'megaraid,' in interface_type:
135        return True
136    else:
137        return False
138
139
140def smartctl_type(interface_type: Optional[str]) -> Optional[str]:
141    """This method basically searchs on smartctl_type_dict to convert from internal
142       smartctl interface type to an understable type for smartctl. However, further
143       transforms may be performed for some special interfaces
144
145    Args:
146        interface_type (str): An internal representation of an smartctl interface type
147
148    Returns:
149        str: Returns the corresponding smartctl interface_type that matches with the internal interface representation.
150             In case it is not supported, None would be returned
151    """
152    if interface_type is None:
153        return None
154
155    if interface_type in smartctl_type_dict:
156        return smartctl_type_dict[interface_type]
157    elif 'megaraid,' in interface_type:
158        return interface_type
159    else:
160        return None
161
162
163def get_object_properties(obj: Any, deep_copy: bool = True, remove_private: bool = False, recursive: bool = True) -> Optional[Dict[str, Any]]:
164    if obj is None:
165        return None
166
167    if not hasattr(obj, '__dict__'):
168        return obj
169
170    prop_names = dir(obj)
171
172    if deep_copy:
173        ret = copy.deepcopy(vars(obj))
174    else:
175        ret = vars(obj)
176
177    available_types = ['dict', 'str', 'int', 'float', 'list', 'NoneType']
178    recursion_types = ['object', 'NvmeError', 'NvmeSelfTest']
179
180    for prop_name in prop_names:
181        prop_val = getattr(obj, prop_name)
182        prop_val_type_name = type(prop_val).__name__
183
184        if (prop_name[0] != '_'):
185            # Get properties from objects
186            if (prop_val_type_name in available_types) and (prop_name not in ret):
187                ret[prop_name] = prop_val
188
189            # Do recursion
190            if recursive:
191                if prop_val_type_name in recursion_types:
192                    ret[prop_name] = get_object_properties(
193                        prop_val, deep_copy, remove_private, recursive)
194                elif prop_val_type_name == 'list':
195                    ret[prop_name] = []
196                    for item in prop_val:
197                        if type(item).__name__ in recursion_types:
198                            ret[prop_name].append(get_object_properties(
199                                item, deep_copy, remove_private, recursive))
200                        else:
201                            ret[prop_name].append(item)
202                elif prop_val_type_name == 'dict':
203                    ret[prop_name] = {}
204                    for key, value in prop_val.items():
205                        if type(value).__name__ in recursion_types:
206                            ret[prop_name][key] = get_object_properties(
207                                value, deep_copy, remove_private, recursive)
208                        else:
209                            ret[prop_name][key] = value
210
211    if remove_private:
212        for key in ret.keys():
213            if key[0] == '_':
214                del ret[key]
215
216    return ret
217
218
219__all__ = ['smartctl_type', 'SMARTCTL_PATH',
220           'all_in', 'any_in', 'get_object_properties']
def smartctl_type(interface_type: Optional[str]) -> Optional[str]:
141def smartctl_type(interface_type: Optional[str]) -> Optional[str]:
142    """This method basically searchs on smartctl_type_dict to convert from internal
143       smartctl interface type to an understable type for smartctl. However, further
144       transforms may be performed for some special interfaces
145
146    Args:
147        interface_type (str): An internal representation of an smartctl interface type
148
149    Returns:
150        str: Returns the corresponding smartctl interface_type that matches with the internal interface representation.
151             In case it is not supported, None would be returned
152    """
153    if interface_type is None:
154        return None
155
156    if interface_type in smartctl_type_dict:
157        return smartctl_type_dict[interface_type]
158    elif 'megaraid,' in interface_type:
159        return interface_type
160    else:
161        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):
 97def all_in(search_in, *searched_items):
 98    """
 99    return True if all of searched_items are in search_in otherwise False
100    does not care about duplicates in searched_items potentially evaluates all of them,
101    """
102    assert len(searched_items) > 0
103    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):
88def any_in(search_in, *searched_items):
89    """
90    return True if any of searched_items is in search_in otherwise False.
91    raise
92    """
93    assert len(searched_items) > 0
94    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]]:
164def get_object_properties(obj: Any, deep_copy: bool = True, remove_private: bool = False, recursive: bool = True) -> Optional[Dict[str, Any]]:
165    if obj is None:
166        return None
167
168    if not hasattr(obj, '__dict__'):
169        return obj
170
171    prop_names = dir(obj)
172
173    if deep_copy:
174        ret = copy.deepcopy(vars(obj))
175    else:
176        ret = vars(obj)
177
178    available_types = ['dict', 'str', 'int', 'float', 'list', 'NoneType']
179    recursion_types = ['object', 'NvmeError', 'NvmeSelfTest']
180
181    for prop_name in prop_names:
182        prop_val = getattr(obj, prop_name)
183        prop_val_type_name = type(prop_val).__name__
184
185        if (prop_name[0] != '_'):
186            # Get properties from objects
187            if (prop_val_type_name in available_types) and (prop_name not in ret):
188                ret[prop_name] = prop_val
189
190            # Do recursion
191            if recursive:
192                if prop_val_type_name in recursion_types:
193                    ret[prop_name] = get_object_properties(
194                        prop_val, deep_copy, remove_private, recursive)
195                elif prop_val_type_name == 'list':
196                    ret[prop_name] = []
197                    for item in prop_val:
198                        if type(item).__name__ in recursion_types:
199                            ret[prop_name].append(get_object_properties(
200                                item, deep_copy, remove_private, recursive))
201                        else:
202                            ret[prop_name].append(item)
203                elif prop_val_type_name == 'dict':
204                    ret[prop_name] = {}
205                    for key, value in prop_val.items():
206                        if type(value).__name__ in recursion_types:
207                            ret[prop_name][key] = get_object_properties(
208                                value, deep_copy, remove_private, recursive)
209                        else:
210                            ret[prop_name][key] = value
211
212    if remove_private:
213        for key in ret.keys():
214            if key[0] == '_':
215                del ret[key]
216
217    return ret