HP 3PAR Add version checking and logging
Check for the minimum supported hp3parclient. The older ones are not supported and this minimum fixes a bug. Log the hp3parclient, 3PAR WSAPI and driver code versions as requested by support. Change-Id: I32cff4c5d9f71a9d110a9b08f73e064c178c796a Closes-Bug: #1463552
This commit is contained in:
parent
b7ab5553fa
commit
7c822401f6
|
@ -58,11 +58,11 @@ Requirements
|
|||
|
||||
On the system running the Manila share service:
|
||||
|
||||
- hp3parclient version 3.2.0 or newer from PyPI
|
||||
- hp3parclient version 3.2.1 or newer from PyPI
|
||||
|
||||
On the HP 3PAR array:
|
||||
|
||||
- HP 3PAR Operating System software version 3.2.1 MU2 or higher
|
||||
- HP 3PAR Operating System software version 3.2.1 MU3 or higher
|
||||
- A license that enables the File Persona feature
|
||||
- The array class and hardware configuration must support File Persona
|
||||
|
||||
|
|
|
@ -80,6 +80,8 @@ class HP3ParShareDriver(driver.ShareDriver):
|
|||
Supports NFS and CIFS protocols on arrays with File Persona.
|
||||
"""
|
||||
|
||||
VERSION = "1.0.00"
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HP3ParShareDriver, self).__init__(False, *args, **kwargs)
|
||||
|
||||
|
@ -94,6 +96,10 @@ class HP3ParShareDriver(driver.ShareDriver):
|
|||
def do_setup(self, context):
|
||||
"""Any initialization the share driver does while starting."""
|
||||
|
||||
LOG.info(_LI("Starting share driver %(driver_name)s (%(version)s)"),
|
||||
{'driver_name': self.__class__.__name__,
|
||||
'version': self.VERSION})
|
||||
|
||||
self.share_ip_address = self.configuration.hp3par_share_ip_address
|
||||
if not self.share_ip_address:
|
||||
raise exception.HP3ParInvalid(
|
||||
|
@ -281,7 +287,7 @@ class HP3ParShareDriver(driver.ShareDriver):
|
|||
'share_backend_name': backend_name,
|
||||
'driver_handles_share_servers': self.driver_handles_share_servers,
|
||||
'vendor_name': 'HP',
|
||||
'driver_version': '1.0',
|
||||
'driver_version': self.VERSION,
|
||||
'storage_protocol': 'NFS_CIFS',
|
||||
'total_capacity_gb': total_capacity_gb,
|
||||
'free_capacity_gb': free_capacity_gb,
|
||||
|
|
|
@ -24,7 +24,7 @@ from oslo_utils import units
|
|||
import six
|
||||
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
from manila.i18n import _, _LI
|
||||
|
||||
hp3parclient = importutils.try_import("hp3parclient")
|
||||
if hp3parclient:
|
||||
|
@ -32,6 +32,7 @@ if hp3parclient:
|
|||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
MIN_CLIENT_VERSION = (3, 2, 1)
|
||||
DENY = '-'
|
||||
ALLOW = '+'
|
||||
OPEN_STACK_MANILA_FSHARE = 'OpenStack Manila fshare'
|
||||
|
@ -39,6 +40,8 @@ OPEN_STACK_MANILA_FSHARE = 'OpenStack Manila fshare'
|
|||
|
||||
class HP3ParMediator(object):
|
||||
|
||||
VERSION = "1.0.00"
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
|
||||
self.hp3par_username = kwargs.get('hp3par_username')
|
||||
|
@ -55,14 +58,28 @@ class HP3ParMediator(object):
|
|||
self.ssh_conn_timeout = kwargs.get('ssh_conn_timeout')
|
||||
self._client = None
|
||||
|
||||
@staticmethod
|
||||
def no_client():
|
||||
return hp3parclient is None
|
||||
|
||||
def do_setup(self):
|
||||
|
||||
if hp3parclient is None:
|
||||
if self.no_client():
|
||||
msg = _('You must install hp3parclient before using the 3PAR '
|
||||
'driver.')
|
||||
LOG.exception(msg)
|
||||
raise exception.HP3ParInvalidClient(message=msg)
|
||||
|
||||
client_version = hp3parclient.version_tuple
|
||||
if client_version < MIN_CLIENT_VERSION:
|
||||
msg = (_('Invalid hp3parclient version found (%(found)s). '
|
||||
'Version %(minimum)s or greater required.') %
|
||||
{'found': '.'.join(map(six.text_type, client_version)),
|
||||
'minimum': '.'.join(map(six.text_type,
|
||||
MIN_CLIENT_VERSION))})
|
||||
LOG.exception(msg)
|
||||
raise exception.HP3ParInvalidClient(message=msg)
|
||||
|
||||
try:
|
||||
self._client = file_client.HP3ParFilePersonaClient(
|
||||
self.hp3par_api_url)
|
||||
|
@ -94,8 +111,22 @@ class HP3ParMediator(object):
|
|||
LOG.exception(msg)
|
||||
raise exception.ShareBackendException(message=msg)
|
||||
|
||||
LOG.info(_LI("HP3ParMediator %(version)s, "
|
||||
"hp3parclient %(client_version)s"),
|
||||
{"version": self.VERSION,
|
||||
"client_version": hp3parclient.get_version_string()})
|
||||
|
||||
try:
|
||||
wsapi_version = self._client.getWsApiVersion()['build']
|
||||
LOG.info(_LI("3PAR WSAPI %s"), wsapi_version)
|
||||
except Exception as e:
|
||||
msg = (_('Failed to get 3PAR WSAPI version: %s') %
|
||||
six.text_type(e))
|
||||
LOG.exception(msg)
|
||||
raise exception.ShareBackendException(message=msg)
|
||||
|
||||
if self.hp3par_debug:
|
||||
self._client.ssh.set_debug_flag(True)
|
||||
self._client.debug_rest(True) # Includes SSH debug (setSSH above)
|
||||
|
||||
def get_capacity(self, fpg):
|
||||
try:
|
||||
|
|
|
@ -425,6 +425,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||
self.init_driver()
|
||||
expected_free = constants.EXPECTED_SIZE_1
|
||||
expected_capacity = constants.EXPECTED_SIZE_2
|
||||
expected_version = self.driver.VERSION
|
||||
|
||||
self.mock_mediator.get_capacity.return_value = {
|
||||
'free_capacity_gb': expected_free,
|
||||
|
@ -434,7 +435,7 @@ class HP3ParDriverTestCase(test.TestCase):
|
|||
expected_result = {
|
||||
'driver_handles_share_servers': False,
|
||||
'QoS_support': False,
|
||||
'driver_version': '1.0',
|
||||
'driver_version': expected_version,
|
||||
'free_capacity_gb': expected_free,
|
||||
'reserved_percentage': 0,
|
||||
'share_backend_name': 'HP_3PAR',
|
||||
|
|
|
@ -26,6 +26,9 @@ from manila.tests.share.drivers.hp import test_hp_3par_constants as constants
|
|||
|
||||
from oslo_utils import units
|
||||
|
||||
CLIENT_VERSION_MIN_OK = hp3parmediator.MIN_CLIENT_VERSION
|
||||
TEST_WSAPI_VERSION_STR = '30201292'
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class HP3ParMediatorTestCase(test.TestCase):
|
||||
|
@ -38,6 +41,7 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||
|
||||
# Take over the hp3parclient module and stub the constructor.
|
||||
hp3parclient = sys.modules['hp3parclient']
|
||||
hp3parclient.version_tuple = CLIENT_VERSION_MIN_OK
|
||||
|
||||
# Need a fake constructor to return the fake client.
|
||||
# This is also be used for constructor error tests.
|
||||
|
@ -59,6 +63,14 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||
hp3par_san_ssh_port=constants.PORT,
|
||||
ssh_conn_timeout=constants.TIMEOUT)
|
||||
|
||||
def test_mediator_no_client(self):
|
||||
"""Test missing hp3parclient error."""
|
||||
|
||||
self.mock_object(hp3parmediator.HP3ParMediator, 'no_client', None)
|
||||
|
||||
self.assertRaises(exception.HP3ParInvalidClient,
|
||||
self.mediator.do_setup)
|
||||
|
||||
def test_mediator_setup_client_init_error(self):
|
||||
"""Any client init exceptions should result in a ManilaException."""
|
||||
|
||||
|
@ -85,7 +97,7 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||
def test_mediator_vfs_exception(self):
|
||||
"""Backend exception during get_vfs_name."""
|
||||
|
||||
self.mediator.do_setup()
|
||||
self.init_mediator()
|
||||
self.mock_client.getvfs.side_effect = Exception('non-manila-except')
|
||||
self.assertRaises(exception.ManilaException,
|
||||
self.mediator.get_vfs_name,
|
||||
|
@ -97,7 +109,7 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||
|
||||
def test_mediator_vfs_not_found(self):
|
||||
"""VFS not found."""
|
||||
self.mediator.do_setup()
|
||||
self.init_mediator()
|
||||
self.mock_client.getvfs.return_value = {'total': 0}
|
||||
self.assertRaises(exception.ManilaException,
|
||||
self.mediator.get_vfs_name,
|
||||
|
@ -110,6 +122,10 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||
def init_mediator(self):
|
||||
"""Basic mediator setup for re-use with tests that need one."""
|
||||
|
||||
self.mock_client.getWsApiVersion.return_value = {
|
||||
'build': TEST_WSAPI_VERSION_STR,
|
||||
}
|
||||
|
||||
self.mock_client.getvfs.return_value = {
|
||||
'total': 1,
|
||||
'members': [{'vfsname': constants.EXPECTED_VFS}]
|
||||
|
@ -136,10 +152,59 @@ class HP3ParMediatorTestCase(test.TestCase):
|
|||
constants.SAN_PASSWORD,
|
||||
port=constants.PORT,
|
||||
conn_timeout=constants.TIMEOUT),
|
||||
mock.call.ssh.set_debug_flag(constants.EXPECTED_HP_DEBUG)
|
||||
mock.call.getWsApiVersion(),
|
||||
mock.call.debug_rest(constants.EXPECTED_HP_DEBUG)
|
||||
]
|
||||
self.mock_client.assert_has_calls(expected_calls)
|
||||
|
||||
def test_mediator_client_version_unsupported(self):
|
||||
"""Try a client with version less than minimum."""
|
||||
|
||||
self.hp3parclient = sys.modules['hp3parclient']
|
||||
self.hp3parclient.version_tuple = (CLIENT_VERSION_MIN_OK[0],
|
||||
CLIENT_VERSION_MIN_OK[1],
|
||||
CLIENT_VERSION_MIN_OK[2] - 1)
|
||||
self.assertRaises(exception.HP3ParInvalidClient,
|
||||
self.init_mediator)
|
||||
|
||||
def test_mediator_client_version_supported(self):
|
||||
"""Try a client with a version greater than the minimum."""
|
||||
|
||||
# The setup success already tests the min version. Try version > min.
|
||||
self.hp3parclient = sys.modules['hp3parclient']
|
||||
self.hp3parclient.version_tuple = (CLIENT_VERSION_MIN_OK[0],
|
||||
CLIENT_VERSION_MIN_OK[1],
|
||||
CLIENT_VERSION_MIN_OK[2] + 1)
|
||||
self.init_mediator()
|
||||
expected_calls = [
|
||||
mock.call.setSSHOptions(constants.EXPECTED_IP_1234,
|
||||
constants.SAN_LOGIN,
|
||||
constants.SAN_PASSWORD,
|
||||
port=constants.PORT,
|
||||
conn_timeout=constants.TIMEOUT),
|
||||
mock.call.getWsApiVersion(),
|
||||
mock.call.debug_rest(constants.EXPECTED_HP_DEBUG)
|
||||
]
|
||||
self.mock_client.assert_has_calls(expected_calls)
|
||||
|
||||
def test_mediator_client_version_exception(self):
|
||||
"""Test the getWsApiVersion exception handling."""
|
||||
|
||||
class FakeException(Exception):
|
||||
pass
|
||||
|
||||
self.mock_client.getWsApiVersion.side_effect = FakeException()
|
||||
self.assertRaises(exception.ShareBackendException,
|
||||
self.init_mediator)
|
||||
|
||||
def test_mediator_client_version_bad_return_value(self):
|
||||
"""Test the getWsApiVersion exception handling with bad value."""
|
||||
|
||||
# Expecting a dict with 'build' in it. This would fail badly.
|
||||
self.mock_client.getWsApiVersion.return_value = 'bogus'
|
||||
self.assertRaises(exception.ShareBackendException,
|
||||
self.mediator.do_setup)
|
||||
|
||||
def get_expected_calls_for_create_share(self,
|
||||
expected_fpg,
|
||||
expected_vfsname,
|
||||
|
|
Loading…
Reference in New Issue