diff options
| author | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2011-10-20 18:07:02 -0500 |
|---|---|---|
| committer | Kevin L. Mitchell <kevin.mitchell@rackspace.com> | 2011-10-20 18:07:02 -0500 |
| commit | 3480cac1893c697aa290e6c3f56b2b7d97007adc (patch) | |
| tree | 3c3a6290f7f619af210384b5befda7c303e2c939 | |
| parent | 75a3fbb21eebd4de8775b63c327d9d57859d090c (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
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): |
