diff options
| author | Chris Behrens <cbehrens@codestud.com> | 2012-06-25 23:17:33 +0000 |
|---|---|---|
| committer | Chris Behrens <cbehrens@codestud.com> | 2012-06-26 18:00:55 +0000 |
| commit | 9f9fbc54e7336da10fc3056bdaca2ec7d01c7f94 (patch) | |
| tree | 73a9a41a2abdda89796dece40ece12cdd6ab989a /nova/api | |
| parent | 1a169b8bf98a7fa0c1361ac942bbd9d4041f8e63 (diff) | |
| download | nova-9f9fbc54e7336da10fc3056bdaca2ec7d01c7f94.tar.gz nova-9f9fbc54e7336da10fc3056bdaca2ec7d01c7f94.tar.xz nova-9f9fbc54e7336da10fc3056bdaca2ec7d01c7f94.zip | |
Remove extra DB calls for instances from OS API extensions
Allow the core API to store DB results so that extensions can use data
already retrieved within the same API request...eliminating extra
expensive DB calls. This implements storing of instances only, so far.
Fixes bug 1017756
Change-Id: I98a2e3323f5ea69ab9e7470f95d821790510989e
Diffstat (limited to 'nova/api')
| -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 |
5 files changed, 101 insertions, 92 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: |
