]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
3PAR FC: add ability to add WWNs to host
authorJim Branen <james.branen@hp.com>
Fri, 6 Dec 2013 23:32:53 +0000 (15:32 -0800)
committerJim Branen <james.branen@hp.com>
Mon, 16 Dec 2013 19:06:36 +0000 (11:06 -0800)
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

cinder/tests/test_hp3par.py
cinder/volume/drivers/san/hp/hp_3par_fc.py

index 80f28e65ceece662a734eadfc0a05d1cc936f776..e4e2b293e4482f248262e69845f0d750e6c9ff4a 100644 (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):
 
index 39f605da7e8fd8b72f5e8fff413acb181bad264f..4e11be05d570b5172556a2b57053267f5e9d45ed 100644 (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)