3PAR driver fails to validate conf share server IPs

With 3PAR, share server(VFS) can have up to 4 IP addresses.
During bootup, driver queries 3PAR to get list of all IP addresses
and validates it against IP addresses provided in manila.conf. If
there is a mismatch, driver throws exception.

The bug was with 3PAR file client which always returns only one IP
address. To make driver backward compatible with 3PAR client,
mediator.py formats the value retured by client and passes it to
driver.py. This patch now correctly accepts all the IP addresses
as obtained from 3PAR and validates configured IPs against it.

Also removing unused function.

Updated and added new unit tests

Added release notes

Closes-Bug: #1621016

Change-Id: I1eeb18cc9905a71cd38c383bc0ab49e0a560ffc9
This commit is contained in:
Jay Mehta 2016-09-13 17:29:20 -07:00
parent 3f922aab6e
commit cef6dddcee
6 changed files with 142 additions and 19 deletions

View File

@ -212,10 +212,12 @@ class HPE3ParShareDriver(driver.ShareDriver):
when a share is deleted #1582931
2.0.5 - Add update_access support
2.0.6 - Multi pool support per backend
2.0.7 - Fix get_vfs() to correctly validate conf IP addresses at
boot up #1621016
"""
VERSION = "2.0.6"
VERSION = "2.0.7"
def __init__(self, *args, **kwargs):
super(HPE3ParShareDriver, self).__init__((True, False),
@ -265,7 +267,6 @@ class HPE3ParShareDriver(driver.ShareDriver):
def _validate_pool_ips(addresses, conf_pool_ips):
# Pool configured IP addresses should be subset of IP addresses
# retured from vfs
addresses = to_list(addresses)
if not set(conf_pool_ips) <= set(addresses):
msg = _("Incorrect configuration. "
"Configuration pool IP address did not match with "
@ -287,8 +288,6 @@ class HPE3ParShareDriver(driver.ShareDriver):
vfs_info = mediator.get_vfs(pool_name)
if self.driver_handles_share_servers:
# Use discovered IP(s) from array
vfs_info['vfsip']['address'] = to_list(
vfs_info['vfsip']['address'])
self.fpgs[pool_name] = {
vfs_info['vfsname']: vfs_info['vfsip']['address']}
elif conf_pool_ips == []:
@ -301,8 +300,6 @@ class HPE3ParShareDriver(driver.ShareDriver):
raise exception.HPE3ParInvalid(err=msg)
else:
# Use discovered pool ips
vfs_info['vfsip']['address'] = to_list(
vfs_info['vfsip']['address'])
self.fpgs[pool_name] = {
vfs_info['vfsname']: vfs_info['vfsip']['address']}
else:

View File

@ -78,10 +78,12 @@ class HPE3ParMediator(object):
2.0.6 - Read-write share from snapshot (using driver mount and copy)
2.0.7 - Add update_access support
2.0.8 - Multi pools support per backend
2.0.9 - Fix get_vfs() to correctly validate conf IP addresses at
boot up #1621016
"""
VERSION = "2.0.8"
VERSION = "2.0.9"
def __init__(self, **kwargs):
@ -1042,9 +1044,6 @@ class HPE3ParMediator(object):
'share_name': SUPER_SHARE})
return path
def get_vfs_name(self, fpg):
return self.get_vfs(fpg)['vfsname']
def get_vfs(self, fpg, vfs=None):
"""Get the VFS or raise an exception."""
@ -1074,7 +1073,23 @@ class HPE3ParMediator(object):
LOG.error(message)
raise exception.ShareBackendException(msg=message)
return result['members'][0]
value = result['members'][0]
if isinstance(value['vfsip'], dict):
# This is for 3parclient returning only one VFS entry
LOG.debug("3parclient version up to 4.2.1 is in use. Client "
"upgrade may be needed if using a VFS with multiple "
"IP addresses.")
value['vfsip']['address'] = [value['vfsip']['address']]
else:
# This is for 3parclient returning list of VFS entries
# Format get_vfs ret value to combine all IP addresses
discovered_vfs_ips = []
for vfs_entry in value['vfsip']:
if vfs_entry['address']:
discovered_vfs_ips.append(vfs_entry['address'])
value['vfsip'] = value['vfsip'][0]
value['vfsip']['address'] = discovered_vfs_ips
return value
@staticmethod
def _is_share_from_snapshot(fshare):

View File

@ -33,6 +33,7 @@ CIDR_PREFIX = '24'
# Constants to use with Mock and expect in results
EXPECTED_IP_10203040 = '10.20.30.40'
EXPECTED_IP_10203041 = '10.20.30.41'
EXPECTED_IP_1234 = '1.2.3.4'
EXPECTED_MY_IP = '9.8.7.6'
EXPECTED_IP_127 = '127.0.0.1'
@ -48,6 +49,7 @@ SHARE_ID = 'share-id'
EXPECTED_SHARE_ID = 'osf-share-id'
EXPECTED_SHARE_ID_RO = 'osf-ro-share-id'
EXPECTED_SHARE_NAME = 'share-name'
EXPECTED_NET_NAME = 'testnet'
EXPECTED_FPG = 'pool'
EXPECTED_HOST = 'hostname@backend#' + EXPECTED_FPG
UNEXPECTED_FPG = 'not_a_pool'
@ -64,8 +66,82 @@ EXPECTED_FPG_CONF = [{EXPECTED_FPG: [EXPECTED_IP_10203040]}]
EXPECTED_FSTORE = EXPECTED_PROJECT_ID
EXPECTED_VFS = 'test_vfs'
EXPECTED_GET_VFS = {'vfsname': EXPECTED_VFS,
'vfsip': {'address': EXPECTED_IP_10203040}}
'vfsip': {'address': [EXPECTED_IP_10203040]}}
EXPECTED_GET_VFS_MULTIPLES = {
'vfsname': EXPECTED_VFS,
'vfsip': {'address': [EXPECTED_IP_10203041, EXPECTED_IP_10203040]}}
EXPECTED_CLIENT_GET_VFS_MEMBERS_MULTI = {
'fspname': EXPECTED_VFS,
'vfsip': [
{'networkName': EXPECTED_NET_NAME,
'fspool': EXPECTED_VFS,
'address': EXPECTED_IP_10203040,
'prefixLen': EXPECTED_SUBNET,
'vfs': EXPECTED_VFS,
'vlanTag': EXPECTED_VLAN_TAG,
},
{'networkName': EXPECTED_NET_NAME,
'fspool': EXPECTED_VFS,
'address': EXPECTED_IP_10203041,
'prefixLen': EXPECTED_SUBNET,
'vfs': EXPECTED_VFS,
'vlanTag': EXPECTED_VLAN_TAG,
},
],
'vfsname': EXPECTED_VFS,
}
EXPECTED_MEDIATOR_GET_VFS_RET_VAL_MULTI = {
'fspname': EXPECTED_VFS,
'vfsip': {
'networkName': EXPECTED_NET_NAME,
'fspool': EXPECTED_VFS,
'address': [
EXPECTED_IP_10203040,
EXPECTED_IP_10203041,
],
'prefixLen': EXPECTED_SUBNET,
'vfs': EXPECTED_VFS,
'vlanTag': EXPECTED_VLAN_TAG
},
'vfsname': EXPECTED_VFS,
}
EXPECTED_CLIENT_GET_VFS_MEMBERS = {
'fspname': EXPECTED_VFS,
'vfsip': {
'networkName': EXPECTED_NET_NAME,
'fspool': EXPECTED_VFS,
'address': EXPECTED_IP_10203040,
'prefixLen': EXPECTED_SUBNET,
'vfs': EXPECTED_VFS,
'vlanTag': EXPECTED_VLAN_TAG,
},
'vfsname': EXPECTED_VFS,
}
EXPECTED_MEDIATOR_GET_VFS_RET_VAL = {
'fspname': EXPECTED_VFS,
'vfsip': {
'networkName': EXPECTED_NET_NAME,
'fspool': EXPECTED_VFS,
'address': [EXPECTED_IP_10203040],
'prefixLen': EXPECTED_SUBNET,
'vfs': EXPECTED_VFS,
'vlanTag': EXPECTED_VLAN_TAG,
},
'vfsname': EXPECTED_VFS,
}
EXPECTED_CLIENT_GET_VFS_RETURN_VALUE = {
'total': 1,
'members': [EXPECTED_CLIENT_GET_VFS_MEMBERS],
}
EXPECTED_CLIENT_GET_VFS_RETURN_VALUE_MULTI = {
'total': 1,
'members': [EXPECTED_CLIENT_GET_VFS_MEMBERS_MULTI],
}
EXPECTED_FPG_MAP = {EXPECTED_FPG: {EXPECTED_VFS: [EXPECTED_IP_10203040]}}
EXPECTED_FPG_MAP_MULTI_VFS = {EXPECTED_FPG: {
EXPECTED_VFS: [EXPECTED_IP_10203041, EXPECTED_IP_10203040]}}
EXPECTED_SHARE_IP = '10.50.3.8'
EXPECTED_HPE_DEBUG = True
EXPECTED_COMMENT = "OpenStack Manila - foo-comment"

View File

@ -117,10 +117,11 @@ class HPE3ParDriverTestCase(test.TestCase):
self.driver = hpe3pardriver.HPE3ParShareDriver(
configuration=self.conf)
def test_driver_setup_success(self):
def test_driver_setup_success(self,
get_vfs_ret_val=constants.EXPECTED_GET_VFS):
"""Driver do_setup without any errors."""
self.mock_mediator.get_vfs.return_value = constants.EXPECTED_GET_VFS
self.mock_mediator.get_vfs.return_value = get_vfs_ret_val
self.driver.do_setup(None)
conf = self.conf
@ -162,6 +163,15 @@ class HPE3ParDriverTestCase(test.TestCase):
self.test_driver_setup_success()
self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
def test_driver_setup_no_dhss_multi_getvfs_success(self):
"""Driver do_setup when dhss=False, getvfs returns multiple IPs."""
self.conf.driver_handles_share_servers = False
self.test_driver_setup_success(
get_vfs_ret_val=constants.EXPECTED_GET_VFS_MULTIPLES)
self.assertEqual(constants.EXPECTED_FPG_MAP,
self.driver.fpgs)
def test_driver_setup_success_no_dhss_no_conf_ss_ip(self):
"""test driver's do_setup()
@ -177,7 +187,7 @@ class HPE3ParDriverTestCase(test.TestCase):
self.test_driver_setup_success()
self.assertEqual(constants.EXPECTED_FPG_MAP, self.driver.fpgs)
self.conf.hpe3par_fpg = original_fpg
constants.EXPECTED_FPG_CONF = original_fpg
def test_driver_setup_failure_no_dhss_no_conf_ss_ip(self):
"""Configured IP address is required for dhss=False."""
@ -193,7 +203,7 @@ class HPE3ParDriverTestCase(test.TestCase):
self.assertRaises(exception.HPE3ParInvalid,
self.driver.do_setup, None)
self.conf.hpe3par_fpg = fpg_without_ss_ip
constants.EXPECTED_FPG_CONF = fpg_without_ss_ip
def test_driver_setup_mediator_error(self):
"""Driver do_setup when the mediator setup fails."""

View File

@ -121,12 +121,12 @@ class HPE3ParMediatorTestCase(test.TestCase):
conn_timeout=constants.TIMEOUT)])
def test_mediator_vfs_exception(self):
"""Backend exception during get_vfs_name."""
"""Backend exception during get_vfs."""
self.init_mediator()
self.mock_client.getvfs.side_effect = Exception('non-manila-except')
self.assertRaises(exception.ManilaException,
self.mediator.get_vfs_name,
self.mediator.get_vfs,
fpg=constants.EXPECTED_FPG)
expected_calls = [
mock.call.getvfs(fpg=constants.EXPECTED_FPG, vfs=None),
@ -138,13 +138,30 @@ class HPE3ParMediatorTestCase(test.TestCase):
self.init_mediator()
self.mock_client.getvfs.return_value = {'total': 0}
self.assertRaises(exception.ManilaException,
self.mediator.get_vfs_name,
self.mediator.get_vfs,
fpg=constants.EXPECTED_FPG)
expected_calls = [
mock.call.getvfs(fpg=constants.EXPECTED_FPG, vfs=None),
]
self.mock_client.assert_has_calls(expected_calls)
@ddt.data((constants.EXPECTED_CLIENT_GET_VFS_RETURN_VALUE,
constants.EXPECTED_MEDIATOR_GET_VFS_RET_VAL),
(constants.EXPECTED_CLIENT_GET_VFS_RETURN_VALUE_MULTI,
constants.EXPECTED_MEDIATOR_GET_VFS_RET_VAL_MULTI))
@ddt.unpack
def test_mediator_get_vfs(self, get_vfs_val, exp_vfs_val):
"""VFS not found."""
self.init_mediator()
self.mock_client.getvfs.return_value = get_vfs_val
ret_val = self.mediator.get_vfs(constants.EXPECTED_FPG)
self.assertEqual(exp_vfs_val, ret_val)
expected_calls = [
mock.call.getvfs(fpg=constants.EXPECTED_FPG, vfs=None),
]
self.mock_client.assert_has_calls(expected_calls)
def init_mediator(self):
"""Basic mediator setup for re-use with tests that need one."""

View File

@ -0,0 +1,8 @@
---
issues:
- 3parclient up to version 4.2.1 always returns only 1 VFS IP address.
This may cause 3PAR driver boot up failure while validating VFS IP
addresses against IP addresses configured in manila.conf.
fixes:
- Fixed 3PAR driver boot up failure while validating share server IP address
provided in manila.conf against IP address set on array.