_mock_issue_api_request.return_value = self.mock_stats_data
_mock_create_template_account.return_value = 1
_fake_get_snaps = [{'snapshotID': 5, 'name': 'testvol'}]
+ _fake_get_volume = (
+ {'volumeID': 99,
+ 'name': 'UUID-a720b3c0-d1f0-11e1-9b23-0800200c9a66',
+ 'attributes': {}})
testvol = {'project_id': 'testprjid',
'name': 'testvol',
with mock.patch.object(sfv,
'_get_sf_snapshots',
return_value=_fake_get_snaps), \
+ mock.patch.object(sfv,
+ '_get_sf_volume',
+ return_value=_fake_get_volume), \
mock.patch.object(sfv,
'_issue_api_request',
side_effect=self.fake_issue_api_request), \
'fake',
_fake_image_meta,
'fake'))
-
- @mock.patch.object(solidfire.SolidFireDriver, '_issue_api_request')
- @mock.patch.object(solidfire.SolidFireDriver, '_create_template_account')
- def test_clone_image_virt_size_not_set(self,
- _mock_create_template_account,
- _mock_issue_api_request):
- _mock_issue_api_request.return_value = self.mock_stats_data
- _mock_create_template_account.return_value = 1
-
- self.configuration.sf_allow_template_caching = True
- sfv = solidfire.SolidFireDriver(configuration=self.configuration)
-
- # Don't run clone_image if virtual_size property not on image
- _fake_image_meta = {'id': '17c550bb-a411-44c0-9aaf-0d96dd47f501',
- 'updated_at': datetime.datetime(2013, 9,
- 28, 15,
- 27, 36,
- 325355),
- 'is_public': True,
- 'owner': 'testprjid'}
-
- self.assertEqual((None, False),
- sfv.clone_image(self.ctxt,
- self.mock_volume,
- 'fake',
- _fake_image_meta,
- 'fake'))
+ # 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'))
def test_create_template_no_account(self):
sfv = solidfire.SolidFireDriver(configuration=self.configuration)
from cinder import context
from cinder import exception
-from cinder.i18n import _, _LE, _LI, _LW
+from cinder.i18n import _, _LE, _LW
from cinder.image import image_utils
from cinder.volume.drivers.san import san
from cinder.volume import qos_specs
src_project_id,
vref):
"""Create a clone of an existing volume or snapshot."""
+
attributes = {}
qos = {}
params['volumeID'] = int(snap['volumeID'])
params['newSize'] = int(vref['size'] * units.Gi)
else:
- sf_vol = self._get_sf_volume(
- src_uuid, {'accountID': sf_account['accountID']})
+ sf_vol = self._get_sf_volume(src_uuid)
if sf_vol is None:
raise exception.VolumeNotFound(volume_id=src_uuid)
params['volumeID'] = int(sf_vol['volumeID'])
qos[key] = int(value)
return qos
- def _get_sf_volume(self, uuid, params):
- vols = self._issue_api_request(
- 'ListVolumesForAccount', params)['result']['volumes']
+ def _get_sf_volume(self, uuid, params=None):
+ if params:
+ vols = self._issue_api_request(
+ 'ListVolumesForAccount', params)['result']['volumes']
+ else:
+ vols = self._issue_api_request(
+ 'ListActiveVolumes', params)['result']['volumes']
found_count = 0
sf_volref = None
def _create_image_volume(self, context,
image_meta, image_service,
image_id):
- # NOTE(jdg): It's callers responsibility to ensure that
- # the optional properties.virtual_size is set on the image
- # before we get here
- virt_size = int(image_meta['properties'].get('virtual_size'))
- min_sz_in_bytes = (
- math.ceil(virt_size / float(units.Gi)) * float(units.Gi))
- min_sz_in_gig = math.ceil(min_sz_in_bytes / float(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(min_sz_in_bytes),
- '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']
-
- connector = 'na'
- conn = self.initialize_connection(tvol, connector)
- attach_info = super(SolidFireDriver, self)._connect_device(conn)
- properties = 'na'
-
- try:
- image_utils.fetch_to_raw(context,
- image_service,
- image_id,
- attach_info['device']['path'],
- self.configuration.volume_dd_blocksize,
- size=min_sz_in_gig)
- except Exception as exc:
- params['volumeID'] = template_vol['volumeID']
- LOG.error(_LE('Failed image conversion during cache creation: %s'),
- exc)
- LOG.debug('Removing SolidFire Cache Volume (SF ID): %s',
- template_vol['volumeID'])
-
- self._detach_volume(context, attach_info, tvol, properties)
- self._issue_api_request('DeleteVolume', params)
- return
+ 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']
+
+ connector = {'multipath': False}
+ conn = self.initialize_connection(tvol, connector)
+ attach_info = super(SolidFireDriver, self)._connect_device(conn)
+ properties = 'na'
+ try:
+ 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(_LE('Failed image conversion during '
+ 'cache creation: %s'),
+ exc)
+ LOG.debug('Removing SolidFire Cache Volume (SF ID): %s',
+ vol['volumeID'])
+ self._detach_volume(context, attach_info, tvol, properties)
+ self._issue_api_request('DeleteVolume', params)
+ return
self._detach_volume(context, attach_info, tvol, properties)
sf_vol = self._get_sf_volume(image_id, params)
def clone_image(self, context,
volume, image_location,
image_meta, image_service):
-
+ public = False
# Check out pre-requisites:
# Is template caching enabled?
if not self.configuration.sf_allow_template_caching:
return None, False
- # Is the image owned by this tenant or public?
- if ((not image_meta.get('is_public', False)) and
- (image_meta['owner'] != volume['project_id'])):
- LOG.warning(_LW("Requested image is not "
- "accessible by current Tenant."))
- return None, False
-
- # Is virtual_size property set on the image?
- if ((not image_meta.get('properties', None)) or
- (not image_meta['properties'].get('virtual_size', None))):
- LOG.info(_LI('Unable to create cache volume because image: %s '
- 'does not include properties.virtual_size'),
- image_meta['id'])
+ # 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
+ # future handling of things like shared and community
+ # but for now, it's owner or public and that's it
+ visibility = image_meta.get('visibility', None)
+ if visibility and visibility == 'public':
+ public = True
+ elif image_meta.get('is_public', False):
+ public = True
+ else:
+ if image_meta['owner'] == volume['project_id']:
+ public = True
+ if not public:
+ LOG.warning(_LW("Requested image is not "
+ "accessible by current Tenant."))
return None, False
try: