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.refresh()
|
||||||
instance.task_state = task_states.IMAGE_SNAPSHOT_PENDING
|
instance.task_state = task_states.IMAGE_SNAPSHOT_PENDING
|
||||||
instance.save(expected_task_state=[None])
|
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,
|
self.compute_rpcapi.snapshot_instance(message.ctxt,
|
||||||
instance,
|
instance,
|
||||||
image_id)
|
image_id)
|
||||||
|
@ -2658,6 +2658,9 @@ class API(base.Base):
|
|||||||
state=state,
|
state=state,
|
||||||
method='snapshot')
|
method='snapshot')
|
||||||
|
|
||||||
|
self._record_action_start(context, instance,
|
||||||
|
instance_actions.CREATE_IMAGE)
|
||||||
|
|
||||||
self.compute_rpcapi.snapshot_instance(context, instance,
|
self.compute_rpcapi.snapshot_instance(context, instance,
|
||||||
image_meta['id'])
|
image_meta['id'])
|
||||||
|
|
||||||
@ -2767,29 +2770,39 @@ class API(base.Base):
|
|||||||
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
|
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
|
||||||
context, instance.uuid)
|
context, instance.uuid)
|
||||||
|
|
||||||
mapping = []
|
@wrap_instance_event(prefix='api')
|
||||||
for bdm in bdms:
|
def snapshot_instance(self, context, instance, bdms):
|
||||||
if bdm.no_device:
|
mapping = []
|
||||||
continue
|
for bdm in bdms:
|
||||||
|
if bdm.no_device:
|
||||||
|
continue
|
||||||
|
|
||||||
if bdm.is_volume:
|
if bdm.is_volume:
|
||||||
# create snapshot based on volume_id
|
# create snapshot based on volume_id
|
||||||
volume = self.volume_api.get(context, bdm.volume_id)
|
volume = self.volume_api.get(context, bdm.volume_id)
|
||||||
# NOTE(yamahata): Should we wait for snapshot creation?
|
# NOTE(yamahata): Should we wait for snapshot creation?
|
||||||
# Linux LVM snapshot creation completes in
|
# Linux LVM snapshot creation completes in
|
||||||
# short time, it doesn't matter for now.
|
# short time, it doesn't matter for now.
|
||||||
name = _('snapshot for %s') % image_meta['name']
|
name = _('snapshot for %s') % image_meta['name']
|
||||||
LOG.debug('Creating snapshot from volume %s.', volume['id'],
|
LOG.debug('Creating snapshot from volume %s.',
|
||||||
instance=instance)
|
volume['id'],
|
||||||
snapshot = self.volume_api.create_snapshot_force(
|
instance=instance)
|
||||||
context, volume['id'], name, volume['display_description'])
|
snapshot = self.volume_api.create_snapshot_force(
|
||||||
mapping_dict = block_device.snapshot_from_bdm(snapshot['id'],
|
context, volume['id'], name,
|
||||||
bdm)
|
volume['display_description'])
|
||||||
mapping_dict = mapping_dict.get_image_mapping()
|
mapping_dict = block_device.snapshot_from_bdm(
|
||||||
else:
|
snapshot['id'],
|
||||||
mapping_dict = bdm.get_image_mapping()
|
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:
|
if quiesced:
|
||||||
self.compute_rpcapi.unquiesce_instance(context, instance, mapping)
|
self.compute_rpcapi.unquiesce_instance(context, instance, mapping)
|
||||||
|
@ -69,3 +69,4 @@ SWAP_VOLUME = 'swap_volume'
|
|||||||
LOCK = 'lock'
|
LOCK = 'lock'
|
||||||
UNLOCK = 'unlock'
|
UNLOCK = 'unlock'
|
||||||
BACKUP = 'createBackup'
|
BACKUP = 'createBackup'
|
||||||
|
CREATE_IMAGE = 'createImage'
|
||||||
|
@ -3229,6 +3229,7 @@ class ComputeManager(manager.Manager):
|
|||||||
|
|
||||||
@wrap_exception()
|
@wrap_exception()
|
||||||
@reverts_task_state
|
@reverts_task_state
|
||||||
|
@wrap_instance_event(prefix='compute')
|
||||||
@wrap_instance_fault
|
@wrap_instance_fault
|
||||||
@delete_image_on_error
|
@delete_image_on_error
|
||||||
def snapshot_instance(self, context, image_id, instance):
|
def snapshot_instance(self, context, image_id, instance):
|
||||||
|
@ -1343,8 +1343,9 @@ class CellsTargetedMethodsTestCase(test.NoDBTestCase):
|
|||||||
self._test_instance_action_method('inject_network_info',
|
self._test_instance_action_method('inject_network_info',
|
||||||
(), {}, (), {}, False)
|
(), {}, (), {}, False)
|
||||||
|
|
||||||
def test_snapshot_instance(self):
|
@mock.patch.object(objects.InstanceAction, 'action_start')
|
||||||
inst = objects.Instance()
|
def test_snapshot_instance(self, action_start):
|
||||||
|
inst = objects.Instance(uuid=uuids.instance)
|
||||||
meth_cls = self.tgt_methods_cls
|
meth_cls = self.tgt_methods_cls
|
||||||
|
|
||||||
self.mox.StubOutWithMock(inst, 'refresh')
|
self.mox.StubOutWithMock(inst, 'refresh')
|
||||||
@ -1371,6 +1372,9 @@ class CellsTargetedMethodsTestCase(test.NoDBTestCase):
|
|||||||
message.need_response = False
|
message.need_response = False
|
||||||
|
|
||||||
meth_cls.snapshot_instance(message, inst, image_id='image-id')
|
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')
|
@mock.patch.object(objects.InstanceAction, 'action_start')
|
||||||
def test_backup_instance(self, action_start):
|
def test_backup_instance(self, action_start):
|
||||||
|
@ -3392,14 +3392,17 @@ class ComputeTestCase(BaseTestCase,
|
|||||||
fake_delete)
|
fake_delete)
|
||||||
|
|
||||||
inst_obj = self._get_snapshotting_instance()
|
inst_obj = self._get_snapshotting_instance()
|
||||||
if method == 'snapshot':
|
with mock.patch.object(compute_utils,
|
||||||
self.assertRaises(test.TestingException,
|
'EventReporter') as mock_event:
|
||||||
self.compute.snapshot_instance,
|
if method == 'snapshot':
|
||||||
self.context, image_id='fakesnap',
|
self.assertRaises(test.TestingException,
|
||||||
instance=inst_obj)
|
self.compute.snapshot_instance,
|
||||||
else:
|
self.context, image_id='fakesnap',
|
||||||
with mock.patch.object(compute_utils,
|
instance=inst_obj)
|
||||||
'EventReporter') as mock_event:
|
mock_event.assert_called_once_with(self.context,
|
||||||
|
'compute_snapshot_instance',
|
||||||
|
inst_obj.uuid)
|
||||||
|
else:
|
||||||
self.assertRaises(test.TestingException,
|
self.assertRaises(test.TestingException,
|
||||||
self.compute.backup_instance,
|
self.compute.backup_instance,
|
||||||
self.context, image_id='fakesnap',
|
self.context, image_id='fakesnap',
|
||||||
|
@ -2594,6 +2594,8 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
res = self.compute_api.snapshot(self.context, instance,
|
res = self.compute_api.snapshot(self.context, instance,
|
||||||
'fake-name',
|
'fake-name',
|
||||||
extra_properties=extra_props)
|
extra_properties=extra_props)
|
||||||
|
mock_record.assert_called_once_with(
|
||||||
|
self.context, instance, instance_actions.CREATE_IMAGE)
|
||||||
else:
|
else:
|
||||||
res = self.compute_api.backup(self.context, instance,
|
res = self.compute_api.backup(self.context, instance,
|
||||||
'fake-name',
|
'fake-name',
|
||||||
@ -2821,9 +2823,20 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
fake_unquiesce_instance)
|
fake_unquiesce_instance)
|
||||||
fake_image.stub_out_image_service(self)
|
fake_image.stub_out_image_service(self)
|
||||||
|
|
||||||
# No block devices defined
|
with test.nested(
|
||||||
self.compute_api.snapshot_volume_backed(
|
mock.patch.object(compute_api.API, '_record_action_start'),
|
||||||
self.context, instance, 'test-snapshot')
|
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(
|
bdm = fake_block_device.FakeDbBlockDeviceDict(
|
||||||
{'no_device': False, 'volume_id': '1', 'boot_index': 0,
|
{'no_device': False, 'volume_id': '1', 'boot_index': 0,
|
||||||
@ -2843,13 +2856,24 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
'destination_type': 'volume', 'delete_on_termination': False,
|
'destination_type': 'volume', 'delete_on_termination': False,
|
||||||
'tag': None})
|
'tag': None})
|
||||||
|
|
||||||
# All the db_only fields and the volume ones are removed
|
with test.nested(
|
||||||
self.compute_api.snapshot_volume_backed(
|
mock.patch.object(compute_api.API, '_record_action_start'),
|
||||||
self.context, instance, 'test-snapshot')
|
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[0])
|
||||||
self.assertEqual(quiesce_expected, quiesced[1])
|
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(
|
instance.system_metadata['image_mappings'] = jsonutils.dumps(
|
||||||
[{'virtual': 'ami', 'device': 'vda'},
|
[{'virtual': 'ami', 'device': 'vda'},
|
||||||
{'device': 'vda', 'virtual': 'ephemeral0'},
|
{'device': 'vda', 'virtual': 'ephemeral0'},
|
||||||
@ -2882,13 +2906,25 @@ class _ComputeAPIUnitTestMixIn(object):
|
|||||||
|
|
||||||
quiesced = [False, False]
|
quiesced = [False, False]
|
||||||
|
|
||||||
# Check that the mappings from the image properties are not included
|
with test.nested(
|
||||||
self.compute_api.snapshot_volume_backed(
|
mock.patch.object(compute_api.API, '_record_action_start'),
|
||||||
self.context, instance, 'test-snapshot')
|
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[0])
|
||||||
self.assertEqual(quiesce_expected, quiesced[1])
|
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):
|
def test_snapshot_volume_backed(self):
|
||||||
self._test_snapshot_volume_backed(False, False)
|
self._test_snapshot_volume_backed(False, False)
|
||||||
|
|
||||||
|
@ -12,3 +12,4 @@ features:
|
|||||||
* unlock
|
* unlock
|
||||||
* shelveOffload
|
* shelveOffload
|
||||||
* createBackup
|
* createBackup
|
||||||
|
* createImage
|
||||||
|
Loading…
x
Reference in New Issue
Block a user