]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Added VAG support to SolidFire
authorChris Morrell <chris@solidfire.com>
Mon, 16 Nov 2015 21:45:48 +0000 (21:45 +0000)
committerChris Morrell <chris@solidfire.com>
Tue, 17 Nov 2015 22:40:19 +0000 (22:40 +0000)
This patch adds VAG support to the SolidFire driver.
This includes a new config option 'sf_enable_vag'.
The driver creates a new VAG based on initiator name during
connection initialization.

By default VAG support is disabled. When enabled, the driver uses both
CHAP and VAG authentication.

DocImpact
Added configuration option sf_enable_vag.

Change-Id: I4ade805640646173c27bc6d969a211189f6301fb

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

index a3811e90ab745f6021e80379932766ee8aec60e0..b12d91ef19f7b633ea42c1844537eb1711d8518a 100644 (file)
@@ -52,6 +52,7 @@ class SolidFireVolumeTestCase(test.TestCase):
         self.configuration.sf_svip = None
         self.configuration.sf_enable_volume_mapping = True
         self.configuration.sf_volume_prefix = 'UUID-'
+        self.configuration.sf_enable_vag = False
 
         super(SolidFireVolumeTestCase, self).setUp()
         self.stubs.Set(solidfire.SolidFireDriver,
@@ -1110,3 +1111,62 @@ class SolidFireVolumeTestCase(test.TestCase):
         with mock.patch.object(
                 sfv, '_issue_api_request', side_effect=_fake_issue_api_req):
             self.assertEqual(5, sfv._get_sf_volume(test_name, 8)['volumeID'])
+
+    def test_create_vag(self):
+        global counter
+        counter = 0
+
+        def _trick_get_vag(vag_name):
+            # On the second call to get_vag we want to return a fake VAG
+            # result as required by logic of _sf_initialize_connection.
+            global counter
+            vag = {'attributes': {},
+                   'deletedVolumes': [],
+                   'initiators': [],
+                   'name': 'TESTIQN',
+                   'volumeAccessGroupID': 1,
+                   'volumes': [],
+                   'virtualNetworkIDs': []}
+
+            if counter == 1:
+                return [vag]
+            counter += 1
+
+        mod_conf = self.configuration
+        mod_conf.sf_enable_vag = True
+        sfv = solidfire.SolidFireDriver(configuration=mod_conf)
+
+        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'}
+
+        def add_volume_to_vag_check(vol_id, vag_id):
+            self.assertEqual(1, vol_id)
+            self.assertEqual(1, vag_id)
+
+        with mock.patch.object(sfv,
+                               '_create_vag',
+                               return_value=1), \
+            mock.patch.object(sfv,
+                              '_get_vags',
+                              side_effect=_trick_get_vag), \
+            mock.patch.object(sfv,
+                              '_add_initiator_to_vag'), \
+            mock.patch.object(sfv,
+                              '_add_volume_to_vag',
+                              side_effect=add_volume_to_vag_check):
+
+            sfv.initialize_connection(testvol, connector)
index 0bac423a9c43b3e27ce3e91355bf4a476b60c36e..3667c9803122d1875e8a5bec162740fbe43e9ab1 100644 (file)
@@ -16,6 +16,7 @@
 import json
 import math
 import random
+import re
 import socket
 import string
 import time
@@ -87,7 +88,11 @@ sf_opts = [
     cfg.PortOpt('sf_api_port',
                 default=443,
                 help='SolidFire API port. Useful if the device api is behind '
-                     'a proxy on a different port.')]
+                     'a proxy on a different port.'),
+
+    cfg.BoolOpt('sf_enable_vag',
+                default=False,
+                help='Utilize volume access groups on a per-tenant basis.')]
 
 CONF = cfg.CONF
 CONF.register_opts(sf_opts)
@@ -814,6 +819,45 @@ class SolidFireDriver(san.SanISCSIDriver):
         vlist = sorted(vlist, key=lambda k: k['volumeID'])
         return vlist
 
+    def _create_vag(self, vag_name):
+        """Create a volume access group(vag).
+
+           Returns the vag_id.
+        """
+        params = {'name': vag_name}
+        result = self._issue_api_request('CreateVolumeAccessGroup',
+                                         params,
+                                         version='7.0')
+        return result['result']['volumeAccessGroupID']
+
+    def _get_vags(self, vag_name):
+        """Retrieve SolidFire volume access group objects by name.
+
+           Returns an array of vags with a matching name value.
+           Returns an empty array if there are no matches.
+        """
+        params = {}
+        vags = self._issue_api_request(
+            'ListVolumeAccessGroups',
+            params,
+            version='7.0')['result']['volumeAccessGroups']
+        matching_vags = [vag for vag in vags if vag['name'] == vag_name]
+        return matching_vags
+
+    def _add_initiator_to_vag(self, iqn, vag_id):
+        params = {"initiators": [iqn],
+                  "volumeAccessGroupID": vag_id}
+        self._issue_api_request('AddInitiatorsToVolumeAccessGroup',
+                                params,
+                                version='7.0')
+
+    def _add_volume_to_vag(self, vol_id, vag_id):
+        params = {"volumeAccessGroupID": vag_id,
+                  "volumes": [vol_id]}
+        self._issue_api_request('AddVolumesToVolumeAccessGroup',
+                                params,
+                                version='7.0')
+
     def clone_image(self, context,
                     volume, image_location,
                     image_meta, image_service):
@@ -1088,6 +1132,15 @@ class SolidFireDriver(san.SanISCSIDriver):
             results['thinProvisioningPercent'])
         self.cluster_stats = data
 
+    def initialize_connection(self, volume, connector, initiator_data=None):
+        """Initialize the connection and return connection info.
+
+           Optionally checks and utilizes volume access groups.
+        """
+        return self._sf_initialize_connection(volume,
+                                              connector,
+                                              initiator_data)
+
     def attach_volume(self, context, volume,
                       instance_uuid, host_name,
                       mountpoint):
@@ -1330,3 +1383,35 @@ class SolidFireISCSI(iscsi_driver.SanISCSITarget):
 
     def terminate_connection(self, volume, connector, **kwargs):
         pass
+
+    def _sf_initialize_connection(self, volume, connector,
+                                  initiator_data=None):
+        """Initialize the connection and return connection info.
+
+           Optionally checks and utilizes volume access groups.
+        """
+        if self.configuration.sf_enable_vag:
+            raw_iqn = connector['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_id = vag[0]['volumeAccessGroupID']
+                vag = vag[0]
+            else:
+                vag_id = self._create_vag(vag_name)
+                vag = self._get_vags(vag_name)[0]
+
+            # Verify IQN matches.
+            if raw_iqn not in vag['initiators']:
+                self._add_initiator_to_vag(raw_iqn,
+                                           vag_id)
+            # Add volume to vag if not already.
+            if vol_id not in vag['volumes']:
+                self._add_volume_to_vag(vol_id, vag_id)
+
+        # Continue along with default behavior
+        return super(SolidFireISCSI, self).initialize_connection(volume,
+                                                                 connector)