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 hasattr(obj, '__getstate__'):
168        obj_state = obj.__getstate__()
169        if obj_state is not None:
170            return obj_state
171
172    if not hasattr(obj, '__dict__'):
173        return obj
174
175    prop_names = dir(obj)
176
177    if deep_copy:
178        ret = copy.deepcopy(vars(obj))
179    else:
180        ret = copy.copy(vars(obj))
181
182    available_types = ['dict', 'str', 'int', 'float', 'list', 'NoneType']
183    recursion_types = ['object',
184                       'NvmeError', 'NvmeSelfTest', 'NvmeAttributes',
185                       'AtaAttributes', 'Attribute',
186                       ]
187
188    for prop_name in prop_names:
189        prop_val = getattr(obj, prop_name)
190        prop_val_type_name = type(prop_val).__name__
191
192        if (prop_name[0] != '_'):
193            # Get properties from objects
194            if (prop_val_type_name in available_types) and (prop_name not in ret):
195                # Check if prop_val has __getstate__ method, if so, call it
196                if hasattr(prop_val, '__getstate__'):
197                    prop_val_state = prop_val.__getstate__()
198                    if prop_val_state is not None:
199                        ret[prop_name] = prop_val
200                        continue  # Do not do recursion
201
202                ret[prop_name] = prop_val
203
204            # Do recursion
205            if recursive:
206                if prop_val_type_name in recursion_types:
207                    ret[prop_name] = get_object_properties(
208                        prop_val, deep_copy, remove_private, recursive)
209                elif prop_val_type_name == 'list':
210                    ret[prop_name] = []
211                    for item in prop_val:
212                        if type(item).__name__ in recursion_types:
213                            ret[prop_name].append(get_object_properties(
214                                item, deep_copy, remove_private, recursive))
215                        else:
216                            ret[prop_name].append(item)
217                elif prop_val_type_name == 'dict':
218                    ret[prop_name] = {}
219                    for key, value in prop_val.items():
220                        if type(value).__name__ in recursion_types:
221                            ret[prop_name][key] = get_object_properties(
222                                value, deep_copy, remove_private, recursive)
223                        else:
224                            ret[prop_name][key] = value
225
226    if remove_private:
227        for key in ret.keys():
228            if key[0] == '_':
229                del ret[key]
230
231    return ret
232
233
234__all__ = ['smartctl_type', 'SMARTCTL_PATH',
235           '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 hasattr(obj, '__getstate__'):
169        obj_state = obj.__getstate__()
170        if obj_state is not None:
171            return obj_state
172
173    if not hasattr(obj, '__dict__'):
174        return obj
175
176    prop_names = dir(obj)
177
178    if deep_copy:
179        ret = copy.deepcopy(vars(obj))
180    else:
181        ret = copy.copy(vars(obj))
182
183    available_types = ['dict', 'str', 'int', 'float', 'list', 'NoneType']
184    recursion_types = ['object',
185                       'NvmeError', 'NvmeSelfTest', 'NvmeAttributes',
186                       'AtaAttributes', 'Attribute',
187                       ]
188
189    for prop_name in prop_names:
190        prop_val = getattr(obj, prop_name)
191        prop_val_type_name = type(prop_val).__name__
192
193        if (prop_name[0] != '_'):
194            # Get properties from objects
195            if (prop_val_type_name in available_types) and (prop_name not in ret):
196                # Check if prop_val has __getstate__ method, if so, call it
197                if hasattr(prop_val, '__getstate__'):
198                    prop_val_state = prop_val.__getstate__()
199                    if prop_val_state is not None:
200                        ret[prop_name] = prop_val
201                        continue  # Do not do recursion
202
203                ret[prop_name] = prop_val
204
205            # Do recursion
206            if recursive:
207                if prop_val_type_name in recursion_types:
208                    ret[prop_name] = get_object_properties(
209                        prop_val, deep_copy, remove_private, recursive)
210                elif prop_val_type_name == 'list':
211                    ret[prop_name] = []
212                    for item in prop_val:
213                        if type(item).__name__ in recursion_types:
214                            ret[prop_name].append(get_object_properties(
215                                item, deep_copy, remove_private, recursive))
216                        else:
217                            ret[prop_name].append(item)
218                elif prop_val_type_name == 'dict':
219                    ret[prop_name] = {}
220                    for key, value in prop_val.items():
221                        if type(value).__name__ in recursion_types:
222                            ret[prop_name][key] = get_object_properties(
223                                value, deep_copy, remove_private, recursive)
224                        else:
225                            ret[prop_name][key] = value
226
227    if remove_private:
228        for key in ret.keys():
229            if key[0] == '_':
230                del ret[key]
231
232    return ret