Make servers API use cell-targeted context

This makes the servers API look up the CellMapping for the instance
in question and target the context to the appropriate cell for the
subsequent call into compute/api.

Note that just as with get() and get_all(), we need to avoid going
direct to the cell database and thus short-circuiting the replication
in the case of cellsv1.

Related to blueprint cells-aware-api
Change-Id: If63a4fa7349890a6e7ac0fa3fe12917dc53334d0
This commit is contained in:
Dan Smith
2017-02-13 15:04:44 -08:00
committed by Dan Smith
parent 6a0f091d9a
commit 799cb96766
5 changed files with 57 additions and 8 deletions
+10 -3
View File
@@ -1135,9 +1135,16 @@ class ServersController(wsgi.Controller):
def _get_instance(self, context, instance_uuid):
try:
attrs = ['system_metadata', 'metadata']
return objects.Instance.get_by_uuid(context, instance_uuid,
expected_attrs=attrs)
except exception.InstanceNotFound as e:
if not CONF.cells.enable:
# NOTE(danms): We can't target a cell database if we're
# in cellsv1 otherwise we'll short-circuit the replication.
mapping = objects.InstanceMapping.get_by_instance_uuid(
context, instance_uuid)
nova_context.set_target_cell(context, mapping.cell_mapping)
return objects.Instance.get_by_uuid(
context, instance_uuid, expected_attrs=attrs)
except (exception.InstanceNotFound,
exception.InstanceMappingNotFound) as e:
raise webob.exc.HTTPNotFound(explanation=e.format_message())
@wsgi.response(202)
+21 -5
View File
@@ -358,19 +358,35 @@ def authorize_quota_class_context(context, class_name):
raise exception.Forbidden()
def set_target_cell(context, cell_mapping):
"""Adds database connection information to the context
for communicating with the given target_cell.
This is used for permanently targeting a cell in a context.
Use this when you want all subsequent code to target a cell.
:param context: The RequestContext to add connection information
:param cell_mapping: An objects.CellMapping object
"""
# avoid circular import
from nova import db
db_connection_string = cell_mapping.database_connection
context.db_connection = db.create_context_manager(db_connection_string)
@contextmanager
def target_cell(context, cell_mapping):
"""Adds database connection information to the context
"""Temporarily adds database connection information to the context
for communicating with the given target cell.
This context manager makes a temporary change to the context
and restores it when complete.
:param context: The RequestContext to add connection information
:param cell_mapping: A objects.CellMapping object
"""
original_db_connection = context.db_connection
# avoid circular import
from nova import db
db_connection_string = cell_mapping.database_connection
context.db_connection = db.create_context_manager(db_connection_string)
set_target_cell(context, cell_mapping)
try:
yield context
finally:
+3
View File
@@ -291,6 +291,9 @@ class SingleCellSimple(fixtures.Fixture):
self.useFixture(fixtures.MonkeyPatch(
'nova.context.target_cell',
self._fake_target_cell))
self.useFixture(fixtures.MonkeyPatch(
'nova.context.set_target_cell',
lambda c, m: None))
def _fake_hostmapping_get(self, *args):
return {'id': 1,
@@ -25,6 +25,7 @@ from nova import db
from nova import exception
from nova import policy
from nova import test
from nova.tests import fixtures as nova_fixtures
from nova.tests.unit.api.openstack import fakes
from nova.tests import uuidsentinel as uuids
@@ -35,6 +36,7 @@ class ServerStartStopTestV21(test.TestCase):
super(ServerStartStopTestV21, self).setUp()
self._setup_controller()
self.req = fakes.HTTPRequest.blank('')
self.useFixture(nova_fixtures.SingleCellSimple())
self.stub_out('nova.db.instance_get_by_uuid',
fakes.fake_instance_get())
@@ -132,6 +134,7 @@ class ServerStartStopPolicyEnforcementV21(test.TestCase):
self.controller = server_v21.ServersController(
extension_info=ext_info)
self.req = fakes.HTTPRequest.blank('')
self.useFixture(nova_fixtures.SingleCellSimple())
self.stub_out(
'nova.db.instance_get_by_uuid',
fakes.fake_instance_get(
@@ -189,6 +189,25 @@ class ServersControllerTest(ControllerTest):
use_admin_context=use_admin_context,
version=self.wsgi_api_version)
@mock.patch('nova.objects.Instance.get_by_uuid')
@mock.patch('nova.objects.InstanceMapping.get_by_instance_uuid')
def test_cellsv1_instance_lookup_no_target(self, mock_get_im,
mock_get_inst):
self.flags(enable=True, group='cells')
ctxt = context.RequestContext('fake', 'fake')
self.controller._get_instance(ctxt, 'foo')
self.assertFalse(mock_get_im.called)
self.assertIsNone(ctxt.db_connection)
@mock.patch('nova.objects.Instance.get_by_uuid')
@mock.patch('nova.objects.InstanceMapping.get_by_instance_uuid')
def test_instance_lookup_targets(self, mock_get_im, mock_get_inst):
ctxt = context.RequestContext('fake', 'fake')
mock_get_im.return_value.cell_mapping.database_connection = uuids.cell1
self.controller._get_instance(ctxt, 'foo')
mock_get_im.assert_called_once_with(ctxt, 'foo')
self.assertIsNotNone(ctxt.db_connection)
def test_requested_networks_prefix(self):
self.flags(use_neutron=True)
uuid = 'br-00000000-0000-0000-0000-000000000000'
@@ -1761,6 +1780,7 @@ class ServersControllerRebuildInstanceTest(ControllerTest):
self.req.headers["content-type"] = "application/json"
self.req_user_id = self.req.environ['nova.context'].user_id
self.req_project_id = self.req.environ['nova.context'].project_id
self.useFixture(nova_fixtures.SingleCellSimple())
def fake_get(ctrl, ctxt, uuid):
if uuid == 'test_inst':