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:
Mark Sturdevant 2015-06-09 13:53:45 -07:00
parent b7ab5553fa
commit 7c822401f6
5 changed files with 113 additions and 10 deletions

View File

@ -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

View File

@ -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,

View File

@ -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:

View File

@ -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',

View File

@ -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,