Remove the extensions framework from wsgi.py

All the API extensions has been merged into
their main controller. which means we can
remove the API extensions framework from wsgi.py.

This commit finish the last step of API extensions
merge work.

Implements: blueprint api-extensions-merge-stein
Change-Id: I365e2ae72b72f75b1eb28b63ad3e87dc2d6dc92f
This commit is contained in:
ghanshyam 2018-10-02 03:09:39 +00:00 committed by Ghanshyam Mann
parent 47befda147
commit a81fa4d83a
4 changed files with 92 additions and 275 deletions

View File

@ -88,66 +88,60 @@ from nova.api.openstack import wsgi
from nova.api import wsgi as base_wsgi
def _create_controller(main_controller, controller_list,
action_controller_list):
def _create_controller(main_controller, action_controller_list):
"""This is a helper method to create controller with a
list of extended controller. This is for backward compatible
with old extension interface. Finally, the controller for the
same resource will be merged into single one controller.
list of action controller.
"""
controller = wsgi.Resource(main_controller())
for ctl in controller_list:
controller.register_extensions(ctl())
for ctl in action_controller_list:
controller.register_actions(ctl())
return controller
agents_controller = functools.partial(
_create_controller, agents.AgentController, [], [])
_create_controller, agents.AgentController, [])
aggregates_controller = functools.partial(
_create_controller, aggregates.AggregateController, [], [])
_create_controller, aggregates.AggregateController, [])
assisted_volume_snapshots_controller = functools.partial(
_create_controller,
assisted_volume_snapshots.AssistedVolumeSnapshotsController, [], [])
assisted_volume_snapshots.AssistedVolumeSnapshotsController, [])
availability_zone_controller = functools.partial(
_create_controller, availability_zone.AvailabilityZoneController, [], [])
_create_controller, availability_zone.AvailabilityZoneController, [])
baremetal_nodes_controller = functools.partial(
_create_controller, baremetal_nodes.BareMetalNodeController, [], [])
_create_controller, baremetal_nodes.BareMetalNodeController, [])
cells_controller = functools.partial(
_create_controller, cells.CellsController, [], [])
_create_controller, cells.CellsController, [])
certificates_controller = functools.partial(
_create_controller, certificates.CertificatesController, [], [])
_create_controller, certificates.CertificatesController, [])
cloudpipe_controller = functools.partial(
_create_controller, cloudpipe.CloudpipeController, [], [])
_create_controller, cloudpipe.CloudpipeController, [])
extensions_controller = functools.partial(
_create_controller, extension_info.ExtensionInfoController, [], [])
_create_controller, extension_info.ExtensionInfoController, [])
fixed_ips_controller = functools.partial(_create_controller,
fixed_ips.FixedIPController, [], [])
fixed_ips.FixedIPController, [])
flavor_controller = functools.partial(_create_controller,
flavors.FlavorsController,
[],
[
flavor_manage.FlavorManageController,
flavor_access.FlavorActionController
@ -156,106 +150,104 @@ flavor_controller = functools.partial(_create_controller,
flavor_access_controller = functools.partial(_create_controller,
flavor_access.FlavorAccessController, [], [])
flavor_access.FlavorAccessController, [])
flavor_extraspec_controller = functools.partial(_create_controller,
flavors_extraspecs.FlavorExtraSpecsController, [], [])
flavors_extraspecs.FlavorExtraSpecsController, [])
floating_ip_dns_controller = functools.partial(_create_controller,
floating_ip_dns.FloatingIPDNSDomainController, [], [])
floating_ip_dns.FloatingIPDNSDomainController, [])
floating_ip_dnsentry_controller = functools.partial(_create_controller,
floating_ip_dns.FloatingIPDNSEntryController, [], [])
floating_ip_dns.FloatingIPDNSEntryController, [])
floating_ip_pools_controller = functools.partial(_create_controller,
floating_ip_pools.FloatingIPPoolsController, [], [])
floating_ip_pools.FloatingIPPoolsController, [])
floating_ips_controller = functools.partial(_create_controller,
floating_ips.FloatingIPController, [], [])
floating_ips.FloatingIPController, [])
floating_ips_bulk_controller = functools.partial(_create_controller,
floating_ips_bulk.FloatingIPBulkController, [], [])
floating_ips_bulk.FloatingIPBulkController, [])
fping_controller = functools.partial(_create_controller,
fping.FpingController, [], [])
fping.FpingController, [])
hosts_controller = functools.partial(
_create_controller, hosts.HostController, [], [])
_create_controller, hosts.HostController, [])
hypervisors_controller = functools.partial(
_create_controller, hypervisors.HypervisorsController, [], [])
_create_controller, hypervisors.HypervisorsController, [])
images_controller = functools.partial(
_create_controller, images.ImagesController,
[], [])
_create_controller, images.ImagesController, [])
image_metadata_controller = functools.partial(
_create_controller, image_metadata.ImageMetadataController,
[], [])
_create_controller, image_metadata.ImageMetadataController, [])
instance_actions_controller = functools.partial(_create_controller,
instance_actions.InstanceActionsController, [], [])
instance_actions.InstanceActionsController, [])
instance_usage_audit_log_controller = functools.partial(_create_controller,
instance_usage_audit_log.InstanceUsageAuditLogController, [], [])
instance_usage_audit_log.InstanceUsageAuditLogController, [])
ips_controller = functools.partial(_create_controller,
ips.IPsController, [], [])
ips.IPsController, [])
keypairs_controller = functools.partial(
_create_controller, keypairs.KeypairController, [], [])
_create_controller, keypairs.KeypairController, [])
limits_controller = functools.partial(
_create_controller, limits.LimitsController, [], [])
_create_controller, limits.LimitsController, [])
migrations_controller = functools.partial(_create_controller,
migrations.MigrationsController, [], [])
migrations.MigrationsController, [])
networks_controller = functools.partial(_create_controller,
networks.NetworkController, [],
networks.NetworkController,
[networks_associate.NetworkAssociateActionController])
quota_classes_controller = functools.partial(_create_controller,
quota_classes.QuotaClassSetsController, [], [])
quota_classes.QuotaClassSetsController, [])
quota_set_controller = functools.partial(_create_controller,
quota_sets.QuotaSetsController, [], [])
quota_sets.QuotaSetsController, [])
security_group_controller = functools.partial(_create_controller,
security_groups.SecurityGroupController, [], [])
security_groups.SecurityGroupController, [])
security_group_default_rules_controller = functools.partial(_create_controller,
security_group_default_rules.SecurityGroupDefaultRulesController, [], [])
security_group_default_rules.SecurityGroupDefaultRulesController, [])
security_group_rules_controller = functools.partial(_create_controller,
security_groups.SecurityGroupRulesController, [], [])
security_groups.SecurityGroupRulesController, [])
server_controller = functools.partial(_create_controller,
servers.ServersController, [],
servers.ServersController,
[
admin_actions.AdminActionsController,
admin_password.AdminPasswordController,
@ -278,83 +270,83 @@ server_controller = functools.partial(_create_controller,
console_auth_tokens_controller = functools.partial(_create_controller,
console_auth_tokens.ConsoleAuthTokensController, [], [])
console_auth_tokens.ConsoleAuthTokensController, [])
consoles_controller = functools.partial(_create_controller,
consoles.ConsolesController, [], [])
consoles.ConsolesController, [])
server_diagnostics_controller = functools.partial(_create_controller,
server_diagnostics.ServerDiagnosticsController, [], [])
server_diagnostics.ServerDiagnosticsController, [])
server_external_events_controller = functools.partial(_create_controller,
server_external_events.ServerExternalEventsController, [], [])
server_external_events.ServerExternalEventsController, [])
server_groups_controller = functools.partial(_create_controller,
server_groups.ServerGroupController, [], [])
server_groups.ServerGroupController, [])
server_metadata_controller = functools.partial(_create_controller,
server_metadata.ServerMetadataController, [], [])
server_metadata.ServerMetadataController, [])
server_migrations_controller = functools.partial(_create_controller,
server_migrations.ServerMigrationsController, [], [])
server_migrations.ServerMigrationsController, [])
server_os_interface_controller = functools.partial(_create_controller,
attach_interfaces.InterfaceAttachmentController, [], [])
attach_interfaces.InterfaceAttachmentController, [])
server_password_controller = functools.partial(_create_controller,
server_password.ServerPasswordController, [], [])
server_password.ServerPasswordController, [])
server_remote_consoles_controller = functools.partial(_create_controller,
remote_consoles.RemoteConsolesController, [], [])
remote_consoles.RemoteConsolesController, [])
server_security_groups_controller = functools.partial(_create_controller,
security_groups.ServerSecurityGroupController, [], [])
security_groups.ServerSecurityGroupController, [])
server_tags_controller = functools.partial(_create_controller,
server_tags.ServerTagsController, [], [])
server_tags.ServerTagsController, [])
server_volume_attachments_controller = functools.partial(_create_controller,
volumes.VolumeAttachmentController, [], [])
volumes.VolumeAttachmentController, [])
services_controller = functools.partial(_create_controller,
services.ServiceController, [], [])
services.ServiceController, [])
simple_tenant_usage_controller = functools.partial(_create_controller,
simple_tenant_usage.SimpleTenantUsageController, [], [])
simple_tenant_usage.SimpleTenantUsageController, [])
snapshots_controller = functools.partial(_create_controller,
volumes.SnapshotController, [], [])
volumes.SnapshotController, [])
tenant_networks_controller = functools.partial(_create_controller,
tenant_networks.TenantNetworkController, [], [])
tenant_networks.TenantNetworkController, [])
version_controller = functools.partial(_create_controller,
versionsV21.VersionsController, [], [])
versionsV21.VersionsController, [])
virtual_interfaces_controller = functools.partial(_create_controller,
virtual_interfaces.ServerVirtualInterfaceController, [], [])
virtual_interfaces.ServerVirtualInterfaceController, [])
volumes_controller = functools.partial(_create_controller,
volumes.VolumeController, [], [])
volumes.VolumeController, [])
# NOTE(alex_xu): This is structure of this route list as below:

View File

@ -332,7 +332,7 @@ class ResourceExceptionHandler(object):
"""Context manager to handle Resource exceptions.
Used when processing exceptions generated by API implementation
methods (or their extensions). Converts most exceptions to Fault
methods. Converts most exceptions to Fault
exceptions, with the appropriate logging.
"""
@ -399,10 +399,6 @@ class Resource(wsgi.Application):
if controller:
self.register_actions(controller)
# Save a mapping of extensions
self.wsgi_extensions = {}
self.wsgi_action_extensions = {}
def register_actions(self, controller):
"""Registers controller actions with this resource."""
@ -410,25 +406,6 @@ class Resource(wsgi.Application):
for key, method_name in actions.items():
self.wsgi_actions[key] = getattr(controller, method_name)
def register_extensions(self, controller):
"""Registers controller extensions with this resource."""
extensions = getattr(controller, 'wsgi_extensions', [])
for method_name, action_name in extensions:
# Look up the extending method
extension = getattr(controller, method_name)
if action_name:
# Extending an action...
if action_name not in self.wsgi_action_extensions:
self.wsgi_action_extensions[action_name] = []
self.wsgi_action_extensions[action_name].append(extension)
else:
# Extending a regular method
if method_name not in self.wsgi_extensions:
self.wsgi_extensions[method_name] = []
self.wsgi_extensions[method_name].append(extension)
def get_action_args(self, request_environment):
"""Parse dictionary created by routes library."""
@ -462,30 +439,6 @@ class Resource(wsgi.Application):
def deserialize(self, body):
return JSONDeserializer().deserialize(body)
def process_extensions(self, extensions, resp_obj, request,
action_args):
for ext in extensions:
response = None
# Regular functions get post-processing...
try:
with ResourceExceptionHandler():
response = ext(req=request, resp_obj=resp_obj,
**action_args)
except exception.VersionNotFoundForAPIMethod:
# If an attached extension (@wsgi.extends) for the
# method has no version match its not an error. We
# just don't run the extends code
continue
except Fault as ex:
response = ex
# We had a response return it, to exit early. This is
# actually a failure mode. None is success.
if response:
return response
return None
def _should_have_body(self, request):
return request.method in _METHODS_WITH_BODY
@ -532,8 +485,8 @@ class Resource(wsgi.Application):
# Get the implementing method
try:
meth, extensions = self.get_method(request, action,
content_type, body)
meth = self.get_method(request, action,
content_type, body)
except (AttributeError, TypeError):
return Fault(webob.exc.HTTPNotFound())
except KeyError as ex:
@ -596,9 +549,6 @@ class Resource(wsgi.Application):
# Do a preserialize to set up the response object
if hasattr(meth, 'wsgi_code'):
resp_obj._default_code = meth.wsgi_code
# Process extensions
response = self.process_extensions(extensions, resp_obj,
request, action_args)
if resp_obj and not response:
response = resp_obj.serialize(request, accept)
@ -636,36 +586,33 @@ class Resource(wsgi.Application):
return contents
def get_method(self, request, action, content_type, body):
meth, extensions = self._get_method(request,
action,
content_type,
body)
return meth, extensions
meth = self._get_method(request,
action,
content_type,
body)
return meth
def _get_method(self, request, action, content_type, body):
"""Look up the action-specific method and its extensions."""
"""Look up the action-specific method."""
# Look up the method
try:
if not self.controller:
meth = getattr(self, action)
else:
meth = getattr(self.controller, action)
return meth
except AttributeError:
if (not self.wsgi_actions or
action not in _ROUTES_METHODS + ['action']):
# Propagate the error
raise
else:
return meth, self.wsgi_extensions.get(action, [])
if action == 'action':
action_name = action_peek(body)
else:
action_name = action
# Look up the action method
return (self.wsgi_actions[action_name],
self.wsgi_action_extensions.get(action_name, []))
return (self.wsgi_actions[action_name])
def dispatch(self, method, request, action_args):
"""Dispatch a call to the action-specific method."""
@ -694,35 +641,6 @@ def action(name):
return decorator
def extends(*args, **kwargs):
"""Indicate a function extends an operation.
Can be used as either::
@extends
def index(...):
pass
or as::
@extends(action='resize')
def _action_resize(...):
pass
"""
def decorator(func):
# Store enough information to find what we're extending
func.wsgi_extends = (func.__name__, kwargs.get('action'))
return func
# If we have positional arguments, call the decorator
if args:
return decorator(*args)
# OK, return the decorator instead
return decorator
def expected_errors(errors):
"""Decorator for v2.1 API methods which specifies expected exceptions.
@ -785,7 +703,6 @@ class ControllerMetaclass(type):
# Find all actions
actions = {}
extensions = []
versioned_methods = None
# start with wsgi actions from base classes
for base in bases:
@ -807,12 +724,9 @@ class ControllerMetaclass(type):
continue
if getattr(value, 'wsgi_action', None):
actions[value.wsgi_action] = key
elif getattr(value, 'wsgi_extends', None):
extensions.append(value.wsgi_extends)
# Add the actions and extensions to the class dict
# Add the actions to the class dict
cls_dict['wsgi_actions'] = actions
cls_dict['wsgi_extensions'] = extensions
if versioned_methods:
cls_dict[VER_METHOD_ATTR] = versioned_methods

View File

@ -103,23 +103,23 @@ class MicroversionsExtendsBaseController(wsgi.Controller):
mv_controller = functools.partial(routes._create_controller,
MicroversionsController, [], [])
MicroversionsController, [])
mv2_controller = functools.partial(routes._create_controller,
MicroversionsController2, [], [])
MicroversionsController2, [])
mv3_controller = functools.partial(routes._create_controller,
MicroversionsController3, [], [])
MicroversionsController3, [])
mv4_controller = functools.partial(routes._create_controller,
MicroversionsController4, [], [])
MicroversionsController4, [])
mv5_controller = functools.partial(routes._create_controller,
MicroversionsExtendsBaseController, [], [])
MicroversionsExtendsBaseController, [])
ROUTES = (

View File

@ -419,7 +419,7 @@ class ResourceTest(MicroversionedTest):
controller = Controller()
resource = wsgi.Resource(controller)
method, extensions = resource.get_method(None, 'index', None, '')
method = resource.get_method(None, 'index', None, '')
actual = resource.dispatch(method, None, {'pants': 'off'})
expected = 'off'
self.assertEqual(actual, expected)
@ -442,9 +442,9 @@ class ResourceTest(MicroversionedTest):
controller = Controller()
resource = wsgi.Resource(controller)
method, extensions = resource.get_method(None, 'action',
'application/json',
'{"fooAction": true}')
method = resource.get_method(None, 'action',
'application/json',
'{"fooAction": true}')
self.assertEqual(controller._action_foo, method)
def test_get_method_action_bad_body(self):
@ -477,9 +477,9 @@ class ResourceTest(MicroversionedTest):
controller = Controller()
resource = wsgi.Resource(controller)
method, extensions = resource.get_method(None, 'action',
'application/xml',
'<fooAction>true</fooAction')
method = resource.get_method(None, 'action',
'application/xml',
'<fooAction>true</fooAction')
self.assertEqual(controller.action, method)
def test_get_action_args(self):
@ -651,50 +651,17 @@ class ResourceTest(MicroversionedTest):
'barAction': extended._action_bar,
}, resource.wsgi_actions)
def test_register_extensions(self):
def test_get_method(self):
class Controller(object):
def index(self, req, pants=None):
return pants
class ControllerExtended(wsgi.Controller):
@wsgi.extends
def index(self, req, resp_obj, pants=None):
return None
@wsgi.extends(action='fooAction')
def _action_foo(self, req, resp, id, body):
return None
controller = Controller()
resource = wsgi.Resource(controller)
self.assertEqual({}, resource.wsgi_extensions)
self.assertEqual({}, resource.wsgi_action_extensions)
extended = ControllerExtended()
resource.register_extensions(extended)
self.assertEqual({'index': [extended.index]}, resource.wsgi_extensions)
self.assertEqual({'fooAction': [extended._action_foo]},
resource.wsgi_action_extensions)
def test_get_method_extensions(self):
class Controller(object):
def index(self, req, pants=None):
return pants
class ControllerExtended(wsgi.Controller):
@wsgi.extends
def index(self, req, resp_obj, pants=None):
return None
controller = Controller()
extended = ControllerExtended()
resource = wsgi.Resource(controller)
resource.register_extensions(extended)
method, extensions = resource.get_method(None, 'index', None, '')
method = resource.get_method(None, 'index', None, '')
self.assertEqual(method, controller.index)
self.assertEqual(extensions, [extended.index])
def test_get_method_action_extensions(self):
def test_get_method_action(self):
class Controller(wsgi.Controller):
def index(self, req, pants=None):
return pants
@ -703,22 +670,14 @@ class ResourceTest(MicroversionedTest):
def _action_foo(self, req, id, body):
return body
class ControllerExtended(wsgi.Controller):
@wsgi.extends(action='fooAction')
def _action_foo(self, req, resp_obj, id, body):
return None
controller = Controller()
extended = ControllerExtended()
resource = wsgi.Resource(controller)
resource.register_extensions(extended)
method, extensions = resource.get_method(None, 'action',
'application/json',
'{"fooAction": true}')
method = resource.get_method(None, 'action',
'application/json',
'{"fooAction": true}')
self.assertEqual(method, controller._action_foo)
self.assertEqual(extensions, [extended._action_foo])
def test_get_method_action_whitelist_extensions(self):
def test_get_method_action_whitelist(self):
class Controller(wsgi.Controller):
def index(self, req, pants=None):
return pants
@ -737,61 +696,13 @@ class ResourceTest(MicroversionedTest):
resource = wsgi.Resource(controller)
resource.register_actions(extended)
method, extensions = resource.get_method(None, 'create',
'application/json',
'{"create": true}')
method = resource.get_method(None, 'create',
'application/json',
'{"create": true}')
self.assertEqual(method, extended._create)
self.assertEqual(extensions, [])
method, extensions = resource.get_method(None, 'delete', None, None)
method = resource.get_method(None, 'delete', None, None)
self.assertEqual(method, extended._delete)
self.assertEqual(extensions, [])
def test_process_extensions_regular(self):
class Controller(object):
def index(self, req, pants=None):
return pants
controller = Controller()
resource = wsgi.Resource(controller)
called = []
def extension1(req, resp_obj):
called.append(1)
return None
def extension2(req, resp_obj):
called.append(2)
return None
response = resource.process_extensions([extension2, extension1],
None, None, {})
self.assertEqual(called, [2, 1])
self.assertIsNone(response)
def test_process_extensions_regular_response(self):
class Controller(object):
def index(self, req, pants=None):
return pants
controller = Controller()
resource = wsgi.Resource(controller)
called = []
def extension1(req, resp_obj):
called.append(1)
return None
def extension2(req, resp_obj):
called.append(2)
return 'foo'
response = resource.process_extensions([extension2, extension1],
None, None, {})
self.assertEqual(called, [2])
self.assertEqual(response, 'foo')
def test_resource_exception_handler_type_error(self):
# A TypeError should be translated to a Fault/HTTP 400.