os-brick/os_brick/initiator/connector.py

255 lines
9.0 KiB
Python

# Copyright 2013 OpenStack Foundation.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
""" Brick Connector objects for each supported transport protocol.
.. module: connector
The connectors here are responsible for discovering and removing volumes for
each of the supported transport protocols.
"""
import platform
import re
import socket
import sys
from oslo_concurrency import lockutils
from oslo_log import log as logging
from oslo_utils import importutils
from os_brick.i18n import _
from os_brick import initiator
from os_brick import utils
LOG = logging.getLogger(__name__)
synchronized = lockutils.synchronized_with_prefix('os-brick-')
# These constants are being deprecated and moving to the init file.
# Please use the constants there instead.
DEVICE_SCAN_ATTEMPTS_DEFAULT = 3
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_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'
OS_TYPE_WINDOWS = 'WIN'
S390X = "s390x"
S390 = "s390"
ISCSI = "ISCSI"
ISER = "ISER"
FIBRE_CHANNEL = "FIBRE_CHANNEL"
AOE = "AOE"
DRBD = "DRBD"
NFS = "NFS"
GLUSTERFS = "GLUSTERFS"
LOCAL = "LOCAL"
HUAWEISDSHYPERVISOR = "HUAWEISDSHYPERVISOR"
HGST = "HGST"
RBD = "RBD"
SCALEIO = "SCALEIO"
SCALITY = "SCALITY"
QUOBYTE = "QUOBYTE"
DISCO = "DISCO"
VZSTORAGE = "VZSTORAGE"
SHEEPDOG = "SHEEPDOG"
# List of connectors to call when getting
# the connector properties for a host
connector_list = [
'os_brick.initiator.connectors.base.BaseLinuxConnector',
'os_brick.initiator.connectors.iscsi.ISCSIConnector',
'os_brick.initiator.connectors.fibre_channel.FibreChannelConnector',
('os_brick.initiator.connectors.fibre_channel_s390x.'
'FibreChannelConnectorS390X'),
'os_brick.initiator.connectors.aoe.AoEConnector',
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
'os_brick.initiator.connectors.rbd.RBDConnector',
'os_brick.initiator.connectors.local.LocalConnector',
'os_brick.initiator.connectors.drbd.DRBDConnector',
'os_brick.initiator.connectors.huawei.HuaweiStorHyperConnector',
'os_brick.initiator.connectors.hgst.HGSTConnector',
'os_brick.initiator.connectors.scaleio.ScaleIOConnector',
'os_brick.initiator.connectors.disco.DISCOConnector',
'os_brick.initiator.windows.base.BaseWindowsConnector',
'os_brick.initiator.windows.iscsi.WindowsISCSIConnector',
]
# Mappings used to determine who to contruct in the factory
_connector_mapping_linux = {
initiator.AOE:
'os_brick.initiator.connectors.aoe.AoEConnector',
initiator.DRBD:
'os_brick.initiator.connectors.drbd.DRBDConnector',
initiator.GLUSTERFS:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.NFS:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.SCALITY:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.QUOBYTE:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.VZSTORAGE:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.ISCSI:
'os_brick.initiator.connectors.iscsi.ISCSIConnector',
initiator.ISER:
'os_brick.initiator.connectors.iscsi.ISCSIConnector',
initiator.FIBRE_CHANNEL:
'os_brick.initiator.connectors.fibre_channel.FibreChannelConnector',
initiator.LOCAL:
'os_brick.initiator.connectors.local.LocalConnector',
initiator.HUAWEISDSHYPERVISOR:
'os_brick.initiator.connectors.huawei.HuaweiStorHyperConnector',
initiator.HGST:
'os_brick.initiator.connectors.hgst.HGSTConnector',
initiator.RBD:
'os_brick.initiator.connectors.rbd.RBDConnector',
initiator.SCALEIO:
'os_brick.initiator.connectors.scaleio.ScaleIOConnector',
initiator.DISCO:
'os_brick.initiator.connectors.disco.DISCOConnector',
initiator.SHEEPDOG:
'os_brick.initiator.connectors.sheepdog.SheepdogConnector',
}
# Mapping for the S390X platform
_connector_mapping_linux_s390x = {
initiator.FIBRE_CHANNEL:
('os_brick.initiator.connectors.fibre_channel_s390x.'
'FibreChannelConnectorS390X'),
}
# Mapping for the windows connectors
_connector_mapping_windows = {
initiator.ISCSI:
'os_brick.initiator.windows.iscsi.WindowsISCSIConnector',
}
@utils.trace
def get_connector_properties(root_helper, my_ip, multipath, enforce_multipath,
host=None, execute=None):
"""Get the connection properties for all protocols.
When the connector wants to use multipath, multipath=True should be
specified. If enforce_multipath=True is specified too, an exception is
thrown when multipathd is not running. Otherwise, it falls back to
multipath=False and only the first path shown up is used.
For the compatibility reason, even if multipath=False is specified,
some cinder storage drivers may export the target for multipath, which
can be found via sendtargets discovery.
:param root_helper: The command prefix for executing as root.
:type root_helper: str
:param my_ip: The IP address of the local host.
:type my_ip: str
:param multipath: Enable multipath?
:type multipath: bool
:param enforce_multipath: Should we enforce that the multipath daemon is
running? If the daemon isn't running then the
return dict will have multipath as False.
:type enforce_multipath: bool
:param host: hostname.
:param execute: execute helper.
:returns: dict containing all of the collected initiator values.
"""
props = {}
props['platform'] = platform.machine()
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)):
props = utils.merge_dict(props,
connector.get_connector_properties(
root_helper,
host=host,
multipath=multipath,
enforce_multipath=enforce_multipath,
execute=execute))
return props
# TODO(walter-boring) We have to keep this class defined here
# so we don't break backwards compatibility
class InitiatorConnector(object):
@staticmethod
def factory(protocol, root_helper, driver=None,
use_multipath=False,
device_scan_attempts=initiator.DEVICE_SCAN_ATTEMPTS_DEFAULT,
arch=None,
*args, **kwargs):
"""Build a Connector object based upon protocol and architecture."""
# We do this instead of assigning it in the definition
# to help mocking for unit tests
if arch is None:
arch = platform.machine()
# Set the correct mapping for imports
if sys.platform == 'win32':
_mapping = _connector_mapping_windows
elif arch in (initiator.S390, initiator.S390X):
_mapping = _connector_mapping_linux_s390x
else:
_mapping = _connector_mapping_linux
LOG.debug("Factory for %(protocol)s on %(arch)s",
{'protocol': protocol, 'arch': arch})
protocol = protocol.upper()
# set any special kwargs needed by connectors
if protocol in (initiator.NFS, initiator.GLUSTERFS,
initiator.SCALITY, initiator.QUOBYTE,
initiator.VZSTORAGE):
kwargs.update({'mount_type': protocol.lower()})
elif protocol == initiator.ISER:
kwargs.update({'transport': 'iser'})
# now set all the default kwargs
kwargs.update(
{'root_helper': root_helper,
'driver': driver,
'use_multipath': use_multipath,
'device_scan_attempts': device_scan_attempts,
})
connector = _mapping.get(protocol)
if not connector:
msg = (_("Invalid InitiatorConnector protocol "
"specified %(protocol)s") %
dict(protocol=protocol))
raise ValueError(msg)
conn_cls = importutils.import_class(connector)
return conn_cls(*args, **kwargs)