Merge "os-brick refactor get_connector_properties"
This commit is contained in:
commit
6d71d55caf
@ -38,13 +38,11 @@ from oslo_concurrency import lockutils
|
|||||||
from oslo_concurrency import processutils as putils
|
from oslo_concurrency import processutils as putils
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_service import loopingcall
|
from oslo_service import loopingcall
|
||||||
|
from oslo_utils import importutils
|
||||||
from oslo_utils import strutils
|
from oslo_utils import strutils
|
||||||
import six
|
import six
|
||||||
from six.moves import urllib
|
from six.moves import urllib
|
||||||
|
|
||||||
S390X = "s390x"
|
|
||||||
S390 = "s390"
|
|
||||||
|
|
||||||
from os_brick import exception
|
from os_brick import exception
|
||||||
from os_brick import executor
|
from os_brick import executor
|
||||||
from os_brick import utils
|
from os_brick import utils
|
||||||
@ -54,7 +52,6 @@ from os_brick.initiator import linuxfc
|
|||||||
from os_brick.initiator import linuxrbd
|
from os_brick.initiator import linuxrbd
|
||||||
from os_brick.initiator import linuxscsi
|
from os_brick.initiator import linuxscsi
|
||||||
from os_brick.initiator import linuxsheepdog
|
from os_brick.initiator import linuxsheepdog
|
||||||
from os_brick.privileged import rootwrap as priv_rootwrap
|
|
||||||
from os_brick.remotefs import remotefs
|
from os_brick.remotefs import remotefs
|
||||||
from os_brick.i18n import _, _LE, _LI, _LW
|
from os_brick.i18n import _, _LE, _LI, _LW
|
||||||
|
|
||||||
@ -66,6 +63,15 @@ MULTIPATH_ERROR_REGEX = re.compile("\w{3} \d+ \d\d:\d\d:\d\d \|.*$")
|
|||||||
MULTIPATH_DEV_CHECK_REGEX = re.compile("\s+dm-\d+\s+")
|
MULTIPATH_DEV_CHECK_REGEX = re.compile("\s+dm-\d+\s+")
|
||||||
MULTIPATH_PATH_CHECK_REGEX = re.compile("\s+\d+:\d+:\d+:\d+\s+")
|
MULTIPATH_PATH_CHECK_REGEX = re.compile("\s+\d+:\d+:\d+:\d+\s+")
|
||||||
|
|
||||||
|
PLATFORM_ALL = 'ALL'
|
||||||
|
PLATFORM_x86 = 'X86'
|
||||||
|
PLATFORM_S390 = 'S390'
|
||||||
|
OS_TYPE_ALL = 'ALL'
|
||||||
|
OS_TYPE_LINUX = 'LINUX'
|
||||||
|
|
||||||
|
S390X = "s390x"
|
||||||
|
S390 = "s390"
|
||||||
|
|
||||||
ISCSI = "ISCSI"
|
ISCSI = "ISCSI"
|
||||||
ISER = "ISER"
|
ISER = "ISER"
|
||||||
FIBRE_CHANNEL = "FIBRE_CHANNEL"
|
FIBRE_CHANNEL = "FIBRE_CHANNEL"
|
||||||
@ -84,19 +90,21 @@ DISCO = "DISCO"
|
|||||||
VZSTORAGE = "VZSTORAGE"
|
VZSTORAGE = "VZSTORAGE"
|
||||||
SHEEPDOG = "SHEEPDOG"
|
SHEEPDOG = "SHEEPDOG"
|
||||||
|
|
||||||
|
connector_list = [
|
||||||
def _check_multipathd_running(root_helper, enforce_multipath):
|
'os_brick.initiator.connector.InitiatorConnector',
|
||||||
try:
|
'os_brick.initiator.connector.ISCSIConnector',
|
||||||
priv_rootwrap.execute('multipathd', 'show', 'status',
|
'os_brick.initiator.connector.FibreChannelConnector',
|
||||||
run_as_root=True, root_helper=root_helper)
|
'os_brick.initiator.connector.FibreChannelConnectorS390X',
|
||||||
except putils.ProcessExecutionError as err:
|
'os_brick.initiator.connector.AoEConnector',
|
||||||
LOG.error(_LE('multipathd is not running: exit code %(err)s'),
|
'os_brick.initiator.connector.RemoteFsConnector',
|
||||||
{'err': err.exit_code})
|
'os_brick.initiator.connector.RBDConnector',
|
||||||
if enforce_multipath:
|
'os_brick.initiator.connector.LocalConnector',
|
||||||
raise
|
'os_brick.initiator.connector.DRBDConnector',
|
||||||
return False
|
'os_brick.initiator.connector.HuaweiStorHyperConnector',
|
||||||
|
'os_brick.initiator.connector.HGSTConnector',
|
||||||
return True
|
'os_brick.initiator.connector.ScaleIOConnector',
|
||||||
|
'os_brick.initiator.connector.DISCOConnector',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def get_connector_properties(root_helper, my_ip, multipath, enforce_multipath,
|
def get_connector_properties(root_helper, my_ip, multipath, enforce_multipath,
|
||||||
@ -123,32 +131,39 @@ def get_connector_properties(root_helper, my_ip, multipath, enforce_multipath,
|
|||||||
:type enforce_multipath: bool
|
:type enforce_multipath: bool
|
||||||
:returns: dict containing all of the collected initiator values.
|
:returns: dict containing all of the collected initiator values.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
iscsi = ISCSIConnector(root_helper=root_helper)
|
|
||||||
fc = linuxfc.LinuxFibreChannel(root_helper=root_helper)
|
|
||||||
|
|
||||||
props = {}
|
props = {}
|
||||||
props['ip'] = my_ip
|
|
||||||
props['host'] = host if host else socket.gethostname()
|
|
||||||
initiator = iscsi.get_initiator()
|
|
||||||
if initiator:
|
|
||||||
props['initiator'] = initiator
|
|
||||||
wwpns = fc.get_fc_wwpns()
|
|
||||||
if wwpns:
|
|
||||||
props['wwpns'] = wwpns
|
|
||||||
wwnns = fc.get_fc_wwnns()
|
|
||||||
if wwnns:
|
|
||||||
props['wwnns'] = wwnns
|
|
||||||
props['multipath'] = (multipath and
|
|
||||||
_check_multipathd_running(root_helper,
|
|
||||||
enforce_multipath))
|
|
||||||
props['platform'] = platform.machine()
|
props['platform'] = platform.machine()
|
||||||
props['os_type'] = sys.platform
|
props['os_type'] = sys.platform
|
||||||
|
props['ip'] = my_ip
|
||||||
|
props['host'] = host if host else socket.gethostname()
|
||||||
|
|
||||||
|
for item in connector_list:
|
||||||
|
connector = importutils.import_class(item)
|
||||||
|
|
||||||
|
if (utils.platform_matches(props['platform'], connector.platform) and
|
||||||
|
utils.os_matches(props['os_type'], connector.os_type)):
|
||||||
|
LOG.debug("Fetching connector for %s" % connector.__name__)
|
||||||
|
props = utils.merge_dict(props,
|
||||||
|
connector.get_connector_properties(
|
||||||
|
root_helper,
|
||||||
|
host=host,
|
||||||
|
multipath=multipath,
|
||||||
|
enforce_multipath=enforce_multipath))
|
||||||
|
|
||||||
return props
|
return props
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class InitiatorConnector(executor.Executor):
|
class InitiatorConnector(executor.Executor):
|
||||||
|
|
||||||
|
# This object can be used on any platform (x86, S390)
|
||||||
|
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
|
||||||
|
|
||||||
def __init__(self, root_helper, driver=None, execute=None,
|
def __init__(self, root_helper, driver=None, execute=None,
|
||||||
device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
|
device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
@ -162,9 +177,21 @@ class InitiatorConnector(executor.Executor):
|
|||||||
|
|
||||||
def set_driver(self, driver):
|
def set_driver(self, driver):
|
||||||
"""The driver is used to find used LUNs."""
|
"""The driver is used to find used LUNs."""
|
||||||
|
|
||||||
self.driver = driver
|
self.driver = driver
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
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
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def factory(protocol, root_helper, driver=None,
|
def factory(protocol, root_helper, driver=None,
|
||||||
use_multipath=False,
|
use_multipath=False,
|
||||||
@ -514,6 +541,17 @@ class ISCSIConnector(InitiatorConnector):
|
|||||||
self.use_multipath = use_multipath
|
self.use_multipath = use_multipath
|
||||||
self.transport = self._validate_iface_transport(transport)
|
self.transport = self._validate_iface_transport(transport)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The iSCSI connector properties."""
|
||||||
|
props = {}
|
||||||
|
iscsi = ISCSIConnector(root_helper=root_helper)
|
||||||
|
initiator = iscsi.get_initiator()
|
||||||
|
if initiator:
|
||||||
|
props['initiator'] = initiator
|
||||||
|
|
||||||
|
return props
|
||||||
|
|
||||||
def get_search_path(self):
|
def get_search_path(self):
|
||||||
"""Where do we look for iSCSI based volumes."""
|
"""Where do we look for iSCSI based volumes."""
|
||||||
return '/dev/disk/by-path'
|
return '/dev/disk/by-path'
|
||||||
@ -1318,6 +1356,21 @@ class FibreChannelConnector(InitiatorConnector):
|
|||||||
self._linuxscsi.set_execute(execute)
|
self._linuxscsi.set_execute(execute)
|
||||||
self._linuxfc.set_execute(execute)
|
self._linuxfc.set_execute(execute)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The Fibre Channel connector properties."""
|
||||||
|
props = {}
|
||||||
|
fc = linuxfc.LinuxFibreChannel(root_helper)
|
||||||
|
|
||||||
|
wwpns = fc.get_fc_wwpns()
|
||||||
|
if wwpns:
|
||||||
|
props['wwpns'] = wwpns
|
||||||
|
wwnns = fc.get_fc_wwnns()
|
||||||
|
if wwnns:
|
||||||
|
props['wwnns'] = wwnns
|
||||||
|
|
||||||
|
return props
|
||||||
|
|
||||||
def get_search_path(self):
|
def get_search_path(self):
|
||||||
"""Where do we look for FC based volumes."""
|
"""Where do we look for FC based volumes."""
|
||||||
return '/dev/disk/by-path'
|
return '/dev/disk/by-path'
|
||||||
@ -1550,6 +1603,7 @@ class FibreChannelConnector(InitiatorConnector):
|
|||||||
|
|
||||||
class FibreChannelConnectorS390X(FibreChannelConnector):
|
class FibreChannelConnectorS390X(FibreChannelConnector):
|
||||||
"""Connector class to attach/detach Fibre Channel volumes on S390X arch."""
|
"""Connector class to attach/detach Fibre Channel volumes on S390X arch."""
|
||||||
|
platform = PLATFORM_S390
|
||||||
|
|
||||||
def __init__(self, root_helper, driver=None,
|
def __init__(self, root_helper, driver=None,
|
||||||
execute=None, use_multipath=False,
|
execute=None, use_multipath=False,
|
||||||
@ -1613,6 +1667,7 @@ class FibreChannelConnectorS390X(FibreChannelConnector):
|
|||||||
|
|
||||||
class AoEConnector(InitiatorConnector):
|
class AoEConnector(InitiatorConnector):
|
||||||
"""Connector class to attach/detach AoE volumes."""
|
"""Connector class to attach/detach AoE volumes."""
|
||||||
|
|
||||||
def __init__(self, root_helper, driver=None,
|
def __init__(self, root_helper, driver=None,
|
||||||
device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
|
device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
|
||||||
*args, **kwargs):
|
*args, **kwargs):
|
||||||
@ -1622,6 +1677,11 @@ class AoEConnector(InitiatorConnector):
|
|||||||
device_scan_attempts=device_scan_attempts,
|
device_scan_attempts=device_scan_attempts,
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The AoE connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_search_path(self):
|
def get_search_path(self):
|
||||||
return '/dev/etherd'
|
return '/dev/etherd'
|
||||||
|
|
||||||
@ -1785,6 +1845,11 @@ class RemoteFsConnector(InitiatorConnector):
|
|||||||
device_scan_attempts=device_scan_attempts,
|
device_scan_attempts=device_scan_attempts,
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The RemoteFS connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def set_execute(self, execute):
|
def set_execute(self, execute):
|
||||||
super(RemoteFsConnector, self).set_execute(execute)
|
super(RemoteFsConnector, self).set_execute(execute)
|
||||||
self._remotefsclient.set_execute(execute)
|
self._remotefsclient.set_execute(execute)
|
||||||
@ -1852,6 +1917,11 @@ class RBDConnector(InitiatorConnector):
|
|||||||
device_scan_attempts,
|
device_scan_attempts,
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The RBD connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_volume_paths(self, connection_properties):
|
def get_volume_paths(self, connection_properties):
|
||||||
# TODO(walter-boring): don't know where the connector
|
# TODO(walter-boring): don't know where the connector
|
||||||
# looks for RBD volumes.
|
# looks for RBD volumes.
|
||||||
@ -1938,6 +2008,11 @@ class LocalConnector(InitiatorConnector):
|
|||||||
super(LocalConnector, self).__init__(root_helper, driver=driver,
|
super(LocalConnector, self).__init__(root_helper, driver=driver,
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The Local connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_volume_paths(self, connection_properties):
|
def get_volume_paths(self, connection_properties):
|
||||||
path = connection_properties['device_path']
|
path = connection_properties['device_path']
|
||||||
return [path]
|
return [path]
|
||||||
@ -1987,6 +2062,20 @@ class LocalConnector(InitiatorConnector):
|
|||||||
class DRBDConnector(InitiatorConnector):
|
class DRBDConnector(InitiatorConnector):
|
||||||
""""Connector class to attach/detach DRBD resources."""
|
""""Connector class to attach/detach DRBD resources."""
|
||||||
|
|
||||||
|
def __init__(self, root_helper, driver=None,
|
||||||
|
execute=putils.execute, *args, **kwargs):
|
||||||
|
|
||||||
|
super(DRBDConnector, self).__init__(root_helper, driver=driver,
|
||||||
|
execute=execute, *args, **kwargs)
|
||||||
|
|
||||||
|
self._execute = execute
|
||||||
|
self._root_helper = root_helper
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The DRBD connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def check_valid_device(self, path, run_as_root=True):
|
def check_valid_device(self, path, run_as_root=True):
|
||||||
"""Verify an existing volume."""
|
"""Verify an existing volume."""
|
||||||
# TODO(linbit): check via drbdsetup first, to avoid blocking/hanging
|
# TODO(linbit): check via drbdsetup first, to avoid blocking/hanging
|
||||||
@ -2058,6 +2147,7 @@ class DRBDConnector(InitiatorConnector):
|
|||||||
|
|
||||||
class HuaweiStorHyperConnector(InitiatorConnector):
|
class HuaweiStorHyperConnector(InitiatorConnector):
|
||||||
""""Connector class to attach/detach SDSHypervisor volumes."""
|
""""Connector class to attach/detach SDSHypervisor volumes."""
|
||||||
|
|
||||||
attached_success_code = 0
|
attached_success_code = 0
|
||||||
has_been_attached_code = 50151401
|
has_been_attached_code = 50151401
|
||||||
attach_mnid_done_code = 50151405
|
attach_mnid_done_code = 50151405
|
||||||
@ -2080,6 +2170,11 @@ class HuaweiStorHyperConnector(InitiatorConnector):
|
|||||||
driver=driver,
|
driver=driver,
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The HuaweiStor connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_search_path(self):
|
def get_search_path(self):
|
||||||
# TODO(walter-boring): Where is the location on the filesystem to
|
# TODO(walter-boring): Where is the location on the filesystem to
|
||||||
# look for Huawei volumes to show up?
|
# look for Huawei volumes to show up?
|
||||||
@ -2216,6 +2311,7 @@ class HuaweiStorHyperConnector(InitiatorConnector):
|
|||||||
|
|
||||||
class HGSTConnector(InitiatorConnector):
|
class HGSTConnector(InitiatorConnector):
|
||||||
"""Connector class to attach/detach HGST volumes."""
|
"""Connector class to attach/detach HGST volumes."""
|
||||||
|
|
||||||
VGCCLUSTER = 'vgc-cluster'
|
VGCCLUSTER = 'vgc-cluster'
|
||||||
|
|
||||||
def __init__(self, root_helper, driver=None,
|
def __init__(self, root_helper, driver=None,
|
||||||
@ -2227,6 +2323,11 @@ class HGSTConnector(InitiatorConnector):
|
|||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
self._vgc_host = None
|
self._vgc_host = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The HGST connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def _log_cli_err(self, err):
|
def _log_cli_err(self, err):
|
||||||
"""Dumps the full command output to a logfile in error cases."""
|
"""Dumps the full command output to a logfile in error cases."""
|
||||||
LOG.error(_LE("CLI fail: '%(cmd)s' = %(code)s\nout: %(stdout)s\n"
|
LOG.error(_LE("CLI fail: '%(cmd)s' = %(code)s\nout: %(stdout)s\n"
|
||||||
@ -2363,6 +2464,7 @@ class HGSTConnector(InitiatorConnector):
|
|||||||
|
|
||||||
class ScaleIOConnector(InitiatorConnector):
|
class ScaleIOConnector(InitiatorConnector):
|
||||||
"""Class implements the connector driver for ScaleIO."""
|
"""Class implements the connector driver for ScaleIO."""
|
||||||
|
|
||||||
OK_STATUS_CODE = 200
|
OK_STATUS_CODE = 200
|
||||||
VOLUME_NOT_MAPPED_ERROR = 84
|
VOLUME_NOT_MAPPED_ERROR = 84
|
||||||
VOLUME_ALREADY_MAPPED_ERROR = 81
|
VOLUME_ALREADY_MAPPED_ERROR = 81
|
||||||
@ -2390,6 +2492,11 @@ class ScaleIOConnector(InitiatorConnector):
|
|||||||
self.iops_limit = None
|
self.iops_limit = None
|
||||||
self.bandwidth_limit = None
|
self.bandwidth_limit = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The ScaleIO connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_search_path(self):
|
def get_search_path(self):
|
||||||
return "/dev/disk/by-id"
|
return "/dev/disk/by-id"
|
||||||
|
|
||||||
@ -2830,6 +2937,11 @@ class DISCOConnector(InitiatorConnector):
|
|||||||
self.server_port = None
|
self.server_port = None
|
||||||
self.server_ip = None
|
self.server_ip = None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The DISCO connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_search_path(self):
|
def get_search_path(self):
|
||||||
"""Get directory path where to get DISCO volumes."""
|
"""Get directory path where to get DISCO volumes."""
|
||||||
return "/dev"
|
return "/dev"
|
||||||
@ -2990,6 +3102,11 @@ class SheepdogConnector(InitiatorConnector):
|
|||||||
device_scan_attempts,
|
device_scan_attempts,
|
||||||
*args, **kwargs)
|
*args, **kwargs)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_connector_properties(root_helper, *args, **kwargs):
|
||||||
|
"""The Sheepdog connector properties."""
|
||||||
|
return {}
|
||||||
|
|
||||||
def get_volume_paths(self, connection_properties):
|
def get_volume_paths(self, connection_properties):
|
||||||
# TODO(lixiaoy1): don't know where the connector
|
# TODO(lixiaoy1): don't know where the connector
|
||||||
# looks for sheepdog volumes.
|
# looks for sheepdog volumes.
|
||||||
|
@ -25,8 +25,10 @@ from oslo_log import log as logging
|
|||||||
|
|
||||||
from os_brick import exception
|
from os_brick import exception
|
||||||
from os_brick import executor
|
from os_brick import executor
|
||||||
|
from os_brick.i18n import _LE
|
||||||
from os_brick.i18n import _LI
|
from os_brick.i18n import _LI
|
||||||
from os_brick.i18n import _LW
|
from os_brick.i18n import _LW
|
||||||
|
from os_brick.privileged import rootwrap as priv_rootwrap
|
||||||
from os_brick import utils
|
from os_brick import utils
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -110,6 +112,21 @@ class LinuxSCSI(executor.Executor):
|
|||||||
root_helper=self._root_helper)
|
root_helper=self._root_helper)
|
||||||
return out.strip()
|
return out.strip()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def is_multipath_running(enforce_multipath, root_helper):
|
||||||
|
try:
|
||||||
|
priv_rootwrap.execute('multipathd', 'show', 'status',
|
||||||
|
run_as_root=True,
|
||||||
|
root_helper=root_helper)
|
||||||
|
except putils.ProcessExecutionError as err:
|
||||||
|
LOG.error(_LE('multipathd is not running: exit code %(err)s'),
|
||||||
|
{'err': err.exit_code})
|
||||||
|
if enforce_multipath:
|
||||||
|
raise
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def remove_multipath_device(self, device):
|
def remove_multipath_device(self, device):
|
||||||
"""This removes LUNs associated with a multipath device
|
"""This removes LUNs associated with a multipath device
|
||||||
and the multipath device itself.
|
and the multipath device itself.
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import platform
|
import platform
|
||||||
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -77,6 +78,29 @@ class ConnectorUtilsTestCase(base.TestCase):
|
|||||||
'platform': platform}
|
'platform': platform}
|
||||||
self.assertEqual(props, props_actual)
|
self.assertEqual(props, props_actual)
|
||||||
|
|
||||||
|
def test_brick_get_connector_properties_connectors_called(self):
|
||||||
|
"""Make sure every connector is called."""
|
||||||
|
|
||||||
|
mock_list = []
|
||||||
|
# Make sure every connector is called
|
||||||
|
for item in connector.connector_list:
|
||||||
|
patched = mock.MagicMock()
|
||||||
|
patched.platform = platform.machine()
|
||||||
|
patched.os_type = sys.platform
|
||||||
|
patched.__name__ = item
|
||||||
|
patched.get_connector_properties.return_value = {}
|
||||||
|
patcher = mock.patch(item, new=patched)
|
||||||
|
patcher.start()
|
||||||
|
self.addCleanup(patcher.stop)
|
||||||
|
mock_list.append(patched)
|
||||||
|
|
||||||
|
connector.get_connector_properties('sudo',
|
||||||
|
MY_IP,
|
||||||
|
True, True)
|
||||||
|
|
||||||
|
for item in mock_list:
|
||||||
|
assert item.get_connector_properties.called
|
||||||
|
|
||||||
def test_brick_get_connector_properties(self):
|
def test_brick_get_connector_properties(self):
|
||||||
self._test_brick_get_connector_properties(False, False, False)
|
self._test_brick_get_connector_properties(False, False, False)
|
||||||
|
|
||||||
@ -138,6 +162,37 @@ class ConnectorTestCase(base.TestCase):
|
|||||||
def test_disconnect_volume(self):
|
def test_disconnect_volume(self):
|
||||||
self.connector = connector.FakeConnector(None)
|
self.connector = connector.FakeConnector(None)
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
with mock.patch.object(priv_rootwrap, 'execute') as mock_exec:
|
||||||
|
mock_exec.return_value = True
|
||||||
|
multipath = True
|
||||||
|
enforce_multipath = True
|
||||||
|
props = connector.InitiatorConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=multipath,
|
||||||
|
enforce_multipath=enforce_multipath)
|
||||||
|
|
||||||
|
expected_props = {'multipath': True}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
|
multipath = False
|
||||||
|
enforce_multipath = True
|
||||||
|
props = connector.InitiatorConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=multipath,
|
||||||
|
enforce_multipath=enforce_multipath)
|
||||||
|
|
||||||
|
expected_props = {'multipath': False}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
|
with mock.patch.object(priv_rootwrap, 'execute',
|
||||||
|
side_effect=putils.ProcessExecutionError):
|
||||||
|
multipath = True
|
||||||
|
enforce_multipath = True
|
||||||
|
self.assertRaises(
|
||||||
|
putils.ProcessExecutionError,
|
||||||
|
connector.InitiatorConnector.get_connector_properties,
|
||||||
|
'sudo', multipath=multipath,
|
||||||
|
enforce_multipath=enforce_multipath)
|
||||||
|
|
||||||
def test_factory(self):
|
def test_factory(self):
|
||||||
obj = connector.InitiatorConnector.factory('iscsi', None)
|
obj = connector.InitiatorConnector.factory('iscsi', None)
|
||||||
self.assertEqual(obj.__class__.__name__, "ISCSIConnector")
|
self.assertEqual(obj.__class__.__name__, "ISCSIConnector")
|
||||||
@ -217,6 +272,7 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
|
|||||||
mock.patch.object(self.connector._linuxscsi, 'get_name_from_path',
|
mock.patch.object(self.connector._linuxscsi, 'get_name_from_path',
|
||||||
return_value="/dev/sdb").start()
|
return_value="/dev/sdb").start()
|
||||||
self.addCleanup(mock.patch.stopall)
|
self.addCleanup(mock.patch.stopall)
|
||||||
|
self._fake_iqn = 'iqn.1234-56.foo.bar:01:23456789abc'
|
||||||
|
|
||||||
def generate_device(self, location, iqn, transport=None, lun=1):
|
def generate_device(self, location, iqn, transport=None, lun=1):
|
||||||
dev_format = "ip-%s-iscsi-%s-lun-%s" % (location, iqn, lun)
|
dev_format = "ip-%s-iscsi-%s-lun-%s" % (location, iqn, lun)
|
||||||
@ -267,29 +323,41 @@ class ISCSIConnectorTestCase(ConnectorTestCase):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _initiator_get_text(self, *arg, **kwargs):
|
||||||
|
text = ('## DO NOT EDIT OR REMOVE THIS FILE!\n'
|
||||||
|
'## If you remove this file, the iSCSI daemon '
|
||||||
|
'will not start.\n'
|
||||||
|
'## If you change the InitiatorName, existing '
|
||||||
|
'access control lists\n'
|
||||||
|
'## may reject this initiator. The InitiatorName must '
|
||||||
|
'be unique\n'
|
||||||
|
'## for each iSCSI initiator. Do NOT duplicate iSCSI '
|
||||||
|
'InitiatorNames.\n'
|
||||||
|
'InitiatorName=%s' % self._fake_iqn)
|
||||||
|
return text, None
|
||||||
|
|
||||||
def test_get_initiator(self):
|
def test_get_initiator(self):
|
||||||
def initiator_no_file(*args, **kwargs):
|
def initiator_no_file(*args, **kwargs):
|
||||||
raise putils.ProcessExecutionError('No file')
|
raise putils.ProcessExecutionError('No file')
|
||||||
|
|
||||||
def initiator_get_text(*arg, **kwargs):
|
|
||||||
text = ('## DO NOT EDIT OR REMOVE THIS FILE!\n'
|
|
||||||
'## If you remove this file, the iSCSI daemon '
|
|
||||||
'will not start.\n'
|
|
||||||
'## If you change the InitiatorName, existing '
|
|
||||||
'access control lists\n'
|
|
||||||
'## may reject this initiator. The InitiatorName must '
|
|
||||||
'be unique\n'
|
|
||||||
'## for each iSCSI initiator. Do NOT duplicate iSCSI '
|
|
||||||
'InitiatorNames.\n'
|
|
||||||
'InitiatorName=iqn.1234-56.foo.bar:01:23456789abc')
|
|
||||||
return text, None
|
|
||||||
|
|
||||||
self.connector._execute = initiator_no_file
|
self.connector._execute = initiator_no_file
|
||||||
initiator = self.connector.get_initiator()
|
initiator = self.connector.get_initiator()
|
||||||
self.assertIsNone(initiator)
|
self.assertIsNone(initiator)
|
||||||
self.connector._execute = initiator_get_text
|
self.connector._execute = self._initiator_get_text
|
||||||
initiator = self.connector.get_initiator()
|
initiator = self.connector.get_initiator()
|
||||||
self.assertEqual(initiator, 'iqn.1234-56.foo.bar:01:23456789abc')
|
self.assertEqual(initiator, self._fake_iqn)
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
with mock.patch.object(priv_rootwrap, 'execute') as mock_exec:
|
||||||
|
mock_exec.return_value = self._initiator_get_text()
|
||||||
|
multipath = True
|
||||||
|
enforce_multipath = True
|
||||||
|
props = connector.ISCSIConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=multipath,
|
||||||
|
enforce_multipath=enforce_multipath)
|
||||||
|
|
||||||
|
expected_props = {'initiator': self._fake_iqn}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
@mock.patch.object(connector.ISCSIConnector, '_run_iscsiadm_bare')
|
@mock.patch.object(connector.ISCSIConnector, '_run_iscsiadm_bare')
|
||||||
def test_brick_iscsi_validate_transport(self, mock_iscsiadm):
|
def test_brick_iscsi_validate_transport(self, mock_iscsiadm):
|
||||||
@ -1185,6 +1253,20 @@ class FibreChannelConnectorTestCase(ConnectorTestCase):
|
|||||||
'target_lun': 1,
|
'target_lun': 1,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
@mock.patch.object(linuxfc.LinuxFibreChannel, 'get_fc_hbas')
|
||||||
|
def test_get_connector_properties(self, mock_hbas):
|
||||||
|
mock_hbas.return_value = self.fake_get_fc_hbas()
|
||||||
|
multipath = True
|
||||||
|
enforce_multipath = True
|
||||||
|
props = connector.FibreChannelConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=multipath,
|
||||||
|
enforce_multipath=enforce_multipath)
|
||||||
|
|
||||||
|
hbas = self.fake_get_fc_hbas()
|
||||||
|
expected_props = {'wwpns': [hbas[0]['port_name'].replace('0x', '')],
|
||||||
|
'wwnns': [hbas[0]['node_name'].replace('0x', '')]}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_get_search_path(self):
|
def test_get_search_path(self):
|
||||||
search_path = self.connector.get_search_path()
|
search_path = self.connector.get_search_path()
|
||||||
expected = "/dev/disk/by-path"
|
expected = "/dev/disk/by-path"
|
||||||
@ -1592,6 +1674,13 @@ class AoEConnectorTestCase(ConnectorTestCase):
|
|||||||
paths = self.connector.get_volume_paths(self.connection_properties)
|
paths = self.connector.get_volume_paths(self.connection_properties)
|
||||||
self.assertEqual(expected, paths)
|
self.assertEqual(expected, paths)
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.AoEConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
@mock.patch.object(os.path, 'exists', side_effect=[True, True])
|
@mock.patch.object(os.path, 'exists', side_effect=[True, True])
|
||||||
def test_connect_volume(self, exists_mock):
|
def test_connect_volume(self, exists_mock):
|
||||||
"""Ensure that if path exist aoe-revalidate was called."""
|
"""Ensure that if path exist aoe-revalidate was called."""
|
||||||
@ -1669,6 +1758,13 @@ class RemoteFsConnectorTestCase(ConnectorTestCase):
|
|||||||
connector.RemoteFsConnector('scality', root_helper='sudo')
|
connector.RemoteFsConnector('scality', root_helper='sudo')
|
||||||
self.assertEqual(1, mock_scality_remotefs_client.call_count)
|
self.assertEqual(1, mock_scality_remotefs_client.call_count)
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.RemoteFsConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_get_search_path(self):
|
def test_get_search_path(self):
|
||||||
expected = self.TEST_BASE
|
expected = self.TEST_BASE
|
||||||
actual = self.connector.get_search_path()
|
actual = self.connector.get_search_path()
|
||||||
@ -1707,6 +1803,13 @@ class LocalConnectorTestCase(ConnectorTestCase):
|
|||||||
'device_path': '/tmp/bar'}
|
'device_path': '/tmp/bar'}
|
||||||
self.connector = connector.LocalConnector(None)
|
self.connector = connector.LocalConnector(None)
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.LocalConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_get_search_path(self):
|
def test_get_search_path(self):
|
||||||
actual = self.connector.get_search_path()
|
actual = self.connector.get_search_path()
|
||||||
self.assertIsNone(actual)
|
self.assertIsNone(actual)
|
||||||
@ -1802,6 +1905,13 @@ class HuaweiStorHyperConnectorTestCase(ConnectorTestCase):
|
|||||||
HuaweiStorHyperConnectorTestCase.attached = True
|
HuaweiStorHyperConnectorTestCase.attached = True
|
||||||
return 'ret_code=330155007', None
|
return 'ret_code=330155007', None
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.HuaweiStorHyperConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_get_search_path(self):
|
def test_get_search_path(self):
|
||||||
actual = self.connector.get_search_path()
|
actual = self.connector.get_search_path()
|
||||||
self.assertIsNone(actual)
|
self.assertIsNone(actual)
|
||||||
@ -2054,6 +2164,13 @@ Request Succeeded
|
|||||||
self.assertEqual('space', dev_info['device'])
|
self.assertEqual('space', dev_info['device'])
|
||||||
self.assertEqual('/dev/space', dev_info['path'])
|
self.assertEqual('/dev/space', dev_info['path'])
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.HGSTConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_connect_volume_nohost_fail(self):
|
def test_connect_volume_nohost_fail(self):
|
||||||
"""This host should not be found, connect should fail."""
|
"""This host should not be found, connect should fail."""
|
||||||
self._fail_set_apphosts = False
|
self._fail_set_apphosts = False
|
||||||
@ -2169,6 +2286,13 @@ class RBDConnectorTestCase(ConnectorTestCase):
|
|||||||
actual = rbd.get_volume_paths(self.connection_properties)
|
actual = rbd.get_volume_paths(self.connection_properties)
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.RBDConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
@mock.patch('os_brick.initiator.linuxrbd.rbd')
|
@mock.patch('os_brick.initiator.linuxrbd.rbd')
|
||||||
@mock.patch('os_brick.initiator.linuxrbd.rados')
|
@mock.patch('os_brick.initiator.linuxrbd.rados')
|
||||||
def test_connect_volume(self, mock_rados, mock_rbd):
|
def test_connect_volume(self, mock_rados, mock_rbd):
|
||||||
@ -2240,6 +2364,13 @@ class DRBDConnectorTestCase(ConnectorTestCase):
|
|||||||
# out, err
|
# out, err
|
||||||
return ('', '')
|
return ('', '')
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.DRBDConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_connect_volume(self):
|
def test_connect_volume(self):
|
||||||
"""Test connect_volume."""
|
"""Test connect_volume."""
|
||||||
|
|
||||||
@ -2417,6 +2548,13 @@ class ScaleIOConnectorTestCase(ConnectorTestCase):
|
|||||||
self.fake_connection_properties)
|
self.fake_connection_properties)
|
||||||
self.assertEqual(expected, actual)
|
self.assertEqual(expected, actual)
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.ScaleIOConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_connect_volume(self):
|
def test_connect_volume(self):
|
||||||
"""Successful connect to volume"""
|
"""Successful connect to volume"""
|
||||||
self.connector.connect_volume(self.fake_connection_properties)
|
self.connector.connect_volume(self.fake_connection_properties)
|
||||||
@ -2584,6 +2722,13 @@ class DISCOConnectorTestCase(ConnectorTestCase):
|
|||||||
volume_path = ''.join(volume_items)
|
volume_path = ''.join(volume_items)
|
||||||
return [volume_path]
|
return [volume_path]
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.DISCOConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_get_search_path(self):
|
def test_get_search_path(self):
|
||||||
"""DISCO volumes should be under /dev."""
|
"""DISCO volumes should be under /dev."""
|
||||||
expected = "/dev"
|
expected = "/dev"
|
||||||
@ -2665,6 +2810,13 @@ class SheepdogConnectorTestCase(ConnectorTestCase):
|
|||||||
'ports': self.ports,
|
'ports': self.ports,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def test_get_connector_properties(self):
|
||||||
|
props = connector.SheepdogConnector.get_connector_properties(
|
||||||
|
'sudo', multipath=True, enforce_multipath=True)
|
||||||
|
|
||||||
|
expected_props = {}
|
||||||
|
self.assertEqual(expected_props, props)
|
||||||
|
|
||||||
def test_get_search_path(self):
|
def test_get_search_path(self):
|
||||||
sheepdog = connector.SheepdogConnector(None)
|
sheepdog = connector.SheepdogConnector(None)
|
||||||
path = sheepdog.get_search_path()
|
path = sheepdog.get_search_path()
|
||||||
|
@ -56,3 +56,42 @@ def retry(exceptions, interval=1, retries=3, backoff_rate=2):
|
|||||||
return _wrapper
|
return _wrapper
|
||||||
|
|
||||||
return _decorator
|
return _decorator
|
||||||
|
|
||||||
|
|
||||||
|
def platform_matches(current_platform, connector_platform):
|
||||||
|
curr_p = current_platform.upper()
|
||||||
|
conn_p = connector_platform.upper()
|
||||||
|
if conn_p == 'ALL':
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Add tests against families of platforms
|
||||||
|
if curr_p == conn_p:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def os_matches(current_os, connector_os):
|
||||||
|
curr_os = current_os.upper()
|
||||||
|
conn_os = connector_os.upper()
|
||||||
|
if conn_os == 'ALL':
|
||||||
|
return True
|
||||||
|
|
||||||
|
# add tests against OSs
|
||||||
|
if (conn_os == curr_os or
|
||||||
|
conn_os in curr_os):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def merge_dict(dict1, dict2):
|
||||||
|
"""Try to safely merge 2 dictionaries."""
|
||||||
|
if type(dict1) is not dict:
|
||||||
|
raise Exception("dict1 is not a dictionary")
|
||||||
|
if type(dict2) is not dict:
|
||||||
|
raise Exception("dict2 is not a dictionary")
|
||||||
|
|
||||||
|
dict3 = dict1.copy()
|
||||||
|
dict3.update(dict2)
|
||||||
|
return dict3
|
||||||
|
Loading…
x
Reference in New Issue
Block a user