# HG changeset patch # User Laszlo Peter # Date 1473284904 25200 # Node ID f2bb9c5b1768ae03b90c5bf09ad79765756aed7b # Parent 59cfe036953ddea0074fe3232b23fd872590f065 PSARC 2016/361 os-brick - managing local volume attaches in OpenStack Cinder 23538847 Python module os-brick should be added to Userland diff -r 59cfe036953d -r f2bb9c5b1768 components/python/os-brick/Makefile --- /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) diff -r 59cfe036953d -r f2bb9c5b1768 components/python/os-brick/files/solaris/solarisfc.py --- /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 diff -r 59cfe036953d -r f2bb9c5b1768 components/python/os-brick/files/solaris/solarisiscsi.py --- /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 diff -r 59cfe036953d -r f2bb9c5b1768 components/python/os-brick/os-brick-PYVER.p5m --- /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 " +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) diff -r 59cfe036953d -r f2bb9c5b1768 components/python/os-brick/patches/01-volume-backup.patch --- /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) diff -r 59cfe036953d -r f2bb9c5b1768 components/python/os-brick/patches/02-remotefs.patch --- /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."""