Merge "Add lock decorator to SolidFire clone_image method"
This commit is contained in:
commit
fa8201b7ce
@ -24,6 +24,7 @@ from cinder import context
|
||||
from cinder import exception
|
||||
from cinder.objects import fields
|
||||
from cinder import test
|
||||
from cinder.tests.unit.image import fake as fake_image
|
||||
from cinder.volume import configuration as conf
|
||||
from cinder.volume.drivers import solidfire
|
||||
from cinder.volume import qos_specs
|
||||
@ -79,7 +80,7 @@ class SolidFireVolumeTestCase(test.TestCase):
|
||||
325355),
|
||||
'is_public': True,
|
||||
'owner': 'testprjid'}
|
||||
self.fake_image_service = 'null'
|
||||
self.fake_image_service = fake_image.FakeImageService()
|
||||
|
||||
def fake_init_cluster_pairs(*args, **kwargs):
|
||||
return None
|
||||
@ -960,7 +961,19 @@ class SolidFireVolumeTestCase(test.TestCase):
|
||||
'fake'))
|
||||
|
||||
@mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
|
||||
def test_clone_image_authorization(self, _mock_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
|
||||
@ -968,14 +981,24 @@ class SolidFireVolumeTestCase(test.TestCase):
|
||||
|
||||
# Make sure if it's NOT public and we're NOT the owner it
|
||||
# doesn't try and cache
|
||||
_fake_image_meta = {'id': '17c550bb-a411-44c0-9aaf-0d96dd47f501',
|
||||
'updated_at': datetime.datetime(2013, 9,
|
||||
28, 15,
|
||||
27, 36,
|
||||
325355),
|
||||
'properties': {'virtual_size': 1},
|
||||
'is_public': False,
|
||||
'owner': 'wrong-owner'}
|
||||
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),
|
||||
@ -983,32 +1006,39 @@ class SolidFireVolumeTestCase(test.TestCase):
|
||||
self.mock_volume,
|
||||
'fake',
|
||||
_fake_image_meta,
|
||||
'fake'))
|
||||
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,
|
||||
'fake'))
|
||||
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,
|
||||
'fake'))
|
||||
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,
|
||||
'fake'))
|
||||
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)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# All Rights Reserved.
|
||||
# Copyright 2013 SolidFire Inc
|
||||
#
|
||||
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import inspect
|
||||
import json
|
||||
import math
|
||||
import random
|
||||
@ -37,6 +38,7 @@ from cinder.i18n import _, _LE, _LW
|
||||
from cinder.image import image_utils
|
||||
from cinder import interface
|
||||
from cinder.objects import fields
|
||||
from cinder import utils
|
||||
from cinder.volume.drivers.san import san
|
||||
from cinder.volume import qos_specs
|
||||
from cinder.volume.targets import iscsi as iscsi_driver
|
||||
@ -151,9 +153,11 @@ class SolidFireDriver(san.SanISCSIDriver):
|
||||
2.0.4 - Implement volume replication
|
||||
2.0.5 - Try and deal with the stupid retry/clear issues from objects
|
||||
and tflow
|
||||
2.0.6 - Add a lock decorator around the clone_image method
|
||||
"""
|
||||
|
||||
VERSION = '2.0.2'
|
||||
VERSION = '2.0.6'
|
||||
driver_prefix = 'solidfire'
|
||||
|
||||
sf_qos_dict = {'slow': {'minIOPS': 100,
|
||||
'maxIOPS': 200,
|
||||
@ -746,6 +750,24 @@ class SolidFireDriver(san.SanISCSIDriver):
|
||||
return self._issue_api_request(
|
||||
'ListSnapshots', params, version='6.0')['result']['snapshots']
|
||||
|
||||
def locked_image_id_operation(f, external=False):
|
||||
def lvo_inner1(inst, *args, **kwargs):
|
||||
lock_tag = inst.driver_prefix
|
||||
call_args = inspect.getcallargs(f, inst, *args, **kwargs)
|
||||
|
||||
if call_args.get('image_meta'):
|
||||
image_id = call_args['image_meta']['id']
|
||||
else:
|
||||
err_msg = _('The decorated method must accept image_meta.')
|
||||
raise exception.VolumeBackendAPIException(data=err_msg)
|
||||
|
||||
@utils.synchronized('%s-%s' % (lock_tag, image_id),
|
||||
external=external)
|
||||
def lvo_inner2():
|
||||
return f(inst, *args, **kwargs)
|
||||
return lvo_inner2()
|
||||
return lvo_inner1
|
||||
|
||||
def _create_image_volume(self, context,
|
||||
image_meta, image_service,
|
||||
image_id):
|
||||
@ -839,24 +861,22 @@ class SolidFireDriver(san.SanISCSIDriver):
|
||||
|
||||
params = {'accountID': self.template_account_id}
|
||||
sf_vol = self._get_sf_volume(image_meta['id'], params)
|
||||
if sf_vol is None:
|
||||
if not sf_vol:
|
||||
self._create_image_volume(context,
|
||||
image_meta,
|
||||
image_service,
|
||||
image_meta['id'])
|
||||
return
|
||||
|
||||
# Check updated_at field, delete copy and update if needed
|
||||
if sf_vol['attributes']['image_info']['image_updated_at'] == (
|
||||
if sf_vol['attributes']['image_info']['image_updated_at'] != (
|
||||
image_meta['updated_at'].isoformat()):
|
||||
return
|
||||
else:
|
||||
# Bummer, it's been updated, delete it
|
||||
params = {'accountID': self.template_account_id}
|
||||
params['volumeID'] = sf_vol['volumeID']
|
||||
self._issue_api_request('DeleteVolume', params)
|
||||
if not self._create_image_volume(context,
|
||||
image_meta,
|
||||
image_service,
|
||||
image_meta['id']):
|
||||
msg = _("Failed to create SolidFire Image-Volume")
|
||||
raise exception.SolidFireAPIException(msg)
|
||||
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(
|
||||
@ -1082,6 +1102,7 @@ class SolidFireDriver(san.SanISCSIDriver):
|
||||
for vag in sorted_targets[:limit]:
|
||||
self._remove_vag(vag['volumeAccessGroupID'])
|
||||
|
||||
@locked_image_id_operation
|
||||
def clone_image(self, context,
|
||||
volume, image_location,
|
||||
image_meta, image_service):
|
||||
@ -1116,21 +1137,9 @@ class SolidFireDriver(san.SanISCSIDriver):
|
||||
except exception.SolidFireAPIException:
|
||||
return None, False
|
||||
|
||||
try:
|
||||
(data, sfaccount, model) = self._do_clone_volume(image_meta['id'],
|
||||
volume)
|
||||
except exception.VolumeNotFound:
|
||||
if self._create_image_volume(context,
|
||||
image_meta,
|
||||
image_service,
|
||||
image_meta['id']) is None:
|
||||
# We failed, dump out
|
||||
return None, False
|
||||
|
||||
# Ok, should be good to go now, try it again
|
||||
(data, sfaccount, model) = self._do_clone_volume(image_meta['id'],
|
||||
volume)
|
||||
|
||||
# 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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user