Save bdm.connection_info before calling volume_api.attach_volume

There is a race in attach/detach of a volume where the volume status
goes to 'in-use' before the bdm.connection_info data is stored in the
database. Since attach is a cast, the caller can see the volume go to
'in-use' and immediately try to detach the volume and blow up in the
compute manager because bdm.connection_info isn't set stored in the
database.

This fixes the issue by saving the connection_info immediately before
calling volume_api.attach_volume (which sets the volume status to
'in-use').

Closes-Bug: #1327218

Conflicts:
        nova/tests/unit/compute/test_compute.py
        nova/tests/unit/virt/test_block_device.py
        nova/virt/block_device.py

NOTE(mriedem): The block_device conflicts are due to using dot
notation when accessing object fields and in kilo the context is
no longer passed to bdm.save(). The test conflicts are due to moving
the test modules in kilo and passing the context on save().

Change-Id: Ib95c8f7b66aca0c4ac7b92d140cbeb5e85c2717f
(cherry picked from commit 6fb2ef96d6)
This commit is contained in:
Matt Riedemann 2015-03-18 12:42:42 -07:00
parent a19a1e52f1
commit bbf6348997
3 changed files with 12 additions and 2 deletions

View File

@ -573,7 +573,7 @@ class ComputeVolumeTestCase(BaseTestCase):
})]
prepped_bdm = self.compute._prep_block_device(
self.context, self.instance, block_device_mapping)
mock_save.assert_called_once_with(self.context)
self.assertEqual(2, mock_save.call_count)
volume_driver_bdm = prepped_bdm['block_device_mapping'][0]
self.assertEqual(volume_driver_bdm['connection_info']['serial'],
self.volume_id)

View File

@ -15,6 +15,7 @@
import contextlib
import mock
import mox
from nova import block_device
from nova import context
@ -381,7 +382,11 @@ class TestDriverBlockDevice(test.NoDBTestCase):
self.volume_api.attach(elevated_context, fake_volume['id'],
'fake_uuid', bdm_dict['device_name'],
mode=access_mode).AndReturn(None)
driver_bdm._bdm_obj.save(self.context).AndReturn(None)
# NOTE(mriedem): save() is called with the elevated context within
# attach() and with the original context from the update_db decorator
# so we ignore which arg it is in test.
driver_bdm._bdm_obj.save(
mox.IgnoreArg()).MultipleTimes().AndReturn(None)
return instance, expected_conn_info
def test_volume_attach(self):

View File

@ -265,6 +265,11 @@ class DriverVolumeBlockDevice(DriverBlockDevice):
if 'data' in connection_info:
mode = connection_info['data'].get('access_mode', 'rw')
if volume['attach_status'] == "detached":
# NOTE(mriedem): save our current state so connection_info is in
# the database before the volume status goes to 'in-use' because
# after that we can detach and connection_info is required for
# detach.
self.save(context)
volume_api.attach(context, volume_id, instance['uuid'],
self['mount_device'], mode=mode)