summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin L. Mitchell <kevin.mitchell@rackspace.com>2011-10-20 18:07:02 -0500
committerKevin L. Mitchell <kevin.mitchell@rackspace.com>2011-10-20 18:07:02 -0500
commit3480cac1893c697aa290e6c3f56b2b7d97007adc (patch)
tree3c3a6290f7f619af210384b5befda7c303e2c939
parent75a3fbb21eebd4de8775b63c327d9d57859d090c (diff)
Refactoring of extensions
This is a refactoring of API extensions. Changes include better use of Python idioms and improved support for loading extensions. Change-Id: I9279c4e5781f049ab9e0e995f6aeda48f17c5831
-rw-r--r--nova/api/openstack/contrib/__init__.py68
-rw-r--r--nova/api/openstack/contrib/admin_actions.py30
-rw-r--r--nova/api/openstack/contrib/createserverext.py19
-rw-r--r--nova/api/openstack/contrib/deferred_delete.py26
-rw-r--r--nova/api/openstack/contrib/diskconfig.py21
-rw-r--r--nova/api/openstack/contrib/flavorextradata.py20
-rw-r--r--nova/api/openstack/contrib/flavorextraspecs.py20
-rw-r--r--nova/api/openstack/contrib/floating_ips.py26
-rw-r--r--nova/api/openstack/contrib/hosts.py18
-rw-r--r--nova/api/openstack/contrib/keypairs.py20
-rw-r--r--nova/api/openstack/contrib/multinic.py38
-rw-r--r--nova/api/openstack/contrib/quotas.py19
-rw-r--r--nova/api/openstack/contrib/rescue.py27
-rw-r--r--nova/api/openstack/contrib/security_groups.py25
-rw-r--r--nova/api/openstack/contrib/simple_tenant_usage.py18
-rw-r--r--nova/api/openstack/contrib/virtual_interfaces.py19
-rw-r--r--nova/api/openstack/contrib/virtual_storage_arrays.py19
-rw-r--r--nova/api/openstack/contrib/volumes.py18
-rw-r--r--nova/api/openstack/contrib/volumetypes.py20
-rw-r--r--nova/api/openstack/contrib/zones.py25
-rw-r--r--nova/api/openstack/extensions.py159
-rw-r--r--nova/flags.py5
-rw-r--r--nova/tests/api/openstack/extensions/foxinsocks.py23
-rw-r--r--nova/tests/api/openstack/test_extensions.py62
-rw-r--r--nova/tests/integrated/test_extensions.py7
25 files changed, 291 insertions, 461 deletions
diff --git a/nova/api/openstack/contrib/__init__.py b/nova/api/openstack/contrib/__init__.py
index acb5eb280..b3d4df69b 100644
--- a/nova/api/openstack/contrib/__init__.py
+++ b/nova/api/openstack/contrib/__init__.py
@@ -20,3 +20,71 @@
It can't be called 'extensions' because that causes namespacing problems.
"""
+
+import os
+
+from nova import exception
+from nova import log as logging
+from nova import utils
+
+
+LOG = logging.getLogger('nova.api.openstack.contrib')
+
+
+def standard_extensions(ext_mgr):
+ """Registers all standard API extensions."""
+
+ # Walk through all the modules in our directory...
+ our_dir = __path__[0]
+ for dirpath, dirnames, filenames in os.walk(our_dir):
+ # Compute the relative package name from the dirpath
+ relpath = os.path.relpath(dirpath, our_dir)
+ if relpath == '.':
+ relpkg = ''
+ else:
+ relpkg = '.%s' % '.'.join(relpath.split(os.sep))
+
+ # Now, consider each file in turn, only considering .py files
+ for fname in filenames:
+ root, ext = os.path.splitext(fname)
+
+ # Skip __init__ and anything that's not .py
+ if ext != '.py' or root == '__init__':
+ continue
+
+ # Try loading it
+ classname = ("%s%s.%s.%s%s" %
+ (__package__, relpkg, root,
+ root[0].upper(), root[1:]))
+ try:
+ ext_mgr.load_extension(classname)
+ except Exception as exc:
+ LOG.warn(_('Failed to load extension %(classname)s: '
+ '%(exc)s') % locals())
+
+ # Now, let's consider any subdirectories we may have...
+ subdirs = []
+ for dname in dirnames:
+ # Skip it if it does not have __init__.py
+ if not os.path.exists(os.path.join(dirpath, dname,
+ '__init__.py')):
+ continue
+
+ # If it has extension(), delegate...
+ ext_name = ("%s%s.%s.extension" %
+ (__package__, relpkg, dname))
+ try:
+ ext = utils.import_class(ext_name)
+ except exception.ClassNotFound:
+ # extension() doesn't exist on it, so we'll explore
+ # the directory for ourselves
+ subdirs.append(dname)
+ else:
+ try:
+ ext(ext_mgr)
+ except Exception as exc:
+ LOG.warn(_('Failed to load extension %(ext_name)s: '
+ '%(exc)s') % locals())
+
+ # Update the list of directories we'll explore...
+ dirnames[:] = subdirs
diff --git a/nova/api/openstack/contrib/admin_actions.py b/nova/api/openstack/contrib/admin_actions.py
index be00eaf29..f501e9a41 100644
--- a/nova/api/openstack/contrib/admin_actions.py
+++ b/nova/api/openstack/contrib/admin_actions.py
@@ -34,10 +34,17 @@ LOG = logging.getLogger("nova.api.openstack.contrib.admin_actions")
class Admin_actions(extensions.ExtensionDescriptor):
- """Adds a set of admin-only actions to servers"""
+ """Adds admin-only server actions: pause, unpause, suspend,
+ resume, migrate, resetNetwork, injectNetworkInfo, lock and unlock
+ """
- def __init__(self):
- super(Admin_actions, self).__init__()
+ name = "AdminActions"
+ alias = "os-admin-actions"
+ namespace = "http://docs.openstack.org/ext/admin-actions/api/v1.1"
+ updated = "2011-09-20T00:00:00+00:00"
+
+ def __init__(self, ext_mgr):
+ super(Admin_actions, self).__init__(ext_mgr)
self.compute_api = compute.API()
@extensions.admin_only
@@ -164,23 +171,6 @@ class Admin_actions(extensions.ExtensionDescriptor):
raise exc.HTTPUnprocessableEntity()
return webob.Response(status_int=202)
- def get_name(self):
- return "AdminActions"
-
- def get_alias(self):
- return "os-admin-actions"
-
- def get_description(self):
- return "Adds admin-only server actions: pause, unpause, " + \
- "suspend, resume, migrate, resetNetwork, " +\
- "injectNetworkInfo, lock and unlock"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/admin-actions/api/v1.1"
-
- def get_updated(self):
- return "2011-09-20T00:00:00+00:00"
-
def get_actions(self):
actions = [
extensions.ActionExtension("servers", "pause", self._pause),
diff --git a/nova/api/openstack/contrib/createserverext.py b/nova/api/openstack/contrib/createserverext.py
index 7546bdeaa..da95164e8 100644
--- a/nova/api/openstack/contrib/createserverext.py
+++ b/nova/api/openstack/contrib/createserverext.py
@@ -40,21 +40,12 @@ class CreateServerController(servers.Controller):
class Createserverext(extensions.ExtensionDescriptor):
- """The servers create ext"""
- def get_name(self):
- return "Createserverext"
+ """Extended support to the Create Server v1.1 API"""
- def get_alias(self):
- return "os-create-server-ext"
-
- def get_description(self):
- return "Extended support to the Create Server v1.1 API"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/createserverext/api/v1.1"
-
- def get_updated(self):
- return "2011-07-19T00:00:00+00:00"
+ name = "Createserverext"
+ alias = "os-create-server-ext"
+ namespace = "http://docs.openstack.org/ext/createserverext/api/v1.1"
+ updated = "2011-07-19T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/deferred_delete.py b/nova/api/openstack/contrib/deferred_delete.py
index 13ee5511e..58223e901 100644
--- a/nova/api/openstack/contrib/deferred_delete.py
+++ b/nova/api/openstack/contrib/deferred_delete.py
@@ -31,8 +31,15 @@ LOG = logging.getLogger("nova.api.contrib.deferred-delete")
class Deferred_delete(extensions.ExtensionDescriptor):
- def __init__(self):
- super(Deferred_delete, self).__init__()
+ """Instance deferred delete"""
+
+ name = "DeferredDelete"
+ alias = "os-deferred-delete"
+ namespace = "http://docs.openstack.org/ext/deferred-delete/api/v1.1"
+ updated = "2011-09-01T00:00:00+00:00"
+
+ def __init__(self, ext_mgr):
+ super(Deferred_delete, self).__init__(ext_mgr)
self.compute_api = compute.API()
def _restore(self, input_dict, req, instance_id):
@@ -49,21 +56,6 @@ class Deferred_delete(extensions.ExtensionDescriptor):
self.compute_api.force_delete(context, instance_id)
return webob.Response(status_int=202)
- def get_name(self):
- return "DeferredDelete"
-
- def get_alias(self):
- return "os-deferred-delete"
-
- def get_description(self):
- return "Instance deferred delete"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/deferred-delete/api/v1.1"
-
- def get_updated(self):
- return "2011-09-01T00:00:00+00:00"
-
def get_actions(self):
"""Return the actions the extension adds, as required by contract."""
actions = [
diff --git a/nova/api/openstack/contrib/diskconfig.py b/nova/api/openstack/contrib/diskconfig.py
index 5173050fd..2225a30fb 100644
--- a/nova/api/openstack/contrib/diskconfig.py
+++ b/nova/api/openstack/contrib/diskconfig.py
@@ -92,23 +92,12 @@ class ImageDiskConfigController(object):
class Diskconfig(extensions.ExtensionDescriptor):
- def __init__(self):
- super(Diskconfig, self).__init__()
-
- def get_name(self):
- return "DiskConfig"
-
- def get_alias(self):
- return "OS-DCFG"
-
- def get_description(self):
- return "Disk Configuration support"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/disk_config/api/v1.1"
+ """Disk Configuration support"""
- def get_updated(self):
- return "2011-08-31T00:00:00+00:00"
+ name = "DiskConfig"
+ alias = "OS-DCFG"
+ namespace = "http://docs.openstack.org/ext/disk_config/api/v1.1"
+ updated = "2011-08-31T00:00:00+00:00"
def _server_extension_controller(self):
metadata = {
diff --git a/nova/api/openstack/contrib/flavorextradata.py b/nova/api/openstack/contrib/flavorextradata.py
index d0554c7b4..f696d161b 100644
--- a/nova/api/openstack/contrib/flavorextradata.py
+++ b/nova/api/openstack/contrib/flavorextradata.py
@@ -26,21 +26,11 @@ from nova.api.openstack import extensions
class Flavorextradata(extensions.ExtensionDescriptor):
- """The Flavor extra data extension for the OpenStack API."""
+ """Provide additional data for flavors"""
- def get_name(self):
- return "FlavorExtraData"
-
- def get_alias(self):
- return "os-flavor-extra-data"
-
- def get_description(self):
- return "Provide additional data for flavors"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/flavor_extra_data/api/v1.1"
-
- def get_updated(self):
- return "2011-09-14T00:00:00+00:00"
+ name = "FlavorExtraData"
+ alias = "os-flavor-extra-data"
+ namespace = "http://docs.openstack.org/ext/flavor_extra_data/api/v1.1"
+ updated = "2011-09-14T00:00:00+00:00"
# vim: tabstop=4 shiftwidth=4 softtabstop=4
diff --git a/nova/api/openstack/contrib/flavorextraspecs.py b/nova/api/openstack/contrib/flavorextraspecs.py
index 2d897a1da..8d2bea88a 100644
--- a/nova/api/openstack/contrib/flavorextraspecs.py
+++ b/nova/api/openstack/contrib/flavorextraspecs.py
@@ -98,22 +98,12 @@ class FlavorExtraSpecsController(object):
class Flavorextraspecs(extensions.ExtensionDescriptor):
+ """Instance type (flavor) extra specs"""
- def get_name(self):
- return "FlavorExtraSpecs"
-
- def get_alias(self):
- return "os-flavor-extra-specs"
-
- def get_description(self):
- return "Instance type (flavor) extra specs"
-
- def get_namespace(self):
- return \
- "http://docs.openstack.org/ext/flavor_extra_specs/api/v1.1"
-
- def get_updated(self):
- return "2011-06-23T00:00:00+00:00"
+ name = "FlavorExtraSpecs"
+ alias = "os-flavor-extra-specs"
+ namespace = "http://docs.openstack.org/ext/flavor_extra_specs/api/v1.1"
+ updated = "2011-06-23T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/floating_ips.py b/nova/api/openstack/contrib/floating_ips.py
index 22d7d1a1d..c6d0a80d2 100644
--- a/nova/api/openstack/contrib/floating_ips.py
+++ b/nova/api/openstack/contrib/floating_ips.py
@@ -121,10 +121,17 @@ class FloatingIPController(object):
class Floating_ips(extensions.ExtensionDescriptor):
- def __init__(self):
+ """Floating IPs support"""
+
+ name = "Floating_ips"
+ alias = "os-floating-ips"
+ namespace = "http://docs.openstack.org/ext/floating_ips/api/v1.1"
+ updated = "2011-06-16T00:00:00+00:00"
+
+ def __init__(self, ext_mgr):
self.compute_api = compute.API()
self.network_api = network.API()
- super(Floating_ips, self).__init__()
+ super(Floating_ips, self).__init__(ext_mgr)
def _add_floating_ip(self, input_dict, req, instance_id):
"""Associate floating_ip to an instance."""
@@ -172,21 +179,6 @@ class Floating_ips(extensions.ExtensionDescriptor):
return webob.Response(status_int=202)
- def get_name(self):
- return "Floating_ips"
-
- def get_alias(self):
- return "os-floating-ips"
-
- def get_description(self):
- return "Floating IPs support"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/floating_ips/api/v1.1"
-
- def get_updated(self):
- return "2011-06-16T00:00:00+00:00"
-
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/hosts.py b/nova/api/openstack/contrib/hosts.py
index 4cd908370..736843c3d 100644
--- a/nova/api/openstack/contrib/hosts.py
+++ b/nova/api/openstack/contrib/hosts.py
@@ -118,20 +118,12 @@ class HostController(object):
class Hosts(extensions.ExtensionDescriptor):
- def get_name(self):
- return "Hosts"
+ """Host administration"""
- def get_alias(self):
- return "os-hosts"
-
- def get_description(self):
- return "Host administration"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/hosts/api/v1.1"
-
- def get_updated(self):
- return "2011-06-29T00:00:00+00:00"
+ name = "Hosts"
+ alias = "os-hosts"
+ namespace = "http://docs.openstack.org/ext/hosts/api/v1.1"
+ updated = "2011-06-29T00:00:00+00:00"
def get_resources(self):
resources = [extensions.ResourceExtension('os-hosts',
diff --git a/nova/api/openstack/contrib/keypairs.py b/nova/api/openstack/contrib/keypairs.py
index 201648ab5..c7965b8c4 100644
--- a/nova/api/openstack/contrib/keypairs.py
+++ b/nova/api/openstack/contrib/keypairs.py
@@ -117,22 +117,12 @@ class KeypairController(object):
class Keypairs(extensions.ExtensionDescriptor):
+ """Keypair Support"""
- def get_name(self):
- return "Keypairs"
-
- def get_alias(self):
- return "os-keypairs"
-
- def get_description(self):
- return "Keypair Support"
-
- def get_namespace(self):
- return \
- "http://docs.openstack.org/ext/keypairs/api/v1.1"
-
- def get_updated(self):
- return "2011-08-08T00:00:00+00:00"
+ name = "Keypairs"
+ alias = "os-keypairs"
+ namespace = "http://docs.openstack.org/ext/keypairs/api/v1.1"
+ updated = "2011-08-08T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/multinic.py b/nova/api/openstack/contrib/multinic.py
index da8dcee5d..c39b87b84 100644
--- a/nova/api/openstack/contrib/multinic.py
+++ b/nova/api/openstack/contrib/multinic.py
@@ -30,47 +30,23 @@ LOG = logging.getLogger("nova.api.multinic")
# Note: The class name is as it has to be for this to be loaded as an
# extension--only first character capitalized.
class Multinic(extensions.ExtensionDescriptor):
- """The multinic extension.
+ """Multiple network support"""
- Exposes addFixedIp and removeFixedIp actions on servers.
+ name = "Multinic"
+ alias = "NMN"
+ namespace = "http://docs.openstack.org/ext/multinic/api/v1.1"
+ updated = "2011-06-09T00:00:00+00:00"
- """
-
- def __init__(self, *args, **kwargs):
+ def __init__(self, ext_mgr):
"""Initialize the extension.
Gets a compute.API object so we can call the back-end
add_fixed_ip() and remove_fixed_ip() methods.
"""
- super(Multinic, self).__init__(*args, **kwargs)
+ super(Multinic, self).__init__(ext_mgr)
self.compute_api = compute.API()
- def get_name(self):
- """Return the extension name, as required by contract."""
-
- return "Multinic"
-
- def get_alias(self):
- """Return the extension alias, as required by contract."""
-
- return "NMN"
-
- def get_description(self):
- """Return the extension description, as required by contract."""
-
- return "Multiple network support"
-
- def get_namespace(self):
- """Return the namespace, as required by contract."""
-
- return "http://docs.openstack.org/ext/multinic/api/v1.1"
-
- def get_updated(self):
- """Return the last updated timestamp, as required by contract."""
-
- return "2011-06-09T00:00:00+00:00"
-
def get_actions(self):
"""Return the actions the extension adds, as required by contract."""
diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py
index 459b71dfd..83d75394a 100644
--- a/nova/api/openstack/contrib/quotas.py
+++ b/nova/api/openstack/contrib/quotas.py
@@ -73,21 +73,12 @@ class QuotaSetsController(object):
class Quotas(extensions.ExtensionDescriptor):
+ """Quotas management support"""
- def get_name(self):
- return "Quotas"
-
- def get_alias(self):
- return "os-quota-sets"
-
- def get_description(self):
- return "Quotas management support"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/quotas-sets/api/v1.1"
-
- def get_updated(self):
- return "2011-08-08T00:00:00+00:00"
+ name = "Quotas"
+ alias = "os-quota-sets"
+ namespace = "http://docs.openstack.org/ext/quotas-sets/api/v1.1"
+ updated = "2011-08-08T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/rescue.py b/nova/api/openstack/contrib/rescue.py
index 3e3459bfe..4e1beb0ba 100644
--- a/nova/api/openstack/contrib/rescue.py
+++ b/nova/api/openstack/contrib/rescue.py
@@ -30,9 +30,15 @@ LOG = logging.getLogger("nova.api.contrib.rescue")
class Rescue(exts.ExtensionDescriptor):
- """The Rescue controller for the OpenStack API."""
- def __init__(self):
- super(Rescue, self).__init__()
+ """Instance rescue mode"""
+
+ name = "Rescue"
+ alias = "os-rescue"
+ namespace = "http://docs.openstack.org/ext/rescue/api/v1.1"
+ updated = "2011-08-18T00:00:00+00:00"
+
+ def __init__(self, ext_mgr):
+ super(Rescue, self).__init__(ext_mgr)
self.compute_api = compute.API()
@exts.wrap_errors
@@ -55,21 +61,6 @@ class Rescue(exts.ExtensionDescriptor):
return webob.Response(status_int=202)
- def get_name(self):
- return "Rescue"
-
- def get_alias(self):
- return "os-rescue"
-
- def get_description(self):
- return "Instance rescue mode"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/rescue/api/v1.1"
-
- def get_updated(self):
- return "2011-08-18T00:00:00+00:00"
-
def get_actions(self):
"""Return the actions the extension adds, as required by contract."""
actions = [
diff --git a/nova/api/openstack/contrib/security_groups.py b/nova/api/openstack/contrib/security_groups.py
index 1fd64f3b8..dd8333525 100644
--- a/nova/api/openstack/contrib/security_groups.py
+++ b/nova/api/openstack/contrib/security_groups.py
@@ -332,25 +332,16 @@ class SecurityGroupRulesController(SecurityGroupController):
class Security_groups(extensions.ExtensionDescriptor):
+ """Security group support"""
- def __init__(self):
- self.compute_api = compute.API()
- super(Security_groups, self).__init__()
-
- def get_name(self):
- return "SecurityGroups"
-
- def get_alias(self):
- return "security_groups"
+ name = "SecurityGroups"
+ alias = "security_groups"
+ namespace = "http://docs.openstack.org/ext/securitygroups/api/v1.1"
+ updated = "2011-07-21T00:00:00+00:00"
- def get_description(self):
- return "Security group support"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/securitygroups/api/v1.1"
-
- def get_updated(self):
- return "2011-07-21T00:00:00+00:00"
+ def __init__(self, ext_mgr):
+ self.compute_api = compute.API()
+ super(Security_groups, self).__init__(ext_mgr)
def _addSecurityGroup(self, input_dict, req, instance_id):
context = req.environ['nova.context']
diff --git a/nova/api/openstack/contrib/simple_tenant_usage.py b/nova/api/openstack/contrib/simple_tenant_usage.py
index 42691a9fa..3399ac2be 100644
--- a/nova/api/openstack/contrib/simple_tenant_usage.py
+++ b/nova/api/openstack/contrib/simple_tenant_usage.py
@@ -211,20 +211,12 @@ class SimpleTenantUsageController(object):
class Simple_tenant_usage(extensions.ExtensionDescriptor):
- def get_name(self):
- return "SimpleTenantUsage"
+ """Simple tenant usage extension"""
- def get_alias(self):
- return "os-simple-tenant-usage"
-
- def get_description(self):
- return "Simple tenant usage extension"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/os-simple-tenant-usage/api/v1.1"
-
- def get_updated(self):
- return "2011-08-19T00:00:00+00:00"
+ name = "SimpleTenantUsage"
+ alias = "os-simple-tenant-usage"
+ namespace = "http://docs.openstack.org/ext/os-simple-tenant-usage/api/v1.1"
+ updated = "2011-08-19T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/virtual_interfaces.py b/nova/api/openstack/contrib/virtual_interfaces.py
index 1981cd372..0afd2119d 100644
--- a/nova/api/openstack/contrib/virtual_interfaces.py
+++ b/nova/api/openstack/contrib/virtual_interfaces.py
@@ -64,21 +64,12 @@ class ServerVirtualInterfaceController(object):
class Virtual_interfaces(extensions.ExtensionDescriptor):
+ """Virtual interface support"""
- def get_name(self):
- return "VirtualInterfaces"
-
- def get_alias(self):
- return "virtual_interfaces"
-
- def get_description(self):
- return "Virtual interface support"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/virtual_interfaces/api/v1.1"
-
- def get_updated(self):
- return "2011-08-17T00:00:00+00:00"
+ name = "VirtualInterfaces"
+ alias = "virtual_interfaces"
+ namespace = "http://docs.openstack.org/ext/virtual_interfaces/api/v1.1"
+ updated = "2011-08-17T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/virtual_storage_arrays.py b/nova/api/openstack/contrib/virtual_storage_arrays.py
index ca219cfa8..5c5afdd22 100644
--- a/nova/api/openstack/contrib/virtual_storage_arrays.py
+++ b/nova/api/openstack/contrib/virtual_storage_arrays.py
@@ -545,21 +545,12 @@ class VsaVCController(servers.Controller):
class Virtual_storage_arrays(extensions.ExtensionDescriptor):
+ """Virtual Storage Arrays support"""
- def get_name(self):
- return "VSAs"
-
- def get_alias(self):
- return "zadr-vsa"
-
- def get_description(self):
- return "Virtual Storage Arrays support"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/vsa/api/v1.1"
-
- def get_updated(self):
- return "2011-08-25T00:00:00+00:00"
+ name = "VSAs"
+ alias = "zadr-vsa"
+ namespace = "http://docs.openstack.org/ext/vsa/api/v1.1"
+ updated = "2011-08-25T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/volumes.py b/nova/api/openstack/contrib/volumes.py
index 50a1e42ab..f706ebfe1 100644
--- a/nova/api/openstack/contrib/volumes.py
+++ b/nova/api/openstack/contrib/volumes.py
@@ -339,20 +339,12 @@ class BootFromVolumeController(servers.Controller):
class Volumes(extensions.ExtensionDescriptor):
- def get_name(self):
- return "Volumes"
+ """Volumes support"""
- def get_alias(self):
- return "os-volumes"
-
- def get_description(self):
- return "Volumes support"
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/volumes/api/v1.1"
-
- def get_updated(self):
- return "2011-03-25T00:00:00+00:00"
+ name = "Volumes"
+ alias = "os-volumes"
+ namespace = "http://docs.openstack.org/ext/volumes/api/v1.1"
+ updated = "2011-03-25T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/volumetypes.py b/nova/api/openstack/contrib/volumetypes.py
index ed33a8819..947dcad2d 100644
--- a/nova/api/openstack/contrib/volumetypes.py
+++ b/nova/api/openstack/contrib/volumetypes.py
@@ -163,22 +163,12 @@ class VolumeTypeExtraSpecsController(object):
class Volumetypes(extensions.ExtensionDescriptor):
+ """Volume types support"""
- def get_name(self):
- return "VolumeTypes"
-
- def get_alias(self):
- return "os-volume-types"
-
- def get_description(self):
- return "Volume types support"
-
- def get_namespace(self):
- return \
- "http://docs.openstack.org/ext/volume_types/api/v1.1"
-
- def get_updated(self):
- return "2011-08-24T00:00:00+00:00"
+ name = "VolumeTypes"
+ alias = "os-volume-types"
+ namespace = "http://docs.openstack.org/ext/volume_types/api/v1.1"
+ updated = "2011-08-24T00:00:00+00:00"
def get_resources(self):
resources = []
diff --git a/nova/api/openstack/contrib/zones.py b/nova/api/openstack/contrib/zones.py
index 5901e321e..628b8182f 100644
--- a/nova/api/openstack/contrib/zones.py
+++ b/nova/api/openstack/contrib/zones.py
@@ -28,22 +28,15 @@ FLAGS = flags.FLAGS
class Zones(extensions.ExtensionDescriptor):
- def get_name(self):
- return "Zones"
-
- def get_alias(self):
- return "os-zones"
-
- def get_description(self):
- return """Enables zones-related functionality such as adding
-child zones, listing child zones, getting the capabilities of the
-local zone, and returning build plans to parent zones' schedulers"""
-
- def get_namespace(self):
- return "http://docs.openstack.org/ext/zones/api/v1.1"
-
- def get_updated(self):
- return "2011-09-21T00:00:00+00:00"
+ """Enables zones-related functionality such as adding child zones,
+ listing child zones, getting the capabilities of the local zone,
+ and returning build plans to parent zones' schedulers
+ """
+
+ name = "Zones"
+ alias = "os-zones"
+ namespace = "http://docs.openstack.org/ext/zones/api/v1.1"
+ updated = "2011-09-21T00:00:00+00:00"
def get_resources(self):
# Nothing yet.
diff --git a/nova/api/openstack/extensions.py b/nova/api/openstack/extensions.py
index 6eb085d09..23e8f71ed 100644
--- a/nova/api/openstack/extensions.py
+++ b/nova/api/openstack/extensions.py
@@ -53,46 +53,26 @@ class ExtensionDescriptor(object):
"""
- def get_name(self):
- """The name of the extension.
+ # The name of the extension, e.g., 'Fox In Socks'
+ name = None
- e.g. 'Fox In Socks'
+ # The alias for the extension, e.g., 'FOXNSOX'
+ alias = None
- """
- raise NotImplementedError()
-
- def get_alias(self):
- """The alias for the extension.
+ # Description comes from the docstring for the class
- e.g. 'FOXNSOX'
+ # The XML namespace for the extension, e.g.,
+ # 'http://www.fox.in.socks/api/ext/pie/v1.0'
+ namespace = None
- """
- raise NotImplementedError()
+ # The timestamp when the extension was last updated, e.g.,
+ # '2011-01-22T13:25:27-06:00'
+ updated = None
- def get_description(self):
- """Friendly description for the extension.
+ def __init__(self, ext_mgr):
+ """Register extension with the extension manager."""
- e.g. 'The Fox In Socks Extension'
-
- """
- raise NotImplementedError()
-
- def get_namespace(self):
- """The XML namespace for the extension.
-
- e.g. 'http://www.fox.in.socks/api/ext/pie/v1.0'
-
- """
- raise NotImplementedError()
-
- def get_updated(self):
- """The timestamp when the extension was last updated.
-
- e.g. '2011-01-22T13:25:27-06:00'
-
- """
- # NOTE(justinsb): Not sure of the purpose of this is, vs the XML NS
- raise NotImplementedError()
+ ext_mgr.register(self)
def get_resources(self):
"""List of extensions.ResourceExtension extension objects.
@@ -194,11 +174,11 @@ class ExtensionsResource(wsgi.Resource):
def _translate(self, ext):
ext_data = {}
- ext_data['name'] = ext.get_name()
- ext_data['alias'] = ext.get_alias()
- ext_data['description'] = ext.get_description()
- ext_data['namespace'] = ext.get_namespace()
- ext_data['updated'] = ext.get_updated()
+ ext_data['name'] = ext.name
+ ext_data['alias'] = ext.alias
+ ext_data['description'] = ext.__doc__
+ ext_data['namespace'] = ext.namespace
+ ext_data['updated'] = ext.updated
ext_data['links'] = [] # TODO(dprince): implement extension links
return ext_data
@@ -275,7 +255,7 @@ class ExtensionMiddleware(base_wsgi.Middleware):
def __init__(self, application, ext_mgr=None):
if ext_mgr is None:
- ext_mgr = ExtensionManager(FLAGS.osapi_extensions_path)
+ ext_mgr = ExtensionManager()
self.ext_mgr = ext_mgr
mapper = nova.api.openstack.ProjectMapper()
@@ -352,19 +332,30 @@ class ExtensionManager(object):
"""
- def __init__(self, path):
+ def __init__(self):
LOG.audit(_('Initializing extension manager.'))
- self.path = path
self.extensions = {}
- self._load_all_extensions()
+ self._load_extensions()
+
+ def register(self, ext):
+ # Do nothing if the extension doesn't check out
+ if not self._check_extension(ext):
+ return
+
+ alias = ext.alias
+ LOG.audit(_('Loaded extension: %s'), alias)
+
+ if alias in self.extensions:
+ raise exception.Error("Found duplicate extension: %s" % alias)
+ self.extensions[alias] = ext
def get_resources(self):
"""Returns a list of ResourceExtension objects."""
resources = []
resources.append(ResourceExtension('extensions',
ExtensionsResource(self)))
- for alias, ext in self.extensions.iteritems():
+ for ext in self.extensions.values():
try:
resources.extend(ext.get_resources())
except AttributeError:
@@ -376,7 +367,7 @@ class ExtensionManager(object):
def get_actions(self):
"""Returns a list of ActionExtension objects."""
actions = []
- for alias, ext in self.extensions.iteritems():
+ for ext in self.extensions.values():
try:
actions.extend(ext.get_actions())
except AttributeError:
@@ -388,7 +379,7 @@ class ExtensionManager(object):
def get_request_extensions(self):
"""Returns a list of RequestExtension objects."""
request_exts = []
- for alias, ext in self.extensions.iteritems():
+ for ext in self.extensions.values():
try:
request_exts.extend(ext.get_request_extensions())
except AttributeError:
@@ -400,66 +391,44 @@ class ExtensionManager(object):
def _check_extension(self, extension):
"""Checks for required methods in extension objects."""
try:
- LOG.debug(_('Ext name: %s'), extension.get_name())
- LOG.debug(_('Ext alias: %s'), extension.get_alias())
- LOG.debug(_('Ext description: %s'), extension.get_description())
- LOG.debug(_('Ext namespace: %s'), extension.get_namespace())
- LOG.debug(_('Ext updated: %s'), extension.get_updated())
+ LOG.debug(_('Ext name: %s'), extension.name)
+ LOG.debug(_('Ext alias: %s'), extension.alias)
+ LOG.debug(_('Ext description: %s'),
+ ' '.join(extension.__doc__.strip().split()))
+ LOG.debug(_('Ext namespace: %s'), extension.namespace)
+ LOG.debug(_('Ext updated: %s'), extension.updated)
except AttributeError as ex:
LOG.exception(_("Exception loading extension: %s"), unicode(ex))
return False
return True
- def _load_all_extensions(self):
- """Load extensions from the configured path.
+ def load_extension(self, ext_factory):
+ """Execute an extension factory.
- Load extensions from the configured path. The extension name is
- constructed from the module_name. If your extension module was named
- widgets.py the extension class within that module should be
- 'Widgets'.
+ Loads an extension. The 'ext_factory' is the name of a
+ callable that will be imported and called with one
+ argument--the extension manager. The factory callable is
+ expected to call the register() method at least once.
+ """
- In addition, extensions are loaded from the 'contrib' directory.
+ LOG.debug(_("Loading extension %s"), ext_factory)
- See nova/tests/api/openstack/extensions/foxinsocks.py for an example
- extension implementation.
+ # Load the factory
+ factory = utils.import_class(ext_factory)
- """
- if os.path.exists(self.path):
- self._load_all_extensions_from_path(self.path)
-
- contrib_path = os.path.join(os.path.dirname(__file__), "contrib")
- if os.path.exists(contrib_path):
- self._load_all_extensions_from_path(contrib_path)
-
- def _load_all_extensions_from_path(self, path):
- for f in os.listdir(path):
- LOG.audit(_('Loading extension file: %s'), f)
- mod_name, file_ext = os.path.splitext(os.path.split(f)[-1])
- ext_path = os.path.join(path, f)
- if file_ext.lower() == '.py' and not mod_name.startswith('_'):
- mod = imp.load_source(mod_name, ext_path)
- ext_name = mod_name[0].upper() + mod_name[1:]
- new_ext_class = getattr(mod, ext_name, None)
- if not new_ext_class:
- LOG.warn(_('Did not find expected name '
- '"%(ext_name)s" in %(file)s'),
- {'ext_name': ext_name,
- 'file': ext_path})
- continue
- new_ext = new_ext_class()
- self.add_extension(new_ext)
-
- def add_extension(self, ext):
- # Do nothing if the extension doesn't check out
- if not self._check_extension(ext):
- return
+ # Call it
+ LOG.debug(_("Calling extension factory %s"), ext_factory)
+ factory(self)
- alias = ext.get_alias()
- LOG.audit(_('Loaded extension: %s'), alias)
+ def _load_extensions(self):
+ """Load extensions specified on the command line."""
- if alias in self.extensions:
- raise exception.Error("Found duplicate extension: %s" % alias)
- self.extensions[alias] = ext
+ for ext_factory in FLAGS.osapi_extension:
+ try:
+ self.load_extension(ext_factory)
+ except Exception as exc:
+ LOG.warn(_('Failed to load extension %(ext_factory)s: '
+ '%(exc)s') % locals())
class RequestExtension(object):
diff --git a/nova/flags.py b/nova/flags.py
index a70a361a8..e98c487aa 100644
--- a/nova/flags.py
+++ b/nova/flags.py
@@ -341,8 +341,9 @@ DEFINE_string('ec2_dmz_host', '$my_ip', 'internal ip of api server')
DEFINE_integer('ec2_port', 8773, 'cloud controller port')
DEFINE_string('ec2_scheme', 'http', 'prefix for ec2')
DEFINE_string('ec2_path', '/services/Cloud', 'suffix for ec2')
-DEFINE_string('osapi_extensions_path', '/var/lib/nova/extensions',
- 'default directory for nova extensions')
+DEFINE_multistring('osapi_extension',
+ ['nova.api.openstack.contrib.standard_extensions'],
+ 'osapi extension to load')
DEFINE_string('osapi_host', '$my_ip', 'ip of api server')
DEFINE_string('osapi_scheme', 'http', 'prefix for openstack')
DEFINE_integer('osapi_port', 8774, 'OpenStack API port')
diff --git a/nova/tests/api/openstack/extensions/foxinsocks.py b/nova/tests/api/openstack/extensions/foxinsocks.py
index 70556a28d..d72365620 100644
--- a/nova/tests/api/openstack/extensions/foxinsocks.py
+++ b/nova/tests/api/openstack/extensions/foxinsocks.py
@@ -27,24 +27,15 @@ class FoxInSocksController(object):
class Foxinsocks(object):
+ """The Fox In Socks Extension"""
- def __init__(self):
- pass
+ name = "Fox In Socks"
+ alias = "FOXNSOX"
+ namespace = "http://www.fox.in.socks/api/ext/pie/v1.0"
+ updated = "2011-01-22T13:25:27-06:00"
- def get_name(self):
- return "Fox In Socks"
-
- def get_alias(self):
- return "FOXNSOX"
-
- def get_description(self):
- return "The Fox In Socks Extension"
-
- def get_namespace(self):
- return "http://www.fox.in.socks/api/ext/pie/v1.0"
-
- def get_updated(self):
- return "2011-01-22T13:25:27-06:00"
+ def __init__(self, ext_mgr):
+ ext_mgr.register(self)
def get_resources(self):
resources = []
diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py
index 92e74e545..f3a9c26e3 100644
--- a/nova/tests/api/openstack/test_extensions.py
+++ b/nova/tests/api/openstack/test_extensions.py
@@ -21,6 +21,7 @@ import webob
from lxml import etree
from nova import context
+from nova import flags
from nova import test
from nova import wsgi as base_wsgi
from nova.api import openstack
@@ -30,6 +31,8 @@ from nova.api.openstack import wsgi
from nova.api.openstack import xmlutil
from nova.tests.api.openstack import fakes
+FLAGS = flags.FLAGS
+
NS = "{http://docs.openstack.org/compute/api/v1.1}"
ATOMNS = "{http://www.w3.org/2005/Atom}"
response_body = "Try to say this Mr. Knox, sir..."
@@ -45,21 +48,16 @@ class StubController(object):
class StubExtensionManager(object):
+ """Provides access to Tweedle Beetles"""
+
+ name = "Tweedle Beetle Extension"
+ alias = "TWDLBETL"
def __init__(self, resource_ext=None, action_ext=None, request_ext=None):
self.resource_ext = resource_ext
self.action_ext = action_ext
self.request_ext = request_ext
- def get_name(self):
- return "Tweedle Beetle Extension"
-
- def get_alias(self):
- return "TWDLBETL"
-
- def get_description(self):
- return "Provides access to Tweedle Beetles"
-
def get_resources(self):
resource_exts = []
if self.resource_ext:
@@ -79,12 +77,19 @@ class StubExtensionManager(object):
return request_extensions
-class ExtensionControllerTest(test.TestCase):
+class ExtensionTestCase(test.TestCase):
+ def setUp(self):
+ super(ExtensionTestCase, self).setUp()
+ ext_list = FLAGS.osapi_extension[:]
+ ext_list.append('nova.tests.api.openstack.extensions.'
+ 'foxinsocks.Foxinsocks')
+ self.flags(osapi_extension=ext_list)
+
+
+class ExtensionControllerTest(ExtensionTestCase):
def setUp(self):
super(ExtensionControllerTest, self).setUp()
- ext_path = os.path.join(os.path.dirname(__file__), "extensions")
- self.flags(osapi_extensions_path=ext_path)
self.ext_list = [
"AdminActions",
"Createserverext",
@@ -212,12 +217,7 @@ class ExtensionControllerTest(test.TestCase):
xmlutil.validate_schema(root, 'extension')
-class ResourceExtensionTest(test.TestCase):
-
- def setUp(self):
- super(ResourceExtensionTest, self).setUp()
- ext_path = os.path.join(os.path.dirname(__file__), "extensions")
- self.flags(osapi_extensions_path=ext_path)
+class ResourceExtensionTest(ExtensionTestCase):
def test_no_extension_present(self):
manager = StubExtensionManager(None)
@@ -255,19 +255,13 @@ class ResourceExtensionTest(test.TestCase):
class InvalidExtension(object):
- def get_alias(self):
- return "THIRD"
+ alias = "THIRD"
-class ExtensionManagerTest(test.TestCase):
+class ExtensionManagerTest(ExtensionTestCase):
response_body = "Try to say this Mr. Knox, sir..."
- def setUp(self):
- super(ExtensionManagerTest, self).setUp()
- ext_path = os.path.join(os.path.dirname(__file__), "extensions")
- self.flags(osapi_extensions_path=ext_path)
-
def test_get_resources(self):
app = openstack.APIRouter()
ext_midware = extensions.ExtensionMiddleware(app)
@@ -283,17 +277,12 @@ class ExtensionManagerTest(test.TestCase):
app = openstack.APIRouter()
ext_midware = extensions.ExtensionMiddleware(app)
ext_mgr = ext_midware.ext_mgr
- ext_mgr.add_extension(InvalidExtension())
+ ext_mgr.register(InvalidExtension())
self.assertTrue('FOXNSOX' in ext_mgr.extensions)
self.assertTrue('THIRD' not in ext_mgr.extensions)
-class ActionExtensionTest(test.TestCase):
-
- def setUp(self):
- super(ActionExtensionTest, self).setUp()
- ext_path = os.path.join(os.path.dirname(__file__), "extensions")
- self.flags(osapi_extensions_path=ext_path)
+class ActionExtensionTest(ExtensionTestCase):
def _send_server_action_request(self, url, body):
app = openstack.APIRouter()
@@ -331,12 +320,7 @@ class ActionExtensionTest(test.TestCase):
self.assertEqual(404, response.status_int)
-class RequestExtensionTest(test.TestCase):
-
- def setUp(self):
- super(RequestExtensionTest, self).setUp()
- ext_path = os.path.join(os.path.dirname(__file__), "extensions")
- self.flags(osapi_extensions_path=ext_path)
+class RequestExtensionTest(ExtensionTestCase):
def test_get_resources_with_stub_mgr(self):
diff --git a/nova/tests/integrated/test_extensions.py b/nova/tests/integrated/test_extensions.py
index c22cf0be0..958f584ab 100644
--- a/nova/tests/integrated/test_extensions.py
+++ b/nova/tests/integrated/test_extensions.py
@@ -17,18 +17,21 @@
import os
+from nova import flags
from nova.log import logging
from nova.tests.integrated import integrated_helpers
+FLAGS = flags.FLAGS
LOG = logging.getLogger('nova.tests.integrated')
class ExtensionsTest(integrated_helpers._IntegratedTestBase):
def _get_flags(self):
f = super(ExtensionsTest, self)._get_flags()
- f['osapi_extensions_path'] = os.path.join(os.path.dirname(__file__),
- "../api/openstack/extensions")
+ f['osapi_extension'] = FLAGS.osapi_extension[:]
+ f['osapi_extension'].append('nova.tests.api.openstack.extensions.'
+ 'foxinsocks.Foxinsocks')
return f
def test_get_foxnsocks(self):