Skip to content

Commit

Permalink
Add Windows iSCSI connector
Browse files Browse the repository at this point in the history
This patch adds a Windows iSCSI connector while the following
changes will add SMBFS and Fibre Channel connectors as well.

os-win is added as a requirement, as well as ddt. Note that
both are in the global requirements list. os-win is under OpenStack
governance and already being used by multiple OpenStack projects
such as Nova and Cinder.

The patch using Windows os-brick connectors in the Hyper-V
Nova driver: https://review.openstack.org/#/c/273504/

Change-Id: I19dfc8dd2e9e8a1b17675b55c63de903804480e4
Partial-Implements: blueprint os-brick-windows-support
  • Loading branch information
petrutlucian94 authored and claudiubelu committed Jun 22, 2016
1 parent f359ecc commit 585445e
Show file tree
Hide file tree
Showing 13 changed files with 830 additions and 23 deletions.
63 changes: 40 additions & 23 deletions os_brick/initiator/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from os_brick.initiator import linuxrbd
from os_brick.initiator import linuxscsi
from os_brick.initiator import linuxsheepdog
from os_brick.initiator import windows as windows_connector
from os_brick.remotefs import remotefs
from os_brick.i18n import _, _LE, _LI, _LW

Expand All @@ -68,6 +69,7 @@
PLATFORM_S390 = 'S390'
OS_TYPE_ALL = 'ALL'
OS_TYPE_LINUX = 'LINUX'
OS_TYPE_WINDOWS = 'WIN'

S390X = "s390x"
S390 = "s390"
Expand Down Expand Up @@ -104,6 +106,8 @@
'os_brick.initiator.connector.HGSTConnector',
'os_brick.initiator.connector.ScaleIOConnector',
'os_brick.initiator.connector.DISCOConnector',
'os_brick.initiator.windows.base.BaseWindowsConnector',
'os_brick.initiator.windows.iscsi.WindowsISCSIConnector',
]


Expand Down Expand Up @@ -189,6 +193,13 @@ def factory(protocol, root_helper, driver=None,
*args, **kwargs):
"""Build a Connector object based upon protocol and architecture."""

if sys.platform == 'win32':
return windows_connector.factory(
protocol,
use_multipath=use_multipath,
device_scan_attempts=device_scan_attempts,
*args, **kwargs)

# We do this instead of assigning it in the definition
# to help mocking for unit tests
if arch is None:
Expand Down Expand Up @@ -549,7 +560,35 @@ def get_all_available_volumes(self, connection_properties=None):
'/dev/disk/by-path/fake-volume-X']


class ISCSIConnector(BaseLinuxConnector):
class BaseISCSIConnector(InitiatorConnector):
def _iterate_all_targets(self, connection_properties):
for portal, iqn, lun in self._get_all_targets(connection_properties):
props = copy.deepcopy(connection_properties)
props['target_portal'] = portal
props['target_iqn'] = iqn
props['target_lun'] = lun
for key in ('target_portals', 'target_iqns', 'target_luns'):
props.pop(key, None)
yield props

def _get_all_targets(self, connection_properties):
if all([key in connection_properties for key in ('target_portals',
'target_iqns',
'target_luns')]):
return zip(connection_properties['target_portals'],
connection_properties['target_iqns'],
connection_properties['target_luns'])

return [(connection_properties['target_portal'],
connection_properties['target_iqn'],
connection_properties.get('target_lun', 0))]


class FakeBaseISCSIConnector(FakeConnector, BaseISCSIConnector):
pass


class ISCSIConnector(BaseLinuxConnector, BaseISCSIConnector):
"""Connector class to attach/detach iSCSI volumes."""
supported_transports = ['be2iscsi', 'bnx2i', 'cxgb3i', 'default',
'cxgb4i', 'qla4xxx', 'ocs', 'iser']
Expand Down Expand Up @@ -795,28 +834,6 @@ def _validate_iface_transport(self, transport_iface):
def _get_transport(self):
return self.transport

def _iterate_all_targets(self, connection_properties):
for portal, iqn, lun in self._get_all_targets(connection_properties):
props = copy.deepcopy(connection_properties)
props['target_portal'] = portal
props['target_iqn'] = iqn
props['target_lun'] = lun
for key in ('target_portals', 'target_iqns', 'target_luns'):
props.pop(key, None)
yield props

def _get_all_targets(self, connection_properties):
if all([key in connection_properties for key in ('target_portals',
'target_iqns',
'target_luns')]):
return zip(connection_properties['target_portals'],
connection_properties['target_iqns'],
connection_properties['target_luns'])

return [(connection_properties['target_portal'],
connection_properties['target_iqn'],
connection_properties.get('target_lun', 0))]

def _discover_iscsi_portals(self, connection_properties):
if all([key in connection_properties for key in ('target_portals',
'target_iqns')]):
Expand Down
43 changes: 43 additions & 0 deletions os_brick/initiator/windows/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright 2016 Cloudbase Solutions Srl
# 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.

from oslo_log import log as logging
from oslo_utils import importutils

from os_brick.i18n import _

LOG = logging.getLogger(__name__)


# TODO(lpetrut): once we move the protocol name constants to a
# separate module, use that instead.
_connector_dict = {
'ISCSI':
'os_brick.initiator.windows.iscsi.WindowsISCSIConnector',
}


def factory(protocol, *args, **kwargs):
LOG.debug("Retrieving connector for protocol: %s.", protocol)

connector = _connector_dict.get(protocol.upper())
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)
108 changes: 108 additions & 0 deletions os_brick/initiator/windows/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Copyright 2016 Cloudbase Solutions Srl
# 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.

from os_win import utilsfactory
from oslo_log import log as logging

from os_brick import exception
from os_brick.i18n import _, _LE
from os_brick.initiator import connector

LOG = logging.getLogger(__name__)


class BaseWindowsConnector(connector.InitiatorConnector):
platform = connector.PLATFORM_ALL
os_type = connector.OS_TYPE_WINDOWS

def __init__(self, root_helper=None, *args, **kwargs):
super(BaseWindowsConnector, self).__init__(root_helper,
*args, **kwargs)
self._diskutils = utilsfactory.get_diskutils()

@staticmethod
def check_multipath_support(enforce_multipath):
hostutils = utilsfactory.get_hostutils()
mpio_enabled = hostutils.check_server_feature(
hostutils.FEATURE_MPIO)
if not mpio_enabled:
err_msg = _LE(
"Using multipath connections for iSCSI and FC disks "
"requires the Multipath IO Windows feature to be "
"enabled. MPIO must be configured to claim such devices.")
LOG.error(err_msg)
if enforce_multipath:
raise exception.BrickException(err_msg)
return False
return True

@staticmethod
def get_connector_properties(*args, **kwargs):
multipath = kwargs['multipath']
enforce_multipath = kwargs['enforce_multipath']

props = {}
props['multipath'] = (
multipath and
BaseWindowsConnector.check_multipath_support(enforce_multipath))
return props

def _get_scsi_wwn(self, device_number):
# NOTE(lpetrut): The Linux connectors use scsi_id to retrieve the
# disk unique id, which prepends the identifier type to the unique id
# retrieved from the page 83 SCSI inquiry data. We'll do the same
# to remain consistent.
disk_uid, uid_type = self._diskutils.get_disk_uid_and_uid_type(
device_number)
scsi_wwn = '%s%s' % (uid_type, disk_uid)
return scsi_wwn

def check_valid_device(self, path, *args, **kwargs):
try:
with open(path, 'r') as dev:
dev.read(1)
except IOError:
LOG.exception(
_LE("Failed to access the device on the path "
"%(path)s"), {"path": path})
return False
return True

def get_all_available_volumes(self):
# TODO(lpetrut): query for disks based on the protocol used.
return []

def _check_device_paths(self, device_paths):
if len(device_paths) > 1:
err_msg = _("Multiple volume paths were found: %s. This can "
"occur if multipath is used and MPIO is not "
"properly configured, thus not claiming the device "
"paths. This issue must be addressed urgently as "
"it can lead to data corruption.")
raise exception.BrickException(err_msg % device_paths)

def extend_volume(self, connection_properties):
volume_paths = self.get_volume_paths(connection_properties)
if not volume_paths:
err_msg = _("Could not find the disk. Extend failed.")
raise exception.NotFound(err_msg)

device_path = volume_paths[0]
device_number = self._diskutils.get_device_number_from_device_name(
device_path)
self._diskutils.refresh_disk(device_number)

def get_search_path(self):
return None
Loading

0 comments on commit 585445e

Please sign in to comment.