diff --git a/cinder/opts.py b/cinder/opts.py index e3541b03603..b63a9d22815 100644 --- a/cinder/opts.py +++ b/cinder/opts.py @@ -288,6 +288,7 @@ def list_opts(): cinder_volume_driver.nvmet_opts, cinder_volume_driver.scst_opts, cinder_volume_driver.image_opts, + cinder_volume_driver.fqdn_opts, cinder_volume_drivers_dell_emc_powermax_common.powermax_opts, cinder_volume_drivers_dell_emc_sc_storagecentercommon. common_opts, diff --git a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py index 9d89c4f4716..b05f7e9c972 100644 --- a/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py +++ b/cinder/tests/unit/volume/drivers/hpe/test_hpe3par.py @@ -18,6 +18,7 @@ import ast import copy from unittest import mock +from oslo_config import cfg from oslo_utils import units from oslo_utils import uuidutils @@ -28,6 +29,7 @@ from cinder import test from cinder.tests.unit import fake_volume from cinder.tests.unit.volume.drivers.hpe \ import fake_hpe_3par_client as hpe3parclient +from cinder.volume import configuration as cvol_cfg from cinder.volume.drivers.hpe import hpe_3par_base as hpedriverbase from cinder.volume.drivers.hpe import hpe_3par_common as hpecommon from cinder.volume.drivers.hpe import hpe_3par_fc as hpefcdriver @@ -38,6 +40,8 @@ from cinder.volume import volume_utils hpeexceptions = hpe3parclient.hpeexceptions +CONF = cfg.CONF + HPE3PAR_CPG = 'OpenStackCPG' HPE3PAR_CPG2 = 'fakepool' @@ -4984,9 +4988,53 @@ class TestHPE3PARDriverBase(HPE3PARBaseDriver): def test__safe_hostname(self): long_hostname = "abc123abc123abc123abc123abc123abc123" fixed_hostname = "abc123abc123abc123abc123abc123a" - common = hpecommon.HPE3PARCommon(None) - safe_host = common._safe_hostname(long_hostname) - self.assertEqual(fixed_hostname, safe_host) + mock_client = self.setup_driver() + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + safe_host = common._safe_hostname(long_hostname) + self.assertEqual(fixed_hostname, safe_host) + + def test__safe_hostname_unique(self): + long_hostname = "abc123abc123abc123abc123abc123abc123" + mock_client = self.setup_driver() + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + + self.addCleanup(CONF.clear_override, + 'unique_fqdn_network', + group=cvol_cfg.SHARED_CONF_GROUP) + CONF.set_override('unique_fqdn_network', + False, + group=cvol_cfg.SHARED_CONF_GROUP) + my_connector = self.connector.copy() + my_connector['initiator'] = 'iqn.1993-08.org.debian:01:222:12345' + ret_name = '54321-222-10-naibed.gro.80-3991' + safe_host = common._safe_hostname(long_hostname, my_connector) + self.assertEqual(ret_name, safe_host) + + def test__safe_hostname_unique_without_initiator(self): + long_hostname = "abc123abc123abc123abc123abc123abc123" + fixed_hostname = "abc123abc123abc123abc123abc123a" + mock_client = self.setup_driver() + with mock.patch.object(hpecommon.HPE3PARCommon, + '_create_client') as mock_create_client: + mock_create_client.return_value = mock_client + common = self.driver._login() + + self.addCleanup(CONF.clear_override, + 'unique_fqdn_network', + group=cvol_cfg.SHARED_CONF_GROUP) + CONF.set_override('unique_fqdn_network', + False, + group=cvol_cfg.SHARED_CONF_GROUP) + my_connector = self.connector.copy() + del(my_connector['initiator']) + safe_host = common._safe_hostname(long_hostname, my_connector) + self.assertEqual(fixed_hostname, safe_host) @mock.patch('cinder.volume.drivers.hpe.hpe_3par_common.HPE3PARCommon.' 'is_volume_group_snap_type') diff --git a/cinder/tests/unit/volume/drivers/test_kaminario.py b/cinder/tests/unit/volume/drivers/test_kaminario.py index 1b264e10cda..a5fd75aeb3b 100644 --- a/cinder/tests/unit/volume/drivers/test_kaminario.py +++ b/cinder/tests/unit/volume/drivers/test_kaminario.py @@ -18,6 +18,7 @@ import time from unittest import mock import ddt +from oslo_config import cfg from oslo_utils import units from cinder import context @@ -34,6 +35,8 @@ from cinder.volume.drivers.kaminario import kaminario_fc from cinder.volume.drivers.kaminario import kaminario_iscsi from cinder.volume import volume_utils +CONF = cfg.CONF + CONNECTOR = {'initiator': 'iqn.1993-08.org.debian:01:12aa12aa12aa', 'ip': '192.168.2.5', 'platform': 'x86_64', 'host': 'test-k2', 'wwpns': ['12341a2a00001234', '12341a2a00001235'], @@ -141,7 +144,6 @@ class TestKaminarioCommon(test.TestCase): self.conf = mock.Mock(spec=configuration.Configuration) self.conf.kaminario_dedup_type_name = "dedup" self.conf.volume_dd_blocksize = 2 - self.conf.unique_fqdn_network = True self.conf.disable_discovery = False def _setup_driver(self): @@ -524,7 +526,12 @@ class TestKaminarioCommon(test.TestCase): self.assertEqual(CONNECTOR['host'], result) def test_get_initiator_host_name_unique(self): - self.driver.configuration.unique_fqdn_network = False + self.addCleanup(CONF.clear_override, + 'unique_fqdn_network', + group=configuration.SHARED_CONF_GROUP) + CONF.set_override('unique_fqdn_network', + False, + group=configuration.SHARED_CONF_GROUP) result = self.driver.get_initiator_host_name(CONNECTOR) expected = re.sub('[:.]', '_', CONNECTOR['initiator'][::-1][:32]) self.assertEqual(expected, result) @@ -627,7 +634,12 @@ class TestKaminarioFC(TestKaminarioCommon): def test_get_initiator_host_name_unique(self): connector = CONNECTOR.copy() del connector['initiator'] - self.driver.configuration.unique_fqdn_network = False + self.addCleanup(CONF.clear_override, + 'unique_fqdn_network', + group=configuration.SHARED_CONF_GROUP) + CONF.set_override('unique_fqdn_network', + False, + group=configuration.SHARED_CONF_GROUP) result = self.driver.get_initiator_host_name(connector) expected = re.sub('[:.]', '_', connector['wwnns'][0][::-1][:32]) self.assertEqual(expected, result) diff --git a/cinder/volume/driver.py b/cinder/volume/driver.py index 0145db7ea4d..fc5b461357e 100644 --- a/cinder/volume/driver.py +++ b/cinder/volume/driver.py @@ -329,6 +329,16 @@ image_opts = [ 'section or in [backend_defaults] section as a common ' 'configuration for all backends.'), ] +fqdn_opts = [ + cfg.BoolOpt('unique_fqdn_network', + default=True, + help="Whether or not our private network has unique FQDN on " + "each initiator or not. For example networks with QA " + "systems usually have multiple servers/VMs with the same " + "FQDN. When true this will create host entries on 3PAR " + "using the FQDN, when false it will use the reversed " + "IQN/WWNN."), +] CONF = cfg.CONF @@ -343,6 +353,7 @@ CONF.register_opts(nvmet_opts) CONF.register_opts(scst_opts) CONF.register_opts(backup_opts) CONF.register_opts(image_opts) +CONF.register_opts(fqdn_opts, group=configuration.SHARED_CONF_GROUP) CONF.import_opt('backup_use_same_host', 'cinder.backup.api') @@ -404,6 +415,7 @@ class BaseVD(object): self.configuration.append_config_values(scst_opts) self.configuration.append_config_values(backup_opts) self.configuration.append_config_values(image_opts) + self.configuration.append_config_values(fqdn_opts) utils.setup_tracing(self.configuration.safe_get('trace_flags')) # NOTE(geguileo): Don't allow to start if we are enabling diff --git a/cinder/volume/drivers/hpe/hpe_3par_common.py b/cinder/volume/drivers/hpe/hpe_3par_common.py index 547c2e30606..4e23c15b55d 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_common.py +++ b/cinder/volume/drivers/hpe/hpe_3par_common.py @@ -295,11 +295,12 @@ class HPE3PARCommon(object): 4.0.13 - Fixed detaching issue for volume with type multiattach enabled. bug #1834660 4.0.14 - Added Peer Persistence feature + 4.0.15 - Support duplicated FQDN in network. Bug #1834695 """ - VERSION = "4.0.14" + VERSION = "4.0.15" stats = {} @@ -374,7 +375,7 @@ class HPE3PARCommon(object): 'san_ip', 'san_login', 'san_password', 'reserved_percentage', 'max_over_subscription_ratio', 'replication_device', 'target_port', 'san_ssh_port', 'ssh_conn_timeout', 'san_private_key', - 'target_ip_address') + 'target_ip_address', 'unique_fqdn_network') return hpe3par_opts + additional_opts def check_flags(self, options, required_flags): @@ -1451,19 +1452,27 @@ class HPE3PARCommon(object): raise exception.VolumeBackendAPIException( data=e.get_description()) - def _safe_hostname(self, hostname): + def _safe_hostname(self, hostname, connector=None): """We have to use a safe hostname length for 3PAR host names.""" - try: - index = hostname.index('.') - except ValueError: - # couldn't find it - index = len(hostname) + SHARED_CONF_GROUP = 'backend_defaults' + shared_backend_conf = CONF._get(SHARED_CONF_GROUP) + unique_fqdn_network = shared_backend_conf.unique_fqdn_network + if(not unique_fqdn_network and connector.get('initiator')): + iqn = connector.get('initiator') + iqn = iqn.replace(":", "-") + return iqn[::-1][:31] + else: + try: + index = hostname.index('.') + except ValueError: + # couldn't find it + index = len(hostname) - # we'll just chop this off for now. - if index > 31: - index = 31 + # we'll just chop this off for now. + if index > 31: + index = 31 - return hostname[:index] + return hostname[:index] def _get_3par_host(self, hostname): return self.client.getHost(hostname) diff --git a/cinder/volume/drivers/hpe/hpe_3par_fc.py b/cinder/volume/drivers/hpe/hpe_3par_fc.py index b80ca974491..2054efab812 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_fc.py +++ b/cinder/volume/drivers/hpe/hpe_3par_fc.py @@ -339,7 +339,7 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): # for now, do not remove zones zone_remove = False else: - hostname = common._safe_hostname(connector['host']) + hostname = common._safe_hostname(connector['host'], connector) common.terminate_connection(volume, hostname, wwn=connector['wwpns'], remote_client=remote_client) @@ -528,7 +528,7 @@ class HPE3PARFCDriver(hpebasedriver.HPE3PARDriverBase): """Creates or modifies existing 3PAR host.""" host = None domain = None - hostname = common._safe_hostname(connector['host']) + hostname = common._safe_hostname(connector['host'], connector) if remote_target: cpg = common._get_cpg_from_cpg_map( remote_target['cpg_map'], src_cpg) diff --git a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py index acf61de4623..e929941e2b6 100644 --- a/cinder/volume/drivers/hpe/hpe_3par_iscsi.py +++ b/cinder/volume/drivers/hpe/hpe_3par_iscsi.py @@ -495,7 +495,7 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase): if is_force_detach: common.terminate_connection(volume, None, None) else: - hostname = common._safe_hostname(connector['host']) + hostname = common._safe_hostname(connector['host'], connector) common.terminate_connection( volume, hostname, @@ -601,7 +601,7 @@ class HPE3PARISCSIDriver(hpebasedriver.HPE3PARDriverBase): domain = None username = None password = None - hostname = common._safe_hostname(connector['host']) + hostname = common._safe_hostname(connector['host'], connector) if remote_target: cpg = common._get_cpg_from_cpg_map( diff --git a/cinder/volume/drivers/kaminario/kaminario_common.py b/cinder/volume/drivers/kaminario/kaminario_common.py index d3f6a9c8c44..7fe418350d0 100644 --- a/cinder/volume/drivers/kaminario/kaminario_common.py +++ b/cinder/volume/drivers/kaminario/kaminario_common.py @@ -51,14 +51,6 @@ kaminario_opts = [ default=False, help="K2 driver will calculate max_oversubscription_ratio " "on setting this option as True."), - cfg.BoolOpt('unique_fqdn_network', - default=True, - help="Whether or not our private network has unique FQDN on " - "each initiator or not. For example networks with QA " - "systems usually have multiple servers/VMs with the same " - "FQDN. When true this will create host entries on K2 " - "using the FQDN, when false it will use the reversed " - "IQN/WWNN."), cfg.BoolOpt('disable_discovery', default=False, help="Disabling iSCSI discovery (sendtargets) for multipath " @@ -139,7 +131,7 @@ class KaminarioCinderDriver(cinder.volume.driver.ISCSIDriver): def get_driver_options(cls): additional_opts = cls._get_oslo_driver_opts( 'san_ip', 'san_login', 'san_password', 'replication_device', - 'volume_dd_blocksize') + 'volume_dd_blocksize', 'unique_fqdn_network') return kaminario_opts + additional_opts @utils.trace @@ -847,7 +839,10 @@ class KaminarioCinderDriver(cinder.volume.driver.ISCSIDriver): """ name = connector.get('initiator', connector.get('wwnns', [''])[0])[::-1] - if self.configuration.unique_fqdn_network: + SHARED_CONF_GROUP = 'backend_defaults' + shared_backend_conf = CONF._get(SHARED_CONF_GROUP) + unique_fqdn_network = shared_backend_conf.unique_fqdn_network + if unique_fqdn_network: name = connector.get('host', name) return re.sub('[^0-9a-zA-Z-_]', '_', name[:32]) diff --git a/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst b/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst index 969669902ff..cc7dc30f7ec 100644 --- a/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst +++ b/doc/source/configuration/block-storage/drivers/hpe-3par-driver.rst @@ -539,3 +539,30 @@ Specify ip address of quorum witness server in ``/etc/cinder/cinder.conf`` ... +Support duplicated FQDN in network +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The 3PAR driver uses the FQDN of the node that is doing the +attach as an unique identifier to map the volume. + +The problem is that the FQDN is not always unique, there are +environments where the same FQDN can be found in different systems, and +in those cases if both try to attach volumes the second system will +fail. + +One example of this happening would be on a QA environment where you are +creating VMs and they all have names like controller-0.localdomain and +compute-0.localdomain. + +To support these kind of environments, the user can specify below flag +in backend_defaults section of cinder.conf as follows: + +``unique_fqdn_network = False`` + +When this flag is used, then during attach volume to instance, +iscsi initiator name is used instead of FQDN. + +If above mentioned flag is not specified in cinder.conf, +then its value is considered as True (by default) and +FQDN is used (existing behavior). + diff --git a/releasenotes/notes/hpe-3par-support-duplicated-fqdn-751ad1dbcd137fbb.yaml b/releasenotes/notes/hpe-3par-support-duplicated-fqdn-751ad1dbcd137fbb.yaml new file mode 100644 index 00000000000..9ca3ac7eec2 --- /dev/null +++ b/releasenotes/notes/hpe-3par-support-duplicated-fqdn-751ad1dbcd137fbb.yaml @@ -0,0 +1,7 @@ +--- +issues: + - | + HPE 3PAR driver now supports networks with duplicated FQDNs via configuration + option `unique_fqdn_network` so attaching in these networks will work + (bug #1834695). +