os-brick/os_brick/initiator/connector.py

307 lines
12 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 socket
import sys
from oslo_concurrency import lockutils
from oslo_log import log as logging
from oslo_utils import importutils
from os_brick import exception
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-')
# 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.fibre_channel_ppc64.'
'FibreChannelConnectorPPC64'),
'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.gpfs.GPFSConnector',
'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.connectors.vmware.VmdkConnector',
'os_brick.initiator.windows.base.BaseWindowsConnector',
'os_brick.initiator.windows.iscsi.WindowsISCSIConnector',
'os_brick.initiator.windows.fibre_channel.WindowsFCConnector',
'os_brick.initiator.windows.smbfs.WindowsSMBFSConnector',
'os_brick.initiator.connectors.vrtshyperscale.HyperScaleConnector',
'os_brick.initiator.connectors.storpool.StorPoolConnector',
'os_brick.initiator.connectors.nvmeof.NVMeOFConnector',
]
# Mappings used to determine who to construct 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',
initiator.VMDK:
'os_brick.initiator.connectors.vmware.VmdkConnector',
initiator.GPFS:
'os_brick.initiator.connectors.gpfs.GPFSConnector',
initiator.VERITAS_HYPERSCALE:
'os_brick.initiator.connectors.vrtshyperscale.HyperScaleConnector',
initiator.STORPOOL:
'os_brick.initiator.connectors.storpool.StorPoolConnector',
# Leave this in for backwards compatibility
# This isn't an NVME connector, but NVME Over Fabrics
initiator.NVME:
'os_brick.initiator.connectors.nvmeof.NVMeOFConnector',
initiator.NVMEOF:
'os_brick.initiator.connectors.nvmeof.NVMeOFConnector',
}
# Mapping for the S390X platform
_connector_mapping_linux_s390x = {
initiator.FIBRE_CHANNEL:
'os_brick.initiator.connectors.fibre_channel_s390x.'
'FibreChannelConnectorS390X',
initiator.DRBD:
'os_brick.initiator.connectors.drbd.DRBDConnector',
initiator.NFS:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.ISCSI:
'os_brick.initiator.connectors.iscsi.ISCSIConnector',
initiator.LOCAL:
'os_brick.initiator.connectors.local.LocalConnector',
initiator.RBD:
'os_brick.initiator.connectors.rbd.RBDConnector',
initiator.GPFS:
'os_brick.initiator.connectors.gpfs.GPFSConnector',
}
# Mapping for the PPC64 platform
_connector_mapping_linux_ppc64 = {
initiator.FIBRE_CHANNEL:
('os_brick.initiator.connectors.fibre_channel_ppc64.'
'FibreChannelConnectorPPC64'),
initiator.DRBD:
'os_brick.initiator.connectors.drbd.DRBDConnector',
initiator.NFS:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.ISCSI:
'os_brick.initiator.connectors.iscsi.ISCSIConnector',
initiator.LOCAL:
'os_brick.initiator.connectors.local.LocalConnector',
initiator.RBD:
'os_brick.initiator.connectors.rbd.RBDConnector',
initiator.GPFS:
'os_brick.initiator.connectors.gpfs.GPFSConnector',
initiator.VZSTORAGE:
'os_brick.initiator.connectors.remotefs.RemoteFsConnector',
initiator.VERITAS_HYPERSCALE:
'os_brick.initiator.connectors.vrtshyperscale.HyperScaleConnector',
initiator.ISER:
'os_brick.initiator.connectors.iscsi.ISCSIConnector',
initiator.SHEEPDOG:
'os_brick.initiator.connectors.sheepdog.SheepdogConnector',
}
# Mapping for the windows connectors
_connector_mapping_windows = {
initiator.ISCSI:
'os_brick.initiator.windows.iscsi.WindowsISCSIConnector',
initiator.FIBRE_CHANNEL:
'os_brick.initiator.windows.fibre_channel.WindowsFCConnector',
initiator.SMBFS:
'os_brick.initiator.windows.smbfs.WindowsSMBFSConnector',
}
# Create aliases to the old names until 2.0.0
# TODO(smcginnis) Remove this lookup once unit test code is updated to
# point to the correct location
for item in connector_list:
_name = item.split('.')[-1]
globals()[_name] = importutils.import_class(item)
@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
def get_connector_mapping(arch=None):
"""Get connector mapping based on platform.
This is used by Nova to get the right connector information.
:param arch: The architecture being requested.
"""
# 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':
return _connector_mapping_windows
elif arch in (initiator.S390, initiator.S390X):
return _connector_mapping_linux_s390x
elif arch in (initiator.PPC64, initiator.PPC64LE):
return _connector_mapping_linux_ppc64
else:
return _connector_mapping_linux
# 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."""
_mapping = get_connector_mapping(arch)
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 exception.InvalidConnectorProtocol(msg)
conn_cls = importutils.import_class(connector)
return conn_cls(*args, **kwargs)