Merge "Add instance action record for attach/detach/swap volumes"

This commit is contained in:
Zuul 2017-11-28 15:32:13 +00:00 committed by Gerrit Code Review
commit ea51587508
7 changed files with 91 additions and 16 deletions

View File

@ -48,6 +48,7 @@ from nova.compute import power_state
from nova.compute import rpcapi as compute_rpcapi from nova.compute import rpcapi as compute_rpcapi
from nova.compute import task_states from nova.compute import task_states
from nova.compute import utils as compute_utils from nova.compute import utils as compute_utils
from nova.compute.utils import wrap_instance_event
from nova.compute import vm_states from nova.compute import vm_states
from nova import conductor from nova import conductor
import nova.conf import nova.conf
@ -3569,6 +3570,8 @@ class API(base.Base):
device_type=device_type, tag=tag) device_type=device_type, tag=tag)
try: try:
self._check_attach_and_reserve_volume(context, volume_id, instance) self._check_attach_and_reserve_volume(context, volume_id, instance)
self._record_action_start(
context, instance, instance_actions.ATTACH_VOLUME)
self.compute_rpcapi.attach_volume(context, instance, volume_bdm) self.compute_rpcapi.attach_volume(context, instance, volume_bdm)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
@ -3588,16 +3591,22 @@ class API(base.Base):
therefore the actual attachment will be performed once the therefore the actual attachment will be performed once the
instance will be unshelved. instance will be unshelved.
""" """
@wrap_instance_event(prefix='api')
def attach_volume(self, context, v_id, instance, dev):
self.volume_api.attach(context,
v_id,
instance.uuid,
dev)
volume_bdm = self._create_volume_bdm( volume_bdm = self._create_volume_bdm(
context, instance, device, volume_id, disk_bus=disk_bus, context, instance, device, volume_id, disk_bus=disk_bus,
device_type=device_type, is_local_creation=True) device_type=device_type, is_local_creation=True)
try: try:
self._check_attach_and_reserve_volume(context, volume_id, instance) self._check_attach_and_reserve_volume(context, volume_id, instance)
self.volume_api.attach(context, self._record_action_start(
volume_id, context, instance,
instance.uuid, instance_actions.ATTACH_VOLUME)
device) attach_volume(self, context, volume_id, instance, device)
except Exception: except Exception:
with excutils.save_and_reraise_exception(): with excutils.save_and_reraise_exception():
volume_bdm.destroy() volume_bdm.destroy()
@ -3652,6 +3661,8 @@ class API(base.Base):
attachment_id = None attachment_id = None
if attachments and instance.uuid in attachments: if attachments and instance.uuid in attachments:
attachment_id = attachments[instance.uuid]['attachment_id'] attachment_id = attachments[instance.uuid]['attachment_id']
self._record_action_start(
context, instance, instance_actions.DETACH_VOLUME)
self.compute_rpcapi.detach_volume(context, instance=instance, self.compute_rpcapi.detach_volume(context, instance=instance,
volume_id=volume['id'], attachment_id=attachment_id) volume_id=volume['id'], attachment_id=attachment_id)
@ -3664,13 +3675,20 @@ class API(base.Base):
If the volume has delete_on_termination option set then we call the If the volume has delete_on_termination option set then we call the
volume api delete as well. volume api delete as well.
""" """
@wrap_instance_event(prefix='api')
def detach_volume(self, context, instance, bdms):
self._local_cleanup_bdm_volumes(bdms, instance, context)
try: try:
self.volume_api.begin_detaching(context, volume['id']) self.volume_api.begin_detaching(context, volume['id'])
except exception.InvalidInput as exc: except exception.InvalidInput as exc:
raise exception.InvalidVolume(reason=exc.format_message()) raise exception.InvalidVolume(reason=exc.format_message())
bdms = [objects.BlockDeviceMapping.get_by_volume_id( bdms = [objects.BlockDeviceMapping.get_by_volume_id(
context, volume['id'], instance.uuid)] context, volume['id'], instance.uuid)]
self._local_cleanup_bdm_volumes(bdms, instance, context) self._record_action_start(
context, instance,
instance_actions.DETACH_VOLUME)
detach_volume(self, context, instance, bdms)
@check_instance_lock @check_instance_lock
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED, @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.PAUSED,
@ -3723,6 +3741,9 @@ class API(base.Base):
new_attachment_id = self.volume_api.attachment_create( new_attachment_id = self.volume_api.attachment_create(
context, new_volume['id'], instance.uuid)['id'] context, new_volume['id'], instance.uuid)['id']
self._record_action_start(
context, instance, instance_actions.SWAP_VOLUME)
try: try:
self.compute_rpcapi.swap_volume( self.compute_rpcapi.swap_volume(
context, instance=instance, context, instance=instance,

View File

@ -62,3 +62,6 @@ TRIGGER_CRASH_DUMP = 'trigger_crash_dump'
EXTEND_VOLUME = 'extend_volume' EXTEND_VOLUME = 'extend_volume'
ATTACH_INTERFACE = 'attach_interface' ATTACH_INTERFACE = 'attach_interface'
DETACH_INTERFACE = 'detach_interface' DETACH_INTERFACE = 'detach_interface'
ATTACH_VOLUME = 'attach_volume'
DETACH_VOLUME = 'detach_volume'
SWAP_VOLUME = 'swap_volume'

View File

@ -5093,6 +5093,7 @@ class ComputeManager(manager.Manager):
return do_reserve() return do_reserve()
@wrap_exception() @wrap_exception()
@wrap_instance_event(prefix='compute')
@wrap_instance_fault @wrap_instance_fault
def attach_volume(self, context, instance, bdm): def attach_volume(self, context, instance, bdm):
"""Attach a volume to an instance.""" """Attach a volume to an instance."""
@ -5235,6 +5236,7 @@ class ComputeManager(manager.Manager):
instance=instance) instance=instance)
@wrap_exception() @wrap_exception()
@wrap_instance_event(prefix='compute')
@wrap_instance_fault @wrap_instance_fault
def detach_volume(self, context, volume_id, instance, attachment_id=None): def detach_volume(self, context, volume_id, instance, attachment_id=None):
"""Detach a volume from an instance. """Detach a volume from an instance.
@ -5399,6 +5401,7 @@ class ComputeManager(manager.Manager):
return (comp_ret, new_cinfo) return (comp_ret, new_cinfo)
@wrap_exception() @wrap_exception()
@wrap_instance_event(prefix='compute')
@wrap_instance_fault @wrap_instance_fault
def swap_volume(self, context, old_volume_id, new_volume_id, instance, def swap_volume(self, context, old_volume_id, new_volume_id, instance,
new_attachment_id=None): new_attachment_id=None):

View File

@ -46,6 +46,7 @@ from nova import block_device
from nova import compute from nova import compute
from nova.compute import api as compute_api from nova.compute import api as compute_api
from nova.compute import flavors from nova.compute import flavors
from nova.compute import instance_actions
from nova.compute import manager as compute_manager from nova.compute import manager as compute_manager
from nova.compute import power_state from nova.compute import power_state
from nova.compute import rpcapi as compute_rpcapi from nova.compute import rpcapi as compute_rpcapi
@ -393,7 +394,8 @@ class ComputeVolumeTestCase(BaseTestCase):
self.stub_out('nova.db.block_device_mapping_create', store_cinfo) self.stub_out('nova.db.block_device_mapping_create', store_cinfo)
self.stub_out('nova.db.block_device_mapping_update', store_cinfo) self.stub_out('nova.db.block_device_mapping_update', store_cinfo)
def test_attach_volume_serial(self): @mock.patch.object(compute_utils, 'EventReporter')
def test_attach_volume_serial(self, mock_event):
fake_bdm = objects.BlockDeviceMapping(context=self.context, fake_bdm = objects.BlockDeviceMapping(context=self.context,
**self.fake_volume) **self.fake_volume)
with (mock.patch.object(cinder.API, 'get_volume_encryption_metadata', with (mock.patch.object(cinder.API, 'get_volume_encryption_metadata',
@ -401,10 +403,14 @@ class ComputeVolumeTestCase(BaseTestCase):
instance = self._create_fake_instance_obj() instance = self._create_fake_instance_obj()
self.compute.attach_volume(self.context, instance, bdm=fake_bdm) self.compute.attach_volume(self.context, instance, bdm=fake_bdm)
self.assertEqual(self.cinfo.get('serial'), uuids.volume_id) self.assertEqual(self.cinfo.get('serial'), uuids.volume_id)
mock_event.assert_called_once_with(
self.context, 'compute_attach_volume', instance.uuid)
@mock.patch.object(compute_utils, 'EventReporter')
@mock.patch('nova.context.RequestContext.elevated') @mock.patch('nova.context.RequestContext.elevated')
@mock.patch('nova.compute.utils.notify_about_volume_attach_detach') @mock.patch('nova.compute.utils.notify_about_volume_attach_detach')
def test_attach_volume_raises(self, mock_notify, mock_elevate): def test_attach_volume_raises(self, mock_notify, mock_elevate,
mock_event):
mock_elevate.return_value = self.context mock_elevate.return_value = self.context
fake_bdm = objects.BlockDeviceMapping(**self.fake_volume) fake_bdm = objects.BlockDeviceMapping(**self.fake_volume)
@ -436,8 +442,11 @@ class ComputeVolumeTestCase(BaseTestCase):
volume_id=uuids.volume_id, volume_id=uuids.volume_id,
exception=expected_exception), exception=expected_exception),
]) ])
mock_event.assert_called_once_with(
self.context, 'compute_attach_volume', instance.uuid)
def test_detach_volume_api_raises(self): @mock.patch.object(compute_utils, 'EventReporter')
def test_detach_volume_api_raises(self, mock_event):
fake_bdm = objects.BlockDeviceMapping(**self.fake_volume) fake_bdm = objects.BlockDeviceMapping(**self.fake_volume)
instance = self._create_fake_instance_obj() instance = self._create_fake_instance_obj()
@ -454,8 +463,11 @@ class ComputeVolumeTestCase(BaseTestCase):
test.TestingException, self.compute.detach_volume, test.TestingException, self.compute.detach_volume,
self.context, 'fake', instance, 'fake_id') self.context, 'fake', instance, 'fake_id')
self.assertFalse(mock_destroy.called) self.assertFalse(mock_destroy.called)
mock_event.assert_called_once_with(
self.context, 'compute_detach_volume', instance.uuid)
def test_detach_volume_bdm_destroyed(self): @mock.patch.object(compute_utils, 'EventReporter')
def test_detach_volume_bdm_destroyed(self, mock_event):
# Assert that the BDM is destroyed given a successful call to detach # Assert that the BDM is destroyed given a successful call to detach
# the volume from the instance in Cinder. # the volume from the instance in Cinder.
fake_bdm = objects.BlockDeviceMapping(**self.fake_volume) fake_bdm = objects.BlockDeviceMapping(**self.fake_volume)
@ -475,6 +487,8 @@ class ComputeVolumeTestCase(BaseTestCase):
instance.uuid, instance.uuid,
uuids.attachment_id) uuids.attachment_id)
self.assertTrue(mock_destroy.called) self.assertTrue(mock_destroy.called)
mock_event.assert_called_once_with(
self.context, 'compute_detach_volume', instance.uuid)
def test_await_block_device_created_too_slow(self): def test_await_block_device_created_too_slow(self):
self.flags(block_device_allocate_retries=2) self.flags(block_device_allocate_retries=2)
@ -10068,8 +10082,9 @@ class ComputeAPITestCase(BaseTestCase):
with test.nested( with test.nested(
mock.patch.object(compute_api.API, mock.patch.object(compute_api.API,
'_check_attach_and_reserve_volume'), '_check_attach_and_reserve_volume'),
mock.patch.object(cinder.API, 'attach') mock.patch.object(cinder.API, 'attach'),
) as (mock_attach_and_reserve, mock_attach): mock.patch.object(compute_utils, 'EventReporter')
) as (mock_attach_and_reserve, mock_attach, mock_event):
self.compute_api._attach_volume_shelved_offloaded( self.compute_api._attach_volume_shelved_offloaded(
self.context, instance, 'fake-volume-id', self.context, instance, 'fake-volume-id',
'/dev/vdb', 'ide', 'cdrom') '/dev/vdb', 'ide', 'cdrom')
@ -10080,6 +10095,9 @@ class ComputeAPITestCase(BaseTestCase):
'fake-volume-id', 'fake-volume-id',
instance.uuid, instance.uuid,
'/dev/vdb') '/dev/vdb')
mock_event.assert_called_once_with(
self.context, 'api_attach_volume', instance.uuid
)
self.assertTrue(mock_attach.called) self.assertTrue(mock_attach.called)
def test_attach_volume_no_device(self): def test_attach_volume_no_device(self):
@ -10125,7 +10143,8 @@ class ComputeAPITestCase(BaseTestCase):
self.assertTrue(called.get('fake_rpc_reserve_block_device_name')) self.assertTrue(called.get('fake_rpc_reserve_block_device_name'))
self.assertTrue(called.get('fake_rpc_attach_volume')) self.assertTrue(called.get('fake_rpc_attach_volume'))
def test_detach_volume(self): @mock.patch('nova.compute.api.API._record_action_start')
def test_detach_volume(self, mock_record):
# Ensure volume can be detached from instance # Ensure volume can be detached from instance
called = {} called = {}
instance = self._create_fake_instance_obj() instance = self._create_fake_instance_obj()
@ -10147,14 +10166,19 @@ class ComputeAPITestCase(BaseTestCase):
instance, volume) instance, volume)
self.assertTrue(called.get('fake_begin_detaching')) self.assertTrue(called.get('fake_begin_detaching'))
self.assertTrue(called.get('fake_rpc_detach_volume')) self.assertTrue(called.get('fake_rpc_detach_volume'))
mock_record.assert_called_once_with(
self.context, instance, instance_actions.DETACH_VOLUME)
@mock.patch('nova.compute.api.API._record_action_start')
@mock.patch.object(compute_utils, 'EventReporter')
@mock.patch.object(nova.volume.cinder.API, 'begin_detaching') @mock.patch.object(nova.volume.cinder.API, 'begin_detaching')
@mock.patch.object(compute_api.API, '_local_cleanup_bdm_volumes') @mock.patch.object(compute_api.API, '_local_cleanup_bdm_volumes')
@mock.patch.object(objects.BlockDeviceMapping, 'get_by_volume_id') @mock.patch.object(objects.BlockDeviceMapping, 'get_by_volume_id')
def test_detach_volume_shelved_offloaded(self, def test_detach_volume_shelved_offloaded(self,
mock_block_dev, mock_block_dev,
mock_local_cleanup, mock_local_cleanup,
mock_begin_detaching): mock_begin_detaching,
mock_event, mock_record):
mock_block_dev.return_value = [block_device_obj.BlockDeviceMapping( mock_block_dev.return_value = [block_device_obj.BlockDeviceMapping(
context=context)] context=context)]
@ -10165,6 +10189,11 @@ class ComputeAPITestCase(BaseTestCase):
volume) volume)
mock_begin_detaching.assert_called_once_with(self.context, mock_begin_detaching.assert_called_once_with(self.context,
volume['id']) volume['id'])
mock_record.assert_called_once_with(
self.context, instance, instance_actions.DETACH_VOLUME)
mock_event.assert_called_once_with(self.context,
'api_detach_volume',
instance.uuid)
self.assertTrue(mock_local_cleanup.called) self.assertTrue(mock_local_cleanup.called)
@mock.patch.object(nova.volume.cinder.API, 'begin_detaching', @mock.patch.object(nova.volume.cinder.API, 'begin_detaching',

View File

@ -381,9 +381,10 @@ class _ComputeAPIUnitTestMixIn(object):
self.assertEqual(result.volume_id, bdm.volume_id) self.assertEqual(result.volume_id, bdm.volume_id)
self.assertTrue(bdm_create.called) self.assertTrue(bdm_create.called)
@mock.patch.object(compute_api.API, '_record_action_start')
@mock.patch.object(compute_rpcapi.ComputeAPI, 'reserve_block_device_name') @mock.patch.object(compute_rpcapi.ComputeAPI, 'reserve_block_device_name')
@mock.patch.object(compute_rpcapi.ComputeAPI, 'attach_volume') @mock.patch.object(compute_rpcapi.ComputeAPI, 'attach_volume')
def test_attach_volume(self, mock_attach, mock_reserve): def test_attach_volume(self, mock_attach, mock_reserve, mock_record):
instance = self._create_instance_obj() instance = self._create_instance_obj()
volume = fake_volume.fake_volume(1, 'test-vol', 'test-vol', volume = fake_volume.fake_volume(1, 'test-vol', 'test-vol',
None, None, None, None, None) None, None, None, None, None)
@ -404,10 +405,14 @@ class _ComputeAPIUnitTestMixIn(object):
volume['id']) volume['id'])
mock_attach.assert_called_once_with(self.context, mock_attach.assert_called_once_with(self.context,
instance, fake_bdm) instance, fake_bdm)
mock_record.assert_called_once_with(
self.context, instance, instance_actions.ATTACH_VOLUME)
@mock.patch.object(compute_api.API, '_record_action_start')
@mock.patch.object(compute_rpcapi.ComputeAPI, 'reserve_block_device_name') @mock.patch.object(compute_rpcapi.ComputeAPI, 'reserve_block_device_name')
@mock.patch.object(compute_rpcapi.ComputeAPI, 'attach_volume') @mock.patch.object(compute_rpcapi.ComputeAPI, 'attach_volume')
def test_tagged_volume_attach(self, mock_attach, mock_reserve): def test_tagged_volume_attach(self, mock_attach, mock_reserve,
mock_record):
instance = self._create_instance_obj() instance = self._create_instance_obj()
volume = fake_volume.fake_volume(1, 'test-vol', 'test-vol', volume = fake_volume.fake_volume(1, 'test-vol', 'test-vol',
None, None, None, None, None) None, None, None, None, None)
@ -432,6 +437,8 @@ class _ComputeAPIUnitTestMixIn(object):
volume['id']) volume['id'])
mock_attach.assert_called_once_with(self.context, mock_attach.assert_called_once_with(self.context,
instance, fake_bdm) instance, fake_bdm)
mock_record.assert_called_once_with(
self.context, instance, instance_actions.ATTACH_VOLUME)
def test_attach_volume_shelved_instance(self): def test_attach_volume_shelved_instance(self):
instance = self._create_instance_obj() instance = self._create_instance_obj()
@ -2307,6 +2314,7 @@ class _ComputeAPIUnitTestMixIn(object):
if volumes[uuids.new_volume]['status'] == 'reserved': if volumes[uuids.new_volume]['status'] == 'reserved':
volumes[uuids.new_volume]['status'] = 'available' volumes[uuids.new_volume]['status'] = 'available'
@mock.patch.object(compute_api.API, '_record_action_start')
@mock.patch.object(self.compute_api.compute_rpcapi, 'swap_volume', @mock.patch.object(self.compute_api.compute_rpcapi, 'swap_volume',
return_value=True) return_value=True)
@mock.patch.object(self.compute_api.volume_api, 'unreserve_volume', @mock.patch.object(self.compute_api.volume_api, 'unreserve_volume',
@ -2326,7 +2334,7 @@ class _ComputeAPIUnitTestMixIn(object):
def _do_test(mock_begin_detaching, mock_get_by_volume_and_instance, def _do_test(mock_begin_detaching, mock_get_by_volume_and_instance,
mock_roll_detaching, mock_attachment_create, mock_roll_detaching, mock_attachment_create,
mock_reserve_volume, mock_attachment_delete, mock_reserve_volume, mock_attachment_delete,
mock_unreserve_volume, mock_swap_volume): mock_unreserve_volume, mock_swap_volume, mock_record):
bdm = objects.BlockDeviceMapping( bdm = objects.BlockDeviceMapping(
**fake_block_device.FakeDbBlockDeviceDict( **fake_block_device.FakeDbBlockDeviceDict(
{'no_device': False, 'volume_id': '1', 'boot_index': 0, {'no_device': False, 'volume_id': '1', 'boot_index': 0,
@ -2383,6 +2391,9 @@ class _ComputeAPIUnitTestMixIn(object):
old_volume_id=uuids.old_volume, old_volume_id=uuids.old_volume,
new_volume_id=uuids.new_volume, new_volume_id=uuids.new_volume,
new_attachment_id=attachment_id) new_attachment_id=attachment_id)
mock_record.assert_called_once_with(self.context,
instance,
instance_actions.SWAP_VOLUME)
_do_test() _do_test()

View File

@ -1832,6 +1832,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
do_test() do_test()
@mock.patch.object(compute_utils, 'EventReporter')
@mock.patch.object(virt_driver.ComputeDriver, 'get_volume_connector', @mock.patch.object(virt_driver.ComputeDriver, 'get_volume_connector',
return_value={}) return_value={})
@mock.patch.object(manager.ComputeManager, '_instance_update', @mock.patch.object(manager.ComputeManager, '_instance_update',
@ -1854,6 +1855,7 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
mock_instance_fault_create, mock_instance_fault_create,
mock_instance_update, mock_instance_update,
mock_get_volume_connector, mock_get_volume_connector,
mock_event,
expected_exception=None): expected_exception=None):
# This test ensures that volume_id arguments are passed to volume_api # This test ensures that volume_id arguments are passed to volume_api
# and that volume states are OK # and that volume states are OK
@ -1955,6 +1957,9 @@ class ComputeManagerUnitTestCase(test.NoDBTestCase):
fields.NotificationAction.VOLUME_SWAP, fields.NotificationAction.VOLUME_SWAP,
fields.NotificationPhase.END, fields.NotificationPhase.END,
uuids.old_volume, uuids.new_volume) uuids.old_volume, uuids.new_volume)
mock_event.assert_called_once_with(self.context,
'compute_swap_volume',
instance1.uuid)
def _assert_volume_api(self, context, volume, *args): def _assert_volume_api(self, context, volume, *args):
self.assertTrue(uuidutils.is_uuid_like(volume)) self.assertTrue(uuidutils.is_uuid_like(volume))

View File

@ -5,3 +5,6 @@ features:
* attach_interface * attach_interface
* detach_interface * detach_interface
* attach_volume
* detach_volume
* swap_volume