Update 3PAR drivers to pass cert test

This patch updates both the HP 3PAR Fibre Channel
and HP 3PAR iSCSI block storage drivers so that they
can pass the driver certification tests.  We also updated
the unit tests to use mock instead of mox.

These versions of the drivers require the new 3.0.0 hp3parclient,
which can be downloaded from the pypi repository:
https://pypi.python.org/pypi/hp3parclient
The new hp3parclient requires the 3.1.3 firmware on the HP 3PAR.

The driver certification results:
Related-Bug: #1278575
Related-Bug: #1278577

Closes-Bug: #1279137

Change-Id: I72e0a76c865e54c58e41cc4409555d9042e30267
DocImpact: Document new driver requirements.
This commit is contained in:
Walter A. Boring IV 2014-02-10 13:09:28 -08:00
parent d033042d74
commit dfb1a09033
5 changed files with 110 additions and 73 deletions

View File

@ -1,5 +1,5 @@
#
# (c) Copyright 2013-2014 Hewlett-Packard Development Company, L.P.
# (c) Copyright 2013 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
@ -40,7 +40,7 @@ HP3PAR_SAN_SSH_CON_TIMEOUT = 44
HP3PAR_SAN_SSH_PRIVATE = 'foobar'
class HP3PARBaseDriver():
class HP3PARBaseDriver(object):
VOLUME_ID = 'd03338a9-9115-48a3-8dfc-35cdfcdc15a7'
CLONE_ID = 'd03338a9-9115-48a3-8dfc-000000000000'
@ -281,7 +281,8 @@ class HP3PARBaseDriver():
self.VOLUME_3PAR_NAME,
'osv-0DM4qZEVSKON-AAAAAAAAA',
HP3PAR_CPG,
HP3PAR_CPG_SNAP, True),
{'snapCPG': 'OpenStackCPGSnap', 'tpvv': True,
'online': True}),
mock.call.logout()]
mock_client.assert_has_calls(expected)
@ -543,33 +544,33 @@ class HP3PARBaseDriver():
# and return the mock HTTP 3PAR client
mock_client = self.setup_driver()
mock_client.getPorts.return_value = {
'members': [{
'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.120.252',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8}, {
'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8}, {
'portWWN': '20210002AC00383D',
'protocol': 1,
'linkState': 4,
'mode': 2,
'device': ['cage2'],
'nodeWWN': '20210002AC00383D',
'type': 2,
'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]}
'members': [
{'portPos': {'node': 0, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.120.252',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': 'iqn.2000-05.com.3pardata:21810002ac00383d',
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8},
{'portWWN': '20210002AC00383D',
'protocol': 1,
'linkState': 4,
'mode': 2,
'device': ['cage2'],
'nodeWWN': '20210002AC00383D',
'type': 2,
'portPos': {'node': 0, 'slot': 6, 'cardPort': 3}}]}
ports = self.driver.common.get_ports()['members']
self.assertEqual(len(ports), 3)
@ -607,7 +608,6 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getCPG(HP3PAR_CPG),
mock.call.setHighConnections(),
mock.call.logout()]
mock_client.assert_has_calls(expected)
mock_client.reset_mock()
@ -909,7 +909,6 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
conn_timeout=HP3PAR_SAN_SSH_CON_TIMEOUT),
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getCPG(HP3PAR_CPG),
mock.call.setHighConnections(),
mock.call.logout(),
mock.call.login(HP3PAR_USER_NAME, HP3PAR_USER_PASS),
mock.call.getPorts(),
@ -1046,11 +1045,11 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
mock_client = self.setup_driver()
mock_client.getVolume.return_value = {'userCPG': HP3PAR_CPG}
mock_client.getCPG.return_value = {}
mock_client.getHost.side_effect = [{
'name': self.FAKE_HOST, 'FCPaths': []}, {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'}, {
'wwn': '123456789054321'}]}]
mock_client.getHost.side_effect = [
{'name': self.FAKE_HOST, 'FCPaths': []},
{'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}]
host = self.driver._create_host(self.volume, self.connector)
@ -1151,25 +1150,25 @@ class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):
config.iscsi_ip_address = '10.10.10.10'
mock_conf = {
'getPorts.return_value': {
'members': [{
'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.220.252',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8}, {
'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8}]}}
'members': [
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 2},
'protocol': 2,
'IPAddr': '10.10.220.252',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D2',
'type': 8},
{'portPos': {'node': 1, 'slot': 8, 'cardPort': 1},
'protocol': 2,
'IPAddr': '10.10.220.253',
'linkState': 4,
'device': [],
'iSCSIName': self.TARGET_IQN,
'mode': 2,
'HWAddr': '2C27D75375D6',
'type': 8}]}}
# no valid ip addr should be configured.
self.assertRaises(exception.InvalidInput,

View File

@ -16,7 +16,7 @@
"""
Volume driver common utilities for HP 3PAR Storage array
The 3PAR drivers requires 3.1.2 MU3 firmware on the 3PAR array.
The 3PAR drivers requires 3.1.3 firmware on the 3PAR array.
You will need to install the python hp3parclient.
sudo pip install hp3parclient
@ -55,7 +55,7 @@ from cinder.volume import volume_types
LOG = logging.getLogger(__name__)
MIN_CLIENT_VERSION = '2.9.0'
MIN_CLIENT_VERSION = '3.0.0'
hp3par_opts = [
cfg.StrOpt('hp3par_api_url',
@ -110,10 +110,11 @@ class HP3PARCommon(object):
1.2.6 - Allow optional specifying n:s:p for vlun creation bug #1269515
This update now requires 3.1.2 MU3 firmware
1.3.0 - Removed all SSH code. We rely on the hp3parclient now.
2.0.0 - Update hp3parclient API uses 3.0.x
"""
VERSION = "1.3.0"
VERSION = "2.0.0"
stats = {}
@ -206,7 +207,6 @@ class HP3PARCommon(object):
try:
# make sure the default CPG exists
self.validate_cpg(self.config.hp3par_cpg)
self.client.setHighConnections()
finally:
self.client_logout()
@ -426,6 +426,7 @@ class HP3PARCommon(object):
try:
self._delete_3par_host(hostname)
self._remove_hosts_naming_dict_host(hostname)
except hpexceptions.HTTPConflict as ex:
# host will only be removed after all vluns
# have been removed
@ -434,6 +435,15 @@ class HP3PARCommon(object):
else:
raise
def _remove_hosts_naming_dict_host(self, hostname):
items = self.hosts_naming_dict.items()
lkey = None
for key, value in items:
if value == hostname:
lkey = key
if lkey is not None:
del self.hosts_naming_dict[lkey]
def _get_volume_type(self, type_id):
ctxt = context.get_admin_context()
return volume_types.get_volume_type(ctxt, type_id)
@ -659,8 +669,11 @@ class HP3PARCommon(object):
tpvv=True):
# Virtual volume sets are not supported with the -online option
LOG.debug('Creating clone of a volume %s' % src_name)
self.client.copyVolume(src_name, dest_name, cpg,
snap_cpg, tpvv)
optional = {'tpvv': tpvv, 'online': True}
if snap_cpg is not None:
optional['snapCPG'] = snap_cpg
self.client.copyVolume(src_name, dest_name, cpg, optional)
def get_next_word(self, s, search_string):
"""Return the next word.
@ -706,6 +719,21 @@ class HP3PARCommon(object):
# volume set name in the error.
try:
self.client.deleteVolume(volume_name)
except hpexceptions.HTTPBadRequest as ex:
if ex.get_code() == 29:
if self.client.isOnlinePhysicalCopy(volume_name):
LOG.debug(_("Found an online copy for %(volume)s")
% {'volume': volume_name})
# the volume is in process of being cloned.
# stopOnlinePhysicalCopy will also delete
# the volume once it stops the copy.
self.client.stopOnlinePhysicalCopy(volume_name)
else:
LOG.error(str(ex))
raise ex
else:
LOG.error(str(ex))
raise ex
except hpexceptions.HTTPConflict as ex:
if ex.get_code() == 34:
# This is a special case which means the

View File

@ -17,11 +17,11 @@
#
"""
Volume driver for HP 3PAR Storage array.
This driver requires 3.1.2 MU2 firmware on the 3PAR array, using
the 2.x version of the hp3parclient.
This driver requires 3.1.3 firmware on the 3PAR array, using
the 3.x version of the hp3parclient.
You will need to install the python hp3parclient.
sudo pip install --upgrade "hp3parclient>=2.0"
sudo pip install --upgrade "hp3parclient>=3.0"
Set the following in the cinder.conf file to enable the
3PAR Fibre Channel Driver along with the required flags:
@ -55,10 +55,11 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
1.2.3 - Added ability to add WWNs to host.
1.2.4 - Added metadata during attach/detach bug #1258033.
1.3.0 - Removed all SSH code. We rely on the hp3parclient now.
2.0.0 - Update hp3parclient API uses 3.0.x
"""
VERSION = "1.3.0"
VERSION = "2.0.0"
def __init__(self, *args, **kwargs):
super(HP3PARFCDriver, self).__init__(*args, **kwargs)
@ -319,7 +320,11 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
@utils.synchronized('3par', external=True)
def extend_volume(self, volume, new_size):
self.common.extend_volume(volume, new_size)
self.common.client_login()
try:
self.common.extend_volume(volume, new_size)
finally:
self.common.client_logout()
@utils.synchronized('3par', external=True)
def attach_volume(self, context, volume, instance_uuid, host_name,

View File

@ -15,11 +15,11 @@
#
"""
Volume driver for HP 3PAR Storage array.
This driver requires 3.1.2 MU3 firmware on the 3PAR array, using
the 2.x version of the hp3parclient.
This driver requires 3.1.3 firmware on the 3PAR array, using
the 3.x version of the hp3parclient.
You will need to install the python hp3parclient.
sudo pip install --upgrade "hp3parclient>=2.0"
sudo pip install --upgrade "hp3parclient>=3.0"
Set the following in the cinder.conf file to enable the
3PAR iSCSI Driver along with the required flags:
@ -59,10 +59,11 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
1.2.6 - Use least-used iscsi n:s:p for iscsi volume attach bug #1269515
This update now requires 3.1.2 MU3 firmware
1.3.0 - Removed all SSH code. We rely on the hp3parclient now.
2.0.0 - Update hp3parclient API uses 3.0.x
"""
VERSION = "1.3.0"
VERSION = "2.0.0"
def __init__(self, *args, **kwargs):
super(HP3PARISCSIDriver, self).__init__(*args, **kwargs)
@ -431,7 +432,11 @@ class HP3PARISCSIDriver(cinder.volume.driver.ISCSIDriver):
@utils.synchronized('3par', external=True)
def extend_volume(self, volume, new_size):
self.common.extend_volume(volume, new_size)
self.common.client_login()
try:
self.common.extend_volume(volume, new_size)
finally:
self.common.client_logout()
@utils.synchronized('3par', external=True)
def attach_volume(self, context, volume, instance_uuid, host_name,

View File

@ -3,7 +3,7 @@ hacking>=0.8.0,<0.9
coverage>=3.6
discover
fixtures>=0.3.14
hp3parclient>=2.0,<3.0
hp3parclient>=3.0,<4.0
hplefthandclient>=1.0.0,<2.0.0
mock>=1.0
mox>=0.5.3