FC discover existing devices for removal

Due to the way Nova live migration works, we can't
rely on having the device path that was discovered at
connect_volume time existing in the device_info.

This patch uses the existing connection_properties
to rediscover the existing volume paths on the system
at the time disconnect_volume is called.   This ensures
that whatever paths/devices are associated with at volume
at disconnect_volume time exists, they will be removed.

Change-Id: I699fcff5dee595caa54ca7e80f67966edd1370ef
Closes-Bug: 1406161
This commit is contained in:
Walter A. Boring IV 2015-07-16 16:09:02 -07:00
parent f3088d0e57
commit 981b84c49c
2 changed files with 53 additions and 17 deletions

View File

@ -818,6 +818,28 @@ class FibreChannelConnector(InitiatorConnector):
self._linuxscsi.set_execute(execute)
self._linuxfc.set_execute(execute)
def _get_possible_volume_paths(self, connection_properties, hbas):
ports = connection_properties['target_wwn']
possible_devs = self._get_possible_devices(hbas, ports)
lun = connection_properties.get('target_lun', 0)
host_paths = self._get_host_devices(possible_devs, lun)
return host_paths
def _get_volume_paths(self, connection_properties):
"""Get a list of existing paths for a volume on the system."""
volume_paths = []
# first fetch all of the potential paths that might exist
# and then filter those by what's actually on the system.
hbas = self._linuxfc.get_fc_hbas_info()
host_paths = self._get_possible_volume_paths(connection_properties,
hbas)
for path in host_paths:
if os.path.exists(path):
volume_paths.append(path)
return volume_paths
@synchronized('connect_volume')
def connect_volume(self, connection_properties):
"""Attach the volume to instance_name.
@ -831,11 +853,8 @@ class FibreChannelConnector(InitiatorConnector):
device_info = {'type': 'block'}
hbas = self._linuxfc.get_fc_hbas_info()
ports = connection_properties['target_wwn']
possible_devs = self._get_possible_devices(hbas, ports)
lun = connection_properties.get('target_lun', 0)
host_devices = self._get_host_devices(possible_devs, lun)
host_devices = self._get_possible_volume_paths(connection_properties,
hbas)
if len(host_devices) == 0:
# this is empty because we don't have any FC HBAs
@ -961,18 +980,16 @@ class FibreChannelConnector(InitiatorConnector):
multipath_id = device_info['multipath_id']
mdev_info = self._linuxscsi.find_multipath_device(multipath_id)
devices = mdev_info['devices']
LOG.debug("devices to remove = %s", devices)
self._linuxscsi.flush_multipath_device(multipath_id)
else:
device = device_info['path']
# now resolve this to a /dev/sdX path
if os.path.exists(device):
# get the /dev/sdX device from the
# /dev/disk/by-path entry
dev_name = os.path.realpath(device)
dev_info = self._linuxscsi.get_device_info(dev_name)
devices = [dev_info]
devices = []
volume_paths = self._get_volume_paths(connection_properties)
for path in volume_paths:
real_path = self._linuxscsi.get_name_from_path(path)
device_info = self._linuxscsi.get_device_info(real_path)
devices.append(device_info)
LOG.debug("devices to remove = %s", devices)
self._remove_devices(connection_properties, devices)
def _remove_devices(self, connection_properties, devices):

View File

@ -920,15 +920,34 @@ class FibreChannelConnectorTestCase(ConnectorTestCase):
'target_lun': 1,
}}
@mock.patch.object(os.path, 'exists', return_value=True)
@mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_hbas')
@mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_hbas_info')
def test_get_volume_paths(self, fake_fc_hbas_info,
fake_fc_hbas, fake_exists):
fake_fc_hbas.side_effect = self.fake_get_fc_hbas
fake_fc_hbas_info.side_effect = self.fake_get_fc_hbas_info
name = 'volume-00000001'
vol = {'id': 1, 'name': name}
location = '10.0.2.15:3260'
wwn = '1234567890123456'
connection_info = self.fibrechan_connection(vol, location, wwn)
volume_paths = self.connector._get_volume_paths(
connection_info['data'])
expected = ['/dev/disk/by-path/pci-0000:05:00.2'
'-fc-0x1234567890123456-lun-1']
self.assertEqual(expected, volume_paths)
@mock.patch.object(os.path, 'exists', return_value=True)
@mock.patch.object(os.path, 'realpath', return_value='/dev/sdb')
@mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_hbas')
@mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_hbas_info')
@mock.patch.object(linuxscsi.LinuxSCSI, 'find_multipath_device')
@mock.patch.object(linuxscsi.LinuxSCSI, 'remove_scsi_device')
@mock.patch.object(linuxscsi.LinuxSCSI, 'get_device_info')
def test_connect_volume(self, get_device_info_mock, remove_device_mock,
find_device_mock, get_fc_hbas_info_mock,
get_fc_hbas_info_mock,
get_fc_hbas_mock, realpath_mock, exists_mock):
get_fc_hbas_mock.side_effect = self.fake_get_fc_hbas
get_fc_hbas_info_mock.side_effect = self.fake_get_fc_hbas_info
@ -940,7 +959,6 @@ class FibreChannelConnectorTestCase(ConnectorTestCase):
'address': '1:0:0:1',
'host': 1, 'channel': 0,
'id': 0, 'lun': 1}]}
find_device_mock.return_value = devices
get_device_info_mock.return_value = devices['devices'][0]
location = '10.0.2.15:3260'
@ -957,6 +975,7 @@ class FibreChannelConnectorTestCase(ConnectorTestCase):
exp_wwn)
self.assertEqual(dev_info['type'], 'block')
self.assertEqual(dev_info['path'], dev_str)
self.assertTrue('multipath_id' not in dev_info)
self.assertTrue('devices' not in dev_info)
self.connector.disconnect_volume(connection_info['data'], dev_info)