PSARC 2016/361 os-brick - managing local volume attaches in OpenStack Cinder
23538847 Python module os-brick should be added to Userland
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/os-brick/Makefile Wed Sep 07 14:48:24 2016 -0700
@@ -0,0 +1,63 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+#
+
+include ../../../make-rules/shared-macros.mk
+
+COMPONENT_NAME= os-brick
+COMPONENT_VERSION= 1.2.0
+COMPONENT_SRC= $(COMPONENT_NAME)-$(COMPONENT_VERSION)
+COMPONENT_ARCHIVE= $(COMPONENT_SRC).tar.gz
+COMPONENT_ARCHIVE_HASH= \
+ sha256:e8c5931d2a5115a7c484276add6be6b5e3ba13402c8a71e32b2313448010be30
+COMPONENT_ARCHIVE_URL= $(call pypi_url)
+COMPONENT_PROJECT_URL= http://docs.openstack.org/developer/os-brick/
+COMPONENT_BUGDB= python-mod/os-brick
+
+TPNO= 29698
+
+# Depends on oslo.serialization which is not Python 3 ready.
+PYTHON_VERSIONS = $(PYTHON2_VERSIONS)
+
+include $(WS_MAKE_RULES)/prep.mk
+include $(WS_MAKE_RULES)/setup.py.mk
+include $(WS_MAKE_RULES)/ips.mk
+
+ASLR_MODE = $(ASLR_NOT_APPLICABLE)
+
+COMPONENT_POST_INSTALL_ACTION += \
+ $(CP) \
+ files/solaris/solarisfc.py \
+ files/solaris/solarisiscsi.py \
+ $(PROTO_DIR)$(PYTHON_LIB)/os_brick/initiator; \
+ $(PYTHON) -m compileall $(PROTO_DIR)/$(PYTHON_VENDOR_PACKAGES)
+
+# common targets
+build: $(BUILD_NO_ARCH)
+
+install: $(INSTALL_NO_ARCH)
+
+# See $(COMPONENT_SRC)/test-requirements.txt for the Python modules
+# required to execute unittests.
+test: $(NO_TESTS)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/os-brick/files/solaris/solarisfc.py Wed Sep 07 14:48:24 2016 -0700
@@ -0,0 +1,198 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2015, 2016, Oracle and/or its affiliates. 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.
+
+"""Generic Solaris Fibre Channel utilities."""
+
+import os
+import platform
+import time
+
+from oslo_concurrency import processutils as putils
+from oslo_log import log as logging
+
+from os_brick import exception
+import oslo_i18n
+
+LOG = logging.getLogger(__name__)
+
+
+class SolarisFibreChannel(object):
+ def __init__(self, *args, **kwargs):
+ self.execute = putils.execute
+
+ def _get_fc_hbas(self):
+ """Get Fibre Channel HBA information."""
+ out = None
+ try:
+ out, err = self.execute('/usr/sbin/fcinfo', 'hba-port')
+ except putils.ProcessExecutionError as err:
+ return None
+
+ if out is None:
+ LOG.info(_("Cannot find any Fibre Channel HBAs"))
+ return None
+
+ hbas = []
+ hba = {}
+ for line in out.splitlines():
+ line = line.strip()
+ # Collect the following hba-port data:
+ # 1: Port WWN
+ # 2: State (online|offline)
+ # 3: Node WWN
+ if line.startswith("HBA Port WWN:"):
+ # New HBA port entry
+ hba = {}
+ wwpn = line.split()[-1]
+ hba['port_name'] = wwpn
+ continue
+ elif line.startswith("Port Mode:"):
+ mode = line.split()[-1]
+ # Skip Target mode ports
+ if mode != 'Initiator':
+ break
+ elif line.startswith("State:"):
+ state = line.split()[-1]
+ hba['port_state'] = state
+ continue
+ elif line.startswith("Node WWN:"):
+ wwnn = line.split()[-1]
+ hba['node_name'] = wwnn
+ continue
+ if len(hba) == 3:
+ hbas.append(hba)
+ hba = {}
+ return hbas
+
+ def get_fc_wwnns(self):
+ """Get Fibre Channel WWNNs from the system, if any."""
+ hbas = self._get_fc_hbas()
+ if hbas is None:
+ return None
+
+ wwnns = []
+ for hba in hbas:
+ if hba['port_state'] == 'online':
+ wwnn = hba['node_name']
+ wwnns.append(wwnn)
+ return wwnns
+
+ def get_fc_wwpns(self):
+ """Get Fibre Channel WWPNs from the system, if any."""
+ hbas = self._get_fc_hbas()
+ if hbas is None:
+ return None
+
+ wwpns = []
+ for hba in hbas:
+ if hba['port_state'] == 'online':
+ wwpn = hba['port_name']
+ wwpns.append(wwpn)
+ return wwpns
+
+ def _refresh_connection(self):
+ """Force the link reinitialization to make the LUN present."""
+ wwpns = self.get_fc_wwpns()
+ for wwpn in wwpns:
+ self.execute('/usr/sbin/fcadm', 'force-lip', wwpn)
+
+ def _get_device_path(self, wwn, target_lun):
+ """Get the Device path for the specified LUN.
+
+ The output of CMD below is like this:
+
+ OS Device Name: /dev/rdsk/c0t600C0FF0000000000036223AE73EB705d0s2
+ HBA Port WWN: 210100e08b27a8a1
+ Remote Port WWN: 256000c0ffc03622
+ LUN: 0
+ Remote Port WWN: 216000c0ff803622
+ LUN: 0
+ HBA Port WWN: 210000e08b07a8a1
+ Remote Port WWN: 256000c0ffc03622
+ LUN: 0
+ Remote Port WWN: 216000c0ff803622
+ LUN: 0
+ Vendor: SUN
+ Product: StorEdge 3510
+ Device Type: Disk device
+ ......
+ """
+ try:
+ out, err = self.execute('/usr/sbin/fcinfo', 'logical-unit', '-v')
+ except putils.ProcessExecutionError as err:
+ return None
+
+ host_device = None
+ remote_port = None
+ if out is not None:
+ for line in [l.strip() for l in out.splitlines()]:
+ if line.startswith("OS Device Name:"):
+ host_device = line.split()[-1]
+ if line.startswith("Remote Port WWN:"):
+ remote_port = line.split()[-1]
+ if line.startswith("LUN:"):
+ lun = line.split()[-1]
+ if remote_port.upper() == wwn and \
+ int(lun) == int(target_lun):
+ return host_device
+
+ return None
+
+ def connect_volume(self, connection_properties, scan_tries):
+ """Attach the volume to instance_name.
+
+ connection_properties for Fibre Channel must include:
+ target_wwn - Specified port WWNs
+ target_lun - LUN id of the volume
+ """
+ device_info = {'type': 'block'}
+ target_wwn = connection_properties['target_wwn']
+ target_lun = connection_properties['target_lun']
+ wwns = []
+ if isinstance(target_wwn, list):
+ wwns = target_wwn
+ else:
+ wwns.append(target_wwn)
+
+ # The scsi_vhci disk node is not always present immediately.
+ # Sometimes we need to reinitialize the connection to trigger
+ # a refresh.
+ for i in range(1, scan_tries):
+ # initiator needs time to refresh the LU list
+ time.sleep(i * 2)
+ host_device = self._get_device_path(wwns[0], target_lun)
+
+ if host_device is not None and os.path.exists(host_device):
+ break
+ else:
+ self._refresh_connection()
+ else:
+ msg = _("Fibre Channel volume device not found.")
+ LOG.error(msg)
+ raise exception.NoFibreChannelVolumeDeviceFound()
+
+ # Set the label EFI to the disk on SPARC before it is accessed and
+ # make sure the correct device path with slice 0
+ # (like '/dev/rdsk/c0t600xxxd0s0').
+ if platform.processor() == 'sparc':
+ tmp_dev_name = host_device.rsplit('s', 1)
+ disk_name = tmp_dev_name[0].split('/')[-1]
+ (out, _err) = self.execute('/usr/sbin/format', '-L', 'efi', '-d',
+ disk_name)
+ host_device = '%ss0' % tmp_dev_name[0]
+
+ device_info['path'] = host_device
+ return device_info
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/os-brick/files/solaris/solarisiscsi.py Wed Sep 07 14:48:24 2016 -0700
@@ -0,0 +1,151 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright (c) 2015, Oracle and/or its affiliates. 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.
+
+"""Generic Solaris iSCSI utilities."""
+
+import os
+import platform
+import time
+
+from oslo_concurrency import processutils as putils
+from oslo_log import log as logging
+
+from os_brick import exception
+import oslo_i18n
+
+LOG = logging.getLogger(__name__)
+
+
+class SolarisiSCSI(object):
+ def __init__(self, *args, **kwargs):
+ self.execute = putils.execute
+
+ def _get_device_path(self, connection_properties):
+ """Get the device path from the target info.
+
+ The output of cmd below is like this:
+ Target: iqn.2010-10.org.openstack:hostname1-tgt-grp-target
+ Alias: -
+ TPGT: 1
+ ISID: 4000002a0000
+ Connections: 1
+ LUN: 1
+ Vendor: SUN
+ Product: COMSTAR
+ OS Device Name: /dev/rdsk/c0t600144F0FDFAD05D0000563C04030003d0s2
+ LUN: 0
+ Vendor: SUN
+ Product: COMSTAR
+ OS Device Name: /dev/rdsk/c0t600144F0FDFAD05D0000563C02270002d0s2
+
+ """
+ (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list',
+ 'target', '-S',
+ connection_properties['target_iqn'])
+
+ found = False
+ for line in [l.strip() for l in out.splitlines()]:
+ if line.startswith("LUN:"):
+ lun = line.split()[-1]
+ if int(lun) == int(connection_properties['target_lun']):
+ found = True
+ continue
+ if found:
+ if line.startswith("OS Device Name:"):
+ dev_path = line.split()[-1]
+ return dev_path
+ elif line.startswith("LUN:"):
+ found = False
+
+ if not found:
+ LOG.error(_("No device is found for the target %s LUN %s.") %
+ (connection_properties['target_iqn'],
+ connection_properties['target_lun']))
+ raise
+
+ def get_initiator(self):
+ """Return the iSCSI initiator node name IQN"""
+ out, err = self.execute('/usr/sbin/iscsiadm', 'list', 'initiator-node')
+
+ # Sample first line of command output:
+ # Initiator node name: iqn.1986-03.com.sun:01:e00000000000.4f757217
+ initiator_name_line = out.splitlines()[0]
+ return initiator_name_line.rsplit(' ', 1)[1]
+
+ def _connect_to_iscsi_portal(self, connection_properties):
+ # TODO(Strony): handle the CHAP authentication
+ target_ip = connection_properties['target_portal'].split(":")[0]
+ self.execute('/usr/sbin/iscsiadm', 'add', 'discovery-address',
+ target_ip)
+ self.execute('/usr/sbin/iscsiadm', 'modify', 'discovery',
+ '--sendtargets', 'enable')
+ (out, _err) = self.execute('/usr/sbin/iscsiadm', 'list',
+ 'discovery-address', '-v',
+ target_ip)
+
+ lines = out.splitlines()
+ if not lines[0].strip().startswith('Discovery Address: ') or \
+ lines[1].strip().startswith('Unable to get targets.'):
+ msg = _("No iSCSI target is found.")
+ LOG.error(msg)
+ raise
+
+ target_iqn = connection_properties['target_iqn']
+ for line in [l.strip() for l in lines]:
+ if line.startswith("Target name:") and \
+ line.split()[-1] == target_iqn:
+ return
+ else:
+ LOG.error(_("No active session is found for the target %s.") %
+ target_iqn)
+ raise
+
+ def connect_volume(self, connection_properties, scan_tries):
+ """Attach the volume to instance_name.
+
+ connection_properties for iSCSI must include:
+ target_portal - ip and optional port
+ target_iqn - iSCSI Qualified Name
+ target_lun - LUN id of the volume
+ """
+ device_info = {'type': 'block'}
+
+ # TODO(Strony): support the iSCSI multipath on Solaris.
+ self._connect_to_iscsi_portal(connection_properties)
+
+ host_device = self._get_device_path(connection_properties)
+
+ # check if it is a valid device path.
+ for i in range(1, scan_tries):
+ if os.path.exists(host_device):
+ break
+ else:
+ time.sleep(i ** 2)
+ else:
+ raise exception.VolumeDeviceNotFound(device=host_device)
+
+ # Set the label EFI to the disk on SPARC before it is accessed and
+ # make sure the correct device path with slice 0
+ # (like '/dev/rdsk/c0t600xxxd0s0').
+ if platform.processor() == 'sparc':
+ tmp_dev_name = host_device.rsplit('s', 1)
+ disk_name = tmp_dev_name[0].split('/')[-1]
+ (out, _err) = self.execute('/usr/sbin/format', '-L', 'efi', '-d',
+ disk_name)
+ host_device = '%ss0' % tmp_dev_name[0]
+
+ device_info['path'] = host_device
+ return device_info
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/os-brick/os-brick-PYVER.p5m Wed Sep 07 14:48:24 2016 -0700
@@ -0,0 +1,107 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+
+#
+# Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
+#
+
+set name=pkg.fmri \
+ value=pkg:/library/python/os-brick-$(PYV)@$(IPS_COMPONENT_VERSION),$(BUILD_VERSION)
+set name=pkg.summary \
+ value="OpenStack Cinder brick library for managing local volume attaches"
+set name=pkg.description \
+ value="OpenStack Cinder brick library for managing local volume attaches, including discovery of volumes being attached to a host for many transport protocols and removal of volumes from a host."
+set name=com.oracle.info.description value="the Python os-brick module"
+set name=com.oracle.info.tpno value=$(TPNO)
+set name=info.classification \
+ value=org.opensolaris.category.2008:Development/Python \
+ value="org.opensolaris.category.2008:System/Administration and Configuration" \
+ value="org.opensolaris.category.2008:System/Enterprise Management"
+set name=info.source-url value=$(COMPONENT_ARCHIVE_URL)
+set name=info.upstream value="OpenStack <[email protected]>"
+set name=info.upstream-url value=$(COMPONENT_PROJECT_URL)
+set name=org.opensolaris.arc-caseid value=PSARC/2016/361
+set name=org.opensolaris.consolidation value=$(CONSOLIDATION)
+#
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick-$(COMPONENT_VERSION)-py$(PYVER).egg-info/PKG-INFO
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick-$(COMPONENT_VERSION)-py$(PYVER).egg-info/SOURCES.txt
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick-$(COMPONENT_VERSION)-py$(PYVER).egg-info/dependency_links.txt
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick-$(COMPONENT_VERSION)-py$(PYVER).egg-info/not-zip-safe
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick-$(COMPONENT_VERSION)-py$(PYVER).egg-info/pbr.json
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick-$(COMPONENT_VERSION)-py$(PYVER).egg-info/requires.txt
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick-$(COMPONENT_VERSION)-py$(PYVER).egg-info/top_level.txt
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/__init__.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/exception.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/executor.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/i18n.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/__init__.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/connector.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/host_driver.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/linuxfc.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/linuxrbd.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/linuxscsi.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/solarisfc.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/initiator/solarisiscsi.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/local_dev/__init__.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/local_dev/lvm.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/remotefs/__init__.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/remotefs/remotefs.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/utils.py
+file path=usr/lib/python$(PYVER)/vendor-packages/os_brick/version.py
+#
+license LICENSE license="Apache v2.0"
+
+# force a dependency on the Python runtime
+depend type=require fmri=__TBD pkg.debug.depend.file=python$(PYVER) \
+ pkg.debug.depend.path=usr/bin
+
+# force a dependency on the os-brick package
+depend type=require \
+ fmri=library/python/os-brick@$(IPS_COMPONENT_VERSION),$(BUILD_VERSION)
+
+# force a dependency on oslo.concurrency; pkgdepend work is needed to flush this
+# out.
+depend type=require fmri=library/python/oslo.concurrency-$(PYV)
+
+# force a dependency on oslo.i18n; pkgdepend work is needed to flush this out.
+depend type=require fmri=library/python/oslo.i18n-$(PYV)
+
+# force a dependency on oslo.log; pkgdepend work is needed to flush this out.
+depend type=require fmri=library/python/oslo.log-$(PYV)
+
+# force a dependency on oslo.service; pkgdepend work is needed to flush this
+# out.
+depend type=require fmri=library/python/oslo.service-$(PYV)
+
+# force a dependency on oslo.utils; pkgdepend work is needed to flush this out.
+depend type=require fmri=library/python/oslo.utils-$(PYV)
+
+# force a dependency on pbr; pkgdepend work is needed to flush this out.
+depend type=require fmri=library/python/pbr-$(PYV)
+
+# force a dependency on requests; pkgdepend work is needed to flush this out.
+depend type=require fmri=library/python/requests-$(PYV)
+
+# force a dependency on retrying; pkgdepend work is needed to flush this out.
+depend type=require fmri=library/python/retrying-$(PYV)
+
+# force a dependency on six; pkgdepend work is needed to flush this out.
+depend type=require fmri=library/python/six-$(PYV)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/os-brick/patches/01-volume-backup.patch Wed Sep 07 14:48:24 2016 -0700
@@ -0,0 +1,139 @@
+This patch is to replace Linux-specific code with conditional checks in
+the Cinder Brick code to support Cinder backup on Solaris. Patch has
+not yet been submitted upstream.
+
+--- os-brick-1.2.0/os_brick/initiator/connector.py.~1~ 2016-03-28 09:30:49.000000000 +0000
++++ os-brick-1.2.0/os_brick/initiator/connector.py 2016-09-06 16:59:42.732438933 +0000
+@@ -54,6 +54,8 @@ from os_brick.initiator import linuxfc
+ from os_brick.initiator import linuxrbd
+ from os_brick.initiator import linuxscsi
+ from os_brick.remotefs import remotefs
++from os_brick.initiator import solarisfc
++from os_brick.initiator import solarisiscsi
+ from os_brick.i18n import _, _LE, _LI, _LW
+
+ LOG = logging.getLogger(__name__)
+@@ -122,7 +124,10 @@ def get_connector_properties(root_helper
+ """
+
+ iscsi = ISCSIConnector(root_helper=root_helper)
+- fc = linuxfc.LinuxFibreChannel(root_helper=root_helper)
++ if sys.platform == 'sunos5':
++ fc = solarisfc.SolarisFibreChannel()
++ else:
++ fc = linuxfc.LinuxFibreChannel(root_helper=root_helper)
+
+ props = {}
+ props['ip'] = my_ip
+@@ -284,8 +289,11 @@ class InitiatorConnector(executor.Execut
+ '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)
++ if sys.platform == 'sunos5':
++ out, info = self._execute(*cmd)
++ else:
++ 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."),
+@@ -502,7 +510,10 @@ class ISCSIConnector(InitiatorConnector)
+ execute=putils.execute, use_multipath=False,
+ device_scan_attempts=DEVICE_SCAN_ATTEMPTS_DEFAULT,
+ transport='default', *args, **kwargs):
+- self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute)
++ if sys.platform == 'sunos5':
++ self._solarisiscsi = solarisiscsi.SolarisiSCSI()
++ else:
++ self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute)
+ super(ISCSIConnector, self).__init__(
+ root_helper, driver=driver,
+ execute=execute,
+@@ -678,6 +689,8 @@ class ISCSIConnector(InitiatorConnector)
+
+ def set_execute(self, execute):
+ super(ISCSIConnector, self).set_execute(execute)
++ if sys.platform == 'sunos5':
++ return
+ self._linuxscsi.set_execute(execute)
+
+ def _validate_iface_transport(self, transport_iface):
+@@ -840,6 +853,9 @@ class ISCSIConnector(InitiatorConnector)
+ Note that plural keys may be used when use_multipath=True
+ """
+
++ if sys.platform == 'sunos5':
++ return self._solarisiscsi.connect_volume(connection_properties,
++ self.device_scan_attempts)
+ device_info = {'type': 'block'}
+
+ host_devices, target_props = self._get_potential_volume_paths(
+@@ -912,6 +928,9 @@ class ISCSIConnector(InitiatorConnector)
+ target_iqn(s) - iSCSI Qualified Name
+ target_lun(s) - LUN id of the volume
+ """
++ if sys.platform == 'sunos5':
++ return
++
+ if self.use_multipath:
+ self._rescan_multipath()
+ host_device = multipath_device = None
+@@ -1002,6 +1021,9 @@ class ISCSIConnector(InitiatorConnector)
+
+ def get_initiator(self):
+ """Secure helper to read file as root."""
++ if sys.platform == 'sunos5':
++ return self._solarisiscsi.get_initiator()
++
+ file_path = '/etc/iscsi/initiatorname.iscsi'
+ try:
+ lines, _err = self._execute('cat', file_path, run_as_root=True,
+@@ -1304,8 +1326,11 @@ class FibreChannelConnector(InitiatorCon
+ execute=putils.execute, 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)
++ if sys.platform == 'sunos5':
++ self._solarisfc = solarisfc.SolarisFibreChannel()
++ else:
++ self._linuxscsi = linuxscsi.LinuxSCSI(root_helper, execute)
++ self._linuxfc = linuxfc.LinuxFibreChannel(root_helper, execute)
+ super(FibreChannelConnector, self).__init__(
+ root_helper, driver=driver,
+ execute=execute,
+@@ -1315,6 +1340,8 @@ class FibreChannelConnector(InitiatorCon
+
+ def set_execute(self, execute):
+ super(FibreChannelConnector, self).set_execute(execute)
++ if sys.platform == 'sunos5':
++ return
+ self._linuxscsi.set_execute(execute)
+ self._linuxfc.set_execute(execute)
+
+@@ -1373,6 +1400,10 @@ class FibreChannelConnector(InitiatorCon
+ target_wwn - World Wide Name
+ target_lun - LUN id of the volume
+ """
++ if sys.platform == 'sunos5':
++ return self._solarisfc.connect_volume(connection_properties,
++ self.device_scan_attempts)
++
+ LOG.debug("execute = %s", self._execute)
+ device_info = {'type': 'block'}
+
+@@ -1505,6 +1536,12 @@ class FibreChannelConnector(InitiatorCon
+ target_wwn - World Wide Name
+ target_lun - LUN id of the volume
+ """
++ if sys.platform == 'sunos5':
++ # There is some latency before the next time connection happens.
++ # The best practice is to offline the state of the switch now
++ # and online it at the next connection.
++ # But now, we just return without any operation.
++ return
+
+ devices = []
+ volume_paths = self.get_volume_paths(connection_properties)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/components/python/os-brick/patches/02-remotefs.patch Wed Sep 07 14:48:24 2016 -0700
@@ -0,0 +1,78 @@
+In-house patch to adapt Linux specific commands and command output
+parsing to Solaris. Patch may be suitable for pushing upsteam.
+
+--- os-brick-1.2.0/os_brick/remotefs/remotefs.py.~1~ 2016-03-28 09:30:49.000000000 +0000
++++ os-brick-1.2.0/os_brick/remotefs/remotefs.py 2016-09-06 17:01:03.004025741 +0000
+@@ -17,6 +17,7 @@
+
+ import hashlib
+ import os
++import platform
+ import re
+ import tempfile
+
+@@ -86,14 +87,21 @@ class RemoteFsClient(object):
+ self._get_hash_str(device_name))
+
+ def _read_mounts(self):
+- (out, _err) = self._execute('mount', check_exit_code=0)
++ if platform.system() == "SunOS":
++ (out, _err) = self._execute('/usr/sbin/mount', check_exit_code=0)
++ else:
++ (out, _err) = self._execute('mount', check_exit_code=0)
+ lines = out.split('\n')
+ mounts = {}
+ for line in lines:
+ tokens = line.split()
+ if 2 < len(tokens):
+- device = tokens[0]
+- mnt_point = tokens[2]
++ if platform.system() == "SunOS":
++ device = tokens[2]
++ mnt_point = tokens[0]
++ else:
++ device = tokens[0]
++ mnt_point = tokens[2]
+ mounts[mnt_point] = device
+ return mounts
+
+@@ -105,8 +113,12 @@ class RemoteFsClient(object):
+ LOG.info(_LI('Already mounted: %s'), mount_path)
+ return
+
+- self._execute('mkdir', '-p', mount_path, check_exit_code=0)
+- if self._mount_type == 'nfs':
++ if platform.system() == "SunOS":
++ self._execute('/usr/bin/mkdir', '-p', mount_path,
++ check_exit_code=0)
++ else:
++ self._execute('mkdir', '-p', mount_path, check_exit_code=0)
++ if self._mount_type == 'nfs' and platform.system() != "SunOS":
+ self._mount_nfs(share, mount_path, flags)
+ elif self._mount_type == 'vzstorage':
+ self._mount_vzstorage(share, mount_path, flags)
+@@ -117,15 +129,21 @@ class RemoteFsClient(object):
+ def _do_mount(self, mount_type, share, mount_path, mount_options=None,
+ flags=None):
+ """Mounts share based on the specified params."""
+- mnt_cmd = ['mount', '-t', mount_type]
++ if platform.system() == "SunOS":
++ mnt_cmd = ['/usr/sbin/mount', '-F', mount_type]
++ else:
++ mnt_cmd = ['mount', '-t', mount_type]
+ if mount_options is not None:
+ mnt_cmd.extend(['-o', mount_options])
+ if flags is not None:
+ mnt_cmd.extend(flags)
+ mnt_cmd.extend([share, mount_path])
+
+- self._execute(*mnt_cmd, root_helper=self.root_helper,
+- run_as_root=True, check_exit_code=0)
++ if platform.system() == "SunOS":
++ self._execute(*mnt_cmd, check_exit_code=0)
++ else:
++ self._execute(*mnt_cmd, root_helper=self.root_helper,
++ run_as_root=True, check_exit_code=0)
+
+ def _mount_nfs(self, nfs_share, mount_path, flags=None):
+ """Mount nfs share using present mount types."""