HPE 3PAR: 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. This patch adds a configuration option to the 3PAR driver called `unique_fqdn_network` to support these kind of environments. Change-Id: I781144cbe68d6908333234a614e021365b0a6d4a Closes-Bug: #1834695
This commit is contained in:
parent
c2291bead4
commit
f1cb8e5d8f
@ -279,6 +279,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,
|
||||
|
@ -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')
|
||||
|
@ -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)
|
||||
|
@ -328,6 +328,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
|
||||
@ -342,6 +352,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')
|
||||
|
||||
|
||||
@ -403,6 +414,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
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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(
|
||||
|
@ -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])
|
||||
|
||||
|
@ -539,3 +539,30 @@ Specify ip address of quorum witness server in ``/etc/cinder/cinder.conf``
|
||||
<other parameters>
|
||||
...
|
||||
|
||||
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).
|
||||
|
||||
|
@ -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).
|
||||
|
Loading…
Reference in New Issue
Block a user