23294485 Cinder ZFSFCDriver fails to find the right target wwn
23072471 nova driver need to accept multiple target_wwn entries in connection_info
--- a/components/openstack/cinder/files/solaris/solarisfc.py Wed May 25 13:56:46 2016 -0700
+++ b/components/openstack/cinder/files/solaris/solarisfc.py Wed May 25 14:31:44 2016 -0700
@@ -1,6 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
-# Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+# 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
@@ -109,23 +109,45 @@
for wwpn in wwpns:
self.execute('/usr/sbin/fcadm', 'force-lip', wwpn)
- def _get_device_path(self, wwn):
- """Get the Device Name of the WWN"""
+ 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_dev = 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_dev = line.split()[-1]
+ host_device = line.split()[-1]
if line.startswith("Remote Port WWN:"):
remote_port = line.split()[-1]
- if remote_port == wwn:
- return host_dev
+ if line.startswith("LUN:"):
+ lun = line.split()[-1]
+ if remote_port.upper() == wwn and \
+ int(lun) == int(target_lun):
+ return host_device
return None
@@ -133,28 +155,30 @@
"""Attach the volume to instance_name.
connection_properties for Fibre Channel must include:
- target_portal - ip and optional port
- target_iqn - iSCSI Qualified Name
+ target_wwn - Specified port WWNs
target_lun - LUN id of the volume
"""
device_info = {'type': 'block'}
target_wwn = connection_properties['target_wwn']
- # Check for multiple target_wwn values in a list
+ target_lun = connection_properties['target_lun']
+ wwns = []
if isinstance(target_wwn, list):
- wwn = target_wwn[0]
+ 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):
- LOG.debug("Looking for Fibre Channel device")
- host_dev = self._get_device_path(wwn)
+ # initiator needs time to refresh the LU list
+ time.sleep(i * 2)
+ host_device = self._get_device_path(wwns[0], target_lun)
- if host_dev is not None and os.path.exists(host_dev):
+ if host_device is not None and os.path.exists(host_device):
break
else:
self._refresh_connection()
- time.sleep(i ** 2)
else:
msg = _("Fibre Channel volume device not found.")
LOG.error(msg)
@@ -170,5 +194,5 @@
disk_name)
host_device = '%ss0' % tmp_dev_name[0]
- device_info['path'] = host_dev
+ device_info['path'] = host_device
return device_info
--- a/components/openstack/cinder/files/solaris/zfs.py Wed May 25 13:56:46 2016 -0700
+++ b/components/openstack/cinder/files/solaris/zfs.py Wed May 25 14:31:44 2016 -0700
@@ -965,14 +965,18 @@
return status
- def check_for_setup_error(self):
- """Check the setup error."""
- wwns = self._get_wwns()
- if not wwns:
+ def do_setup(self, context):
+ """Check wwns and setup the target group."""
+ self.wwns = self._get_wwns()
+ if not self.wwns:
msg = (_("Could not determine fibre channel world wide "
"node names."))
raise exception.VolumeBackendAPIException(data=msg)
+ self.tg = 'tg-wwn-%s' % self.wwns[0]
+ if not self._check_tg(self.tg):
+ self._setup_tg(self.tg)
+
def _get_wwns(self):
"""Get the FC port WWNs of the host."""
(out, _err) = self._execute('/usr/sbin/fcinfo', 'hba-port', '-t')
@@ -986,19 +990,45 @@
return wwns
- def _check_wwn_tg(self, wwn):
- """Check if the target group 'tg-wwn-xxx' exists."""
- (out, _err) = self._execute('/usr/sbin/stmfadm', 'list-tg')
+ def _get_target_wwns(self, tg):
+ """Get the target members in the tg."""
+ (out, _err) = self._execute('/usr/sbin/stmfadm', 'list-tg',
+ '-v', tg)
+ wwns = []
for line in [l.strip() for l in out.splitlines()]:
- if line.startswith("Target Group:") and wwn in line:
- tg = line.split()[-1]
- break
- else:
- LOG.debug(_("The target group 'tg-wwn-%s' doesn't exist.") % wwn)
- tg = None
+ if line.startswith("Member:"):
+ wwn = line.split()[-1]
+ target_wwn = wwn.split('.')[-1]
+ wwns.append(target_wwn)
+ return wwns
+
+ def _setup_tg(self, tg):
+ """Setup the target group."""
+ self._stmf_execute('/usr/sbin/stmfadm', 'create-tg', tg)
- return tg
+ # Add free target wwns into the target group
+ for wwn in self.wwns:
+ if not self._target_in_tg(wwn, None):
+ target_wwn = 'wwn.%s' % wwn
+ try:
+ self._stmf_execute('/usr/sbin/stmfadm', 'offline-target',
+ target_wwn)
+ self._stmf_execute('/usr/sbin/stmfadm', 'add-tg-member',
+ '-g', tg, target_wwn)
+ self._stmf_execute('/usr/sbin/stmfadm', 'online-target',
+ target_wwn)
+ assert self._check_target(wwn, 'Channel') == 'Online'
+
+ except:
+ LOG.error(_LE("Failed to add and online the target '%s'.")
+ % (target_wwn))
+
+ target_wwns = self._get_target_wwns(tg)
+ if not target_wwns:
+ msg = (_("No target members exist in the target group '%s'.")
+ % tg)
+ raise exception.VolumeBackendAPIException(data=msg)
def _only_lu(self, lu):
"""Check if the LU is the only one."""
@@ -1025,13 +1055,19 @@
'-v', tg)
else:
(out, _err) = self._execute('/usr/sbin/stmfadm', 'list-tg', '-v')
-
for line in [l.strip() for l in out.splitlines()]:
if line.startswith("Member:") and target in line:
return True
- LOG.debug(_("The target '%s' is not in any target group.") % target)
+ LOG.debug(_("The target '%s' is not in %s target group.") %
+ (target, tg if tg else 'any'))
return False
+ def _force_lip_wwn(self):
+ """Force the link to reinitialize."""
+ target_wwns = self._get_target_wwns(self.tg)
+ for target_wwn in target_wwns:
+ self._stmf_execute('/usr/sbin/fcadm', 'force-lip', target_wwn)
+
def create_export(self, context, volume):
"""Export the volume."""
# If the volume is already exported there is nothing to do, as we
@@ -1047,7 +1083,6 @@
raise exception.VolumeBackendAPIException(data=msg)
zvol = self._get_zvol_path(volume)
-
# Create a Logical Unit (LU)
self._stmf_execute('/usr/sbin/stmfadm', 'create-lu', zvol)
luid = self._get_luid(volume)
@@ -1056,55 +1091,28 @@
% volume['name'])
raise exception.VolumeBackendAPIException(data=msg)
- wwns = self._get_wwns()
- wwn = wwns[0]
- target_group = self._check_wwn_tg(wwn)
- if target_group is None:
- target_group = 'tg-wwn-%s' % wwn
- if self._target_in_tg(wwn, None):
- msg = (_("Target WWN '%s' has been found in another"
- "target group, so it will not be added "
- "into the expected target group '%s'.") %
- (wwn, target_group))
- raise exception.VolumeBackendAPIException(data=msg)
-
- # Create a target group for the wwn
- self._stmf_execute('/usr/sbin/stmfadm', 'create-tg', target_group)
-
- # Enable the target and add it to the 'tg-wwn-xxx' group
- self._stmf_execute('/usr/sbin/stmfadm', 'offline-target',
- 'wwn.%s' % wwn)
- self._stmf_execute('/usr/sbin/stmfadm', 'add-tg-member', '-g',
- target_group, 'wwn.%s' % wwn)
-
+ # setup the target group if it doesn't exist.
+ if not self._check_tg(self.tg):
+ self._setup_tg(self.tg)
# Add a logical unit view entry
- # TODO(Strony): replace the auto assigned LUN with '-n' option
- if luid is not None:
- self._stmf_execute('/usr/sbin/stmfadm', 'add-view', '-t',
- target_group, luid)
- self._stmf_execute('/usr/sbin/stmfadm', 'online-target',
- 'wwn.%s' % wwn)
- assert self._target_in_tg(wwn, target_group)
+ self._stmf_execute('/usr/sbin/stmfadm', 'add-view', '-t',
+ self.tg, luid)
+ self._force_lip_wwn()
def remove_export(self, context, volume):
"""Remove an export for a volume."""
luid = self._get_luid(volume)
if luid is not None:
- wwns = self._get_wwns()
- wwn = wwns[0]
- target_wwn = 'wwn.%s' % wwn
- target_group = 'tg-wwn-%s' % wwn
+ target_group = self.tg
view_lun = self._get_view_and_lun(luid)
if view_lun['view']:
self._stmf_execute('/usr/sbin/stmfadm', 'remove-view', '-l',
luid, view_lun['view'])
- # Remove the target group when only one LU exists.
+ # Remove the target group when the LU to be deleted is last one
+ # exposed by the target group.
if self._only_lu(luid):
- if self._check_target(target_wwn, 'Channel') == 'Online':
- self._stmf_execute('/usr/sbin/stmfadm', 'offline-target',
- target_wwn)
if self._check_tg(target_group):
self._stmf_execute('/usr/sbin/stmfadm', 'delete-tg',
target_group)
@@ -1120,12 +1128,6 @@
:target_lun: the lun assigned to the LU for the view entry
"""
- wwns = self._get_wwns()
- if not wwns:
- msg = (_("Could not determine fibre channel world wide "
- "node names."))
- raise exception.VolumeBackendAPIException(data=msg)
-
luid = self._get_luid(volume)
if not luid:
msg = (_("Failed to get logic unit for volume '%s'")
@@ -1135,7 +1137,7 @@
properties = {}
properties['target_discovered'] = True
- properties['target_wwn'] = wwns
+ properties['target_wwn'] = self._get_target_wwns(self.tg)
view_lun = self._get_view_and_lun(luid)
if view_lun['lun'] is not None:
properties['target_lun'] = view_lun['lun']
--- a/components/openstack/nova/files/nova.exec_attr Wed May 25 13:56:46 2016 -0700
+++ b/components/openstack/nova/files/nova.exec_attr Wed May 25 14:31:44 2016 -0700
@@ -10,6 +10,8 @@
nova-compute:solaris:cmd:RO::/usr/sbin/iscsiadm:euid=0
+nova-compute:solaris:cmd:RO::/usr/sbin/suriadm:privs=file_dac_read
+
nova-compute:solaris:cmd:RO::/usr/sbin/zfs:euid=0
nova-compute:solaris:cmd:RO::/usr/sbin/zlogin:uid=0
--- a/components/openstack/nova/files/solariszones/driver.py Wed May 25 13:56:46 2016 -0700
+++ b/components/openstack/nova/files/solariszones/driver.py Wed May 25 14:31:44 2016 -0700
@@ -1124,15 +1124,11 @@
elif driver_type == 'fibre_channel':
data = connection_info['data']
target_wwn = data['target_wwn']
- # Check for multiple target_wwn values in a list
- if isinstance(target_wwn, list):
- target_wwn = target_wwn[0]
# Ensure there's a fibre channel HBA.
hbas = self._get_fc_hbas()
if not hbas:
- LOG.error(_("Cannot attach Fibre Channel volume '%s' because "
- "no Fibre Channel HBA initiators were found")
- % (target_wwn))
+ LOG.error(_("Cannot attach Fibre Channel volume because "
+ "no Fibre Channel HBA initiators were found"))
raise exception.InvalidVolume(
reason="No host Fibre Channel initiator found")
@@ -1145,22 +1141,36 @@
utils.execute('/usr/sbin/fcinfo', 'remote-port',
'-p', wwpn)
- # Use suriadm(1M) to generate a Fibre Channel storage URI.
- try:
- out, err = utils.execute('/usr/sbin/suriadm', 'lookup-uri',
- '-p', 'target=naa.%s' % target_wwn,
- '-p', 'lun=%s' % target_lun)
- except processutils.ProcessExecutionError as ex:
- reason = ex.stderr
- LOG.error(_("Lookup failure of Fibre Channel volume '%s', lun "
- "%s: %s") % (target_wwn, target_lun, reason))
- raise
-
- lines = out.split('\n')
- # Use the long form SURI on the second output line.
- suri = lines[1].strip()
+ suri = self._lookup_fc_volume_suri(target_wwn, target_lun)
return suri
+ def _lookup_fc_volume_suri(self, target_wwn, target_lun):
+ """Searching the LU based URI for the FC LU. """
+ wwns = []
+ if isinstance(target_wwn, list):
+ wwns = target_wwn
+ else:
+ wwns.append(target_wwn)
+
+ for _none in range(3):
+ for wwn in wwns:
+ try:
+ out, err = utils.execute('/usr/sbin/suriadm', 'lookup-uri',
+ '-p', 'target=naa.%s' % wwn,
+ '-p', 'lun=%s' % target_lun)
+ for line in [l.strip() for l in out.splitlines()]:
+ if line.startswith("lu:luname.naa."):
+ return line
+ except processutils.ProcessExecutionError as ex:
+ reason = ex.stderr
+ LOG.debug(_("Failed to lookup-uri for volume '%s', lun "
+ "%s: %s") % (wwn, target_lun, reason))
+ greenthread.sleep(2)
+ else:
+ msg = _("Unable to lookup URI of Fibre Channel volume "
+ "with lun '%s'." % target_lun)
+ raise exception.InvalidVolume(reason=msg)
+
def _set_global_properties(self, name, extra_specs, brand):
"""Set Solaris Zone's global properties if supplied via flavor."""
zone = self._get_zone_by_name(name)