]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Separate FlashSystem FC and iSCSI common code
authorxiaolei hu <xlhu@cn.ibm.com>
Thu, 25 Jun 2015 02:22:50 +0000 (10:22 +0800)
committerEdwin Wang <edwin.wang@cn.ibm.com>
Mon, 6 Jul 2015 01:05:36 +0000 (01:05 +0000)
The patch is mainly to split original FC driver code into common
code and FC driver for IBM FlashSystem. iSCSI driver merged in L-1
has been inherited from original FC driver for common functions
already.

In this patch,
* Separate FC driver into new driver and common code.
* Modify iSCSI driver to inherite the common code.
* Common function _get_node_data in iSCSI driver is moved into
common code.
* Add locks in initialize_connection and terminate_connection
for iSCSI driver.
* Remove flashsystem_multipath_enabled to use Cinder iSCSI
multipath support.

Implements: blueprint ibm-flashsystem-split-fc
Change-Id: I166a14e3eef370a22f4c0a675d451a3a4a6989f1

cinder/tests/unit/test_ibm_flashsystem.py
cinder/volume/drivers/ibm/flashsystem_common.py [moved from cinder/volume/drivers/ibm/flashsystem.py with 73% similarity]
cinder/volume/drivers/ibm/flashsystem_fc.py [new file with mode: 0644]
cinder/volume/drivers/ibm/flashsystem_iscsi.py

index 3daf32b30435bb816270a9b4facdad724f069a7b..cdd9cec4c24716ff2b171f77f525088dbb93411d 100644 (file)
@@ -34,7 +34,7 @@ from cinder import exception
 from cinder import test
 from cinder import utils
 from cinder.volume import configuration as conf
-from cinder.volume.drivers.ibm import flashsystem
+from cinder.volume.drivers.ibm import flashsystem_fc
 from cinder.volume import utils as volume_utils
 from cinder.volume import volume_types
 
@@ -656,7 +656,7 @@ class FlashSystemManagementSimulator(object):
         self._next_cmd_error[cmd] = error
 
 
-class FlashSystemFakeDriver(flashsystem.FlashSystemDriver):
+class FlashSystemFakeDriver(flashsystem_fc.FlashSystemFCDriver):
     def __init__(self, *args, **kwargs):
         super(FlashSystemFakeDriver, self).__init__(*args, **kwargs)
 
@@ -893,7 +893,7 @@ class FlashSystemDriverTestCase(test.TestCase):
                           vol2, self.connector)
 
         # case 3: _get_vdisk_map_properties raises exception
-        with mock.patch.object(flashsystem.FlashSystemDriver,
+        with mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
                                '_get_vdisk_map_properties') as get_properties:
             get_properties.side_effect = exception.VolumeBackendAPIException
             self.assertRaises(exception.VolumeBackendAPIException,
@@ -903,7 +903,7 @@ class FlashSystemDriverTestCase(test.TestCase):
         # clear environment
         self.driver.delete_volume(vol1)
 
-    @mock.patch.object(flashsystem.FlashSystemDriver,
+    @mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
                        '_create_and_copy_vdisk_data')
     def test_flashsystem_create_snapshot(self, _create_and_copy_vdisk_data):
         # case 1: good path
@@ -923,7 +923,7 @@ class FlashSystemDriverTestCase(test.TestCase):
         self.assertRaises(exception.InvalidVolume,
                           self.driver.create_snapshot, snap2)
 
-    @mock.patch.object(flashsystem.FlashSystemDriver,
+    @mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
                        '_delete_vdisk')
     def test_flashsystem_delete_snapshot(self, _delete_vdisk):
         vol1 = self._generate_vol_info(None)
@@ -933,7 +933,7 @@ class FlashSystemDriverTestCase(test.TestCase):
                                          vol1['status'])
         self.driver.delete_snapshot(snap1)
 
-    @mock.patch.object(flashsystem.FlashSystemDriver,
+    @mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
                        '_create_and_copy_vdisk_data')
     def test_flashsystem_create_volume_from_snapshot(
             self, _create_and_copy_vdisk_data):
@@ -966,7 +966,7 @@ class FlashSystemDriverTestCase(test.TestCase):
                           self.driver.create_volume_from_snapshot,
                           vol, snap)
 
-    @mock.patch.object(flashsystem.FlashSystemDriver,
+    @mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
                        '_create_and_copy_vdisk_data')
     def test_flashsystem_create_cloned_volume(
             self, _create_and_copy_vdisk_data):
@@ -1002,7 +1002,7 @@ class FlashSystemDriverTestCase(test.TestCase):
         self.assertRaises(exception.VolumeBackendAPIException,
                           self.driver.get_volume_stats, refresh=True)
 
-    @mock.patch.object(flashsystem.FlashSystemDriver,
+    @mock.patch.object(flashsystem_fc.FlashSystemFCDriver,
                        '_copy_vdisk_data')
     def test_flashsystem_create_and_copy_vdisk_data(self, _copy_vdisk_data):
         # case 1: when volume does not exist
@@ -1033,8 +1033,8 @@ class FlashSystemDriverTestCase(test.TestCase):
         self.driver.delete_volume(vol2)
 
     @mock.patch.object(volume_utils, 'copy_volume')
-    @mock.patch.object(flashsystem.FlashSystemDriver, '_scan_device')
-    @mock.patch.object(flashsystem.FlashSystemDriver, '_remove_device')
+    @mock.patch.object(flashsystem_fc.FlashSystemFCDriver, '_scan_device')
+    @mock.patch.object(flashsystem_fc.FlashSystemFCDriver, '_remove_device')
     @mock.patch.object(utils, 'brick_get_connector_properties')
     def test_flashsystem_copy_vdisk_data(self,
                                          _connector,
similarity index 73%
rename from cinder/volume/drivers/ibm/flashsystem.py
rename to cinder/volume/drivers/ibm/flashsystem_common.py
index 593ceeae466ab6a25c3a75a6f9746d917d809664..9744dbbf3dd9ab823f8190098ce28367eb79957e 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2014 - 2015 IBM Corporation.
+# Copyright 2015 IBM Corporation.
 # All Rights Reserved.
 #
 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
@@ -19,14 +19,11 @@ Volume driver for IBM FlashSystem storage systems.
 
 Limitations:
 1. Cinder driver only works when open_access_enabled=off.
-2. Cinder driver only works when connection protocol is FC.
 
 """
 
-import random
 import re
 import string
-import threading
 
 from oslo_concurrency import processutils
 from oslo_config import cfg
@@ -44,7 +41,6 @@ from cinder import utils
 from cinder.volume.drivers.san import san
 from cinder.volume import utils as volume_utils
 from cinder.volume import volume_types
-from cinder.zonemanager import utils as fczm_utils
 
 LOG = logging.getLogger(__name__)
 
@@ -54,13 +50,12 @@ FLASHSYSTEM_VOL_IOGRP = 0
 flashsystem_opts = [
     cfg.StrOpt('flashsystem_connection_protocol',
                default='FC',
-               help='Connection protocol should be FC.'),
-    cfg.BoolOpt('flashsystem_multipath_enabled',
-                default=False,
-                help='Connect with multipath (FC only).'),
+               help='Connection protocol should be FC. '
+                    '(Default is FC.)'),
     cfg.BoolOpt('flashsystem_multihostmap_enabled',
                 default=True,
-                help='Allows vdisk to multi host mapping.')
+                help='Allows vdisk to multi host mapping. '
+                     '(Default is True)')
 ]
 
 CONF = cfg.CONF
@@ -68,17 +63,19 @@ CONF.register_opts(flashsystem_opts)
 
 
 class FlashSystemDriver(san.SanDriver):
-    """IBM FlashSystem 840 FC volume driver.
+    """IBM FlashSystem volume driver.
 
     Version history:
     1.0.0 - Initial driver
     1.0.1 - Code clean up
     1.0.2 - Add lock into vdisk map/unmap, connection
             initialize/terminate
+    1.0.3 - Initial driver for iSCSI
+    1.0.4 - Split Flashsystem driver into common and FC
 
     """
 
-    VERSION = "1.0.1"
+    VERSION = "1.0.4"
 
     def __init__(self, *args, **kwargs):
         super(FlashSystemDriver, self).__init__(*args, **kwargs)
@@ -134,16 +131,6 @@ class FlashSystemDriver(san.SanDriver):
                 map[idx].append(t_wwpn)
         return map
 
-    def _check_vdisk_params(self, params):
-        # Check that the requested protocol is enabled
-        if params['protocol'] != self._protocol:
-            msg = (_("Illegal value '%(prot)s' specified for "
-                     "flashsystem_connection_protocol: "
-                     "valid value(s) are %(enabled)s.")
-                   % {'prot': params['protocol'],
-                      'enabled': self._protocol})
-            raise exception.InvalidInput(reason=msg)
-
     def _connector_to_hostname_prefix(self, connector):
         """Translate connector info to storage system host name.
 
@@ -172,8 +159,8 @@ class FlashSystemDriver(san.SanDriver):
                 invalid_ch_in_host, '-' * len(invalid_ch_in_host))
             host_name = host_name.translate(string_host_name_filter)
         else:
-            msg = (_('_create_host: Can not clean host name. Host name '
-                     'is not unicode or string.'))
+            msg = _('_create_host: Can not translate host name. Host name '
+                    'is not unicode or string.')
             LOG.error(msg)
             raise exception.NoValidHost(reason=msg)
 
@@ -237,8 +224,7 @@ class FlashSystemDriver(san.SanDriver):
                 self.configuration.volume_dd_blocksize)
         except Exception:
             with excutils.save_and_reraise_exception():
-                LOG.error(_LE('_copy_vdisk_data: Failed to '
-                              'copy %(src)s to %(dest)s.'),
+                LOG.error(_LE('Failed to copy %(src)s to %(dest)s.'),
                           {'src': src_vdisk_name, 'dest': dest_vdisk_name})
         finally:
             if not dest_map:
@@ -275,50 +261,6 @@ class FlashSystemDriver(san.SanDriver):
             self._unset_vdisk_copy_in_progress(
                 [src_vdisk_name, dest_vdisk_name])
 
-    def _create_host(self, connector):
-        """Create a new host on the storage system.
-
-        We create a host and associate it with the given connection
-        information.
-
-        """
-
-        LOG.debug('enter: _create_host: host %s.', connector['host'])
-
-        rand_id = six.text_type(random.randint(0, 99999999)).zfill(8)
-        host_name = '%s-%s' % (self._connector_to_hostname_prefix(connector),
-                               rand_id)
-
-        ports = []
-        if 'FC' == self._protocol and 'wwpns' in connector:
-            for wwpn in connector['wwpns']:
-                ports.append('-hbawwpn %s' % wwpn)
-
-        self._driver_assert(ports,
-                            (_('_create_host: No connector ports.')))
-        port1 = ports.pop(0)
-        arg_name, arg_val = port1.split()
-        ssh_cmd = ['svctask', 'mkhost', '-force', arg_name, arg_val, '-name',
-                   '"%s"' % host_name]
-        out, err = self._ssh(ssh_cmd)
-        self._assert_ssh_return('successfully created' in out,
-                                '_create_host', ssh_cmd, out, err)
-
-        for port in ports:
-            arg_name, arg_val = port.split()
-            ssh_cmd = ['svctask', 'addhostport', '-force',
-                       arg_name, arg_val, host_name]
-            out, err = self._ssh(ssh_cmd)
-            self._assert_ssh_return(
-                (not out.strip()),
-                '_create_host', ssh_cmd, out, err)
-
-        LOG.debug(
-            'leave: _create_host: host %(host)s - %(host_name)s.',
-            {'host': connector['host'], 'host_name': host_name})
-
-        return host_name
-
     def _create_vdisk(self, name, size, unit, opts):
         """Create a new vdisk."""
 
@@ -405,8 +347,8 @@ class FlashSystemDriver(san.SanDriver):
         try:
             out, err = self._ssh(ssh_cmd)
         except processutils.ProcessExecutionError:
-            LOG.warning(_LW('_execute_command_and_parse_attributes: Failed to '
-                            'run command: %s.'), ssh_cmd)
+            LOG.warning(_LW('Failed to run command: '
+                            '%s.'), ssh_cmd)
             # Does not raise exception when command encounters error.
             # Only return and the upper logic decides what to do.
             return None
@@ -430,22 +372,6 @@ class FlashSystemDriver(san.SanDriver):
 
         return attributes
 
-    def _find_host_exhaustive(self, connector, hosts):
-        for host in hosts:
-            ssh_cmd = ['svcinfo', 'lshost', '-delim', '!', host]
-            out, err = self._ssh(ssh_cmd)
-            self._assert_ssh_return(
-                out.strip(),
-                '_find_host_exhaustive', ssh_cmd, out, err)
-            for attr_line in out.split('\n'):
-                # If '!' not found, return the string and two empty strings
-                attr_name, foo, attr_val = attr_line.partition('!')
-                if (attr_name == 'WWPN' and
-                        'wwpns' in connector and attr_val.lower() in
-                        map(str.lower, map(str, connector['wwpns']))):
-                    return host
-        return None
-
     def _get_hdr_dic(self, header, row, delim):
         """Return CLI row data as a dictionary indexed by names from header.
         string. The strings are converted to columns using the delimiter in
@@ -462,38 +388,6 @@ class FlashSystemDriver(san.SanDriver):
         dic = {a: v for a, v in map(None, attributes, values)}
         return dic
 
-    def _get_conn_fc_wwpns(self):
-        wwpns = []
-
-        cmd = ['svcinfo', 'lsportfc']
-
-        generator = self._port_conf_generator(cmd)
-        header = next(generator, None)
-        if not header:
-            return wwpns
-
-        for port_data in generator:
-            try:
-                if port_data['status'] == 'active':
-                    wwpns.append(port_data['WWPN'])
-            except KeyError:
-                self._handle_keyerror('lsportfc', header)
-
-        return wwpns
-
-    def _get_fc_wwpns(self):
-        for key in self._storage_nodes:
-            node = self._storage_nodes[key]
-            ssh_cmd = ['svcinfo', 'lsnode', '-delim', '!', node['id']]
-            attributes = self._execute_command_and_parse_attributes(ssh_cmd)
-            wwpns = set(node['WWPN'])
-            for i, s in zip(attributes['port_id'], attributes['port_status']):
-                if 'unconfigured' != s:
-                    wwpns.add(i)
-            node['WWPN'] = list(wwpns)
-            LOG.info(_LI('WWPN on node %(node)s: %(wwpn)s.'),
-                     {'node': node['id'], 'wwpn': node['WWPN']})
-
     def _get_host_from_connector(self, connector):
         """List the hosts defined in the storage.
 
@@ -546,6 +440,78 @@ class FlashSystemDriver(san.SanDriver):
 
         return return_data
 
+    def _get_node_data(self):
+        """Get and verify node configuration."""
+
+        # Get storage system name and id
+        ssh_cmd = ['svcinfo', 'lssystem', '-delim', '!']
+        attributes = self._execute_command_and_parse_attributes(ssh_cmd)
+        if not attributes or not ('name' in attributes):
+            msg = _('Could not get system name.')
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+        self._system_name = attributes['name']
+        self._system_id = attributes['id']
+
+        # Validate value of open_access_enabled flag, for now only
+        # support when open_access_enabled is off
+        if not attributes or not ('open_access_enabled' in attributes) or (
+                attributes['open_access_enabled'] != 'off'):
+            msg = _('open_access_enabled is not off.')
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+
+        # Validate that the array exists
+        pool = FLASHSYSTEM_VOLPOOL_NAME
+        ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool]
+        attributes = self._execute_command_and_parse_attributes(ssh_cmd)
+        if not attributes:
+            msg = _('Unable to parse attributes.')
+            LOG.error(msg)
+            raise exception.InvalidInput(reason=msg)
+        if not ('status' in attributes) or (
+                attributes['status'] == 'offline'):
+            msg = (_('Array does not exist or is offline. '
+                     'Current status of array is %s.')
+                   % attributes['status'])
+            LOG.error(msg)
+            raise exception.InvalidInput(reason=msg)
+
+        # Get the iSCSI names of the FlashSystem nodes
+        ssh_cmd = ['svcinfo', 'lsnode', '-delim', '!']
+        out, err = self._ssh(ssh_cmd)
+        self._assert_ssh_return(
+            out.strip(), '_get_config_data', ssh_cmd, out, err)
+
+        nodes = out.strip().splitlines()
+        self._assert_ssh_return(nodes, '_get_node_data', ssh_cmd, out, err)
+        header = nodes.pop(0)
+        for node_line in nodes:
+            try:
+                node_data = self._get_hdr_dic(header, node_line, '!')
+            except exception.VolumeBackendAPIException:
+                with excutils.save_and_reraise_exception():
+                    self._log_cli_output_error('_get_node_data',
+                                               ssh_cmd, out, err)
+            try:
+                node = {
+                    'id': node_data['id'],
+                    'name': node_data['name'],
+                    'IO_group': node_data['IO_group_id'],
+                    'WWNN': node_data['WWNN'],
+                    'status': node_data['status'],
+                    'WWPN': [],
+                    'protocol': None,
+                    'iscsi_name': node_data['iscsi_name'],
+                    'config_node': node_data['config_node'],
+                    'ipv4': [],
+                    'ipv6': [],
+                }
+                if node['status'] == 'online':
+                    self._storage_nodes[node['id']] = node
+            except KeyError:
+                self._handle_keyerror('lsnode', header)
+
     def _get_vdisk_attributes(self, vdisk_name):
         """Return vdisk attributes
 
@@ -574,70 +540,6 @@ class FlashSystemDriver(san.SanDriver):
 
         return return_data
 
-    def _get_vdisk_map_properties(
-            self, connector, lun_id, vdisk_name, vdisk_id, vdisk_params):
-        """Get the map properties of vdisk."""
-
-        LOG.debug(
-            'enter: _get_vdisk_map_properties: vdisk '
-            '%(vdisk_name)s.', {'vdisk_name': vdisk_name})
-
-        preferred_node = '0'
-        IO_group = '0'
-
-        # Get preferred node and other nodes in I/O group
-        preferred_node_entry = None
-        io_group_nodes = []
-        for k, node in self._storage_nodes.items():
-            if vdisk_params['protocol'] != node['protocol']:
-                continue
-            if node['id'] == preferred_node:
-                preferred_node_entry = node
-            if node['IO_group'] == IO_group:
-                io_group_nodes.append(node)
-
-        if not io_group_nodes:
-            msg = (_('_get_vdisk_map_properties: No node found in '
-                     'I/O group %(gid)s for volume %(vol)s.')
-                   % {'gid': IO_group, 'vol': vdisk_name})
-            LOG.error(msg)
-            raise exception.VolumeBackendAPIException(data=msg)
-
-        if not preferred_node_entry and not vdisk_params['multipath']:
-            # Get 1st node in I/O group
-            preferred_node_entry = io_group_nodes[0]
-            LOG.warning(_LW('_get_vdisk_map_properties: Did not find a '
-                            'preferred node for vdisk %s.'), vdisk_name)
-        properties = {}
-        properties['target_discovered'] = False
-        properties['target_lun'] = lun_id
-        properties['volume_id'] = vdisk_id
-
-        type_str = 'fibre_channel'
-        conn_wwpns = self._get_conn_fc_wwpns()
-
-        if not conn_wwpns:
-            msg = (_('_get_vdisk_map_properties: Could not get FC '
-                     'connection information for the host-volume '
-                     'connection. Is the host configured properly '
-                     'for FC connections?'))
-            LOG.error(msg)
-            raise exception.VolumeBackendAPIException(data=msg)
-
-        properties['target_wwn'] = conn_wwpns
-
-        if "zvm_fcp" in connector:
-            properties['zvm_fcp'] = connector['zvm_fcp']
-
-        properties['initiator_target_map'] = self._build_initiator_target_map(
-            connector['wwpns'], conn_wwpns)
-
-        LOG.debug(
-            'leave: _get_vdisk_map_properties: vdisk '
-            '%(vdisk_name)s.', {'vdisk_name': vdisk_name})
-
-        return {'driver_volume_type': type_str, 'data': properties}
-
     def _get_vdisk_params(self, type_id):
         params = self._build_default_params()
         if type_id:
@@ -792,11 +694,11 @@ class FlashSystemDriver(san.SanDriver):
             out, err = self._ssh(ssh_cmd, check_exit_code=False)
             if err and err.startswith('CMMVC6071E'):
                 if not self.configuration.flashsystem_multihostmap_enabled:
-                    msg = (_('flashsystem_multihostmap_enabled is set '
-                             'to False, not allow multi host mapping. '
-                             'CMMVC6071E The VDisk-to-host mapping '
-                             'was not created because the VDisk is '
-                             'already mapped to a host.'))
+                    msg = _('flashsystem_multihostmap_enabled is set '
+                            'to False, not allow multi host mapping. '
+                            'CMMVC6071E The VDisk-to-host mapping '
+                            'was not created because the VDisk is '
+                            'already mapped to a host.')
                     LOG.error(msg)
                     raise exception.VolumeBackendAPIException(data=msg)
 
@@ -961,7 +863,7 @@ class FlashSystemDriver(san.SanDriver):
         ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool]
         attributes = self._execute_command_and_parse_attributes(ssh_cmd)
         if not attributes:
-            msg = (_('_update_volume_stats: Could not get storage pool data.'))
+            msg = _('_update_volume_stats: Could not get storage pool data.')
             LOG.error(msg)
             raise exception.VolumeBackendAPIException(data=msg)
 
@@ -1014,97 +916,6 @@ class FlashSystemDriver(san.SanDriver):
             self._is_vdisk_copy_in_progress, vdisk_name)
         timer.start(interval=self._check_lock_interval).wait()
 
-    def do_setup(self, ctxt):
-        """Check that we have all configuration details from the storage."""
-
-        LOG.debug('enter: do_setup')
-
-        self._context = ctxt
-
-        # Get storage system name and id
-        ssh_cmd = ['svcinfo', 'lssystem', '-delim', '!']
-        attributes = self._execute_command_and_parse_attributes(ssh_cmd)
-        if not attributes or not ('name' in attributes):
-            msg = (_('do_setup: Could not get system name.'))
-            LOG.error(msg)
-            raise exception.VolumeBackendAPIException(data=msg)
-        self._system_name = attributes['name']
-        self._system_id = attributes['id']
-
-        # Validate value of open_access_enabled flag, for now only
-        # support when open_access_enabled is off
-        if not attributes or not ('open_access_enabled' in attributes) or (
-                attributes['open_access_enabled'] != 'off'):
-            msg = (_('do_setup: open_access_enabled is not off.'))
-            LOG.error(msg)
-            raise exception.VolumeBackendAPIException(data=msg)
-
-        # Validate that the array exists
-        pool = FLASHSYSTEM_VOLPOOL_NAME
-        ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool]
-        attributes = self._execute_command_and_parse_attributes(ssh_cmd)
-        if not attributes or not ('status' in attributes) or (
-                attributes['status'] == 'offline'):
-            msg = (_('do_setup: Array does not exist or is offline.'))
-            LOG.error(msg)
-            raise exception.InvalidInput(reason=msg)
-
-        # Get the FC names of the FlashSystem nodes
-        ssh_cmd = ['svcinfo', 'lsnode', '-delim', '!']
-        out, err = self._ssh(ssh_cmd)
-        self._assert_ssh_return(
-            out.strip(), 'do_setup', ssh_cmd, out, err)
-
-        nodes = out.strip().splitlines()
-        self._assert_ssh_return(nodes, 'do_setup', ssh_cmd, out, err)
-        header = nodes.pop(0)
-        for node_line in nodes:
-            try:
-                node_data = self._get_hdr_dic(header, node_line, '!')
-            except exception.VolumeBackendAPIException:
-                with excutils.save_and_reraise_exception():
-                    self._log_cli_output_error('do_setup', ssh_cmd, out, err)
-            node = {}
-            try:
-                node['id'] = node_data['id']
-                node['name'] = node_data['name']
-                node['IO_group'] = node_data['IO_group_id']
-                node['WWNN'] = node_data['WWNN']
-                node['status'] = node_data['status']
-                node['WWPN'] = []
-                node['protocol'] = None
-                if node['status'] == 'online':
-                    self._storage_nodes[node['id']] = node
-            except KeyError:
-                self._handle_keyerror('lsnode', header)
-
-        # Get the WWPNs of the FlashSystem nodes
-        self._get_fc_wwpns()
-
-        # For each node, check what connection modes it supports.  Delete any
-        # nodes that do not support any types (may be partially configured).
-        to_delete = []
-        for k, node in self._storage_nodes.items():
-            if not node['WWPN']:
-                to_delete.append(k)
-
-        for delkey in to_delete:
-            del self._storage_nodes[delkey]
-
-        # Make sure we have at least one node configured
-        self._driver_assert(
-            self._storage_nodes,
-            'do_setup: No configured nodes.')
-
-        self._protocol = node['protocol'] = 'FC'
-
-        # Set for vdisk synchronization
-        self._vdisk_copy_in_progress = set()
-        self._vdisk_copy_lock = threading.Lock()
-        self._check_lock_interval = 5
-
-        LOG.debug('leave: do_setup')
-
     def check_for_setup_error(self):
         """Ensure that the flags are set properly."""
         LOG.debug('enter: check_for_setup_error')
@@ -1115,7 +926,7 @@ class FlashSystemDriver(san.SanDriver):
                 _('check_for_setup_error: Unable to determine system name.'))
             raise exception.VolumeBackendAPIException(data=msg)
         if self._system_id is None:
-            msg = (_('check_for_setup_error: Unable to determine system id.'))
+            msg = _('check_for_setup_error: Unable to determine system id.')
             raise exception.VolumeBackendAPIException(data=msg)
 
         required_flags = ['san_ip', 'san_ssh_port', 'san_login']
@@ -1127,9 +938,9 @@ class FlashSystemDriver(san.SanDriver):
         # Ensure that either password or keyfile were set
         if not (self.configuration.san_password or
                 self.configuration.san_private_key):
-            msg = (_('check_for_setup_error: Password or SSH private key '
-                     'is required for authentication: set either '
-                     'san_password or san_private_key option.'))
+            msg = _('check_for_setup_error: Password or SSH private key '
+                    'is required for authentication: set either '
+                    'san_password or san_private_key option.')
             raise exception.InvalidInput(reason=msg)
 
         params = self._build_default_params()
@@ -1137,13 +948,6 @@ class FlashSystemDriver(san.SanDriver):
 
         LOG.debug('leave: check_for_setup_error')
 
-    def validate_connector(self, connector):
-        """Check connector."""
-        if 'FC' == self._protocol and 'wwpns' not in connector:
-            LOG.error(_LE('The connector does not contain the '
-                          'required information: wwpns is missing'))
-            raise exception.InvalidConnectorException(missing='wwpns')
-
     def create_volume(self, volume):
         """Create volume."""
         vdisk_name = volume['name']
@@ -1175,96 +979,6 @@ class FlashSystemDriver(san.SanDriver):
 
         LOG.debug('leave: extend_volume: volume %s.', volume['name'])
 
-    @fczm_utils.AddFCZone
-    @utils.synchronized('flashsystem-init-conn', external=True)
-    def initialize_connection(self, volume, connector):
-        """Perform the necessary work so that a FC connection can
-        be made.
-
-        To be able to create a FC connection from a given host to a
-        volume, we must:
-        1. Translate the given WWNN to a host name
-        2. Create new host on the storage system if it does not yet exist
-        3. Map the volume to the host if it is not already done
-        4. Return the connection information for relevant nodes (in the
-           proper I/O group)
-
-        """
-
-        LOG.debug(
-            'enter: initialize_connection: volume %(vol)s with '
-            'connector %(conn)s.', {'vol': volume, 'conn': connector})
-
-        vdisk_name = volume['name']
-        vdisk_id = volume['id']
-        vdisk_params = self._get_vdisk_params(volume['volume_type_id'])
-
-        self._wait_vdisk_copy_completed(vdisk_name)
-
-        self._driver_assert(
-            self._is_vdisk_defined(vdisk_name),
-            (_('initialize_connection: vdisk %s is not defined.')
-             % vdisk_name))
-
-        lun_id = self._map_vdisk_to_host(vdisk_name, connector)
-
-        properties = {}
-        try:
-            properties = self._get_vdisk_map_properties(
-                connector, lun_id, vdisk_name, vdisk_id, vdisk_params)
-        except exception.VolumeBackendAPIException:
-            with excutils.save_and_reraise_exception():
-                self.terminate_connection(volume, connector)
-                LOG.error(_LE('initialize_connection: Failed to collect '
-                              'return properties for volume %(vol)s and '
-                              'connector %(conn)s.'),
-                          {'vol': volume, 'conn': connector})
-
-        LOG.debug(
-            'leave: initialize_connection:\n volume: %(vol)s\n connector '
-            '%(conn)s\n properties: %(prop)s.',
-            {'vol': volume,
-             'conn': connector,
-             'prop': properties})
-
-        return properties
-
-    @fczm_utils.RemoveFCZone
-    @utils.synchronized('flashsystem-term-conn', external=True)
-    def terminate_connection(self, volume, connector, **kwargs):
-        """Cleanup after connection has been terminated.
-
-        When we clean up a terminated connection between a given connector
-        and volume, we:
-        1. Translate the given connector to a host name
-        2. Remove the volume-to-host mapping if it exists
-        3. Delete the host if it has no more mappings (hosts are created
-           automatically by this driver when mappings are created)
-        """
-        LOG.debug(
-            'enter: terminate_connection: volume %(vol)s with '
-            'connector %(conn)s.',
-            {'vol': volume, 'conn': connector})
-
-        vdisk_name = volume['name']
-        self._wait_vdisk_copy_completed(vdisk_name)
-        self._unmap_vdisk_from_host(vdisk_name, connector)
-
-        properties = {}
-        conn_wwpns = self._get_conn_fc_wwpns()
-        properties['target_wwn'] = conn_wwpns
-        properties['initiator_target_map'] = self._build_initiator_target_map(
-            connector['wwpns'], conn_wwpns)
-
-        LOG.debug(
-            'leave: terminate_connection: volume %(vol)s with '
-            'connector %(conn)s.', {'vol': volume, 'conn': connector})
-
-        return {
-            'driver_volume_type': 'fibre_channel',
-            'data': properties
-        }
-
     def create_snapshot(self, snapshot):
         """Create snapshot from volume."""
 
@@ -1311,8 +1025,8 @@ class FlashSystemDriver(san.SanDriver):
             '%(snap)s.', {'vol': volume['name'], 'snap': snapshot['name']})
 
         if volume['size'] != snapshot['volume_size']:
-            msg = (_('create_volume_from_snapshot: Volume size is different '
-                     'from snapshot based volume.'))
+            msg = _('create_volume_from_snapshot: Volume size is different '
+                    'from snapshot based volume.')
             LOG.error(msg)
             raise exception.VolumeDriverException(message=msg)
 
@@ -1339,8 +1053,8 @@ class FlashSystemDriver(san.SanDriver):
                   {'src': src_volume['name'], 'vol': volume['name']})
 
         if src_volume['size'] != volume['size']:
-            msg = (_('create_cloned_volume: Source and destination '
-                     'size differ.'))
+            msg = _('create_cloned_volume: Source and destination '
+                    'size differ.')
             LOG.error(msg)
             raise exception.VolumeDriverException(message=msg)
 
diff --git a/cinder/volume/drivers/ibm/flashsystem_fc.py b/cinder/volume/drivers/ibm/flashsystem_fc.py
new file mode 100644 (file)
index 0000000..ba3ac95
--- /dev/null
@@ -0,0 +1,379 @@
+# Copyright 2015 IBM Corp.
+# 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.
+#
+
+
+"""
+Volume driver for IBM FlashSystem storage systems with FC protocol.
+
+Limitations:
+1. Cinder driver only works when open_access_enabled=off.
+
+"""
+
+import random
+import threading
+
+from oslo_config import cfg
+from oslo_log import log as logging
+from oslo_utils import excutils
+import six
+
+from cinder import exception
+from cinder.i18n import _, _LE, _LI, _LW
+from cinder import utils
+import cinder.volume.driver
+from cinder.volume.drivers.ibm import flashsystem_common as fscommon
+from cinder.volume.drivers.san import san
+from cinder.zonemanager import utils as fczm_utils
+
+LOG = logging.getLogger(__name__)
+
+flashsystem_fc_opts = [
+    cfg.BoolOpt('flashsystem_multipath_enabled',
+                default=False,
+                help='Connect with multipath (FC only).'
+                     '(Default is false.)')
+]
+
+CONF = cfg.CONF
+CONF.register_opts(flashsystem_fc_opts)
+
+
+class FlashSystemFCDriver(fscommon.FlashSystemDriver,
+                          cinder.volume.driver.FibreChannelDriver):
+    """IBM FlashSystem FC volume driver.
+
+    Version history:
+    1.0.0 - Initial driver
+    1.0.1 - Code clean up
+    1.0.2 - Add lock into vdisk map/unmap, connection
+            initialize/terminate
+    1.0.3 - Initial driver for iSCSI
+    1.0.4 - Split Flashsystem driver into common and FC
+
+    """
+
+    VERSION = "1.0.4"
+
+    def __init__(self, *args, **kwargs):
+        super(FlashSystemFCDriver, self).__init__(*args, **kwargs)
+        self.configuration.append_config_values(fscommon.flashsystem_opts)
+        self.configuration.append_config_values(flashsystem_fc_opts)
+        self.configuration.append_config_values(san.san_opts)
+
+    def _check_vdisk_params(self, params):
+        # Check that the requested protocol is enabled
+        if params['protocol'] != self._protocol:
+            msg = (_("Illegal value '%(prot)s' specified for "
+                     "flashsystem_connection_protocol: "
+                     "valid value(s) are %(enabled)s.")
+                   % {'prot': params['protocol'],
+                      'enabled': self._protocol})
+            raise exception.InvalidInput(reason=msg)
+
+    def _create_host(self, connector):
+        """Create a new host on the storage system.
+
+        We create a host and associate it with the given connection
+        information.
+
+        """
+
+        LOG.debug('enter: _create_host: host %s.', connector['host'])
+
+        rand_id = six.text_type(random.randint(0, 99999999)).zfill(8)
+        host_name = '%s-%s' % (self._connector_to_hostname_prefix(connector),
+                               rand_id)
+
+        ports = []
+        if 'FC' == self._protocol and 'wwpns' in connector:
+            for wwpn in connector['wwpns']:
+                ports.append('-hbawwpn %s' % wwpn)
+
+        self._driver_assert(ports,
+                            (_('_create_host: No connector ports.')))
+        port1 = ports.pop(0)
+        arg_name, arg_val = port1.split()
+        ssh_cmd = ['svctask', 'mkhost', '-force', arg_name, arg_val, '-name',
+                   '"%s"' % host_name]
+        out, err = self._ssh(ssh_cmd)
+        self._assert_ssh_return('successfully created' in out,
+                                '_create_host', ssh_cmd, out, err)
+
+        for port in ports:
+            arg_name, arg_val = port.split()
+            ssh_cmd = ['svctask', 'addhostport', '-force',
+                       arg_name, arg_val, host_name]
+            out, err = self._ssh(ssh_cmd)
+            self._assert_ssh_return(
+                (not out.strip()),
+                '_create_host', ssh_cmd, out, err)
+
+        LOG.debug(
+            'leave: _create_host: host %(host)s - %(host_name)s.',
+            {'host': connector['host'], 'host_name': host_name})
+
+        return host_name
+
+    def _find_host_exhaustive(self, connector, hosts):
+        for host in hosts:
+            ssh_cmd = ['svcinfo', 'lshost', '-delim', '!', host]
+            out, err = self._ssh(ssh_cmd)
+            self._assert_ssh_return(
+                out.strip(),
+                '_find_host_exhaustive', ssh_cmd, out, err)
+            for attr_line in out.split('\n'):
+                # If '!' not found, return the string and two empty strings
+                attr_name, foo, attr_val = attr_line.partition('!')
+                if (attr_name == 'WWPN' and
+                        'wwpns' in connector and attr_val.lower() in
+                        map(str.lower, map(str, connector['wwpns']))):
+                    return host
+        return None
+
+    def _get_conn_fc_wwpns(self):
+        wwpns = []
+
+        cmd = ['svcinfo', 'lsportfc']
+
+        generator = self._port_conf_generator(cmd)
+        header = next(generator, None)
+        if not header:
+            return wwpns
+
+        for port_data in generator:
+            try:
+                if port_data['status'] == 'active':
+                    wwpns.append(port_data['WWPN'])
+            except KeyError:
+                self._handle_keyerror('lsportfc', header)
+
+        return wwpns
+
+    def _get_fc_wwpns(self):
+        for key in self._storage_nodes:
+            node = self._storage_nodes[key]
+            ssh_cmd = ['svcinfo', 'lsnode', '-delim', '!', node['id']]
+            attributes = self._execute_command_and_parse_attributes(ssh_cmd)
+            wwpns = set(node['WWPN'])
+            for i, s in zip(attributes['port_id'], attributes['port_status']):
+                if 'unconfigured' != s:
+                    wwpns.add(i)
+            node['WWPN'] = list(wwpns)
+            LOG.info(_LI('WWPN on node %(node)s: %(wwpn)s.'),
+                     {'node': node['id'], 'wwpn': node['WWPN']})
+
+    def _get_vdisk_map_properties(
+            self, connector, lun_id, vdisk_name, vdisk_id, vdisk_params):
+        """Get the map properties of vdisk."""
+
+        LOG.debug(
+            'enter: _get_vdisk_map_properties: vdisk '
+            '%(vdisk_name)s.', {'vdisk_name': vdisk_name})
+
+        preferred_node = '0'
+        IO_group = '0'
+
+        # Get preferred node and other nodes in I/O group
+        preferred_node_entry = None
+        io_group_nodes = []
+        for k, node in self._storage_nodes.items():
+            if vdisk_params['protocol'] != node['protocol']:
+                continue
+            if node['id'] == preferred_node:
+                preferred_node_entry = node
+            if node['IO_group'] == IO_group:
+                io_group_nodes.append(node)
+
+        if not io_group_nodes:
+            msg = (_('_get_vdisk_map_properties: No node found in '
+                     'I/O group %(gid)s for volume %(vol)s.')
+                   % {'gid': IO_group, 'vol': vdisk_name})
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+
+        if not preferred_node_entry and not vdisk_params['multipath']:
+            # Get 1st node in I/O group
+            preferred_node_entry = io_group_nodes[0]
+            LOG.warning(_LW('_get_vdisk_map_properties: Did not find a '
+                            'preferred node for vdisk %s.'), vdisk_name)
+        properties = {}
+        properties['target_discovered'] = False
+        properties['target_lun'] = lun_id
+        properties['volume_id'] = vdisk_id
+
+        type_str = 'fibre_channel'
+        conn_wwpns = self._get_conn_fc_wwpns()
+
+        if not conn_wwpns:
+            msg = _('_get_vdisk_map_properties: Could not get FC '
+                    'connection information for the host-volume '
+                    'connection. Is the host configured properly '
+                    'for FC connections?')
+            LOG.error(msg)
+            raise exception.VolumeBackendAPIException(data=msg)
+
+        properties['target_wwn'] = conn_wwpns
+
+        if "zvm_fcp" in connector:
+            properties['zvm_fcp'] = connector['zvm_fcp']
+
+        properties['initiator_target_map'] = self._build_initiator_target_map(
+            connector['wwpns'], conn_wwpns)
+
+        LOG.debug(
+            'leave: _get_vdisk_map_properties: vdisk '
+            '%(vdisk_name)s.', {'vdisk_name': vdisk_name})
+
+        return {'driver_volume_type': type_str, 'data': properties}
+
+    @fczm_utils.AddFCZone
+    @utils.synchronized('flashsystem-init-conn', external=True)
+    def initialize_connection(self, volume, connector):
+        """Perform work so that an FC connection can be made.
+
+        To be able to create a FC connection from a given host to a
+        volume, we must:
+        1. Translate the given WWNN to a host name
+        2. Create new host on the storage system if it does not yet exist
+        3. Map the volume to the host if it is not already done
+        4. Return the connection information for relevant nodes (in the
+           proper I/O group)
+
+        """
+
+        LOG.debug(
+            'enter: initialize_connection: volume %(vol)s with '
+            'connector %(conn)s.', {'vol': volume, 'conn': connector})
+
+        vdisk_name = volume['name']
+        vdisk_id = volume['id']
+        vdisk_params = self._get_vdisk_params(volume['volume_type_id'])
+
+        # TODO(edwin): might fix it after vdisk copy function is
+        # ready in FlashSystem thin-provision layer. As this validation
+        # is to check the vdisk which is in copying, at present in firmware
+        # level vdisk doesn't allow to map host which it is copy. New
+        # vdisk clone and snapshot function will cover it. After that the
+        # _wait_vdisk_copy_completed need some modification.
+        self._wait_vdisk_copy_completed(vdisk_name)
+
+        self._driver_assert(
+            self._is_vdisk_defined(vdisk_name),
+            (_('initialize_connection: vdisk %s is not defined.')
+             % vdisk_name))
+
+        lun_id = self._map_vdisk_to_host(vdisk_name, connector)
+
+        properties = {}
+        try:
+            properties = self._get_vdisk_map_properties(
+                connector, lun_id, vdisk_name, vdisk_id, vdisk_params)
+        except exception.VolumeBackendAPIException:
+            with excutils.save_and_reraise_exception():
+                self.terminate_connection(volume, connector)
+                LOG.error(_LE('initialize_connection: Failed to collect '
+                              'return properties for volume %(vol)s and '
+                              'connector %(conn)s.'),
+                          {'vol': volume, 'conn': connector})
+
+        LOG.debug(
+            'leave: initialize_connection:\n volume: %(vol)s\n connector '
+            '%(conn)s\n properties: %(prop)s.',
+            {'vol': volume,
+             'conn': connector,
+             'prop': properties})
+
+        return properties
+
+    @fczm_utils.RemoveFCZone
+    @utils.synchronized('flashsystem-term-conn', external=True)
+    def terminate_connection(self, volume, connector, **kwargs):
+        """Cleanup after connection has been terminated.
+
+        When we clean up a terminated connection between a given connector
+        and volume, we:
+        1. Translate the given connector to a host name
+        2. Remove the volume-to-host mapping if it exists
+        3. Delete the host if it has no more mappings (hosts are created
+           automatically by this driver when mappings are created)
+        """
+        LOG.debug(
+            'enter: terminate_connection: volume %(vol)s with '
+            'connector %(conn)s.',
+            {'vol': volume, 'conn': connector})
+
+        vdisk_name = volume['name']
+        self._wait_vdisk_copy_completed(vdisk_name)
+        self._unmap_vdisk_from_host(vdisk_name, connector)
+
+        properties = {}
+        conn_wwpns = self._get_conn_fc_wwpns()
+        properties['target_wwn'] = conn_wwpns
+        # TODO(edwin): add judgement here. No initiator_target_map within
+        # properties need if no more I/T connection. Otherwise the FCZone
+        # manager will remove the zoning between I/T.
+        properties['initiator_target_map'] = self._build_initiator_target_map(
+            connector['wwpns'], conn_wwpns)
+
+        LOG.debug(
+            'leave: terminate_connection: volume %(vol)s with '
+            'connector %(conn)s.', {'vol': volume, 'conn': connector})
+
+        return {
+            'driver_volume_type': 'fibre_channel',
+            'data': properties
+        }
+
+    def do_setup(self, ctxt):
+        """Check that we have all configuration details from the storage."""
+
+        self._context = ctxt
+
+        # Get data of configured node
+        self._get_node_data()
+
+        # Get the WWPNs of the FlashSystem nodes
+        self._get_fc_wwpns()
+
+        # For each node, check what connection modes it supports.  Delete any
+        # nodes that do not support any types (may be partially configured).
+        to_delete = []
+        for k, node in self._storage_nodes.items():
+            if not node['WWPN']:
+                to_delete.append(k)
+
+        for delkey in to_delete:
+            del self._storage_nodes[delkey]
+
+        # Make sure we have at least one node configured
+        self._driver_assert(self._storage_nodes,
+                            'do_setup: No configured nodes.')
+
+        self._protocol = node['protocol'] = 'FC'
+
+        # Set for vdisk synchronization
+        self._vdisk_copy_in_progress = set()
+        self._vdisk_copy_lock = threading.Lock()
+        self._check_lock_interval = 5
+
+    def validate_connector(self, connector):
+        """Check connector."""
+        if 'FC' == self._protocol and 'wwpns' not in connector:
+            msg = _LE('The connector does not contain the '
+                      'required information: wwpns is missing')
+            LOG.error(msg)
+            raise exception.InvalidConnectorException(missing='wwpns')
index e7f3185ae57f3535633ea749d6a114fbf8ac8a9e..b378a0812eedec5126c43656a00da0f82b0a30de 100644 (file)
@@ -33,8 +33,9 @@ import six
 
 from cinder import exception
 from cinder.i18n import _, _LE, _LW
+from cinder import utils
 import cinder.volume.driver
-from cinder.volume.drivers.ibm import flashsystem as fscommon
+from cinder.volume.drivers.ibm import flashsystem_common as fscommon
 from cinder.volume.drivers.san import san
 
 LOG = logging.getLogger(__name__)
@@ -52,14 +53,19 @@ CONF.register_opts(flashsystem_iscsi_opts)
 
 class FlashSystemISCSIDriver(fscommon.FlashSystemDriver,
                              cinder.volume.driver.ISCSIDriver):
-    """IBM FlashSystem iSCSI volume driver
+    """IBM FlashSystem iSCSI volume driver.
 
     Version history:
-    1.0.2 - Initial driver for iSCSI
+    1.0.0 - Initial driver
+    1.0.1 - Code clean up
+    1.0.2 - Add lock into vdisk map/unmap, connection
+            initialize/terminate
+    1.0.3 - Initial driver for iSCSI
+    1.0.4 - Split Flashsystem driver into common and FC
 
     """
 
-    VERSION = "1.0.2"
+    VERSION = "1.0.4"
 
     def __init__(self, *args, **kwargs):
         super(FlashSystemISCSIDriver, self).__init__(*args, **kwargs)
@@ -171,11 +177,11 @@ class FlashSystemISCSIDriver(fscommon.FlashSystemDriver,
             LOG.error(msg)
             raise exception.VolumeBackendAPIException(data=msg)
 
-        if not preferred_node_entry and not vdisk_params['multipath']:
+        if not preferred_node_entry:
             # Get 1st node in I/O group
             preferred_node_entry = io_group_nodes[0]
             LOG.warning(_LW('_get_vdisk_map_properties: Did not find a '
-                            'preferred node for vdisk %s.'), vdisk_name)
+                        'preferred node for vdisk %s.'), vdisk_name)
         properties = {
             'target_discovered': False,
             'target_lun': lun_id,
@@ -197,6 +203,7 @@ class FlashSystemISCSIDriver(fscommon.FlashSystemDriver,
 
         return {'driver_volume_type': type_str, 'data': properties}
 
+    @utils.synchronized('flashsystem-init-conn', external=True)
     def initialize_connection(self, volume, connector):
         """Perform work so that an iSCSI connection can be made.
 
@@ -222,7 +229,7 @@ class FlashSystemISCSIDriver(fscommon.FlashSystemDriver,
 
         self._driver_assert(
             self._is_vdisk_defined(vdisk_name),
-            (_('initialize_connection: vdisk %s is not defined.')
+            (_('vdisk %s is not defined.')
              % vdisk_name))
 
         lun_id = self._map_vdisk_to_host(vdisk_name, connector)
@@ -247,6 +254,7 @@ class FlashSystemISCSIDriver(fscommon.FlashSystemDriver,
 
         return properties
 
+    @utils.synchronized('flashsystem-term-conn', external=True)
     def terminate_connection(self, volume, connector, **kwargs):
         """Cleanup after connection has been terminated.
 
@@ -350,92 +358,16 @@ class FlashSystemISCSIDriver(fscommon.FlashSystemDriver,
 
         LOG.debug('leave: do_setup')
 
-    def _get_node_data(self):
-        """Get and verify node configuration."""
-
-        LOG.debug('enter: _get_node_data')
-
-        # Get storage system name and id
-        ssh_cmd = ['svcinfo', 'lssystem', '-delim', '!']
-        attributes = self._execute_command_and_parse_attributes(ssh_cmd)
-        if not attributes or not ('name' in attributes):
-            msg = _('Could not get system name.')
-            LOG.error(msg)
-            raise exception.VolumeBackendAPIException(data=msg)
-        self._system_name = attributes['name']
-        self._system_id = attributes['id']
-
-        # Validate value of open_access_enabled flag, for now only
-        # support when open_access_enabled is off
-        if not attributes or not ('open_access_enabled' in attributes) or (
-                attributes['open_access_enabled'] != 'off'):
-            msg = _('open_access_enabled is not off.')
-            LOG.error(msg)
-            raise exception.VolumeBackendAPIException(data=msg)
-
-        # Validate that the array exists
-        pool = fscommon.FLASHSYSTEM_VOLPOOL_NAME
-        ssh_cmd = ['svcinfo', 'lsmdiskgrp', '-bytes', '-delim', '!', pool]
-        attributes = self._execute_command_and_parse_attributes(ssh_cmd)
-        if not attributes:
-            LOG.debug('_get_node_data: lssystem attributes:', attributes)
-            msg = _('Unable to parse attributes')
-            LOG.error(msg)
-            raise exception.InvalidInput(reason=msg)
-        if not ('status' in attributes) or (
-                attributes['status'] == 'offline'):
-            msg = (_('Array does not exist or is offline. '
-                     'Current status of array is %ds.')
-                   % attributes['status'])
-            LOG.error(msg)
-            raise exception.InvalidInput(reason=msg)
-
-        # Get the iSCSI names of the FlashSystem nodes
-        ssh_cmd = ['svcinfo', 'lsnode', '-delim', '!']
-        out, err = self._ssh(ssh_cmd)
-        self._assert_ssh_return(
-            out.strip(), '_get_config_data', ssh_cmd, out, err)
-
-        nodes = out.strip().splitlines()
-        self._assert_ssh_return(nodes, '_get_node_data', ssh_cmd, out, err)
-        header = nodes.pop(0)
-        for node_line in nodes:
-            try:
-                node_data = self._get_hdr_dic(header, node_line, '!')
-            except exception.VolumeBackendAPIException:
-                with excutils.save_and_reraise_exception():
-                    self._log_cli_output_error('_get_node_data',
-                                               ssh_cmd, out, err)
-            try:
-                node = {
-                    'id': node_data['id'],
-                    'name': node_data['name'],
-                    'IO_group': node_data['IO_group_id'],
-                    'WWNN': node_data['WWNN'],
-                    'status': node_data['status'],
-                    'WWPN': [],
-                    'protocol': None,
-                    'iscsi_name': node_data['iscsi_name'],
-                    'config_node': node_data['config_node'],
-                    'ipv4': [],
-                    'ipv6': [],
-                }
-                if node['status'] == 'online':
-                    self._storage_nodes[node['id']] = node
-            except KeyError:
-                self._handle_keyerror('lsnode', header)
-
-        LOG.debug('leave: _get_iscsi_ip_addrs')
-
     def _build_default_params(self):
         protocol = self.configuration.flashsystem_connection_protocol
         if protocol.lower() == 'iscsi':
             protocol = 'iSCSI'
-        return {'protocol': protocol,
-                'multipath': self.configuration.flashsystem_multipath_enabled,
-                'iscsi_ip': self.configuration.iscsi_ip_address,
-                'iscsi_port': self.configuration.iscsi_port,
-                'iscsi_ported': self.configuration.flashsystem_iscsi_portid}
+        return {
+            'protocol': protocol,
+            'iscsi_ip': self.configuration.iscsi_ip_address,
+            'iscsi_port': self.configuration.iscsi_port,
+            'iscsi_ported': self.configuration.flashsystem_iscsi_portid,
+        }
 
     def validate_connector(self, connector):
         """Check connector for enabled protocol."""