Add instance action record for snapshot instances

We currently don't record snapshot instance
actions. This is useful for auditing and debugging.

This patch adds instance snapshot actions.

partial-implements: blueprint fill-the-gap-for-instance-action-records

Change-Id: I9ce48e768cc67543f27a6c87c57b47501fff38c2
This commit is contained in:
Kevin_Zheng 2017-12-06 11:57:32 +08:00
parent 0aa7aafdd7
commit d0336ee172
8 changed files with 104 additions and 40 deletions

View File

@ -909,6 +909,11 @@ class _TargetedMessageMethods(_BaseMessageMethods):
instance.refresh()
instance.task_state = task_states.IMAGE_SNAPSHOT_PENDING
instance.save(expected_task_state=[None])
objects.InstanceAction.action_start(
message.ctxt, instance.uuid, instance_actions.CREATE_IMAGE,
want_result=False)
self.compute_rpcapi.snapshot_instance(message.ctxt,
instance,
image_id)

View File

@ -2658,6 +2658,9 @@ class API(base.Base):
state=state,
method='snapshot')
self._record_action_start(context, instance,
instance_actions.CREATE_IMAGE)
self.compute_rpcapi.snapshot_instance(context, instance,
image_meta['id'])
@ -2767,6 +2770,8 @@ class API(base.Base):
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
context, instance.uuid)
@wrap_instance_event(prefix='api')
def snapshot_instance(self, context, instance, bdms):
mapping = []
for bdm in bdms:
if bdm.no_device:
@ -2779,17 +2784,25 @@ class API(base.Base):
# Linux LVM snapshot creation completes in
# short time, it doesn't matter for now.
name = _('snapshot for %s') % image_meta['name']
LOG.debug('Creating snapshot from volume %s.', volume['id'],
LOG.debug('Creating snapshot from volume %s.',
volume['id'],
instance=instance)
snapshot = self.volume_api.create_snapshot_force(
context, volume['id'], name, volume['display_description'])
mapping_dict = block_device.snapshot_from_bdm(snapshot['id'],
context, volume['id'], name,
volume['display_description'])
mapping_dict = block_device.snapshot_from_bdm(
snapshot['id'],
bdm)
mapping_dict = mapping_dict.get_image_mapping()
else:
mapping_dict = bdm.get_image_mapping()
mapping.append(mapping_dict)
return mapping
self._record_action_start(context, instance,
instance_actions.CREATE_IMAGE)
mapping = snapshot_instance(self, context, instance, bdms)
if quiesced:
self.compute_rpcapi.unquiesce_instance(context, instance, mapping)

View File

@ -69,3 +69,4 @@ SWAP_VOLUME = 'swap_volume'
LOCK = 'lock'
UNLOCK = 'unlock'
BACKUP = 'createBackup'
CREATE_IMAGE = 'createImage'

View File

@ -3229,6 +3229,7 @@ class ComputeManager(manager.Manager):
@wrap_exception()
@reverts_task_state
@wrap_instance_event(prefix='compute')
@wrap_instance_fault
@delete_image_on_error
def snapshot_instance(self, context, image_id, instance):

View File

@ -1343,8 +1343,9 @@ class CellsTargetedMethodsTestCase(test.NoDBTestCase):
self._test_instance_action_method('inject_network_info',
(), {}, (), {}, False)
def test_snapshot_instance(self):
inst = objects.Instance()
@mock.patch.object(objects.InstanceAction, 'action_start')
def test_snapshot_instance(self, action_start):
inst = objects.Instance(uuid=uuids.instance)
meth_cls = self.tgt_methods_cls
self.mox.StubOutWithMock(inst, 'refresh')
@ -1371,6 +1372,9 @@ class CellsTargetedMethodsTestCase(test.NoDBTestCase):
message.need_response = False
meth_cls.snapshot_instance(message, inst, image_id='image-id')
action_start.assert_called_once_with(
message.ctxt, inst.uuid, instance_actions.CREATE_IMAGE,
want_result=False)
@mock.patch.object(objects.InstanceAction, 'action_start')
def test_backup_instance(self, action_start):

View File

@ -3392,14 +3392,17 @@ class ComputeTestCase(BaseTestCase,
fake_delete)
inst_obj = self._get_snapshotting_instance()
with mock.patch.object(compute_utils,
'EventReporter') as mock_event:
if method == 'snapshot':
self.assertRaises(test.TestingException,
self.compute.snapshot_instance,
self.context, image_id='fakesnap',
instance=inst_obj)
mock_event.assert_called_once_with(self.context,
'compute_snapshot_instance',
inst_obj.uuid)
else:
with mock.patch.object(compute_utils,
'EventReporter') as mock_event:
self.assertRaises(test.TestingException,
self.compute.backup_instance,
self.context, image_id='fakesnap',

View File

@ -2594,6 +2594,8 @@ class _ComputeAPIUnitTestMixIn(object):
res = self.compute_api.snapshot(self.context, instance,
'fake-name',
extra_properties=extra_props)
mock_record.assert_called_once_with(
self.context, instance, instance_actions.CREATE_IMAGE)
else:
res = self.compute_api.backup(self.context, instance,
'fake-name',
@ -2821,10 +2823,21 @@ class _ComputeAPIUnitTestMixIn(object):
fake_unquiesce_instance)
fake_image.stub_out_image_service(self)
with test.nested(
mock.patch.object(compute_api.API, '_record_action_start'),
mock.patch.object(compute_utils, 'EventReporter')) as (
mock_record, mock_event):
# No block devices defined
self.compute_api.snapshot_volume_backed(
self.context, instance, 'test-snapshot')
mock_record.assert_called_once_with(self.context,
instance,
instance_actions.CREATE_IMAGE)
mock_event.assert_called_once_with(self.context,
'api_snapshot_instance',
instance.uuid)
bdm = fake_block_device.FakeDbBlockDeviceDict(
{'no_device': False, 'volume_id': '1', 'boot_index': 0,
'connection_info': 'inf', 'device_name': '/dev/vda',
@ -2843,6 +2856,10 @@ class _ComputeAPIUnitTestMixIn(object):
'destination_type': 'volume', 'delete_on_termination': False,
'tag': None})
with test.nested(
mock.patch.object(compute_api.API, '_record_action_start'),
mock.patch.object(compute_utils, 'EventReporter')) as (
mock_record, mock_event):
# All the db_only fields and the volume ones are removed
self.compute_api.snapshot_volume_backed(
self.context, instance, 'test-snapshot')
@ -2850,6 +2867,13 @@ class _ComputeAPIUnitTestMixIn(object):
self.assertEqual(quiesce_expected, quiesced[0])
self.assertEqual(quiesce_expected, quiesced[1])
mock_record.assert_called_once_with(self.context,
instance,
instance_actions.CREATE_IMAGE)
mock_event.assert_called_once_with(self.context,
'api_snapshot_instance',
instance.uuid)
instance.system_metadata['image_mappings'] = jsonutils.dumps(
[{'virtual': 'ami', 'device': 'vda'},
{'device': 'vda', 'virtual': 'ephemeral0'},
@ -2882,13 +2906,25 @@ class _ComputeAPIUnitTestMixIn(object):
quiesced = [False, False]
# Check that the mappings from the image properties are not included
with test.nested(
mock.patch.object(compute_api.API, '_record_action_start'),
mock.patch.object(compute_utils, 'EventReporter')) as (
mock_record, mock_event):
# Check that the mappings from the image properties are not
# included
self.compute_api.snapshot_volume_backed(
self.context, instance, 'test-snapshot')
self.assertEqual(quiesce_expected, quiesced[0])
self.assertEqual(quiesce_expected, quiesced[1])
mock_record.assert_called_once_with(self.context,
instance,
instance_actions.CREATE_IMAGE)
mock_event.assert_called_once_with(self.context,
'api_snapshot_instance',
instance.uuid)
def test_snapshot_volume_backed(self):
self._test_snapshot_volume_backed(False, False)

View File

@ -12,3 +12,4 @@ features:
* unlock
* shelveOffload
* createBackup
* createImage