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()
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):
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']}
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,
# 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)