SF remove deprecated sf_allow_template_caching

The solidfire driver currently has built in template image caching. It
is not manageable through the normal OpenStack API's. SolidFires
template image caching is the main cause for the exception
DuplicateSFVolumeNames when the system is running in an HA
A/A configuration.

The current implementation has the configuration variable
sf_allow_template_caching, the module _create_image_volume, and
_verify_image_volume, which will be removed.

Cinder has the same functionality which is managed through the OpenStack
APIs and will not have the same HA A/A side effect of throwing
DuplicateSDuplicateSFVolumeNames exception.

Change-Id: I69ce8bda4302bf821739eb7276f2db12b7a259fe
This commit is contained in:
jarbassaidai 2019-06-11 12:15:41 -06:00 committed by Jay Rubenstein
parent 99a6fcdcf6
commit fd495d010c
3 changed files with 16 additions and 383 deletions

View File

@ -314,6 +314,8 @@ class SolidFireVolumeTestCase(test.TestCase):
}]
}
}
elif method == 'ListSnapshots':
raise exception.VolumeNotFound('test clone unconfigured image')
else:
# Crap, unimplemented API call in Fake
return None
@ -347,12 +349,9 @@ class SolidFireVolumeTestCase(test.TestCase):
return {'fake': 'fake-model'}
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
def test_create_volume_with_qos_type(self,
_mock_create_template_account,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
_mock_create_template_account.return_value = 1
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
@ -411,12 +410,9 @@ class SolidFireVolumeTestCase(test.TestCase):
sfv.create_volume(testvol)['qos'])
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
def test_create_volume(self,
_mock_create_template_account,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
_mock_create_template_account.return_value = 1
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
@ -440,12 +436,9 @@ class SolidFireVolumeTestCase(test.TestCase):
self.assertIsNone(model_update.get('provider_geometry', None))
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
def test_create_volume_non_512e(self,
_mock_create_template_account,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
_mock_create_template_account.return_value = 1
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
@ -500,12 +493,9 @@ class SolidFireVolumeTestCase(test.TestCase):
sfv.delete_snapshot(testsnap)
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
def test_create_clone(self,
_mock_create_template_account,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
_mock_create_template_account.return_value = 1
_fake_get_snaps = [{'snapshotID': 5, 'name': 'testvol'}]
_fake_get_volume = (
{'volumeID': 99,
@ -1218,12 +1208,9 @@ class SolidFireVolumeTestCase(test.TestCase):
self.assertEqual(2, size)
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
def test_create_volume_for_migration(self,
_mock_create_template_account,
_mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
_mock_create_template_account.return_value = 1
testvol = {'project_id': 'testprjid',
'name': 'testvol',
'size': 1,
@ -1258,81 +1245,6 @@ class SolidFireVolumeTestCase(test.TestCase):
self.assertEqual('UUID-a720b3c0-d1f0-11e1-9b23-0800200c9a66',
sf_vol_object['name'])
@mock.patch.object(solidfire.SolidFireDriver, '_update_cluster_status')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_create_image_volume')
def test_verify_image_volume_out_of_date(self,
_mock_create_image_volume,
_mock_get_sf_volume,
_mock_get_sfaccount,
_mock_issue_api_request,
_mock_update_cluster_status):
fake_sf_vref = {
'status': 'active', 'volumeID': 1,
'attributes': {
'image_info':
{'image_updated_at': '2014-12-17T00:16:23+00:00',
'image_id': '17c550bb-a411-44c0-9aaf-0d96dd47f501',
'image_name': 'fake-image',
'image_created_at': '2014-12-17T00:16:23+00:00'}}}
_mock_update_cluster_status.return_value = None
_mock_issue_api_request.side_effect = (
self.fake_issue_api_request)
_mock_get_sfaccount.return_value = {'username': 'openstack-vtemplate',
'accountID': 7777}
_mock_get_sf_volume.return_value = fake_sf_vref
_mock_create_image_volume.return_value = fake_sf_vref
image_meta = {'id': '17c550bb-a411-44c0-9aaf-0d96dd47f501',
'updated_at': datetime.datetime(2013, 9, 28,
15, 27, 36,
325355)}
image_service = 'null'
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv._verify_image_volume(self.ctxt, image_meta, image_service)
self.assertTrue(_mock_create_image_volume.called)
@mock.patch.object(solidfire.SolidFireDriver, '_update_cluster_status')
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sfaccount')
@mock.patch.object(solidfire.SolidFireDriver, '_get_sf_volume')
@mock.patch.object(solidfire.SolidFireDriver, '_create_image_volume')
def test_verify_image_volume_ok(self,
_mock_create_image_volume,
_mock_get_sf_volume,
_mock_get_sfaccount,
_mock_issue_api_request,
_mock_update_cluster_status):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
_mock_update_cluster_status.return_value = None
_mock_get_sfaccount.return_value = {'username': 'openstack-vtemplate',
'accountID': 7777}
_mock_get_sf_volume.return_value =\
{'status': 'active', 'volumeID': 1,
'attributes': {
'image_info':
{'image_updated_at': '2013-09-28T15:27:36.325355',
'image_id': '17c550bb-a411-44c0-9aaf-0d96dd47f501',
'image_name': 'fake-image',
'image_created_at': '2014-12-17T00:16:23+00:00'}}}
_mock_create_image_volume.return_value = None
image_meta = {'id': '17c550bb-a411-44c0-9aaf-0d96dd47f501',
'updated_at': datetime.datetime(2013, 9, 28,
15, 27, 36,
325355)}
image_service = 'null'
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
sfv._verify_image_volume(self.ctxt, image_meta, image_service)
self.assertFalse(_mock_create_image_volume.called)
@mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
def test_clone_image_not_configured(self, _mock_issue_api_request):
_mock_issue_api_request.side_effect = self.fake_issue_api_request
@ -1345,129 +1257,6 @@ class SolidFireVolumeTestCase(test.TestCase):
self.fake_image_meta,
'fake'))
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
@mock.patch.object(solidfire.SolidFireDriver, '_create_image_volume')
def test_clone_image_authorization(self,
_mock_create_image_volume,
_mock_create_template_account):
fake_sf_vref = {
'status': 'active', 'volumeID': 1,
'attributes': {
'image_info':
{'image_updated_at': '2014-12-17T00:16:23+00:00',
'image_id': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
'image_name': 'fake-image',
'image_created_at': '2014-12-17T00:16:23+00:00'}}}
_mock_create_image_volume.return_value = fake_sf_vref
_mock_create_template_account.return_value = 1
self.configuration.sf_allow_template_caching = True
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
# Make sure if it's NOT public and we're NOT the owner it
# doesn't try and cache
timestamp = datetime.datetime(2011, 1, 1, 1, 2, 3)
_fake_image_meta = {
'id': '155d900f-4e14-4e4c-a73d-069cbf4541e6',
'name': 'fakeimage123456',
'created_at': timestamp,
'updated_at': timestamp,
'deleted_at': None,
'deleted': False,
'status': 'active',
'visibility': 'private',
'protected': False,
'container_format': 'raw',
'disk_format': 'raw',
'owner': 'wrong-owner',
'properties': {'kernel_id': 'nokernel',
'ramdisk_id': 'nokernel',
'architecture': 'x86_64'}}
with mock.patch.object(sfv, '_do_clone_volume',
return_value=('fe', 'fi', 'fo')):
self.assertEqual((None, False),
sfv.clone_image(self.ctxt,
self.mock_volume,
'fake',
_fake_image_meta,
self.fake_image_service))
# And is_public False, but the correct owner does work
_fake_image_meta['owner'] = 'testprjid'
self.assertEqual(
('fo', True),
sfv.clone_image(
self.ctxt,
self.mock_volume,
'fake',
_fake_image_meta,
self.fake_image_service))
# And is_public True, even if not the correct owner
_fake_image_meta['is_public'] = True
_fake_image_meta['owner'] = 'wrong-owner'
self.assertEqual(
('fo', True),
sfv.clone_image(self.ctxt,
self.mock_volume,
'fake',
_fake_image_meta,
self.fake_image_service))
# And using the new V2 visibility tag
_fake_image_meta['visibility'] = 'public'
_fake_image_meta['owner'] = 'wrong-owner'
self.assertEqual(
('fo', True),
sfv.clone_image(self.ctxt,
self.mock_volume,
'fake',
_fake_image_meta,
self.fake_image_service))
def test_create_template_no_account(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
def _fake_issue_api_req(method, params, version=0):
if 'GetAccountByName' in method:
raise solidfire.SolidFireAPIException
return {'result': {'accountID': 1}}
with mock.patch.object(sfv,
'_issue_api_request',
side_effect=_fake_issue_api_req):
self.assertEqual(1,
sfv._create_template_account('foo'))
def test_configured_svip(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
def _fake_get_volumes(account_id, endpoint=None):
return [{'volumeID': 1,
'iqn': ''}]
def _fake_get_cluster_info():
return {'clusterInfo': {'svip': '10.10.10.10',
'mvip': '1.1.1.1'}}
with mock.patch.object(sfv,
'_get_volumes_by_sfaccount',
side_effect=_fake_get_volumes),\
mock.patch.object(sfv,
'_issue_api_request',
side_effect=self.fake_issue_api_request):
sfaccount = {'targetSecret': 'yakitiyak',
'accountID': 5,
'username': 'bobthebuilder'}
v = sfv._get_model_info(sfaccount, 1)
self.assertEqual('1.1.1.1:3260 0', v['provider_location'])
configured_svip = '9.9.9.9:6500'
sfv.active_cluster['svip'] = configured_svip
v = sfv._get_model_info(sfaccount, 1)
self.assertEqual('%s 0' % configured_svip, v['provider_location'])
def test_init_volume_mappings(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)

View File

@ -34,7 +34,6 @@ import six
from cinder import context
from cinder import exception
from cinder.i18n import _
from cinder.image import image_utils
from cinder import interface
from cinder.objects import fields
from cinder import utils
@ -68,21 +67,6 @@ sf_opts = [
'are of the form <sf_volume_prefix><cinder-volume-id>. '
'The default is to use a prefix of \'UUID-\'.'),
cfg.StrOpt('sf_template_account_name',
default='openstack-vtemplate',
help='Account name on the SolidFire Cluster to use as owner of '
'template/cache volumes (created if does not exist).'),
cfg.BoolOpt('sf_allow_template_caching',
deprecated_for_removal=True,
deprecated_reason='The Cinder caching feature should be '
'used rather than this driver specific '
'implementation.',
default=False,
help='This option is deprecated and will be removed in '
'the next OpenStack release. Please use the general '
'cinder image-caching feature instead.'),
cfg.StrOpt('sf_svip',
help='Overrides default cluster SVIP with the one specified. '
'This is required or deployments that have implemented '
@ -311,10 +295,6 @@ class SolidFireDriver(san.SanISCSIDriver):
except SolidFireAPIException:
pass
if self.configuration.sf_allow_template_caching:
account = self.configuration.sf_template_account_name
self.template_account_id = self._create_template_account(account)
self._set_cluster_pairs()
@staticmethod
@ -497,27 +477,6 @@ class SolidFireDriver(san.SanISCSIDriver):
snapshot_updates = self._init_snapshot_mappings(snaprefs)
return (volume_updates, snapshot_updates)
def _create_template_account(self, account_name):
# We raise an API exception if the account doesn't exist
# We need to take account_prefix settings into consideration
# This just uses the same method to do template account create
# as we use for any other OpenStack account
account_name = self._get_sf_account_name(account_name)
try:
id = self._issue_api_request(
'GetAccountByName',
{'username': account_name})['result']['account']['accountID']
except SolidFireAPIException:
chap_secret = self._generate_random_string(12)
params = {'username': account_name,
'initiatorSecret': chap_secret,
'targetSecret': chap_secret,
'attributes': {}}
id = self._issue_api_request('AddAccount',
params)['result']['accountID']
return id
def _build_endpoint_info(self, **kwargs):
endpoint = {}
@ -1001,122 +960,6 @@ class SolidFireDriver(san.SanISCSIDriver):
return self._issue_api_request(
'ListSnapshots', params, version='6.0')['result']['snapshots']
def _create_image_volume(self, context,
image_meta, image_service,
image_id):
with image_utils.TemporaryImages.fetch(image_service,
context,
image_id) as tmp_image:
data = image_utils.qemu_img_info(tmp_image)
fmt = data.file_format
if fmt is None:
raise exception.ImageUnacceptable(
reason=_("'qemu-img info' parsing failed."),
image_id=image_id)
backing_file = data.backing_file
if backing_file is not None:
raise exception.ImageUnacceptable(
image_id=image_id,
reason=_("fmt=%(fmt)s backed by:%(backing_file)s")
% {'fmt': fmt, 'backing_file': backing_file, })
virtual_size = int(math.ceil(float(data.virtual_size) / units.Gi))
attributes = {}
attributes['image_info'] = {}
attributes['image_info']['image_updated_at'] = (
image_meta['updated_at'].isoformat())
attributes['image_info']['image_name'] = (
image_meta['name'])
attributes['image_info']['image_created_at'] = (
image_meta['created_at'].isoformat())
attributes['image_info']['image_id'] = image_meta['id']
params = {'name': 'OpenStackIMG-%s' % image_id,
'accountID': self.template_account_id,
'sliceCount': 1,
'totalSize': int(virtual_size * units.Gi),
'enable512e': self.configuration.sf_emulate_512,
'attributes': attributes,
'qos': {}}
sf_account = self._issue_api_request(
'GetAccountByID',
{'accountID': self.template_account_id})['result']['account']
template_vol = self._do_volume_create(sf_account, params)
tvol = {}
tvol['id'] = image_id
tvol['provider_location'] = template_vol['provider_location']
tvol['provider_auth'] = template_vol['provider_auth']
attach_info = None
try:
connector = {'multipath': False}
conn = self.initialize_connection(tvol, connector)
attach_info = super(SolidFireDriver, self)._connect_device(
conn)
properties = 'na'
image_utils.convert_image(tmp_image,
attach_info['device']['path'],
'raw',
run_as_root=True)
data = image_utils.qemu_img_info(attach_info['device']['path'],
run_as_root=True)
if data.file_format != 'raw':
raise exception.ImageUnacceptable(
image_id=image_id,
reason=_("Converted to %(vol_format)s, but format is "
"now %(file_format)s") % {'vol_format': 'raw',
'file_format': data.
file_format})
except Exception as exc:
vol = self._get_sf_volume(image_id)
LOG.error('Failed image conversion during '
'cache creation: %s',
exc)
LOG.debug('Removing SolidFire Cache Volume (SF ID): %s',
vol['volumeID'])
if attach_info is not None:
self._detach_volume(context, attach_info, tvol, properties)
self._issue_api_request('DeleteVolume', params)
self._issue_api_request('PurgeDeletedVolume', params)
return
self._detach_volume(context, attach_info, tvol, properties)
sf_vol = self._get_sf_volume(image_id, params)
LOG.debug('Successfully created SolidFire Image Template '
'for image-id: %s', image_id)
return sf_vol
def _verify_image_volume(self, context, image_meta, image_service):
# This method just verifies that IF we have a cache volume that
# it's still up to date and current WRT the image in Glance
# ie an image-update hasn't occurred since we grabbed it
# If it's out of date, just delete it and we'll create a new one
# Any other case we don't care and just return without doing anything
params = {'accountID': self.template_account_id}
sf_vol = self._get_sf_volume(image_meta['id'], params)
if not sf_vol:
self._create_image_volume(context,
image_meta,
image_service,
image_meta['id'])
return
if sf_vol['attributes']['image_info']['image_updated_at'] != (
image_meta['updated_at'].isoformat()):
params = {'accountID': self.template_account_id}
params['volumeID'] = sf_vol['volumeID']
self._issue_api_request('DeleteVolume', params)
self._issue_api_request('PurgeDeletedVolume', params)
self._create_image_volume(context,
image_meta,
image_service,
image_meta['id'])
def _get_sfaccounts_for_tenant(self, cinder_project_id):
accounts = self._issue_api_request(
'ListAccounts', {})['result']['accounts']
@ -1341,11 +1184,6 @@ class SolidFireDriver(san.SanISCSIDriver):
image_meta, image_service):
"""Clone an existing image volume."""
public = False
# Check out pre-requisites:
# Is template caching enabled?
if not self.configuration.sf_allow_template_caching:
return None, False
# NOTE(jdg): Glance V2 moved from is_public to visibility
# so we check both, as we don't necessarily know or want
# to care which we're using. Will need to look at
@ -1363,17 +1201,14 @@ class SolidFireDriver(san.SanISCSIDriver):
LOG.warning("Requested image is not "
"accessible by current Tenant.")
return None, False
# If we don't have the image-volume to clone from return failure
# cinder driver will then create source for clone first
try:
self._verify_image_volume(context,
image_meta,
image_service)
except SolidFireAPIException:
(data, sfaccount, model) = self._do_clone_volume(image_meta['id'],
volume)
except exception.VolumeNotFound:
return None, False
# Ok, should be good to go now, try it again
(data, sfaccount, model) = self._do_clone_volume(image_meta['id'],
volume)
return model, True
def _retrieve_qos_setting(self, volume):

View File

@ -0,0 +1,9 @@
---
prelude: >
SolidFire cinder driver deprecate sf_allow_template_caching
deprecations:
- |
The configuration option sf_allow_template_caching for the
SolidFire cinder driver has been removed. Use image_volume_cache_enabled
equals True for a better template image cache that is managed from
cinder.