3PAR FC: add ability to add WWNs to host

When a host is created on the HP 3PAR array,
at initialize connection time, the host is
created with all WWNs provided in the connector
structure. However, the driver did not have
the ability to add new WWNs after a host is created.

This patch adds the ability to add WWNs to a
host, at initialize connection time, if a new
WWN is provided in the connector structure.

Change-Id: I6fd8a5511f83d5460da30ff5558a3e95964a95f5
Closes-bug: 1258229
This commit is contained in:
Jim Branen 2013-12-06 15:32:53 -08:00
parent 62617e656f
commit 96ef263f61
2 changed files with 120 additions and 8 deletions

View File

@ -19,6 +19,7 @@
Unit tests for OpenStack Cinder volume drivers
"""
import ast
import mock
import mox
import shutil
import tempfile
@ -787,7 +788,10 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
'123456789054321'])
_run_ssh(create_host_cmd, False).AndReturn([CLI_CR, ''])
getHost('fakehost').AndReturn({'name': self.FAKE_HOST})
getHost('fakehost').AndReturn({'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}
)
self.mox.ReplayAll()
host = self.driver._create_host(self.volume, self.connector)
@ -818,7 +822,12 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
'already used by host fakehost.foo (19)')
_run_ssh(create_host_cmd, False).AndReturn([create_host_ret, ''])
getHost('fakehost.foo').AndReturn({'name': 'fakehost.foo'})
host_ret = {
'name': 'fakehost.foo',
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}
getHost('fakehost.foo').AndReturn(host_ret)
self.mox.ReplayAll()
host = self.driver._create_host(self.volume, self.connector)
@ -849,8 +858,8 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
'pathOperation': 1})
getHost('fakehost').AndReturn({'name': self.FAKE_HOST,
'FCPaths': [{'WWN': '123456789012345'},
{'WWN': '123456789054321'}]}
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}
)
self.mox.ReplayAll()
@ -859,6 +868,82 @@ class TestHP3PARFCDriver(HP3PARBaseDriver, test.TestCase):
self.assertEqual(host['name'], self.FAKE_HOST)
self.assertEqual(len(host['FCPaths']), 2)
def test_modify_host_with_new_wwn(self):
self.flags(lock_path=self.tempdir)
self.clear_mox()
hpdriver.hpcommon.HP3PARCommon.get_cpg = mock.Mock(
return_value=self.fake_get_cpg)
hpdriver.hpcommon.HP3PARCommon.get_domain = mock.Mock(
return_value=self.fake_get_domain)
# set up the getHost mock
self.driver.common.client.getHost = mock.Mock()
# define the return values for the 2 calls
getHost_ret1 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789054321'}]}
getHost_ret2 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'}]}
self.driver.common.client.getHost.side_effect = [
getHost_ret1, getHost_ret2]
# setup the modifyHost mock
self.driver.common.client.modifyHost = mock.Mock()
host = self.driver._create_host(self.volume, self.connector)
# mock assertions
self.driver.common.client.getHost.assert_has_calls([
mock.call('fakehost'),
mock.call('fakehost')])
self.driver.common.client.modifyHost.assert_called_once_with(
'fakehost', {'FCWWNs': ['123456789012345'], 'pathOperation': 1})
self.assertEqual(host['name'], self.FAKE_HOST)
self.assertEqual(len(host['FCPaths']), 2)
def test_modify_host_with_unknown_wwn_and_new_wwn(self):
self.flags(lock_path=self.tempdir)
self.clear_mox()
hpdriver.hpcommon.HP3PARCommon.get_cpg = mock.Mock(
return_value=self.fake_get_cpg)
hpdriver.hpcommon.HP3PARCommon.get_domain = mock.Mock(
return_value=self.fake_get_domain)
# set up the getHost mock
self.driver.common.client.getHost = mock.Mock()
# define the return values for the 2 calls
getHost_ret1 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789054321'},
{'wwn': 'xxxxxxxxxxxxxxx'}]}
getHost_ret2 = {
'name': self.FAKE_HOST,
'FCPaths': [{'wwn': '123456789012345'},
{'wwn': '123456789054321'},
{'wwn': 'xxxxxxxxxxxxxxx'}]}
self.driver.common.client.getHost.side_effect = [
getHost_ret1, getHost_ret2]
# setup the modifyHost mock
self.driver.common.client.modifyHost = mock.Mock()
host = self.driver._create_host(self.volume, self.connector)
# mock assertions
self.driver.common.client.getHost.assert_has_calls([
mock.call('fakehost'),
mock.call('fakehost')])
self.driver.common.client.modifyHost.assert_called_once_with(
'fakehost', {'FCWWNs': ['123456789012345'], 'pathOperation': 1})
self.assertEqual(host['name'], self.FAKE_HOST)
self.assertEqual(len(host['FCPaths']), 3)
class TestHP3PARISCSIDriver(HP3PARBaseDriver, test.TestCase):

View File

@ -56,9 +56,10 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
the drivers to use the new APIs.
1.2.1 - Synchronized extend_volume method.
1.2.2 - Added try/finally around client login/logout.
1.2.3 - Added ability to add WWNs to host.
"""
VERSION = "1.2.2"
VERSION = "1.2.3"
def __init__(self, *args, **kwargs):
super(HP3PARFCDriver, self).__init__(*args, **kwargs)
@ -261,9 +262,6 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
domain = self.common.get_domain(cpg)
try:
host = self.common._get_3par_host(hostname)
if 'FCPaths' not in host or len(host['FCPaths']) < 1:
self._modify_3par_fibrechan_host(hostname, connector['wwpns'])
host = self.common._get_3par_host(hostname)
except hpexceptions.HTTPNotFound as ex:
# get persona from the volume type extra specs
persona_id = self.common.get_persona_type(volume)
@ -274,6 +272,35 @@ class HP3PARFCDriver(cinder.volume.driver.FibreChannelDriver):
persona_id)
host = self.common._get_3par_host(hostname)
return self._add_new_wwn_to_host(host, connector['wwpns'])
def _add_new_wwn_to_host(self, host, wwns):
"""Add wwns to a host if one or more don't exist.
Identify if argument wwns contains any world wide names
not configured in the 3PAR host path. If any are found,
add them to the 3PAR host.
"""
# get the currently configured wwns
# from the host's FC paths
host_wwns = []
if 'FCPaths' in host:
for path in host['FCPaths']:
wwn = path.get('wwn', None)
if wwn is not None:
host_wwns.append(wwn.lower())
# lower case all wwns in the compare list
compare_wwns = [x.lower() for x in wwns]
# calculate wwns in compare list, but not in host_wwns list
new_wwns = list(set(compare_wwns).difference(host_wwns))
# if any wwns found that were not in host list,
# add them to the host
if (len(new_wwns) > 0):
self._modify_3par_fibrechan_host(host['name'], new_wwns)
host = self.common._get_3par_host(host['name'])
return host
@utils.synchronized('3par', external=True)