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:
parent
0aa7aafdd7
commit
d0336ee172
@ -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)
|
||||
|
@ -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,29 +2770,39 @@ class API(base.Base):
|
||||
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
|
||||
context, instance.uuid)
|
||||
|
||||
mapping = []
|
||||
for bdm in bdms:
|
||||
if bdm.no_device:
|
||||
continue
|
||||
@wrap_instance_event(prefix='api')
|
||||
def snapshot_instance(self, context, instance, bdms):
|
||||
mapping = []
|
||||
for bdm in bdms:
|
||||
if bdm.no_device:
|
||||
continue
|
||||
|
||||
if bdm.is_volume:
|
||||
# create snapshot based on volume_id
|
||||
volume = self.volume_api.get(context, bdm.volume_id)
|
||||
# NOTE(yamahata): Should we wait for snapshot creation?
|
||||
# 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'],
|
||||
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'],
|
||||
bdm)
|
||||
mapping_dict = mapping_dict.get_image_mapping()
|
||||
else:
|
||||
mapping_dict = bdm.get_image_mapping()
|
||||
if bdm.is_volume:
|
||||
# create snapshot based on volume_id
|
||||
volume = self.volume_api.get(context, bdm.volume_id)
|
||||
# NOTE(yamahata): Should we wait for snapshot creation?
|
||||
# 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'],
|
||||
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'],
|
||||
bdm)
|
||||
mapping_dict = mapping_dict.get_image_mapping()
|
||||
else:
|
||||
mapping_dict = bdm.get_image_mapping()
|
||||
|
||||
mapping.append(mapping_dict)
|
||||
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)
|
||||
|
@ -69,3 +69,4 @@ SWAP_VOLUME = 'swap_volume'
|
||||
LOCK = 'lock'
|
||||
UNLOCK = 'unlock'
|
||||
BACKUP = 'createBackup'
|
||||
CREATE_IMAGE = 'createImage'
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -3392,14 +3392,17 @@ class ComputeTestCase(BaseTestCase,
|
||||
fake_delete)
|
||||
|
||||
inst_obj = self._get_snapshotting_instance()
|
||||
if method == 'snapshot':
|
||||
self.assertRaises(test.TestingException,
|
||||
self.compute.snapshot_instance,
|
||||
self.context, image_id='fakesnap',
|
||||
instance=inst_obj)
|
||||
else:
|
||||
with mock.patch.object(compute_utils,
|
||||
'EventReporter') as mock_event:
|
||||
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:
|
||||
self.assertRaises(test.TestingException,
|
||||
self.compute.backup_instance,
|
||||
self.context, image_id='fakesnap',
|
||||
|
@ -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,9 +2823,20 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||
fake_unquiesce_instance)
|
||||
fake_image.stub_out_image_service(self)
|
||||
|
||||
# No block devices defined
|
||||
self.compute_api.snapshot_volume_backed(
|
||||
self.context, instance, 'test-snapshot')
|
||||
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,
|
||||
@ -2843,13 +2856,24 @@ class _ComputeAPIUnitTestMixIn(object):
|
||||
'destination_type': 'volume', 'delete_on_termination': False,
|
||||
'tag': None})
|
||||
|
||||
# All the db_only fields and the volume ones are removed
|
||||
self.compute_api.snapshot_volume_backed(
|
||||
self.context, instance, 'test-snapshot')
|
||||
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')
|
||||
|
||||
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
|
||||
self.compute_api.snapshot_volume_backed(
|
||||
self.context, instance, 'test-snapshot')
|
||||
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)
|
||||
|
||||
|
@ -12,3 +12,4 @@ features:
|
||||
* unlock
|
||||
* shelveOffload
|
||||
* createBackup
|
||||
* createImage
|
||||
|
Loading…
Reference in New Issue
Block a user