summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--nova/tests/api/openstack/fakes.py4
-rw-r--r--nova/tests/api/openstack/test_wsgi.py23
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