def __init__(self): self.loaded_plugins = {} self.errored_plugins = [] self.resource_class_id_to_class = {} self.resource_class_class_to_id = {} from settings import INSTALLED_STORAGE_PLUGINS for plugin in INSTALLED_STORAGE_PLUGINS: try: self.load_plugin(plugin) except (ImportError, SyntaxError, ResourceProgrammingError, PluginProgrammingError) as e: storage_plugin_log.error("Failed to load plugin '%s': %s" % (plugin, traceback.format_exc())) self.errored_plugins.append((plugin, e)) for id, klass in self.resource_class_id_to_class.items(): klass._meta.relations = list(klass._meta.orig_relations) def can_satisfy_relation(klass, attributes): for attribute in attributes: if not attribute in klass._meta.storage_attributes: return False return True for id, klass in self.resource_class_id_to_class.items(): for relation in klass._meta.relations: # If ('linux', 'ScsiDevice') form was used, substitute the real class if isinstance(relation, relations.Provide): if isinstance(relation.provide_to, tuple): prov_klass, prov_klass_id = self.get_plugin_resource_class( *relation.provide_to) relation.provide_to = prov_klass elif isinstance(relation, relations.Subscribe): if isinstance(relation.subscribe_to, tuple): sub_klass, sub_klass_id = self.get_plugin_resource_class( *relation.subscribe_to) relation.subscribe_to = sub_klass # Generate reverse-Subscribe relations if isinstance(relation, relations.Provide): # Synthesize Subscribe objects on the objects which might # be on the receiving event of a Provide relation. The original # Provide object plays no further role. subscription = relations.Subscribe(klass, relation.attributes, relation.ignorecase) if can_satisfy_relation(relation.provide_to, relation.attributes): relation.provide_to._meta.relations.append( subscription) for sc in all_subclasses(relation.provide_to): if can_satisfy_relation(sc, relation.attributes): sc._meta.relations.append(subscription)
def __getattr__(self, key): if key.startswith("_") or not key in self._meta.storage_attributes: raise AttributeError("Unknown attribute %s" % key) else: try: return self._storage_dict[key] except KeyError: attr = self._meta.storage_attributes[key] if attr.optional: return None elif attr.default is not None: if callable(attr.default): return attr.default(self._storage_dict) else: return attr.default else: log.error("Missing attribute %s, %s" % (key, self._storage_dict)) raise AttributeError("attribute %s not found" % key)
def load_plugin(self, module): """Load a BaseStoragePlugin class from a module given a python path like chroma_core.lib.lvm', or simply return it if it was already loaded. Note that the BaseStoragePlugin within the module will not be instantiated when this returns, caller is responsible for instantiating it. @return A subclass of BaseStoragePlugin""" if module in self.loaded_plugins: raise PluginProgrammingError("Duplicate storage plugin module %s" % module) if module in sys.modules: storage_plugin_log.warning("Reloading module %s (okay if testing)" % module) mod = sys.modules[module] else: # Load the module try: mod = __import__(module) except (ImportError, ResourceProgrammingError, SyntaxError) as e: storage_plugin_log.error("Error importing %s: %s" % (module, e)) raise components = module.split(".") plugin_name = module for comp in components[1:]: mod = getattr(mod, comp) plugin_name = comp plugin_module = mod self._validate_api_version(plugin_module) # Find all BaseStoragePlugin subclasses in the module from chroma_core.lib.storage_plugin.api.plugin import Plugin plugin_klasses = [] import inspect for name, cls in inspect.getmembers(plugin_module): if ( inspect.isclass(cls) and issubclass(cls, BaseStoragePlugin) and cls != BaseStoragePlugin and cls != Plugin ): plugin_klasses.append(cls) # Make sure we have exactly one BaseStoragePlugin subclass if len(plugin_klasses) > 1: raise PluginProgrammingError( "Module %s defines more than one BaseStoragePlugin: %s!" % (module, plugin_klasses) ) elif len(plugin_klasses) == 0: raise PluginProgrammingError("Module %s does not define a BaseStoragePlugin!" % module) else: plugin_klass = plugin_klasses[0] # Hook in a logger to the BaseStoragePlugin subclass if not plugin_klass._log: import logging import settings log = logging.getLogger("storage_plugin_log_%s" % module) if module in settings.STORAGE_PLUGIN_DEBUG_PLUGINS or settings.STORAGE_PLUGIN_DEBUG: log.setLevel(logging.DEBUG) else: log.setLevel(logging.WARNING) plugin_klass._log = log plugin_klass._log_format = "[%%(asctime)s: %%(levelname)s/%s] %%(message)s" % module else: storage_plugin_log.warning("Double load of %s (okay if testing)" % plugin_name) try: self._load_plugin(plugin_module, plugin_name, plugin_klass) except ResourceProgrammingError as e: storage_plugin_log.error("Error loading %s: %s" % (plugin_name, e)) raise else: return plugin_klass