Merge "Remove 'instance_update_from_api'"

This commit is contained in:
Zuul 2019-05-29 03:56:11 +00:00 committed by Gerrit Code Review
commit 38cb0a353c
8 changed files with 9 additions and 262 deletions

View File

@ -426,14 +426,6 @@ class CellsManager(manager.Manager):
migrations.extend(response.value_or_raise().objects)
return objects.MigrationList(objects=migrations)
def instance_update_from_api(self, ctxt, instance, expected_vm_state,
expected_task_state, admin_state_reset):
"""Update an instance in its cell."""
self.msg_runner.instance_update_from_api(ctxt, instance,
expected_vm_state,
expected_task_state,
admin_state_reset)
def start_instance(self, ctxt, instance):
"""Start an instance in its cell."""
self.msg_runner.start_instance(ctxt, instance)

View File

@ -768,23 +768,6 @@ class _TargetedMessageMethods(_BaseMessageMethods):
def get_migrations(self, message, filters):
return self.compute_api.get_migrations(message.ctxt, filters)
def instance_update_from_api(self, message, instance,
expected_vm_state,
expected_task_state,
admin_state_reset):
"""Update an instance in this cell."""
if not admin_state_reset:
# NOTE(comstud): We don't want to nuke this cell's view
# of vm_state and task_state unless it's a forced reset
# via admin API.
instance.obj_reset_changes(['vm_state', 'task_state'])
# NOTE(alaski): A cell should be authoritative for its system_metadata
# and metadata so we don't want to sync it down from the api.
instance.obj_reset_changes(['metadata', 'system_metadata'])
with instance.skip_cells_sync():
instance.save(expected_vm_state=expected_vm_state,
expected_task_state=expected_task_state)
def _call_compute_api_with_obj(self, ctxt, instance, method, *args,
**kwargs):
try:
@ -1475,24 +1458,6 @@ class MessageRunner(object):
need_response=need_response)
return message.process()
def instance_update_from_api(self, ctxt, instance,
expected_vm_state, expected_task_state,
admin_state_reset):
"""Update an instance object in its cell."""
cell_name = instance.cell_name
if not cell_name:
LOG.warning("No cell_name for instance update from API",
instance=instance)
return
method_kwargs = {'instance': instance,
'expected_vm_state': expected_vm_state,
'expected_task_state': expected_task_state,
'admin_state_reset': admin_state_reset}
message = _TargetedMessage(self, ctxt, 'instance_update_from_api',
method_kwargs, 'down',
cell_name)
message.process()
def start_instance(self, ctxt, instance):
"""Start an instance in its cell."""
self._instance_action(ctxt, instance, 'start_instance')

View File

@ -382,19 +382,6 @@ class CellsAPI(object):
cctxt = self.client.prepare(version='1.11')
return cctxt.call(ctxt, 'get_migrations', filters=filters)
def instance_update_from_api(self, ctxt, instance, expected_vm_state,
expected_task_state, admin_state_reset):
"""Update an instance in its cell.
This method takes a new-world instance object.
"""
cctxt = self.client.prepare(version='1.16')
cctxt.cast(ctxt, 'instance_update_from_api',
instance=instance,
expected_vm_state=expected_vm_state,
expected_task_state=expected_task_state,
admin_state_reset=admin_state_reset)
def start_instance(self, ctxt, instance):
"""Start an instance in its cell.

View File

@ -29,10 +29,6 @@ from nova.objects import base as obj_base
# Separator used between cell names for the 'full cell name' and routing
# path
PATH_CELL_SEP = '!'
# Flag prepended to a cell name to indicate data shouldn't be synced during
# an instance save. There are no illegal chars in a cell name so using the
# meaningful PATH_CELL_SEP in an invalid way will need to suffice.
BLOCK_SYNC_FLAG = '!!'
# Separator used between cell name and item
CELL_ITEM_SEP = '@'

View File

@ -25,9 +25,6 @@ from sqlalchemy.sql import func
from sqlalchemy.sql import null
from nova import availability_zones as avail_zone
from nova.cells import opts as cells_opts
from nova.cells import rpcapi as cells_rpcapi
from nova.cells import utils as cells_utils
from nova.compute import task_states
from nova.compute import vm_states
from nova.db import api as db
@ -185,6 +182,7 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
'shutdown_terminate': fields.BooleanField(default=False),
'disable_terminate': fields.BooleanField(default=False),
# TODO(stephenfin): Remove this in version 3.0 of the object
'cell_name': fields.StringField(nullable=True),
'metadata': fields.DictOfStringsField(),
@ -720,6 +718,8 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
value = jsonutils.dumps(obj.obj_to_primitive())
self._extra_values_to_save[field] = value
# TODO(stephenfin): Remove the 'admin_state_reset' field in version 3.0 of
# the object
@base.remotable
def save(self, expected_vm_state=None,
expected_task_state=None, admin_state_reset=False):
@ -737,35 +737,7 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
:param admin_state_reset: True if admin API is forcing setting
of task_state/vm_state
"""
# Store this on the class because _cell_name_blocks_sync is useless
# after the db update call below.
self._sync_cells = not self._cell_name_blocks_sync()
context = self._context
cell_type = cells_opts.get_cell_type()
if cell_type is not None:
# NOTE(comstud): We need to stash a copy of ourselves
# before any updates are applied. When we call the save
# methods on nested objects, we will lose any changes to
# them. But we need to make sure child cells can tell
# what is changed.
#
# We also need to nuke any updates to vm_state and task_state
# unless admin_state_reset is True. compute cells are
# authoritative for their view of vm_state and task_state.
stale_instance = self.obj_clone()
cells_update_from_api = (cell_type == 'api' and self.cell_name and
self._sync_cells)
if cells_update_from_api:
def _handle_cell_update_from_api():
cells_api = cells_rpcapi.CellsAPI()
cells_api.instance_update_from_api(context, stale_instance,
expected_vm_state,
expected_task_state,
admin_state_reset)
self._extra_values_to_save = {}
updates = {}
@ -794,20 +766,13 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
# as a result of bug.
raise exception.InstanceNotFound(instance_id=self.uuid)
elif field in changes:
if (field == 'cell_name' and self[field] is not None and
self[field].startswith(cells_utils.BLOCK_SYNC_FLAG)):
updates[field] = self[field].replace(
cells_utils.BLOCK_SYNC_FLAG, '', 1)
else:
updates[field] = self[field]
updates[field] = self[field]
if self._extra_values_to_save:
db.instance_extra_update_by_uuid(context, self.uuid,
self._extra_values_to_save)
if not updates:
if cells_update_from_api:
_handle_cell_update_from_api()
return
# Cleaned needs to be turned back into an int here
@ -839,24 +804,11 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
self._from_db_object(context, self, inst_ref,
expected_attrs=expected_attrs)
if cells_update_from_api:
_handle_cell_update_from_api()
def _notify():
# NOTE(danms): We have to be super careful here not to trigger
# any lazy-loads that will unmigrate or unbackport something. So,
# make a copy of the instance for notifications first.
new_ref = self.obj_clone()
notifications.send_update(context, old_ref, new_ref)
# NOTE(alaski): If cell synchronization is blocked it means we have
# already run this block of code in either the parent or child of this
# cell. Therefore this notification has already been sent.
if not self._sync_cells:
_notify = lambda: None # noqa: F811
_notify()
# NOTE(danms): We have to be super careful here not to trigger
# any lazy-loads that will unmigrate or unbackport something. So,
# make a copy of the instance for notifications first.
new_ref = self.obj_clone()
notifications.send_update(context, old_ref, new_ref)
self.obj_reset_changes()
@ -1206,49 +1158,6 @@ class Instance(base.NovaPersistentObject, base.NovaObject,
if not md_was_changed:
self.obj_reset_changes(['metadata'])
def _cell_name_blocks_sync(self):
if (self.obj_attr_is_set('cell_name') and
self.cell_name is not None and
self.cell_name.startswith(cells_utils.BLOCK_SYNC_FLAG)):
return True
return False
def _normalize_cell_name(self):
"""Undo skip_cell_sync()'s cell_name modification if applied"""
if not self.obj_attr_is_set('cell_name') or self.cell_name is None:
return
cn_changed = 'cell_name' in self.obj_what_changed()
if self.cell_name.startswith(cells_utils.BLOCK_SYNC_FLAG):
self.cell_name = self.cell_name.replace(
cells_utils.BLOCK_SYNC_FLAG, '', 1)
# cell_name is not normally an empty string, this means it was None
# or unset before cells_utils.BLOCK_SYNC_FLAG was applied.
if len(self.cell_name) == 0:
self.cell_name = None
if not cn_changed:
self.obj_reset_changes(['cell_name'])
@contextlib.contextmanager
def skip_cells_sync(self):
"""Context manager to save an instance without syncing cells.
Temporarily disables the cells syncing logic, if enabled. This should
only be used when saving an instance that has been passed down/up from
another cell in order to avoid passing it back to the originator to be
re-saved.
"""
cn_changed = 'cell_name' in self.obj_what_changed()
if not self.obj_attr_is_set('cell_name') or self.cell_name is None:
self.cell_name = ''
self.cell_name = '%s%s' % (cells_utils.BLOCK_SYNC_FLAG, self.cell_name)
if not cn_changed:
self.obj_reset_changes(['cell_name'])
try:
yield
finally:
self._normalize_cell_name()
def get_network_info(self):
if self.info_cache is None:
return network_model.NetworkInfo.hydrate([])

View File

@ -637,20 +637,6 @@ class CellsManagerClassTestCase(test.NoDBTestCase):
response = self.cells_manager.get_migrations(self.ctxt, filters)
self.assertEqual(migrations.objects, response.objects)
def test_instance_update_from_api(self):
self.mox.StubOutWithMock(self.msg_runner,
'instance_update_from_api')
self.msg_runner.instance_update_from_api(self.ctxt,
'fake-instance',
'exp_vm', 'exp_task',
'admin_reset')
self.mox.ReplayAll()
self.cells_manager.instance_update_from_api(
self.ctxt, instance='fake-instance',
expected_vm_state='exp_vm',
expected_task_state='exp_task',
admin_state_reset='admin_reset')
def test_start_instance(self):
self.mox.StubOutWithMock(self.msg_runner, 'start_instance')
self.msg_runner.start_instance(self.ctxt, 'fake-instance')

View File

@ -1069,78 +1069,6 @@ class CellsTargetedMethodsTestCase(test.NoDBTestCase):
'delete')
delete.assert_called_once_with(self.ctxt, instance)
def _instance_update_helper(self, admin_state_reset):
class FakeMessage(object):
pass
message = FakeMessage()
message.ctxt = self.ctxt
instance = objects.Instance()
instance.cell_name = self.tgt_cell_name
instance.obj_reset_changes()
instance.task_state = 'meow'
instance.vm_state = 'wuff'
instance.user_data = 'foo'
instance.metadata = {'meta': 'data'}
instance.system_metadata = {'system': 'metadata'}
self.assertEqual(set(['user_data', 'vm_state', 'task_state',
'metadata', 'system_metadata']),
instance.obj_what_changed())
self.mox.StubOutWithMock(instance, 'save')
def _check_object(*args, **kwargs):
# task_state and vm_state changes should have been cleared
# before calling save()
if admin_state_reset:
self.assertEqual(
set(['user_data', 'vm_state', 'task_state']),
instance.obj_what_changed())
else:
self.assertEqual(set(['user_data']),
instance.obj_what_changed())
instance.save(expected_task_state='exp_task',
expected_vm_state='exp_vm').WithSideEffects(
_check_object)
self.mox.ReplayAll()
self.tgt_methods_cls.instance_update_from_api(
message,
instance,
expected_vm_state='exp_vm',
expected_task_state='exp_task',
admin_state_reset=admin_state_reset)
def test_instance_update_from_api(self):
self._instance_update_helper(False)
def test_instance_update_from_api_admin_state_reset(self):
self._instance_update_helper(True)
def test_instance_update_from_api_calls_skip_cells_sync(self):
self.flags(enable=True, cell_type='compute', group='cells')
instance = fake_instance.fake_instance_obj(self.ctxt)
instance.cell_name = self.tgt_cell_name
instance.task_state = 'meow'
instance.vm_state = 'wuff'
instance.user_data = 'foo'
message = ''
@mock.patch.object(instance, 'save', side_effect=test.TestingException)
@mock.patch.object(instance, 'skip_cells_sync')
def _ensure_skip_cells_sync_called(mock_sync, mock_save):
self.assertRaises(test.TestingException,
self.tgt_methods_cls.instance_update_from_api,
message, instance, expected_vm_state='exp_vm',
expected_task_state='exp_task', admin_state_reset=False)
mock_sync.assert_has_calls([mock.call()])
_ensure_skip_cells_sync_called()
self.assertEqual(self.tgt_cell_name, instance.cell_name)
def _test_instance_action_method(self, method, args, kwargs,
expected_args, expected_kwargs,
expect_result):

View File

@ -433,22 +433,6 @@ class CellsAPITestCase(test.NoDBTestCase):
self._check_result(call_info, 'get_migrations', expected_args,
version="1.11")
def test_instance_update_from_api(self):
call_info = self._stub_rpc_method('cast', None)
self.cells_rpcapi.instance_update_from_api(
self.fake_context, 'fake-instance',
expected_vm_state='exp_vm',
expected_task_state='exp_task',
admin_state_reset='admin_reset')
expected_args = {'instance': 'fake-instance',
'expected_vm_state': 'exp_vm',
'expected_task_state': 'exp_task',
'admin_state_reset': 'admin_reset'}
self._check_result(call_info, 'instance_update_from_api',
expected_args, version='1.16')
def test_start_instance(self):
call_info = self._stub_rpc_method('cast', None)