summaryrefslogtreecommitdiff
path: root/nova/loadables.py
diff options
context:
space:
mode:
authorChris Behrens <cbehrens@codestud.com>2012-11-12 23:33:01 +0000
committerChris Behrens <cbehrens@codestud.com>2012-11-14 19:01:57 +0000
commit56778928303b74112a83e9208f107b9fa06f12e7 (patch)
tree28d7c18f7bb69bf157f8d8eaa0e8fc97cfd0c659 /nova/loadables.py
parent16266a4afbfdbed5e3c2334e03e2c9c43b133020 (diff)
downloadnova-56778928303b74112a83e9208f107b9fa06f12e7.tar.gz
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
Diffstat (limited to 'nova/loadables.py')
-rw-r--r--nova/loadables.py116
1 files changed, 116 insertions, 0 deletions
diff --git a/nova/loadables.py b/nova/loadables.py
new file mode 100644
index 0000000000..0c930267ef
--- /dev/null
+++ b/nova/loadables.py
@@ -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