, A volume recovery of source code analysis based on P version based snapshots
1. Characterization
In pike version, openstack official website added a new feature, Cinder volume revert to snapshot, this feature will restore the volume to support the most recent snapshot. Reduction process overwrites the current state and data volumes. If the volume was extended after the snapshot, then the request will be rejected. The purpose of this characteristic is to allow the user to more easily recovered from instance and volume, and reduce downtime.
2, api interface parameters Sample
POST /v3/{project_id}/volumes/{volume_id}/action
Restore a volume to its own latest snapshot, this interface supports only restore the volume is unmounted, and the volume status is valid
Request Example { "revert": { "snapshot_id": "5aa119a8-d25b-45a7-8d1b-88e127885635" } }
3, source tracking
Processing cinder-api service side
api interfaces, http request processing
Cinder / API / V3 / volumes.py
@ wsgi.response (http_client.ACCEPTED)
@ wsgi.Controller.api_version ( '3.40')
@ wsgi.action ( 'Revert')
DEF Revert (Self, REQ, ID, body):
"" "A Revert to Snapshot Volume A" ""
context = req.environ [ 'cinder.context']
self.assert_valid_body (body, 'Revert')
snapshot_id in the body = [ 'Revert'] GET ( 'snapshot_id in the'). - ---------- get a snapshot of the incoming user uuid
volume = self.volume_api.get_volume (context, id) id ----------- by volume, to obtain volume object
try:
l_snap = volume.get_latest_snapshot () ------ read the database to obtain the latest snapshot volume UUID
the except exception.VolumeSnapshotNotFound:
msg = _("Volume %s doesn't have any snapshots.")
the raise EXC.HTTPBadRequest(explanation=msg % volume.id)
Volume Snapshot match of Ensure and #.
IF snapshot_id IS None or snapshot_id! = L_snap.id:------- determine whether the user input and the input snapshot snapshot is up to date snapshot
msg = _ ( "Specified snapshot% (s_id ) or S IS None Not "
." The Latest One of Volume% (v_id) S ")
The raise exc.HTTPBadRequest (explanation% MSG = { 's_id': snapshot_id in the,
'v_id': volume.id})
the try:
MSG = '. Reverting Volume% (v_id) Snapshot% to S (s_id) S'
log.info (MSG, { 'v_id': volume.id,
's_id': l_snap .id})
self.volume_api.revert_to_snapshot (context, Volume, l_snap) ------ s1 call volume snapshot restore interface
except (exception.InvalidVolume,exception.InvalidSnapshot) as e:
raise exc.HTTPConflict(explanation=six.text_type(e))
except exception.VolumeSizeExceedsAvailableQuota as e:
raise exc.HTTPForbidden(explanation=six.text_type(e))
cinder volume processing module requests
cinder/volume/api.py
@wrap_check_policy
def revert_to_snapshot(self, context, volume, snapshot):
"""revert a volume to a snapshot"""
v_res = volume.update_single_status_where(-----------------更新卷的状态由 available 变为 reverting,
'reverting', 'available')
if not v_res:
msg = _("Can't revert volume %s to its latest snapshot. "
"Volume's status must be 'available'.") % volume.id
raise exception.InvalidVolume(reason=msg)
s_res = snapshot.update_single_status_where(------------更新快照的状态由available 变为 restoring
fields.SnapshotStatus.RESTORING,
fields.SnapshotStatus.AVAILABLE)
if not s_res:
msg = _("Can't revert volume %s to its latest snapshot. "
"Snapshot's status must be 'available'.") % snapshot.id
raise exception.InvalidSnapshot(reason=msg)
self.volume_rpcapi.revert_to_snapshot(context, volume, snapshot)--------s2调用cinder volume的rpc接口
Rpc call Interface
cinder/volume/rpcapi.py @rpc.assert_min_rpc_version('3.15') def revert_to_snapshot(self, ctxt, volume, snapshot): version = self._compat_ver('3.15') cctxt = self._get_cctxt(volume.host, version) cctxt.cast(ctxt, 'revert_to_snapshot', volume=volume,snapshot=snapshot)
cinder-voume side receives rpc request, the information processing rpc
Cinder / Volume / manager.py DEF revert_to_snapshot (Self, context, Volume, Snapshot): "" "A the Revert to Snapshot Volume A. of The Process of Snapshot Reverting to Consists of Several Steps: 1. Create Backup for Snapshot A (in Case of data loss) in order to prevent loss of data to create a snapshot backup 2.1. use driver's specific logic to revert volume calls the driver's revert_to_snapshot the interface to restore volumes 2.2. try the generic way to revert volume if driver's method is missing if driven revert_to_snapshot method does not , then the normal way to restore volume 3. delete the backup snapshot backup delete snapshots "" " backup_snapshot = None the try: log.info (" Start to the perform Revert to snapshot Process. ") The Create A Snapshot Which CAN # BE Used to Restore at The Volume # by the Data Process failed The Hand IF Revert. Backup_snapshot = self._create_backup_snapshot (context, Volume) --------- create a snapshot volume backup self._revert_to_snapshot ( context, volume, snapshot) -------------- s1 restore snapshot of the except ........... v_res = volume.update_single_status_where ( 'Available', 'Reverting' ) ----------- the volume status is updated by reverting Available IF Not v_res: msg_args = { "ID": volume.id, "status": 'Available'} MSG = _ ( "the Revert finished, but failed to reset "---------- underlying recovery is over, but the state of the state is not set successfully, you need to manually set "Volume% (ID) to S% Status (Status) S," "Please Manually RESET IT.")% msg_args The raise exception.BadResetResourceStatus (Message = MSG) s_res = snapshot.update_single_status_where (--------- ------ state and the snapshot is updated by reverting Available fields.SnapshotStatus.AVAILABLE, fields.SnapshotStatus.RESTORING) IF Not s_res: msg_args = { "ID": snapshot.id, "status": fields.SnapshotStatus. } AVAILABLE MSG = _ ( "Finished the Revert, but failed to RESET" bottom ----------- complete recovery, but the state is not set successfully snapshot, you need to manually set "snapshot %(id)s status to %(status)s, " "please manually reset it.") % msg_args raise exception.BadResetResourceStatus(message=msg) if backup_snapshot: self.delete_snapshot(context,----------------------删除备份的快照 backup_snapshot, handle_quota=False) msg = ('Volume %(v_id)s reverted to snapshot %(snap_id)s ' 'successfully.') msg_args = {'v_id': volume.id, 'snap_id': snapshot.id} LOG.info(msg, msg_args) cinder/volume/manager.py def _revert_to_snapshot(self, context, volume, snapshot): """Use driver or generic method to rollback volume.""" self._notify_about_volume_usage (context, Volume, "revert.start") self._notify_about_snapshot_usage (context, Snapshot, "revert.start") the try: . Self driver.revert_to_snapshot (context, Volume, Snapshot) ------ call the relevant snapshot drive to restore the except (NotImplementedError, AttributeError): log.info ( "driver's 'revert_to_snapshot' IS not found." "to use the Try-to-Copy-snapshot Method, Volume.") Self. _revert_to_snapshot_generic (context, Volume, snapshot) ---------- revert_to_snapshot function not implemented, then take the path ceph general call is this self._notify_about_volume_usage (context, volume, "revert.end ") self._notify_about_snapshot_usage(context, snapshot, "revert.end")
Based driving lvm
Cinder / Volume / the Drivers / lvm.py DEF revert_to_snapshot (Self, context, Volume, Snapshot): "" "A Revert to Snapshot Volume A" "" # NOTE (tommylikehu): We Still CAN Revert at The Volume Because Cinder # the try by Will IF Alternative Approach at The 'NotImplementedError' # IS raised here Wallpaper. IF self.configuration.lvm_type == 'Thin': -------- If lvm configured thin, it does not support snapshots recovery msg = _ ( "Revert volume snapshot for not Implemented the Thin to LVM "). the raise NotImplementedError (msg) the else: self.vg.revert (self._escape_snapshot (snapshot.name)) ----- execution is lvconvert --merge command to merge a snapshot of the original volume, this time the snapshot will be destroyed self.vg.deactivate_lv (volume.name) ------ lvchange -an command is executed, the change of volume lv invalid state, the lvchange lv represents a change in the active state, y represents lv active or live, n-represents lv inactive or invalid self.vg.activate_lv (volume.name) -------- execution is lvchange -ay --yes, lv volume represents a change of state to the active state, - yes means no prompt for confirmation interactive, direct regarded, # Recreate at The snapshot that WAS Destroyed by at The Revert self.create_snapshot (snapshot) ----------- create a snapshot
Since ceph currently not implemented revert_to_snapshot method, so calling _revert_to_snapshot_generic Interface
Cinder / Volume / manager.py DEF _revert_to_snapshot_generic (Self, ctxt, Volume, Snapshot): "" "Way to the Generic A Revert to Snapshot Volume. The use Will Framework The Generic Way to Implement The Revert to Snapshot Feature: 1. Create A temporary volume from snapshot ----- create a temporary volume from a snapshot 2. mount two volumes to host -------------- mounts two volumes on the host 3. copy data from temporary volume to original volume -------- volume copy data from the temporary to the original volume 4. detach and destroy temporary volume ---------- unloading and destroying the temporary volume "" " temp_vol = None the try: v_options = { 'DISPLAY_NAME': '[Revert] Temporary Volume Created' 'Snapshot from S%'% snapshot.id} ctxt = context.get_internal_tenant_context () or ctxt temp_vol = self.driver._create_temp_volume_from_snapshot (-------- create a temporary snapshot volume ctxt, volume, snapshot, volume_options = v_options ) self._copy_volume_data (ctxt, temp_vol, volume) ------- volume copy data from the temporary to the original volume, the process will be divided into three smaller steps: Mounting the original volume and the temporary volume; volume copy data from temporary to the original volume;
unloading temporary volume and the original volume self.driver.delete_volume (temp_vol) ----- delete the temporary volume temp_vol.destroy () the except Exception: with excutils.save_and_reraise_exception (): LOG.exception ( "Failed to use Snapshot% (snapshot) s to create " "a temporary volume and copy data to volume " " %(volume)s.", {'snapshot': snapshot.id, 'volume': volume.id}) if temp_vol and temp_vol.status == 'available': self.driver.delete_volume(temp_vol) temp_vol.destroy()