Add module for loading specific classes
This adds nova/loadables.py which contains code that is to be shared by host and cell scheduling filtering and weighing. Most of this code originated from nova/scheduler/filters/__init__.py (which was copied from nova/api extension loading). This makes it more generic so that it can be shared. Note that this functionality is quite different than the generic plugin manager that exists in openstack-common. That code could not be used here without some significant changes. It also seems pretty overkill for the functionality required by scheduling filtering and weighing. Change-Id: I1b217dc2bc2d1dc2235c8251318d06b46597e8f4
This commit is contained in:
parent
3998cc2afc
commit
8ee7798af7
116
nova/loadables.py
Normal file
116
nova/loadables.py
Normal file
@ -0,0 +1,116 @@
|
||||
# Copyright (c) 2011-2012 OpenStack, LLC.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
Generic Loadable class support.
|
||||
|
||||
Meant to be used by such things as scheduler filters and weights where we
|
||||
want to load modules from certain directories and find certain types of
|
||||
classes within those modules. Note that this is quite different than
|
||||
generic plugins and the pluginmanager code that exists elsewhere.
|
||||
|
||||
Usage:
|
||||
|
||||
Create a directory with an __init__.py with code such as:
|
||||
|
||||
class SomeLoadableClass(object):
|
||||
pass
|
||||
|
||||
|
||||
class MyLoader(nova.loadables.BaseLoader)
|
||||
def __init__(self):
|
||||
super(MyLoader, self).__init__(SomeLoadableClass)
|
||||
|
||||
If you create modules in the same directory and subclass SomeLoadableClass
|
||||
within them, MyLoader().get_all_classes() will return a list
|
||||
of such classes.
|
||||
"""
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
|
||||
from nova import exception
|
||||
from nova.openstack.common import importutils
|
||||
|
||||
|
||||
class BaseLoader(object):
|
||||
def __init__(self, loadable_cls_type):
|
||||
mod = sys.modules[self.__class__.__module__]
|
||||
self.path = mod.__path__[0]
|
||||
self.package = mod.__package__
|
||||
self.loadable_cls_type = loadable_cls_type
|
||||
|
||||
def _is_correct_class(self, obj):
|
||||
"""Return whether an object is a class of the correct type and
|
||||
is not prefixed with an underscore.
|
||||
"""
|
||||
return (inspect.isclass(obj) and
|
||||
(not obj.__name__.startswith('_')) and
|
||||
issubclass(obj, self.loadable_cls_type))
|
||||
|
||||
def _get_classes_from_module(self, module_name):
|
||||
"""Get the classes from a module that match the type we want."""
|
||||
classes = []
|
||||
module = importutils.import_module(module_name)
|
||||
for obj_name in dir(module):
|
||||
# Skip objects that are meant to be private.
|
||||
if obj_name.startswith('_'):
|
||||
continue
|
||||
itm = getattr(module, obj_name)
|
||||
if self._is_correct_class(itm):
|
||||
classes.append(itm)
|
||||
return classes
|
||||
|
||||
def get_all_classes(self):
|
||||
"""Get the classes of the type we want from all modules found
|
||||
in the directory that defines this class.
|
||||
"""
|
||||
classes = []
|
||||
for dirpath, dirnames, filenames in os.walk(self.path):
|
||||
relpath = os.path.relpath(dirpath, self.path)
|
||||
if relpath == '.':
|
||||
relpkg = ''
|
||||
else:
|
||||
relpkg = '.%s' % '.'.join(relpath.split(os.sep))
|
||||
for fname in filenames:
|
||||
root, ext = os.path.splitext(fname)
|
||||
if ext != '.py' or root == '__init__':
|
||||
continue
|
||||
module_name = "%s%s.%s" % (self.package, relpkg, root)
|
||||
mod_classes = self._get_classes_from_module(module_name)
|
||||
classes.extend(mod_classes)
|
||||
return classes
|
||||
|
||||
def get_matching_classes(self, loadable_class_names):
|
||||
"""Get loadable classes from a list of names. Each name can be
|
||||
a full module path or the full path to a method that returns
|
||||
classes to use. The latter behavior is useful to specify a method
|
||||
that returns a list of classes to use in a default case.
|
||||
"""
|
||||
classes = []
|
||||
for cls_name in loadable_class_names:
|
||||
obj = importutils.import_class(cls_name)
|
||||
if self._is_correct_class(obj):
|
||||
classes.append(obj)
|
||||
elif inspect.isfunction(obj):
|
||||
# Get list of classes from a function
|
||||
for cls in obj():
|
||||
classes.append(cls)
|
||||
else:
|
||||
error_str = 'Not a class of the correct type'
|
||||
raise exception.ClassNotFound(class_name=cls_name,
|
||||
exception=error_str)
|
||||
return classes
|
Loading…
Reference in New Issue
Block a user