]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Additional VAG support for SolidFire
authorChris Morrell <chris@solidfire.com>
Wed, 18 Nov 2015 22:14:15 +0000 (22:14 +0000)
committerChris Morrell <chris@solidfire.com>
Wed, 18 Nov 2015 22:14:15 +0000 (22:14 +0000)
This patch removes volumes from volume access groups, and potentially volume
access groups, when a connection is terminated. When the previously added
'sf_enable_vag' option is enabled and a volume connection is terminated, if the
volume was part of a VAG then the volume is removed from the VAG. If the volume
is the only remaining volume in the VAG then the VAG is also removed.

Change-Id: Ib508a67f012f6f186abba0d381ab104edc163fa9

cinder/tests/unit/test_solidfire.py
cinder/volume/drivers/solidfire.py

index b12d91ef19f7b633ea42c1844537eb1711d8518a..369a17f6516cf4cb4ead0c3ac1aa5531b6aff4e2 100644 (file)
@@ -1170,3 +1170,79 @@ class SolidFireVolumeTestCase(test.TestCase):
                               side_effect=add_volume_to_vag_check):
 
             sfv.initialize_connection(testvol, connector)
+
+    def test_remove_vag(self):
+        vag = {'attributes': {},
+               'deletedVolumes': [],
+               'initiators': [],
+               'name': 'TESTIQN',
+               'volumeAccessGroupID': 1,
+               'volumes': [1],
+               'virtualNetworkIDs': []}
+        testvol = {'project_id': 'testprjid',
+                   'name': 'testvol',
+                   'size': 1,
+                   'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
+                   'volume_type_id': None,
+                   'provider_location': '10.10.7.1:3260 iqn.2010-01.com.'
+                                        'solidfire:87hg.uuid-2cc06226-cc'
+                                        '74-4cb7-bd55-14aed659a0cc.4060 0',
+                   'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
+                                    'c76370d66b 2FE0CQ8J196R',
+                   'provider_geometry': '4096 4096',
+                   'created_at': timeutils.utcnow(),
+                   'provider_id': "1 1 1"
+                   }
+        connector = {'initiator': 'iqn.2012-07.org.fake:01'}
+        mod_conf = self.configuration
+        mod_conf.sf_enable_vag = True
+        sfv = solidfire.SolidFireDriver(configuration=mod_conf)
+
+        with mock.patch.object(sfv,
+                               '_get_vags',
+                               return_value=[vag]), \
+            mock.patch.object(sfv,
+                              '_remove_vag') as mock_rem_vag:
+            sfv.terminate_connection(testvol, connector, force=False)
+            mock_rem_vag.assert_called_with(vag['volumeAccessGroupID'])
+
+    def test_remove_volume_from_vag(self):
+        vag = {'attributes': {},
+               'deletedVolumes': [],
+               'initiators': [],
+               'name': 'TESTIQN',
+               'volumeAccessGroupID': 1,
+               'volumes': [1, 2],
+               'virtualNetworkIDs': []}
+        testvol = {'project_id': 'testprjid',
+                   'name': 'testvol',
+                   'size': 1,
+                   'id': 'a720b3c0-d1f0-11e1-9b23-0800200c9a66',
+                   'volume_type_id': None,
+                   'provider_location': '10.10.7.1:3260 iqn.2010-01.com.'
+                                        'solidfire:87hg.uuid-2cc06226-cc'
+                                        '74-4cb7-bd55-14aed659a0cc.4060 0',
+                   'provider_auth': 'CHAP stack-1-a60e2611875f40199931f2'
+                                    'c76370d66b 2FE0CQ8J196R',
+                   'provider_geometry': '4096 4096',
+                   'created_at': timeutils.utcnow(),
+                   'provider_id': "1 1 1"
+                   }
+        connector = {'initiator': 'iqn.2012-07.org.fake:01'}
+        mod_conf = self.configuration
+        mod_conf.sf_enable_vag = True
+        sfv = solidfire.SolidFireDriver(configuration=mod_conf)
+        provider_id = testvol['provider_id']
+        vol_id = int(sfv._parse_provider_id_string(provider_id)[0])
+
+        with mock.patch.object(sfv,
+                               '_get_vags',
+                               return_value=[vag]), \
+            mock.patch.object(sfv,
+                              '_remove_vag') as mock_rem_vag, \
+            mock.patch.object(sfv,
+                              '_remove_volume_from_vag') as mock_rem_vol_vag:
+            sfv.terminate_connection(testvol, connector, force=False)
+            mock_rem_vol_vag.assert_called_with(vol_id,
+                                                vag['volumeAccessGroupID'])
+            mock_rem_vag.assert_not_called()
index 3667c9803122d1875e8a5bec162740fbe43e9ab1..2021ea2a9dd07dff8f45f97795c397d7ce028f19 100644 (file)
@@ -858,6 +858,19 @@ class SolidFireDriver(san.SanISCSIDriver):
                                 params,
                                 version='7.0')
 
+    def _remove_volume_from_vag(self, vol_id, vag_id):
+        params = {"volumeAccessGroupID": vag_id,
+                  "volumes": [vol_id]}
+        self._issue_api_request('RemoveVolumesFromVolumeAccessGroup',
+                                params,
+                                version='7.0')
+
+    def _remove_vag(self, vag_id):
+        params = {"volumeAccessGroupID": vag_id}
+        self._issue_api_request('DeleteVolumeAccessGroup',
+                                params,
+                                version='7.0')
+
     def clone_image(self, context,
                     volume, image_location,
                     image_meta, image_service):
@@ -1165,6 +1178,11 @@ class SolidFireDriver(san.SanISCSIDriver):
 
         self._issue_api_request('ModifyVolume', params)
 
+    def terminate_connection(self, volume, properties, force):
+        return self._sf_terminate_connection(volume,
+                                             properties,
+                                             force)
+
     def detach_volume(self, context, volume, attachment=None):
         sfaccount = self._get_sfaccount(volume['project_id'])
         params = {'accountID': sfaccount['accountID']}
@@ -1404,6 +1422,13 @@ class SolidFireISCSI(iscsi_driver.SanISCSITarget):
                 vag_id = self._create_vag(vag_name)
                 vag = self._get_vags(vag_name)[0]
 
+            # TODO(chrismorrell): There is a potential race condition if a
+            # volume is attached and another is detached on the same
+            # host/initiator. The detach may purge the VAG before the attach
+            # has a chance to add the volume to the VAG. Propose combining
+            # add_initiator_to_vag and add_volume_to_vag with a retry on
+            # SFAPI exception regarding nonexistent VAG.
+
             # Verify IQN matches.
             if raw_iqn not in vag['initiators']:
                 self._add_initiator_to_vag(raw_iqn,
@@ -1415,3 +1440,28 @@ class SolidFireISCSI(iscsi_driver.SanISCSITarget):
         # Continue along with default behavior
         return super(SolidFireISCSI, self).initialize_connection(volume,
                                                                  connector)
+
+    def _sf_terminate_connection(self, volume, properties, force):
+        """Terminate the volume connection.
+
+           Optionally remove volume from volume access group.
+           If the VAG is empty then the VAG is also removed.
+        """
+        if self.configuration.sf_enable_vag:
+            raw_iqn = properties['initiator']
+            vag_name = re.sub('[^0-9a-zA-Z]+', '-', raw_iqn)
+            vag = self._get_vags(vag_name)
+            provider_id = volume['provider_id']
+            vol_id = int(self._parse_provider_id_string(provider_id)[0])
+
+            if vag:
+                vag = vag[0]
+                vag_id = vag['volumeAccessGroupID']
+                if [vol_id] == vag['volumes']:
+                    self._remove_vag(vag_id)
+                elif vol_id in vag['volumes']:
+                    self._remove_volume_from_vag(vol_id, vag_id)
+
+        return super(SolidFireISCSI, self).terminate_connection(volume,
+                                                                properties,
+                                                                force=force)