Package pyhai
[hide private]
[frames] | no frames]

Source Code for Package pyhai

  1  """ 
  2  @copyright: 2011 Mark LaPerriere 
  3   
  4  @license: 
  5      Licensed under the Apache License, Version 2.0 (the "License"); 
  6      you may not use this file except in compliance with the License. 
  7      You may obtain a copy of the License at 
  8   
  9      U{http://www.apache.org/licenses/LICENSE-2.0} 
 10   
 11      Unless required by applicable law or agreed to in writing, software 
 12      distributed under the License is distributed on an "AS IS" BASIS, 
 13      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 14      See the License for the specific language governing permissions and 
 15      limitations under the License. 
 16   
 17  @summary: 
 18      pyHai base classes 
 19   
 20  @author: Mark LaPerriere 
 21  @contact: pyhai@mindmind.com 
 22  @organization: Mind Squared Design / www.mindmind.com 
 23  @version: 0.1.3 
 24  @date: Jan 19, 2012 
 25  """ 
 26  import abc 
 27  import os 
 28  import sys 
 29  import datetime 
 30  import time 
 31  import utils 
 32  import profilers.base 
 33  import profilers.default 
 34   
 35  import logging 
 36  # set some default logging behavior 
 37  _logger = logging.getLogger(__name__) 
 38  _logger.addHandler(logging.NullHandler()) 
 39   
 40  # current version 
 41  __VERSION__ = (0, 1, 3) 
 42  VERSION = '.'.join(map(str, __VERSION__)) 
 43   
 44  # set some default variables 
 45  PACKAGE_PLUGIN_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'plugins') 
 46  DEFAULT_CUSTOM_PLUGIN_PATH = os.path.join(os.path.dirname(os.path.abspath('.')), 'plugins') 
 47  PLUGIN_LOADER_EXCLUSIONS = ('.', '..', '__init__.py', '__init__.pyc', '__init__.pyo') 
 48  DEFAULT_PROFILER_CLASS = profilers.default.DefaultProfiler 
 49   
 50   
51 -class Auditor(object):
52 """ 53 Auditor class 54 55 @ivar custom_plugin_path: The path to any custom plugins 56 @type custom_plugin_path: C{str} 57 @ivar profile: A dictionary of properties for this host 58 @type profile: C{dict} 59 @ivar architecture: The name of the architecture as is normally returned by platform.architecture()[0] 60 @type architecture: C{str} 61 @ivar plugins: A list of successfully loaded plugins 62 @type plugins: C{list} 63 """ 64 plugin_paths = [PACKAGE_PLUGIN_PATH] 65 profile = None 66 plugins = {} 67
68 - def __init__(self, plugin_paths=None, **kwargs):
69 """ 70 Initialize System object 71 72 @param plugin_paths: A path (or list of paths) to a custom set of plugins 73 @type plugin_paths: C{str | list} 74 @keyword profiler_class: The name of a class that extends L{ProfilerBase} or the name of the module where the 75 L{ProfilerBase} class can be found. If supplying a module, must supply the profiler_class keyword arg 76 @type profiler_class: C{class | str} 77 @keyword enable_default_plugins: A flag to use (or suppress) the builtin plugins 78 @type enable_default_plugins: C{bool} 79 @keyword profiler_package: The name of the package that contains a class that extends L{ProfilerBase} 80 @type profiler_package: C{str} 81 """ 82 profiler_class = kwargs.get('profiler_class', DEFAULT_PROFILER_CLASS) 83 profiler_package = kwargs.get('profiler_package', None) 84 enable_default_plugins = kwargs.get('enable_default_plugins', True) 85 86 87 if type(profiler_class) is str: 88 profiler_package = kwargs.get('profiler_package', '') 89 if profiler_package: 90 profiler_class = self.__load_module(profiler_package, profiler_class) 91 else: 92 profiler_class = self.__load_module(profiler_class) 93 94 if issubclass(profiler_class, profilers.base.ProfilerBase): 95 profiler = profiler_class() 96 if hasattr(profiler, 'profile'): 97 self.profile = profiler.profile() 98 if type(self.profile) is dict and 'pyhai_version' not in self.profile: 99 self.profile['pyhai_version'] = VERSION 100 self.system_class = profiler.system_class() 101 self.system = profiler.system() 102 _logger.debug('Successfully loaded the profiler: %s', profiler.__class__.__name__) 103 else: 104 raise Exception('Failed to load a valid profiler: %s' % profiler.__class__.__name__) 105 else: 106 raise Exception('Arguments supplied for profiler are not valid') 107 108 if plugin_paths: 109 if type(plugin_paths) is str: 110 plugin_paths = [plugin_paths] 111 112 if enable_default_plugins: 113 for path in plugin_paths: 114 if path not in self.plugin_paths: 115 self.plugin_paths.append(path) 116 else: 117 self.plugin_paths = plugin_paths 118 elif not plugin_paths and not enable_default_plugins: 119 # TODO: Is there a better way to do this? Would like to stop execution and allow for exception information to 120 # be sent to the logger. 121 try: 122 raise ValueError('Incompatible init params... plugin_paths is empty and enable_default_plugins=False') 123 except: 124 _logger.exception('Must provide a list for plugin_paths or set enable_default_plugins=True. Nothing to do.') 125 raise 126 127 for path in self.plugin_paths: 128 sys.path.insert(0, path) 129 _logger.debug('Setting plugin_paths to: ["%s"]', '", "'.join(self.plugin_paths)) 130 self.__load_plugins(self.system_class, self.system)
131 132
133 - def __load_profiler(self, profiler, package=''):
134 """ 135 Loads a profiler plugin 136 137 @param profiler: The name of a class that extends L{ProfilerBase} 138 @type profiler: C{str} 139 @param package: The name of the package where the profiler exists 140 @type package: C{str} 141 """ 142 return self.__load_module(profiler, package)
143 144
145 - def __load_plugins(self, system_class, system, plugin_module=None):
146 """ 147 Imports plugin modules and stores the list of successfully loaded plugins 148 149 @param plugin_module: A specific plugin_module to load 150 @type plugin_module: C{str} 151 """ 152 _logger.info('Loading plugins...') 153 if plugin_module: 154 raise NotImplementedError('Planned for future release') 155 else: 156 plugin_map = self.__resolve_plugin_paths(system_class, system) 157 for plugin, (path, namespace) in plugin_map.items(): 158 _logger.debug('Loading plugin: %s, path: %s, package: %s', plugin, path, namespace) 159 if path not in sys.path: 160 sys.path.insert(0, path) 161 try: 162 plugin_class = '%s%s' % (utils._underscore_to_camel_case(plugin), 'Plugin') 163 self.plugins[plugin] = self.__load_module(namespace, plugin_class) 164 _logger.debug('Loaded plugin: %s [%s::%s]', plugin, namespace, plugin_class) 165 except: 166 _logger.exception('Failed to load plugin: %s [%s::%s]', plugin, namespace, plugin_class)
167 168
169 - def __load_module(self, module, cls=''):
170 """ 171 Loads a module and class dynamically return a reference to the class 172 173 @param module: The name of module to load 174 @type module: C{str} 175 @param cls: The name of a class to load 176 @type cls: C{str} 177 @return: A reference to the loaded class 178 @rtype: C{class} 179 """ 180 try: 181 module_instance = __import__(module, globals(), locals(), [cls]) 182 if cls and hasattr(module_instance, cls): 183 _logger.debug('Successfully loaded module: %s, class: %s', module, cls) 184 return getattr(module_instance, cls) 185 except: 186 _logger.exception('Failed to import module: %s, class: %s', module, cls) 187 raise
188 189
190 - def __resolve_plugin_paths(self, system_class, system, **kwargs):
191 """ 192 Checks plugin paths for validity and returns only those that are valid 193 194 @param system: The type of system 195 @type system: C{str} 196 @keyword plugin_paths: A path (or list of paths) to a custom set of plugins 197 @type plugin_paths: C{str}|C{list} 198 @return: A list of valid plugin paths 199 @rtype: C{list} 200 """ 201 plugins = {} 202 plugin_paths = kwargs.get('plugin_paths', self.plugin_paths) 203 for plugin_path in plugin_paths: 204 _logger.debug('Searching plugin path: %s', plugin_path) 205 system_class_path = os.path.join(plugin_path, system_class) 206 system_path = os.path.join(system_class_path, system) 207 208 valid_plugins = self.__validate_plugins(plugin_path) 209 valid_plugins = self.__validate_plugins(system_class_path, system_class, valid_plugins) 210 valid_plugins = self.__validate_plugins(system_path, '%s.%s' % (system_class, system), valid_plugins) 211 212 if valid_plugins is not None and len(valid_plugins) > 0: 213 _logger.debug('Merging plugins dictionaries') 214 plugins = dict(plugins.items() + valid_plugins.items()) 215 216 return plugins
217 218
219 - def __validate_plugins(self, path, base=None, plugins=None):
220 """ 221 Performs an initial sanity check on all the plugins found in a path 222 223 @param path: The path to look for plugins 224 @type path: C{str} 225 @param base: The base of the package name if path is a subfolder of a python package 226 - I{To assist with the import, i.e. from package.module import plugin} 227 @type base: C{str} 228 """ 229 if plugins is None: 230 _logger.debug('Creating plugins dict') 231 plugins = {} 232 233 if os.path.exists(path) and os.path.isdir(path): 234 for plugin_entry in os.listdir(path): 235 _logger.debug('Validating file as plugin: %s', plugin_entry) 236 if plugin_entry in PLUGIN_LOADER_EXCLUSIONS or os.path.isdir(plugin_entry) or not plugin_entry.endswith('.py') or plugin_entry.startswith('_'): 237 continue 238 239 plugin = plugin_entry[:-3] 240 package = plugin 241 242 if base is not None: 243 package = '%s.%s' % (base, plugin) 244 _logger.debug('package: %s', package) 245 246 _logger.debug('File appears to be a valid plugin: %s, package: %s [%s]', plugin, package, os.path.join(path, plugin_entry)) 247 248 plugins[plugin] = (path, package) 249 250 return plugins
251 252
253 - def audit(self, convert_date_to_iso=True):
254 """ 255 Profiles the system using the default plugins and all custom plugins, returning a dictionary of the results 256 257 @param convert_date_to_iso: Converts the 'audit_completed' date to iso format before returning 258 @type convert_date_to_iso: C{bool} 259 @return: A dictionary representing the current state of the system 260 @rtype: C{dict} 261 """ 262 start = time.time() 263 if len(self.plugins) <= 0: 264 # TODO: Same deal as TODO in class __init__... 265 try: 266 raise ValueError('No plugins found') 267 except: 268 _logger.exception('List of plugins is empty. Nothing to do.') 269 raise 270 271 results = {'profile': self.profile} 272 for plugin_name, plugin_class in self.plugins.items(): 273 _logger.debug('Running plugin: %s [%s]', plugin_name, plugin_class.__name__) 274 plugin = plugin_class(self.profile, results) 275 results[plugin_name] = plugin._get_results() 276 end = time.time() 277 now = datetime.datetime.now() 278 if convert_date_to_iso: 279 results['profile']['audit_completed'] = now.isoformat() 280 else: 281 results['profile']['audit_completed'] = now 282 results['profile']['audit_took_sec'] = end - start 283 return results
284 285 286
287 -def audit(plugin_paths=DEFAULT_CUSTOM_PLUGIN_PATH, **kwargs):
288 """ 289 Instatiates a System object and executes it's profile method 290 291 @param plugin_paths: A path (or list of paths) to a custom set of plugins 292 @type plugin_paths: C{str}|C{list} 293 @keyword debug: Set the logging level to DEBUG 294 @type debug: C{bool} 295 """ 296 debug = kwargs.pop('debug', False) 297 298 if debug: 299 _logger.setLevel(logging.DEBUG) 300 301 auditor = Auditor(plugin_paths, **kwargs) 302 return auditor.audit()
303