diff options
| -rw-r--r-- | nova/api/openstack/compute/contrib/disk_config.py | 32 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/extended_server_attributes.py | 37 | ||||
| -rw-r--r-- | nova/api/openstack/compute/contrib/extended_status.py | 41 | ||||
| -rw-r--r-- | nova/api/openstack/compute/servers.py | 37 | ||||
| -rw-r--r-- | nova/api/openstack/wsgi.py | 46 | ||||
| -rw-r--r-- | nova/tests/api/openstack/fakes.py | 4 | ||||
| -rw-r--r-- | nova/tests/api/openstack/test_wsgi.py | 23 |
7 files changed, 125 insertions, 95 deletions
diff --git a/nova/api/openstack/compute/contrib/disk_config.py b/nova/api/openstack/compute/contrib/disk_config.py index 041686a57..d803035af 100644 --- a/nova/api/openstack/compute/contrib/disk_config.py +++ b/nova/api/openstack/compute/contrib/disk_config.py @@ -21,7 +21,6 @@ from webob import exc from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil -from nova import db from nova import utils ALIAS = 'OS-DCF' @@ -102,30 +101,25 @@ class ServersDiskConfigTemplate(xmlutil.TemplateBuilder): class ServerDiskConfigController(wsgi.Controller): - def _add_disk_config(self, context, servers): - # Get DB information for servers - uuids = [server['id'] for server in servers] - db_servers = db.instance_get_all_by_filters(context, - {'uuid': uuids}) - db_servers_by_uuid = dict((s['uuid'], s) for s in db_servers) - + def _add_disk_config(self, req, servers): for server in servers: - db_server = db_servers_by_uuid.get(server['id']) - if db_server: - value = db_server[INTERNAL_DISK_CONFIG] - server[API_DISK_CONFIG] = disk_config_to_api(value) + db_server = req.get_db_instance(server['id']) + # server['id'] is guaranteed to be in the cache due to + # the core API adding it in its 'show'/'detail' methods. + value = db_server[INTERNAL_DISK_CONFIG] + server[API_DISK_CONFIG] = disk_config_to_api(value) - def _show(self, context, resp_obj): + def _show(self, req, resp_obj): if 'server' in resp_obj.obj: resp_obj.attach(xml=ServerDiskConfigTemplate()) server = resp_obj.obj['server'] - self._add_disk_config(context, [server]) + self._add_disk_config(req, [server]) @wsgi.extends def show(self, req, resp_obj, id): context = req.environ['nova.context'] if authorize(context): - self._show(context, resp_obj) + self._show(req, resp_obj) @wsgi.extends def detail(self, req, resp_obj): @@ -133,7 +127,7 @@ class ServerDiskConfigController(wsgi.Controller): if 'servers' in resp_obj.obj and authorize(context): resp_obj.attach(xml=ServersDiskConfigTemplate()) servers = resp_obj.obj['servers'] - self._add_disk_config(context, servers) + self._add_disk_config(req, servers) def _set_disk_config(self, dict_): if API_DISK_CONFIG in dict_: @@ -147,7 +141,7 @@ class ServerDiskConfigController(wsgi.Controller): if authorize(context): self._set_disk_config(body['server']) resp_obj = (yield) - self._show(context, resp_obj) + self._show(req, resp_obj) @wsgi.extends def update(self, req, id, body): @@ -155,7 +149,7 @@ class ServerDiskConfigController(wsgi.Controller): if authorize(context): self._set_disk_config(body['server']) resp_obj = (yield) - self._show(context, resp_obj) + self._show(req, resp_obj) @wsgi.extends(action='rebuild') def _action_rebuild(self, req, id, body): @@ -163,7 +157,7 @@ class ServerDiskConfigController(wsgi.Controller): if authorize(context): self._set_disk_config(body['rebuild']) resp_obj = (yield) - self._show(context, resp_obj) + self._show(req, resp_obj) @wsgi.extends(action='resize') def _action_resize(self, req, id, body): diff --git a/nova/api/openstack/compute/contrib/extended_server_attributes.py b/nova/api/openstack/compute/contrib/extended_server_attributes.py index 4c584ec05..46d4df2d1 100644 --- a/nova/api/openstack/compute/contrib/extended_server_attributes.py +++ b/nova/api/openstack/compute/contrib/extended_server_attributes.py @@ -14,14 +14,11 @@ """The Extended Server Attributes API extension.""" -from webob import exc - from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute from nova import db -from nova import exception from nova import flags from nova import log as logging @@ -38,11 +35,6 @@ class ExtendedServerAttributesController(wsgi.Controller): **kwargs) self.compute_api = compute.API() - def _get_instances(self, context, instance_uuids): - filters = {'uuid': instance_uuids} - instances = self.compute_api.get_all(context, filters) - return dict((instance['uuid'], instance) for instance in instances) - def _get_hypervisor_hostname(self, context, instance): compute_node = db.compute_node_get_by_host(context, instance["host"]) @@ -69,14 +61,11 @@ class ExtendedServerAttributesController(wsgi.Controller): if authorize(context): # Attach our slave template to the response object resp_obj.attach(xml=ExtendedServerAttributeTemplate()) - - try: - instance = self.compute_api.get(context, id) - except exception.NotFound: - explanation = _("Server not found.") - raise exc.HTTPNotFound(explanation=explanation) - - self._extend_server(context, resp_obj.obj['server'], instance) + server = resp_obj.obj['server'] + db_instance = req.get_db_instance(server['id']) + # server['id'] is guaranteed to be in the cache due to + # the core API adding it in its 'show' method. + self._extend_server(context, server, db_instance) @wsgi.extends def detail(self, req, resp_obj): @@ -86,17 +75,11 @@ class ExtendedServerAttributesController(wsgi.Controller): resp_obj.attach(xml=ExtendedServerAttributesTemplate()) servers = list(resp_obj.obj['servers']) - instance_uuids = [server['id'] for server in servers] - instances = self._get_instances(context, instance_uuids) - - for server_object in servers: - try: - instance_data = instances[server_object['id']] - except KeyError: - # Ignore missing instance data - continue - - self._extend_server(context, server_object, instance_data) + for server in servers: + db_instance = req.get_db_instance(server['id']) + # server['id'] is guaranteed to be in the cache due to + # the core API adding it in its 'detail' method. + self._extend_server(context, server, db_instance) class Extended_server_attributes(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/compute/contrib/extended_status.py b/nova/api/openstack/compute/contrib/extended_status.py index 879d17b53..d17319c67 100644 --- a/nova/api/openstack/compute/contrib/extended_status.py +++ b/nova/api/openstack/compute/contrib/extended_status.py @@ -14,13 +14,10 @@ """The Extended Status Admin API extension.""" -from webob import exc - from nova.api.openstack import extensions from nova.api.openstack import wsgi from nova.api.openstack import xmlutil from nova import compute -from nova import exception from nova import flags from nova import log as logging @@ -35,14 +32,6 @@ class ExtendedStatusController(wsgi.Controller): super(ExtendedStatusController, self).__init__(*args, **kwargs) self.compute_api = compute.API() - def _get_instances(self, context, instance_uuids): - if not instance_uuids: - return {} - - filters = {'uuid': instance_uuids} - instances = self.compute_api.get_all(context, filters) - return dict((instance['uuid'], instance) for instance in instances) - def _extend_server(self, server, instance): for state in ['task_state', 'vm_state', 'power_state']: key = "%s:%s" % (Extended_status.alias, state) @@ -54,14 +43,11 @@ class ExtendedStatusController(wsgi.Controller): if authorize(context): # Attach our slave template to the response object resp_obj.attach(xml=ExtendedStatusTemplate()) - - try: - instance = self.compute_api.get(context, id) - except exception.NotFound: - explanation = _("Server not found.") - raise exc.HTTPNotFound(explanation=explanation) - - self._extend_server(resp_obj.obj['server'], instance) + server = resp_obj.obj['server'] + db_instance = req.get_db_instance(server['id']) + # server['id'] is guaranteed to be in the cache due to + # the core API adding it in its 'show' method. + self._extend_server(server, db_instance) @wsgi.extends def detail(self, req, resp_obj): @@ -69,19 +55,12 @@ class ExtendedStatusController(wsgi.Controller): if authorize(context): # Attach our slave template to the response object resp_obj.attach(xml=ExtendedStatusesTemplate()) - servers = list(resp_obj.obj['servers']) - instance_uuids = [server['id'] for server in servers] - instances = self._get_instances(context, instance_uuids) - - for server_object in servers: - try: - instance_data = instances[server_object['id']] - except KeyError: - # Ignore missing instance data - continue - - self._extend_server(server_object, instance_data) + for server in servers: + db_instance = req.get_db_instance(server['id']) + # server['id'] is guaranteed to be in the cache due to + # the core API adding it in its 'detail' method. + self._extend_server(server, db_instance) class Extended_status(extensions.ExtensionDescriptor): diff --git a/nova/api/openstack/compute/servers.py b/nova/api/openstack/compute/servers.py index 69f1b2f3d..9791caa54 100644 --- a/nova/api/openstack/compute/servers.py +++ b/nova/api/openstack/compute/servers.py @@ -461,16 +461,20 @@ class Controller(wsgi.Controller): limited_list = self._limit_items(instance_list, req) if is_detail: self._add_instance_faults(context, limited_list) - return self._view_builder.detail(req, limited_list) + response = self._view_builder.detail(req, limited_list) else: - return self._view_builder.index(req, limited_list) + response = self._view_builder.index(req, limited_list) + req.cache_db_instances(limited_list) + return response - def _get_server(self, context, instance_uuid): + def _get_server(self, context, req, instance_uuid): """Utility function for looking up an instance by uuid.""" try: - return self.compute_api.get(context, instance_uuid) + instance = self.compute_api.get(context, instance_uuid) except exception.NotFound: raise exc.HTTPNotFound() + req.cache_db_instance(instance) + return instance def _validate_server_name(self, value): if not isinstance(value, basestring): @@ -580,6 +584,7 @@ class Controller(wsgi.Controller): try: context = req.environ['nova.context'] instance = self.compute_api.get(context, id) + req.cache_db_instance(instance) self._add_instance_faults(context, [instance]) return self._view_builder.show(req, instance) except exception.NotFound: @@ -732,6 +737,7 @@ class Controller(wsgi.Controller): if ret_resv_id: return {'reservation_id': resv_id} + req.cache_db_instances(instances) server = self._view_builder.create(req, instances[0]) if '_is_precooked' in server['server'].keys(): @@ -744,8 +750,8 @@ class Controller(wsgi.Controller): return self._add_location(robj) - def _delete(self, context, id): - instance = self._get_server(context, id) + def _delete(self, context, req, instance_uuid): + instance = self._get_server(context, req, instance_uuid) if FLAGS.reclaim_instance_interval: self.compute_api.soft_delete(context, instance) else: @@ -789,6 +795,7 @@ class Controller(wsgi.Controller): try: instance = self.compute_api.get(ctxt, id) + req.cache_db_instance(instance) self.compute_api.update(ctxt, instance, **update_dict) except exception.NotFound: raise exc.HTTPNotFound() @@ -804,7 +811,7 @@ class Controller(wsgi.Controller): @wsgi.action('confirmResize') def _action_confirm_resize(self, req, id, body): context = req.environ['nova.context'] - instance = self._get_server(context, id) + instance = self._get_server(context, req, id) try: self.compute_api.confirm_resize(context, instance) except exception.MigrationNotFound: @@ -824,7 +831,7 @@ class Controller(wsgi.Controller): @wsgi.action('revertResize') def _action_revert_resize(self, req, id, body): context = req.environ['nova.context'] - instance = self._get_server(context, id) + instance = self._get_server(context, req, id) try: self.compute_api.revert_resize(context, instance) except exception.MigrationNotFound: @@ -856,7 +863,7 @@ class Controller(wsgi.Controller): raise exc.HTTPBadRequest(explanation=msg) context = req.environ['nova.context'] - instance = self._get_server(context, id) + instance = self._get_server(context, req, id) try: self.compute_api.reboot(context, instance, reboot_type) @@ -871,7 +878,7 @@ class Controller(wsgi.Controller): def _resize(self, req, instance_id, flavor_id, **kwargs): """Begin the resize process with given instance/flavor.""" context = req.environ["nova.context"] - instance = self._get_server(context, instance_id) + instance = self._get_server(context, req, instance_id) try: self.compute_api.resize(context, instance, flavor_id, **kwargs) @@ -891,7 +898,7 @@ class Controller(wsgi.Controller): def delete(self, req, id): """Destroys a server.""" try: - self._delete(req.environ['nova.context'], id) + self._delete(req.environ['nova.context'], req, id) except exception.NotFound: raise exc.HTTPNotFound() except exception.InstanceInvalidState as state_error: @@ -947,7 +954,7 @@ class Controller(wsgi.Controller): if not isinstance(password, basestring): msg = _("Invalid adminPass") raise exc.HTTPBadRequest(explanation=msg) - server = self._get_server(context, id) + server = self._get_server(context, req, id) self.compute_api.set_admin_password(context, server, password) return webob.Response(status_int=202) @@ -1009,7 +1016,7 @@ class Controller(wsgi.Controller): password = utils.generate_password(FLAGS.password_length) context = req.environ['nova.context'] - instance = self._get_server(context, id) + instance = self._get_server(context, req, id) attr_map = { 'personality': 'files_to_inject', @@ -1065,7 +1072,7 @@ class Controller(wsgi.Controller): except exception.InstanceTypeDiskTooSmall as error: raise exc.HTTPBadRequest(explanation=unicode(error)) - instance = self._get_server(context, id) + instance = self._get_server(context, req, id) self._add_instance_faults(context, [instance]) view = self._view_builder.show(req, instance) @@ -1103,7 +1110,7 @@ class Controller(wsgi.Controller): msg = _("Invalid metadata") raise exc.HTTPBadRequest(explanation=msg) - instance = self._get_server(context, id) + instance = self._get_server(context, req, id) try: image = self.compute_api.snapshot(context, diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index f15891c8f..4f78a4697 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -64,6 +64,52 @@ _MEDIA_TYPE_MAP = { class Request(webob.Request): """Add some OpenStack API-specific logic to the base webob.Request.""" + def __init__(self, *args, **kwargs): + super(Request, self).__init__(*args, **kwargs) + self._extension_data = {'db_instances': {}} + + def cache_db_instances(self, instances): + """ + Allow API methods to store instances from a DB query to be + used by API extensions within the same API request. + + An instance of this class only lives for the lifetime of a + single API request, so there's no need to implement full + cache management. + """ + db_instances = self._extension_data['db_instances'] + for instance in instances: + db_instances[instance['uuid']] = instance + + def cache_db_instance(self, instance): + """ + Allow API methods to store an instance from a DB query to be + used by API extensions within the same API request. + + An instance of this class only lives for the lifetime of a + single API request, so there's no need to implement full + cache management. + """ + self.cache_db_instances([instance]) + + def get_db_instances(self): + """ + Allow an API extension to get previously stored instances within + the same API request. + + Note that the instance data will be slightly stale. + """ + return self._extension_data['db_instances'] + + def get_db_instance(self, instance_uuid): + """ + Allow an API extension to get a previously stored instance + within the same API request. + + Note that the instance data will be slightly stale. + """ + return self._extension_data['db_instances'].get(instance_uuid) + def best_match_content_type(self): """Determine the requested response content-type.""" if 'nova.best_content_type' not in self.environ: diff --git a/nova/tests/api/openstack/fakes.py b/nova/tests/api/openstack/fakes.py index 1479a8f6e..ca6b25c67 100644 --- a/nova/tests/api/openstack/fakes.py +++ b/nova/tests/api/openstack/fakes.py @@ -301,13 +301,13 @@ class FakeRequestContext(context.RequestContext): return super(FakeRequestContext, self).__init__(*args, **kwargs) -class HTTPRequest(webob.Request): +class HTTPRequest(os_wsgi.Request): @classmethod def blank(cls, *args, **kwargs): kwargs['base_url'] = 'http://localhost/v2' use_admin_context = kwargs.pop('use_admin_context', False) - out = webob.Request.blank(*args, **kwargs) + out = os_wsgi.Request.blank(*args, **kwargs) out.environ['nova.context'] = FakeRequestContext('fake_user', 'fake', is_admin=use_admin_context) return out diff --git a/nova/tests/api/openstack/test_wsgi.py b/nova/tests/api/openstack/test_wsgi.py index f32704775..f45450495 100644 --- a/nova/tests/api/openstack/test_wsgi.py +++ b/nova/tests/api/openstack/test_wsgi.py @@ -75,6 +75,27 @@ class RequestTest(test.TestCase): result = request.best_match_content_type() self.assertEqual(result, "application/json") + def test_cache_and_retrieve_instances(self): + request = wsgi.Request.blank('/foo') + instances = [] + for x in xrange(3): + instances.append({'uuid': 'uuid%s' % x}) + # Store 2 + request.cache_db_instances(instances[:2]) + # Store 1 + request.cache_db_instance(instances[2]) + self.assertEqual(request.get_db_instance('uuid0'), + instances[0]) + self.assertEqual(request.get_db_instance('uuid1'), + instances[1]) + self.assertEqual(request.get_db_instance('uuid2'), + instances[2]) + self.assertEqual(request.get_db_instance('uuid3'), None) + self.assertEqual(request.get_db_instances(), + {'uuid0': instances[0], + 'uuid1': instances[1], + 'uuid2': instances[2]}) + class ActionDispatcherTest(test.TestCase): def test_dispatch(self): @@ -215,7 +236,7 @@ class ResourceTest(test.TestCase): expected = 'off' self.assertEqual(actual, expected) - def test_get_method_unknown_controller_action(self): + def test_get_method_unknown_controller_method(self): class Controller(object): def index(self, req, pants=None): return pants |
