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,
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)
import json
import math
import random
+import re
import socket
import string
import time
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)
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):
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):
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)