summaryrefslogtreecommitdiffstats
path: root/nova/api
diff options
context:
space:
mode:
authorChris Behrens <cbehrens@codestud.com>2012-06-25 23:17:33 +0000
committerChris Behrens <cbehrens@codestud.com>2012-06-26 18:00:55 +0000
commit9f9fbc54e7336da10fc3056bdaca2ec7d01c7f94 (patch)
tree73a9a41a2abdda89796dece40ece12cdd6ab989a /nova/api
parent1a169b8bf98a7fa0c1361ac942bbd9d4041f8e63 (diff)
downloadnova-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.py32
-rw-r--r--nova/api/openstack/compute/contrib/extended_server_attributes.py37
-rw-r--r--nova/api/openstack/compute/contrib/extended_status.py41
-rw-r--r--nova/api/openstack/compute/servers.py37
-rw-r--r--nova/api/openstack/wsgi.py46
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: