def _handle_bootable_volume_glance_meta(self, context, volume_id, **kwargs): """Enable bootable flag and properly handle glance metadata. Caller should provide one and only one of snapshot_id,source_volid and image_id. If an image_id specified, a image_meta should also be provided, otherwise will be treated as an empty dictionary. """ log_template = _("Copying metadata from %(src_type)s %(src_id)s to " "%(vol_id)s.") exception_template = _("Failed updating volume %(vol_id)s metadata" " using the provided %(src_type)s" " %(src_id)s metadata") src_type = None src_id = None self._enable_bootable_flag(context, volume_id) try: if kwargs.get('snapshot_id'): src_type = 'snapshot' src_id = kwargs['snapshot_id'] snapshot_id = src_id LOG.debug(log_template % { 'src_type': src_type, 'src_id': src_id, 'vol_id': volume_id }) self.db.volume_glance_metadata_copy_to_volume( context, volume_id, snapshot_id) elif kwargs.get('source_volid'): src_type = 'source volume' src_id = kwargs['source_volid'] source_volid = src_id LOG.debug(log_template % { 'src_type': src_type, 'src_id': src_id, 'vol_id': volume_id }) self.db.volume_glance_metadata_copy_from_volume_to_volume( context, source_volid, volume_id) elif kwargs.get('image_id'): src_type = 'image' src_id = kwargs['image_id'] image_id = src_id image_meta = kwargs.get('image_meta', {}) LOG.debug(log_template % { 'src_type': src_type, 'src_id': src_id, 'vol_id': volume_id }) self._capture_volume_image_metadata(context, volume_id, image_id, image_meta) except exception.CinderException as ex: LOG.exception(exception_template % { 'src_type': src_type, 'src_id': src_id, 'vol_id': volume_id }) raise exception.MetadataCopyFailure(reason=ex)
def create_snapshot(self, context, volume_id, snapshot_id): """Creates and exports the snapshot.""" caller_context = context context = context.elevated() snapshot_ref = self.db.snapshot_get(context, snapshot_id) LOG.info(_("snapshot %s: creating"), snapshot_ref['id']) self._notify_about_snapshot_usage(context, snapshot_ref, "create.start") try: LOG.debug(_("snapshot %(snap_id)s: creating"), {'snap_id': snapshot_ref['id']}) # Pass context so that drivers that want to use it, can, # but it is not a requirement for all drivers. snapshot_ref['context'] = caller_context model_update = self.driver.create_snapshot(snapshot_ref) if model_update: self.db.snapshot_update(context, snapshot_ref['id'], model_update) except Exception: with excutils.save_and_reraise_exception(): self.db.snapshot_update(context, snapshot_ref['id'], {'status': 'error'}) self.db.snapshot_update(context, snapshot_ref['id'], { 'status': 'available', 'progress': '100%' }) vol_ref = self.db.volume_get(context, volume_id) if vol_ref.bootable: try: self.db.volume_glance_metadata_copy_to_snapshot( context, snapshot_ref['id'], volume_id) except exception.CinderException as ex: LOG.exception( _("Failed updating %(snapshot_id)s" " metadata using the provided volumes" " %(volume_id)s metadata") % { 'volume_id': volume_id, 'snapshot_id': snapshot_id }) raise exception.MetadataCopyFailure(reason=ex) LOG.info(_("snapshot %s: created successfully"), snapshot_ref['id']) self._notify_about_snapshot_usage(context, snapshot_ref, "create.end") return snapshot_id
def test_create_snapshot_from_bootable_volume_fail(self, mock_qemu_info): """Test create snapshot from bootable volume. But it fails to volume_glance_metadata_copy_to_snapshot. As a result, status of snapshot is changed to ERROR. """ # create bootable volume from image volume = self._create_volume_from_image() volume_id = volume['id'] self.assertEqual('available', volume['status']) self.assertTrue(volume['bootable']) image_info = imageutils.QemuImgInfo() image_info.virtual_size = '1073741824' mock_qemu_info.return_value = image_info # get volume's volume_glance_metadata ctxt = context.get_admin_context() vol_glance_meta = db.volume_glance_metadata_get(ctxt, volume_id) self.assertTrue(vol_glance_meta) snap = create_snapshot(volume_id) snap_stat = snap.status self.assertTrue(snap.id) self.assertTrue(snap_stat) # set to return DB exception with mock.patch.object(db, 'volume_glance_metadata_copy_to_snapshot')\ as mock_db: mock_db.side_effect = exception.MetadataCopyFailure( reason="Because of DB service down.") # create snapshot from bootable volume self.assertRaises(exception.MetadataCopyFailure, self.volume.create_snapshot, ctxt, snap) # get snapshot's volume_glance_metadata self.assertRaises(exception.GlanceMetadataNotFound, db.volume_snapshot_glance_metadata_get, ctxt, snap.id) # ensure that status of snapshot is 'error' self.assertEqual(fields.SnapshotStatus.ERROR, snap.status) # cleanup resource snap.destroy() db.volume_destroy(ctxt, volume_id)
def create_volume(self, volume): ''' Create volume ''' LOG.info(_('create_volume,Enter method')) element_path = None metadata = None v_metadata = None d_metadata = {} data = None v_metadata = volume.get('volume_metadata') for data in v_metadata: d_metadata[data['key']] = data['value'] if FJ_SRC_VOL_ID not in d_metadata: # create volume (element_path, metadata) = self.common.create_volume(volume) else: # create cloned volume for backup solution # get source & target volume information ctxt = context.get_admin_context() volume_id = volume['id'] volume_size = volume['size'] src_volid = d_metadata[FJ_SRC_VOL_ID] if src_volid is None: msg = (_('create_volume,Source volume id is None')) LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) # end of if src_vref = self.db.volume_get(ctxt, src_volid) # check target volume information with reference to source volume src_vref_size = src_vref['size'] src_vref_status = src_vref['status'] if ('error' in src_vref_status) or ('deleting' in src_vref_status): msg = (_('Invalid volume status : %(status)s') % { 'status': src_vref_status }) LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) elif volume_size < src_vref_size: msg = (_( 'Volume size %(volume_size)sGB cannot be smaller than original volume size %(src_vref_size)sGB. They must be >= original volume size.' ) % { 'volume_size': volume_size, 'src_vref_size': src_vref_size }) LOG.error(msg) raise exception.VolumeBackendAPIException(data=msg) # end of if # main processing LOG.info(_('create_volume,Call create cloned volume')) (element_path, metadata) = self.common.create_cloned_volume( volume, src_vref, use_service_name=True) LOG.info(_('create_volume,Return create cloned volume')) # post processing try: if src_vref.bootable: self.db.volume_update(ctxt, volume['id'], {'bootable': True}) self.db.volume_glance_metadata_copy_from_volume_to_volume( ctxt, src_volid, volume['id']) # end of if self.db.volume_update(ctxt, volume['id'], {'source_volid': src_volid}) except Exception as ex: msg = (_('create volume, Failed updating volume metadata,' 'reason: %(reason)s,' 'volume_id: %(volume_id)s,' 'src_volid: %(src_volid)s,') % { 'reason': ex, 'volume_id': volume['id'], 'src_volid': src_volid }) raise exception.MetadataCopyFailure(reason=msg) # end of if d_metadata.update(metadata) LOG.info(_('create_volume,Exit method')) return { 'provider_location': six.text_type(element_path), 'metadata': d_metadata }
def create_snapshot(self, context, volume_id, snapshot_id): """Creates and exports the snapshot.""" caller_context = context context = context.elevated() snapshot_ref = self.db.snapshot_get(context, snapshot_id) LOG.info(_("snapshot %s: creating"), snapshot_ref['id']) vol_ref = self.db.volume_get(context, volume_id) volume_type_id = vol_ref.get('volume_type_id', None) if self._is_share_volume(volume_type_id): msg = _("Volume is share volume") self.db.snapshot_update(context, snapshot_ref['id'], {'status': 'error'}) raise exception.InvalidVolume(reason=msg) self._notify_about_snapshot_usage( context, snapshot_ref, "create.start") try: # NOTE(flaper87): Verify the driver is enabled # before going forward. The exception will be caught # and the snapshot status updated. utils.require_driver_initialized(self.driver) LOG.debug(_("snapshot %(snap_id)s: creating"), {'snap_id': snapshot_ref['id']}) # Pass context so that drivers that want to use it, can, # but it is not a requirement for all drivers. snapshot_ref['context'] = caller_context model_update = self.driver.create_snapshot(snapshot_ref) if model_update: self.db.snapshot_update(context, snapshot_ref['id'], model_update) except Exception: with excutils.save_and_reraise_exception(): self.db.snapshot_update(context, snapshot_ref['id'], {'status': 'error'}) self.db.snapshot_update(context, snapshot_ref['id'], {'status': 'available', 'progress': '100%'}) if vol_ref.bootable: try: self.db.volume_glance_metadata_copy_to_snapshot( context, snapshot_ref['id'], volume_id) except exception.CinderException as ex: LOG.exception(_("Failed updating %(snapshot_id)s" " metadata using the provided volumes" " %(volume_id)s metadata") % {'volume_id': volume_id, 'snapshot_id': snapshot_id}) raise exception.MetadataCopyFailure(reason=ex) LOG.info(_("snapshot %s: created successfully"), snapshot_ref['id']) self._notify_about_snapshot_usage(context, snapshot_ref, "create.end") return snapshot_id