From b13a8f810b143f5f0f465ab45eab453c1e65de7d Mon Sep 17 00:00:00 2001 From: yixuanzhang <yixuan_z@hotmail.com> Date: Fri, 12 Jan 2018 15:33:25 +0800 Subject: [PATCH] Storwize: modify hyperswap host_site configuration The new configuration storwize_preferred_host_site was introduced in volume type for hyperswap feature on Queens release. Actually, the host_site is related to host rather than volume. This patch removes the parameter storwzie_preferred_host_site from volume type configuration and updates it from StrOpt to DictOpt in cinder back-end configuration. Closes-Bug: 1742866 Change-Id: Ie1df4cc45c2d1f72f33beea7ddc5bb3797a34728 --- .../volume/drivers/ibm/test_storwize_svc.py | 191 +++++++----------- .../ibm/storwize_svc/storwize_svc_common.py | 60 +++++- .../ibm/storwize_svc/storwize_svc_fc.py | 36 ++-- .../ibm/storwize_svc/storwize_svc_iscsi.py | 38 ++-- .../drivers/ibm-storwize-svc-driver.rst | 21 +- ...wap-host-site-update-621e763768fab9ee.yaml | 6 + 6 files changed, 178 insertions(+), 174 deletions(-) create mode 100644 releasenotes/notes/storwize-hyperswap-host-site-update-621e763768fab9ee.yaml diff --git a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py index 25b873d8366..54649ecc871 100644 --- a/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py +++ b/cinder/tests/unit/volume/drivers/ibm/test_storwize_svc.py @@ -3038,6 +3038,7 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase): if self.USESIM: self.iscsi_driver = StorwizeSVCISCSIFakeDriver( configuration=conf.Configuration([], conf.SHARED_CONF_GROUP)) + self.host_site = {'site1': 'iqn.1993-08.org.debian:01:eac5ccc1aaa'} self._def_flags = {'san_ip': 'hostname', 'san_login': 'user', 'san_password': 'pass', @@ -3045,7 +3046,8 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase): 'storwize_svc_flashcopy_timeout': 20, 'storwize_svc_flashcopy_rate': 49, 'storwize_svc_multipath_enabled': False, - 'storwize_svc_allow_tenant_qos': True} + 'storwize_svc_allow_tenant_qos': True, + 'storwize_preferred_host_site': self.host_site} wwpns = [ six.text_type(random.randint(0, 9999999999999999)).zfill(16), six.text_type(random.randint(0, 9999999999999999)).zfill(16)] @@ -3210,73 +3212,37 @@ class StorwizeSVCISCSIDriverTestCase(test.TestCase): 'wwpns': ['ff00000000000000', 'ff00000000000001'], 'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1aaa'} - # host_site is None - volume0_iSCSI = self._create_volume() - vol_type_iSCSI_0 = volume_types.create(self.ctxt, 'iSCSI0', None) - volume0_iSCSI['volume_type_id'] = vol_type_iSCSI_0['id'] - self.iscsi_driver.initialize_connection(volume0_iSCSI, connector) - - # host_site is site1 + volume_iSCSI_1 = self._create_volume() volume_iSCSI = self._create_volume() extra_spec = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1', - 'host_site': 'site1'} + 'peer_pool': 'openstack1'} vol_type_iSCSI = volume_types.create(self.ctxt, 'iSCSI', extra_spec) volume_iSCSI['volume_type_id'] = vol_type_iSCSI['id'] + volume_iSCSI_2 = self._create_volume() + volume_iSCSI_2['volume_type_id'] = vol_type_iSCSI['id'] self.iscsi_driver.initialize_connection(volume_iSCSI, connector) - - # host_site is site2, different with site1. - volume1_iSCSI = self._create_volume() - extra_spec_1 = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1', - 'host_site': 'site2'} - vol_type_iSCSI_1 = volume_types.create(self.ctxt, 'iSCSI1', - extra_spec_1) - volume1_iSCSI['volume_type_id'] = vol_type_iSCSI_1['id'] - self.assertRaises(exception.VolumeDriverException, - self.iscsi_driver.initialize_connection, - volume1_iSCSI, - connector) - - # host_site is None. - volume2_iSCSI = self._create_volume() - vol_type_iSCSI_2 = volume_types.create(self.ctxt, 'iSCSI2', None) - volume2_iSCSI['volume_type_id'] = vol_type_iSCSI_2['id'] - self.iscsi_driver.initialize_connection(volume2_iSCSI, connector) - - # create new host with host_site, the host site should be update - connector2 = {'host': 'STORWIZE-SVC-HOST', - 'wwnns': ['30000090fa17311e', '30000090fa17311f'], - 'wwpns': ['ffff000000000000', 'ffff000000000001'], - 'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1bbb'} - # attach hyperswap volume without host_site - volume3_iSCSI = self._create_volume() - extra_spec_3 = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1'} - vol_type_iSCSI_3 = volume_types.create(self.ctxt, 'iSCSI3', - extra_spec_3) - volume3_iSCSI['volume_type_id'] = vol_type_iSCSI_3['id'] with mock.patch.object(storwize_svc_common.StorwizeHelpers, - 'is_volume_hyperswap') as hyperswap: - hyperswap.return_value = True - self.assertRaises(exception.VolumeDriverException, - self.iscsi_driver.initialize_connection, - volume3_iSCSI, - connector2) + 'is_volume_hyperswap') as is_volume_hyperswap: + is_volume_hyperswap.return_value = True + self.iscsi_driver.initialize_connection(volume_iSCSI, connector) + host_name = self.iscsi_driver._helpers.get_host_from_connector( + connector, iscsi=True) + host_info = self.iscsi_driver._helpers.ssh.lshost(host=host_name) + self.assertEqual('site1', host_info[0]['site_name']) + self.iscsi_driver.terminate_connection(volume_iSCSI, connector) + self.iscsi_driver.initialize_connection(volume_iSCSI_1, connector) + with mock.patch.object(storwize_svc_common.StorwizeHelpers, + 'is_volume_hyperswap') as is_volume_hyperswap: + is_volume_hyperswap.return_value = True + self.iscsi_driver.initialize_connection(volume_iSCSI, connector) - # attach hyperswap volume with host_site - volume4_iSCSI = self._create_volume() - extra_spec_4 = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1', - 'host_site': 'site2'} - vol_type_iSCSI_4 = volume_types.create(self.ctxt, 'iSCSI4', - extra_spec_4) - volume4_iSCSI['volume_type_id'] = vol_type_iSCSI_4['id'] - self.iscsi_driver.initialize_connection(volume4_iSCSI, connector2) - host_name = self.iscsi_driver._helpers.get_host_from_connector( - connector2, iscsi=True) - host_info = self.iscsi_driver._helpers.ssh.lshost(host=host_name) - self.assertEqual('site2', host_info[0]['site_name']) + host_site = {'site1': 'iqn.1993-08.org.debian:01:eac5ccc1aaa', + 'site2': 'iqn.1993-08.org.debian:01:eac5ccc1aaa'} + self._set_flag('storwize_preferred_host_site', host_site) + self.assertRaises(exception.InvalidConfigurationValue, + self.iscsi_driver.initialize_connection, + volume_iSCSI_2, + connector) @mock.patch.object(storwize_svc_iscsi.StorwizeSVCISCSIDriver, '_do_terminate_connection') @@ -4040,73 +4006,60 @@ class StorwizeSVCFcDriverTestCase(test.TestCase): def test_storwize_initialize_fc_connection_with_host_site(self): connector = {'host': 'storwize-svc-host', 'wwnns': ['20000090fa17311e', '20000090fa17311f'], - 'wwpns': ['ff00000000000000', 'ff00000000000001'], + 'wwpns': ['ffff000000000000', 'ffff000000000001'], 'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1aaa'} - - # host_site is None - volume0_fc = self._create_volume() - vol_type_fc_0 = volume_types.create(self.ctxt, 'FC0', None) - volume0_fc['volume_type_id'] = vol_type_fc_0['id'] - self.fc_driver.initialize_connection(volume0_fc, connector) - - # host_site is site1 + # attach hyperswap volume without host_site volume_fc = self._create_volume() extra_spec = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1', - 'host_site': 'site1'} + 'peer_pool': 'openstack1'} vol_type_fc = volume_types.create(self.ctxt, 'FC', extra_spec) volume_fc['volume_type_id'] = vol_type_fc['id'] - self.fc_driver.initialize_connection(volume_fc, connector) - host_name = self.fc_driver._helpers.get_host_from_connector( - connector, iscsi=True) - host_info = self.fc_driver._helpers.ssh.lshost(host=host_name) - self.assertEqual('site1', host_info[0]['site_name']) - - # host_site is site2, different with site1. - volume1_fc = self._create_volume() - extra_spec_1 = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1', - 'host_site': 'site2'} - vol_type_fc_1 = volume_types.create(self.ctxt, 'FC1', extra_spec_1) - volume1_fc['volume_type_id'] = vol_type_fc_1['id'] - self.assertRaises(exception.VolumeDriverException, - self.fc_driver.initialize_connection, - volume1_fc, - connector) - - # host_site is None. - volume2_fc = self._create_volume() - vol_type_fc_2 = volume_types.create(self.ctxt, 'FC2', None) - volume2_fc['volume_type_id'] = vol_type_fc_2['id'] - self.fc_driver.initialize_connection(volume2_fc, connector) - - # create new host with host_site - connector2 = {'host': 'STORWIZE-SVC-HOST', - 'wwnns': ['30000090fa17311e', '30000090fa17311f'], - 'wwpns': ['ffff000000000000', 'ffff000000000001'], - 'initiator': 'iqn.1993-08.org.debian:01:eac5ccc1bbb'} - # attach hyperswap volume without host_site - volume3_fc = self._create_volume() - extra_spec_3 = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1'} - vol_type_fc_3 = volume_types.create(self.ctxt, 'FC3', extra_spec_3) - volume3_fc['volume_type_id'] = vol_type_fc_3['id'] + volume_fc_2 = self._create_volume() + volume_fc_2['volume_type_id'] = vol_type_fc['id'] with mock.patch.object(storwize_svc_common.StorwizeHelpers, 'is_volume_hyperswap') as is_volume_hyperswap: is_volume_hyperswap.return_value = True self.assertRaises(exception.VolumeDriverException, self.fc_driver.initialize_connection, - volume3_fc, - connector2) + volume_fc, + connector) + # the wwpns of 1 host config to 2 different sites + host_site = {'site1': 'ffff000000000000', + 'site2': 'ffff000000000001'} + self.fc_driver.configuration.set_override( + 'storwize_preferred_host_site', host_site) + self.assertRaises(exception.InvalidConfigurationValue, + self.fc_driver.initialize_connection, + volume_fc, + connector) + # All the wwpns of this host are not configured. + host_site_2 = {'site1': 'ff00000000000000', + 'site1': 'ff00000000000001'} + self.fc_driver.configuration.set_override( + 'storwize_preferred_host_site', host_site_2) + self.assertRaises(exception.VolumeDriverException, + self.fc_driver.initialize_connection, + volume_fc, + connector) - # attach hyperswap volume with host_site - volume4_fc = self._create_volume() - extra_spec_4 = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'openstack1', - 'host_site': 'site2'} - vol_type_fc_4 = volume_types.create(self.ctxt, 'FC4', extra_spec_4) - volume4_fc['volume_type_id'] = vol_type_fc_4['id'] - self.fc_driver.initialize_connection(volume4_fc, connector2) + host_site_3 = {'site1': 'ffff000000000000', + 'site1': 'ffff000000000001'} + self.fc_driver.configuration.set_override( + 'storwize_preferred_host_site', host_site_3) + self.fc_driver.initialize_connection(volume_fc, connector) + host_name = self.fc_driver._helpers.get_host_from_connector( + connector, iscsi=True) + host_info = self.fc_driver._helpers.ssh.lshost(host=host_name) + self.assertEqual('site1', host_info[0]['site_name']) + + host_site_4 = {'site2': 'ffff000000000000', + 'site2': 'ffff000000000001'} + self.fc_driver.configuration.set_override( + 'storwize_preferred_host_site', host_site_4) + self.assertRaises(exception.InvalidConfigurationValue, + self.fc_driver.initialize_connection, + volume_fc_2, + connector) @mock.patch.object(storwize_svc_fc.StorwizeSVCFCDriver, '_do_terminate_connection') @@ -4812,8 +4765,7 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): def _create_hyperswap_type(self, type_name): spec = {'drivers:volume_topology': 'hyperswap', - 'peer_pool': 'hyperswap2', - 'host_site': 'site1'} + 'peer_pool': 'hyperswap2'} hyper_type = self._create_volume_type(spec, type_name) return hyper_type @@ -4939,7 +4891,6 @@ class StorwizeSVCCommonDriverTestCase(test.TestCase): 'mirror_pool': None, 'volume_topology': None, 'peer_pool': None, - 'host_site': None, 'cycle_period_seconds': 300, } return opt diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py index a72817885c6..db076390883 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_common.py @@ -130,9 +130,14 @@ storwize_svc_opts = [ default=None, help='Specifies the name of the peer pool for hyperswap ' 'volume, the peer pool must exist on the other site.'), - cfg.StrOpt('storwize_preferred_host_site', - default=None, - help='Specifies the preferred host site name.'), + cfg.DictOpt('storwize_preferred_host_site', + default={}, + help='Specifies the site information for host. ' + 'One WWPN or multi WWPNs used in the host can be ' + 'specified. For example: ' + 'storwize_preferred_host_site=site1:wwpn1,' + 'site2:wwpn2&wwpn3 or ' + 'storwize_preferred_host_site=site1:iqn1,site2:iqn2'), cfg.IntOpt('cycle_period_seconds', default=300, min=60, max=86400, @@ -866,6 +871,10 @@ class StorwizeHelpers(object): site_iogrp = [] pool_data = self.get_pool_attrs(pool) + if pool_data is None: + msg = (_('Failed getting details for pool %s.') % pool) + LOG.error(msg) + raise exception.InvalidConfigurationValue(message=msg) if 'site_id' in pool_data and pool_data['site_id']: for node in state['storage_nodes'].values(): if pool_data['site_id'] == node['site_id']: @@ -1258,7 +1267,6 @@ class StorwizeHelpers(object): 'mirror_pool': config.storwize_svc_mirror_pool, 'volume_topology': None, 'peer_pool': config.storwize_peer_pool, - 'host_site': config.storwize_preferred_host_site, 'cycle_period_seconds': config.cycle_period_seconds} return opt @@ -5554,3 +5562,47 @@ class StorwizeSVCCommonDriver(san.SanDriver, "from rccg. Exception: %(exception)s.", {'vol': volume.name, 'exception': err}) return model_update, added_vols, removed_vols + + def _get_volume_host_site_from_conf(self, volume, connector, iscsi=False): + host_site = self.configuration.safe_get('storwize_preferred_host_site') + select_site = None + if not host_site: + LOG.debug('There is no host_site configured for volume %s.', + volume.name) + return select_site + if iscsi: + for site, iqn in host_site.items(): + if connector['initiator'].lower() in iqn.lower(): + if select_site is None: + select_site = site + elif select_site != site: + msg = _('Configured the host IQN in both sites.') + LOG.error(msg) + raise exception.InvalidConfigurationValue(message=msg) + else: + for wwpn in connector['wwpns']: + for site, wwpn_list in host_site.items(): + if wwpn.lower() in wwpn_list.lower(): + if select_site is None: + select_site = site + elif select_site != site: + msg = _('Configured the host wwpns not in the' + ' same site.') + LOG.error(msg) + raise exception.InvalidConfigurationValue( + message=msg) + return select_site + + def _update_host_site_for_hyperswap_volume(self, host_name, host_site): + host_info = self._helpers.ssh.lshost(host=host_name) + if not host_info[0]['site_name'] and host_site: + self._helpers.update_host(host_name, host_site) + elif host_info[0]['site_name']: + ref_host_site = host_info[0]['site_name'] + if host_site and host_site != ref_host_site: + msg = (_('The existing host site is %(ref_host_site)s,' + ' but the new host site is %(host_site)s.') % + {'ref_host_site': ref_host_site, + 'host_site': host_site}) + LOG.error(msg) + raise exception.InvalidConfigurationValue(message=msg) diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py index 910f7490f13..6e0ff48dee2 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_fc.py @@ -165,36 +165,24 @@ class StorwizeSVCFCDriver(storwize_common.StorwizeSVCCommonDriver): else: volume_name, backend_helper, node_state = self._get_vol_sys_info( volume) - opts = self._get_vdisk_params(volume.volume_type_id) - host_site = opts['host_site'] + + host_site = self._get_volume_host_site_from_conf(volume, + connector) + is_hyper_volume = backend_helper.is_volume_hyperswap(volume_name) + # The host_site is necessary for hyperswap volume. + if is_hyper_volume and host_site is None: + msg = (_('There is no correct storwize_preferred_host_site ' + 'configured for a hyperswap volume %s.') % volume.name) + LOG.error(msg) + raise exception.VolumeDriverException(message=msg) # Check if a host object is defined for this host name host_name = backend_helper.get_host_from_connector(connector) if host_name is None: # Host does not exist - add a new host to Storwize/SVC - # The host_site is necessary for hyperswap volume. - if backend_helper.is_volume_hyperswap( - volume_name) and host_site is None: - msg = (_('There is no host_site configured for a hyperswap' - ' volume %s.') % volume_name) - LOG.error(msg) - raise exception.VolumeDriverException(message=msg) - host_name = backend_helper.create_host(connector, site=host_site) - else: - host_info = backend_helper.ssh.lshost(host=host_name) - if 'site_name' in host_info[0]: - if not host_info[0]['site_name'] and host_site: - backend_helper.update_host(host_name, host_site) - elif host_info[0]['site_name']: - ref_host_site = host_info[0]['site_name'] - if host_site and host_site != ref_host_site: - msg = (_('The existing host site is %(ref_host_site)s,' - ' but the new host site is %(host_site)s.') % - {'ref_host_site': ref_host_site, - 'host_site': host_site}) - LOG.error(msg) - raise exception.VolumeDriverException(message=msg) + elif is_hyper_volume: + self._update_host_site_for_hyperswap_volume(host_name, host_site) volume_attributes = backend_helper.get_vdisk_attributes(volume_name) if volume_attributes is None: diff --git a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py index 9e7e4edc950..376da38f67f 100644 --- a/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py +++ b/cinder/volume/drivers/ibm/storwize_svc/storwize_svc_iscsi.py @@ -163,38 +163,26 @@ class StorwizeSVCISCSIDriver(storwize_common.StorwizeSVCCommonDriver): else: volume_name, backend_helper, node_state = self._get_vol_sys_info( volume) - opts = self._get_vdisk_params(volume.volume_type_id) - host_site = opts['host_site'] + + host_site = self._get_volume_host_site_from_conf(volume, + connector, + iscsi=True) + is_hyper_volume = backend_helper.is_volume_hyperswap(volume_name) + if is_hyper_volume and host_site is None: + msg = (_('There is no correct storwize_preferred_host_site ' + 'configured for a hyperswap volume %s.') % volume.name) + LOG.error(msg) + raise exception.VolumeDriverException(message=msg) # Check if a host object is defined for this host name host_name = backend_helper.get_host_from_connector(connector, iscsi=True) if host_name is None: # Host does not exist - add a new host to Storwize/SVC - # The host_site is necessary for hyperswap volume - if backend_helper.is_volume_hyperswap( - volume_name) and host_site is None: - msg = (_('There is no host_site configured for a hyperswap' - ' volume %s.') % volume_name) - LOG.error(msg) - raise exception.VolumeDriverException(message=msg) - host_name = backend_helper.create_host(connector, iscsi=True, - site = host_site) - else: - host_info = backend_helper.ssh.lshost(host=host_name) - if 'site_name' in host_info[0]: - if not host_info[0]['site_name'] and host_site: - backend_helper.update_host(host_name, host_site) - elif host_info[0]['site_name']: - ref_host_site = host_info[0]['site_name'] - if host_site and host_site != ref_host_site: - msg = (_('The existing host site is %(ref_host_site)s,' - ' but the new host site is %(host_site)s.') % - {'ref_host_site': ref_host_site, - 'host_site': host_site}) - LOG.error(msg) - raise exception.VolumeDriverException(message=msg) + site=host_site) + elif is_hyper_volume: + self._update_host_site_for_hyperswap_volume(host_name, host_site) chap_secret = backend_helper.get_chap_secret_for_host(host_name) chap_enabled = self.configuration.storwize_svc_iscsi_chap_enabled diff --git a/doc/source/configuration/block-storage/drivers/ibm-storwize-svc-driver.rst b/doc/source/configuration/block-storage/drivers/ibm-storwize-svc-driver.rst index 0e8dcf7cb37..cb4a05ff79d 100755 --- a/doc/source/configuration/block-storage/drivers/ibm-storwize-svc-driver.rst +++ b/doc/source/configuration/block-storage/drivers/ibm-storwize-svc-driver.rst @@ -584,12 +584,31 @@ A hyperswap volume is created with a volume-type that has the extra spec ``drivers:volume_topology`` set to ``hyperswap``. To support hyperswap volumes, IBM Storwize/SVC firmware version 7.6.0 or later is required. +Add the following to the back-end configuration to specify the host preferred +site for hyperswap volume. +FC: + +.. code-block:: ini + + storwize_preferred_host_site = site1:20000090fa17311e&ff00000000000001, + site2:20000089762sedce&ff00000000000000 + +iSCSI: + +.. code-block:: ini + + storwize_preferred_host_site = site1:iqn.1993-08.org.debian:01:eac5ccc1aaa&iqn.1993-08.org.debian:01:be53b7e236be, + site2:iqn.1993-08.org.debian:01:eac5ccc1bbb&iqn.1993-08.org.debian:01:abcdefg9876w + +The site1 and site2 are names of the two host sites used in Storwize +storage. The WWPNs and IQNs are the connectors used for host mapping in +Storwize. .. code-block:: console $ cinder type-create hyper_type $ cinder type-key hyper_type set drivers:volume_topology=hyperswap \ - drivers:peer_pool=Pool_site2 drivers:host_site=site1 + drivers:peer_pool=Pool_site2 .. note:: diff --git a/releasenotes/notes/storwize-hyperswap-host-site-update-621e763768fab9ee.yaml b/releasenotes/notes/storwize-hyperswap-host-site-update-621e763768fab9ee.yaml new file mode 100644 index 00000000000..afd037701b4 --- /dev/null +++ b/releasenotes/notes/storwize-hyperswap-host-site-update-621e763768fab9ee.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + Updated the parameter storwzie_preferred_host_site from StrOpt to DictOpt + in cinder back-end configuration, and removed it from volume type + configuration.