Unit tests for OpenStack Cinder volume drivers
"""
import ast
+import mock
import mox
import shutil
import tempfile
'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)
'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)
'pathOperation': 1})
getHost('fakehost').AndReturn({'name': self.FAKE_HOST,
- 'FCPaths': [{'WWN': '123456789012345'},
- {'WWN': '123456789054321'}]}
+ 'FCPaths': [{'wwn': '123456789012345'},
+ {'wwn': '123456789054321'}]}
)
self.mox.ReplayAll()
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):
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)
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)
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)