Ensure that the base connector is platform independent

This change pulls the Linux specific methods out of the base
initiator connector, moving them to a new class.

This will allow us to easily add Windows connectors.

Partial-Implements: blueprint os-brick-windows-support

Change-Id: Id0acd52a352f837897255f485f46285602468c63
This commit is contained in:
Lucian Petrut 2016-05-05 15:45:07 +03:00 committed by Petrut Lucian
parent 6d71d55caf
commit e7e6801ce0
2 changed files with 122 additions and 103 deletions

View File

@ -91,7 +91,7 @@ VZSTORAGE = "VZSTORAGE"
SHEEPDOG = "SHEEPDOG"
connector_list = [
'os_brick.initiator.connector.InitiatorConnector',
'os_brick.initiator.connector.BaseLinuxConnector',
'os_brick.initiator.connector.ISCSIConnector',
'os_brick.initiator.connector.FibreChannelConnector',
'os_brick.initiator.connector.FibreChannelConnectorS390X',
@ -160,37 +160,23 @@ class InitiatorConnector(executor.Executor):
platform = PLATFORM_ALL
# This object can be used on any os type (linux, windows)
# TODO(walter-boring) This class stil has a reliance on
# linuxscsi object, making it specific to linux. Need to fix that.
os_type = OS_TYPE_LINUX
os_type = OS_TYPE_ALL
def __init__(self, root_helper, driver=None, execute=None,
device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
*args, **kwargs):
super(InitiatorConnector, self).__init__(root_helper, execute=execute,
*args, **kwargs)
if not driver:
driver = host_driver.HostDriver()
self.set_driver(driver)
self.device_scan_attempts = device_scan_attempts
self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute=execute)
def set_driver(self, driver):
"""The driver is used to find used LUNs."""
self.driver = driver
@staticmethod
@abc.abstractmethod
def get_connector_properties(root_helper, *args, **kwargs):
"""The generic connector properties."""
multipath = kwargs['multipath']
enforce_multipath = kwargs['enforce_multipath']
props = {}
# TODO(walter-boring) move this into platform specific lib
props['multipath'] = (multipath and
linuxscsi.LinuxSCSI.is_multipath_running(
enforce_multipath, root_helper))
return props
pass
@staticmethod
def factory(protocol, root_helper, driver=None,
@ -293,6 +279,7 @@ class InitiatorConnector(executor.Executor):
dict(protocol=protocol))
raise ValueError(msg)
@abc.abstractmethod
def check_valid_device(self, path, run_as_root=True):
"""Test to see if the device path is a real device.
@ -302,61 +289,7 @@ class InitiatorConnector(executor.Executor):
:type run_as_root: bool
:returns: bool
"""
cmd = ('dd', 'if=%(path)s' % {"path": path},
'of=/dev/null', 'count=1')
out, info = None, None
try:
out, info = self._execute(*cmd, run_as_root=run_as_root,
root_helper=self._root_helper)
except putils.ProcessExecutionError as e:
LOG.error(_LE("Failed to access the device on the path "
"%(path)s: %(error)s %(info)s."),
{"path": path, "error": e.stderr,
"info": info})
return False
# If the info is none, the path does not exist.
if info is None:
return False
return True
def _discover_mpath_device(self, device_wwn, connection_properties,
device_name):
"""This method discovers a multipath device.
Discover a multipath device based on a defined connection_property
and a device_wwn and return the multipath_id and path of the multipath
enabled device if there is one.
"""
path = self._linuxscsi.find_multipath_device_path(device_wwn)
device_path = None
multipath_id = None
if path is None:
mpath_info = self._linuxscsi.find_multipath_device(
device_name)
if mpath_info:
device_path = mpath_info['device']
multipath_id = device_wwn
else:
# we didn't find a multipath device.
# so we assume the kernel only sees 1 device
device_path = self.host_device
LOG.debug("Unable to find multipath device name for "
"volume. Using path %(device)s for volume.",
{'device': self.host_device})
else:
device_path = path
multipath_id = device_wwn
if connection_properties.get('access_mode', '') != 'ro':
try:
# Sometimes the multipath devices will show up as read only
# initially and need additional time/rescans to get to RW.
self._linuxscsi.wait_for_rw(device_wwn, device_path)
except exception.BlockDeviceReadOnly:
LOG.warning(_LW('Block device %s is still read-only. '
'Continuing anyway.'), device_path)
return device_path, multipath_id
pass
@abc.abstractmethod
def connect_volume(self, connection_properties):
@ -462,6 +395,7 @@ class InitiatorConnector(executor.Executor):
"""
pass
@abc.abstractmethod
def get_all_available_volumes(self, connection_properties=None):
"""Return all volumes that exist in the search directory.
@ -478,6 +412,62 @@ class InitiatorConnector(executor.Executor):
of the target volume attributes.
:type connection_properties: dict
"""
pass
def check_IO_handle_valid(self, handle, data_type, protocol):
"""Check IO handle has correct data type."""
if (handle and not isinstance(handle, data_type)):
raise exception.InvalidIOHandleObject(
protocol=protocol,
actual_type=type(handle))
class BaseLinuxConnector(InitiatorConnector):
os_type = OS_TYPE_LINUX
def __init__(self, root_helper, driver=None, execute=None,
*args, **kwargs):
self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute=execute)
if not driver:
driver = host_driver.HostDriver()
self.set_driver(driver)
super(BaseLinuxConnector, self).__init__(root_helper, execute=execute,
*args, **kwargs)
@staticmethod
def get_connector_properties(root_helper, *args, **kwargs):
"""The generic connector properties."""
multipath = kwargs['multipath']
enforce_multipath = kwargs['enforce_multipath']
props = {}
props['multipath'] = (multipath and
linuxscsi.LinuxSCSI.is_multipath_running(
enforce_multipath, root_helper))
return props
def check_valid_device(self, path, run_as_root=True):
cmd = ('dd', 'if=%(path)s' % {"path": path},
'of=/dev/null', 'count=1')
out, info = None, None
try:
out, info = self._execute(*cmd, run_as_root=run_as_root,
root_helper=self._root_helper)
except putils.ProcessExecutionError as e:
LOG.error(_LE("Failed to access the device on the path "
"%(path)s: %(error)s %(info)s."),
{"path": path, "error": e.stderr,
"info": info})
return False
# If the info is none, the path does not exist.
if info is None:
return False
return True
def get_all_available_volumes(self, connection_properties=None):
volumes = []
path = self.get_search_path()
if path:
@ -489,15 +479,47 @@ class InitiatorConnector(executor.Executor):
return volumes
def check_IO_handle_valid(self, handle, data_type, protocol):
"""Check IO handle has correct data type."""
if (handle and not isinstance(handle, data_type)):
raise exception.InvalidIOHandleObject(
protocol=protocol,
actual_type=type(handle))
def _discover_mpath_device(self, device_wwn, connection_properties,
device_name):
"""This method discovers a multipath device.
Discover a multipath device based on a defined connection_property
and a device_wwn and return the multipath_id and path of the multipath
enabled device if there is one.
"""
path = self._linuxscsi.find_multipath_device_path(device_wwn)
device_path = None
multipath_id = None
if path is None:
mpath_info = self._linuxscsi.find_multipath_device(
device_name)
if mpath_info:
device_path = mpath_info['device']
multipath_id = device_wwn
else:
# we didn't find a multipath device.
# so we assume the kernel only sees 1 device
device_path = self.host_device
LOG.debug("Unable to find multipath device name for "
"volume. Using path %(device)s for volume.",
{'device': self.host_device})
else:
device_path = path
multipath_id = device_wwn
if connection_properties.get('access_mode', '') != 'ro':
try:
# Sometimes the multipath devices will show up as read only
# initially and need additional time/rescans to get to RW.
self._linuxscsi.wait_for_rw(device_wwn, device_path)
except exception.BlockDeviceReadOnly:
LOG.warning(_LW('Block device %s is still read-only. '
'Continuing anyway.'), device_path)
return device_path, multipath_id
class FakeConnector(InitiatorConnector):
class FakeConnector(BaseLinuxConnector):
fake_path = '/dev/vdFAKE'
@ -523,7 +545,7 @@ class FakeConnector(InitiatorConnector):
'/dev/disk/by-path/fake-volume-X']
class ISCSIConnector(InitiatorConnector):
class ISCSIConnector(BaseLinuxConnector):
"""Connector class to attach/detach iSCSI volumes."""
supported_transports = ['be2iscsi', 'bnx2i', 'cxgb3i', 'default',
'cxgb4i', 'qla4xxx', 'ocs', 'iser']
@ -532,7 +554,6 @@ class ISCSIConnector(InitiatorConnector):
execute=None, use_multipath=False,
device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
transport='default', *args, **kwargs):
self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute)
super(ISCSIConnector, self).__init__(
root_helper, driver=driver,
execute=execute,
@ -1335,14 +1356,13 @@ class ISCSIConnector(InitiatorConnector):
self._run_multipath(['-r'], check_exit_code=[0, 1, 21])
class FibreChannelConnector(InitiatorConnector):
class FibreChannelConnector(BaseLinuxConnector):
"""Connector class to attach/detach Fibre Channel volumes."""
def __init__(self, root_helper, driver=None,
execute=None, use_multipath=False,
device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
*args, **kwargs):
self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute)
self._linuxfc = linuxfc.LinuxFibreChannel(root_helper, execute)
super(FibreChannelConnector, self).__init__(
root_helper, driver=driver,
@ -1616,7 +1636,6 @@ class FibreChannelConnectorS390X(FibreChannelConnector):
device_scan_attempts=device_scan_attempts,
*args, **kwargs)
LOG.debug("Initializing Fibre Channel connector for S390")
self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute)
self._linuxfc = linuxfc.LinuxFibreChannelS390X(root_helper, execute)
self.use_multipath = use_multipath
@ -1665,7 +1684,7 @@ class FibreChannelConnectorS390X(FibreChannelConnector):
target_lun)
class AoEConnector(InitiatorConnector):
class AoEConnector(BaseLinuxConnector):
"""Connector class to attach/detach AoE volumes."""
def __init__(self, root_helper, driver=None,
@ -1811,7 +1830,7 @@ class AoEConnector(InitiatorConnector):
raise NotImplementedError
class RemoteFsConnector(InitiatorConnector):
class RemoteFsConnector(BaseLinuxConnector):
"""Connector class to attach/detach NFS and GlusterFS volumes."""
def __init__(self, mount_type, root_helper, driver=None,
@ -1905,7 +1924,7 @@ class RemoteFsConnector(InitiatorConnector):
raise NotImplementedError
class RBDConnector(InitiatorConnector):
class RBDConnector(BaseLinuxConnector):
""""Connector class to attach/detach RBD volumes."""
def __init__(self, root_helper, driver=None, use_multipath=False,
@ -2000,7 +2019,7 @@ class RBDConnector(InitiatorConnector):
raise NotImplementedError
class LocalConnector(InitiatorConnector):
class LocalConnector(BaseLinuxConnector):
""""Connector class to attach/detach File System backed volumes."""
def __init__(self, root_helper, driver=None,
@ -2059,7 +2078,7 @@ class LocalConnector(InitiatorConnector):
raise NotImplementedError
class DRBDConnector(InitiatorConnector):
class DRBDConnector(BaseLinuxConnector):
""""Connector class to attach/detach DRBD resources."""
def __init__(self, root_helper, driver=None,
@ -2145,7 +2164,7 @@ class DRBDConnector(InitiatorConnector):
raise NotImplementedError
class HuaweiStorHyperConnector(InitiatorConnector):
class HuaweiStorHyperConnector(BaseLinuxConnector):
""""Connector class to attach/detach SDSHypervisor volumes."""
attached_success_code = 0
@ -2309,7 +2328,7 @@ class HuaweiStorHyperConnector(InitiatorConnector):
raise NotImplementedError
class HGSTConnector(InitiatorConnector):
class HGSTConnector(BaseLinuxConnector):
"""Connector class to attach/detach HGST volumes."""
VGCCLUSTER = 'vgc-cluster'
@ -2462,7 +2481,7 @@ class HGSTConnector(InitiatorConnector):
raise NotImplementedError
class ScaleIOConnector(InitiatorConnector):
class ScaleIOConnector(BaseLinuxConnector):
"""Class implements the connector driver for ScaleIO."""
OK_STATUS_CODE = 200
@ -2917,7 +2936,7 @@ class ScaleIOConnector(InitiatorConnector):
raise NotImplementedError
class DISCOConnector(InitiatorConnector):
class DISCOConnector(BaseLinuxConnector):
"""Class implements the connector driver for DISCO."""
DISCO_PREFIX = 'dms'
@ -3090,7 +3109,7 @@ class DISCOConnector(InitiatorConnector):
raise NotImplementedError
class SheepdogConnector(InitiatorConnector):
class SheepdogConnector(BaseLinuxConnector):
""""Connector class to attach/detach sheepdog volumes."""
def __init__(self, root_helper, driver=None, use_multipath=False,

View File

@ -167,7 +167,7 @@ class ConnectorTestCase(base.TestCase):
mock_exec.return_value = True
multipath = True
enforce_multipath = True
props = connector.InitiatorConnector.get_connector_properties(
props = connector.BaseLinuxConnector.get_connector_properties(
'sudo', multipath=multipath,
enforce_multipath=enforce_multipath)
@ -176,7 +176,7 @@ class ConnectorTestCase(base.TestCase):
multipath = False
enforce_multipath = True
props = connector.InitiatorConnector.get_connector_properties(
props = connector.BaseLinuxConnector.get_connector_properties(
'sudo', multipath=multipath,
enforce_multipath=enforce_multipath)
@ -189,7 +189,7 @@ class ConnectorTestCase(base.TestCase):
enforce_multipath = True
self.assertRaises(
putils.ProcessExecutionError,
connector.InitiatorConnector.get_connector_properties,
connector.BaseLinuxConnector.get_connector_properties,
'sudo', multipath=multipath,
enforce_multipath=enforce_multipath)
@ -609,7 +609,7 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
@mock.patch.object(connector.ISCSIConnector, '_rescan_iscsi')
@mock.patch.object(connector.ISCSIConnector, '_rescan_multipath')
@mock.patch.object(os.path, 'exists', return_value=True)
@mock.patch.object(connector.InitiatorConnector, '_discover_mpath_device')
@mock.patch.object(connector.BaseLinuxConnector, '_discover_mpath_device')
def test_connect_volume_with_multipath(
self, mock_discover_mpath_device, exists_mock,
rescan_multipath_mock, rescan_iscsi_mock, connect_to_mock,
@ -690,7 +690,7 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
@mock.patch.object(connector.ISCSIConnector, '_get_iscsi_devices')
@mock.patch.object(connector.ISCSIConnector, '_run_multipath')
@mock.patch.object(connector.ISCSIConnector, '_get_multipath_iqns')
@mock.patch.object(connector.InitiatorConnector, '_discover_mpath_device')
@mock.patch.object(connector.BaseLinuxConnector, '_discover_mpath_device')
@mock.patch.object(linuxscsi.LinuxSCSI, 'process_lun_id')
def test_connect_volume_with_multiple_portals(
self, mock_process_lun_id, mock_discover_mpath_device,
@ -747,7 +747,7 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
@mock.patch.object(connector.ISCSIConnector, '_run_multipath')
@mock.patch.object(connector.ISCSIConnector, '_get_multipath_iqns')
@mock.patch.object(connector.ISCSIConnector, '_run_iscsiadm')
@mock.patch.object(connector.InitiatorConnector, '_discover_mpath_device')
@mock.patch.object(connector.BaseLinuxConnector, '_discover_mpath_device')
@mock.patch.object(linuxscsi.LinuxSCSI, 'process_lun_id')
def test_connect_volume_with_multiple_portals_primary_error(
self, mock_process_lun_id, mock_discover_mpath_device,
@ -826,7 +826,7 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
@mock.patch.object(connector.ISCSIConnector, '_get_iscsi_devices')
@mock.patch.object(connector.ISCSIConnector, '_rescan_multipath')
@mock.patch.object(connector.ISCSIConnector, '_run_multipath')
@mock.patch.object(connector.InitiatorConnector, '_discover_mpath_device')
@mock.patch.object(connector.BaseLinuxConnector, '_discover_mpath_device')
def test_connect_volume_with_multipath_connecting(
self, mock_discover_mpath_device, mock_run_multipath,
mock_rescan_multipath, mock_iscsi_devices, mock_devices,
@ -1508,7 +1508,7 @@ class FibreChannelConnectorTestCase(ConnectorTestCase):
'ro',
False)
@mock.patch.object(connector.InitiatorConnector, '_discover_mpath_device')
@mock.patch.object(connector.BaseLinuxConnector, '_discover_mpath_device')
@mock.patch.object(linuxscsi.LinuxSCSI, 'find_multipath_device')
@mock.patch.object(linuxscsi.LinuxSCSI, 'wait_for_rw')
@mock.patch.object(os.path, 'exists', return_value=True)