卷基於快照進行恢復


基於P版本,對卷基於快照進行恢復的源碼分析

1、特性描述

在pike版本中,openstack官網增加了一個新特性,Cinder volume revert to snapshot,該特性支持將卷恢復到最近的快照。還原過程將覆蓋卷的當前狀態和數據。如果在快照之后對卷進行了擴展,那么請求將被拒絕。該特性的目的是讓用戶能夠更方便地恢復實例和卷,並減少停機時間。

2、api接口參數樣例

POST /v3/{project_id}/volumes/{volume_id}/action

恢復一個卷到它自己最新的快照,這個接口僅僅支持恢復卸載的卷,並且卷的狀態是有效的

Request Example
{
    "revert": {
        "snapshot_id": "5aa119a8-d25b-45a7-8d1b-88e127885635"
    }
}

3、源碼跟蹤

cinder-api服務側的處理

api接口,對http請求的處理 

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):
        """revert a volume to a snapshot"""

        context = req.environ['cinder.context']
        self.assert_valid_body(body, 'revert')
        snapshot_id = body['revert'].get('snapshot_id')-----------獲取用戶傳入的快照uuid
        volume = self.volume_api.get_volume(context, id)-----------通過卷的id,獲取卷對象
        try:
            l_snap = volume.get_latest_snapshot()------讀取數據庫獲取卷最新的快照UUID
        except exception.VolumeSnapshotNotFound:
            msg = _("Volume %s doesn't have any snapshots.")
            raise exc.HTTPBadRequest(explanation=msg % volume.id)
        # Ensure volume and snapshot match.
        if snapshot_id is None or snapshot_id != l_snap.id:-------判斷用戶是否輸入快照及輸入的快照是否為最新的快照
            msg = _("Specified snapshot %(s_id)s is None or not "
                    "the latest one of volume %(v_id)s.")
            raise exc.HTTPBadRequest(explanation=msg % {'s_id': snapshot_id,
                                                        'v_id': volume.id})
        try:
            msg = 'Reverting volume %(v_id)s to snapshot %(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 調用恢復卷快照的接口
        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模塊對請求的處理

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接口

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側接受到rpc請求,對rpc信息進行處理

cinder/volume/manager.py
    def revert_to_snapshot(self, context, volume, snapshot):
        """Revert a volume to a snapshot.

        The process of reverting to snapshot consists of several steps:
        1.   create a snapshot for backup (in case of data loss) 為了防止數據丟失創建一個快照的備份
        2.1. use driver's specific logic to revert volume 調用驅動的revert_to_snapshot接口來恢復卷
        2.2. try the generic way to revert volume if driver's method is missing 如果驅動的revert_to_snapshot 方法沒有,那么就使用一般的方式去恢復卷
        3.   delete the backup snapshot 刪除快照的備份
        """
        backup_snapshot = None
        try:
            LOG.info("Start to perform revert to snapshot process.")
            # Create a snapshot which can be used to restore the volume
            # data by hand if revert process failed.
            backup_snapshot = self._create_backup_snapshot(context, volume)---------創建卷的快照的備份
            self._revert_to_snapshot(context, volume, snapshot)--------------s1 執行快照的恢復
		except 
			...........
        v_res = volume.update_single_status_where('available', 'reverting')-----------把卷的狀態由reverting更新為available
        if not v_res:
            msg_args = {"id": volume.id,
                        "status": 'available'}
            msg = _("Revert finished, but failed to reset "----------底層恢復完了,但是狀態的狀態沒有設置成功,需要手動設置
                    "volume %(id)s status to %(status)s, "
                    "please manually reset it.") % msg_args
            raise exception.BadResetResourceStatus(message=msg)

        s_res = snapshot.update_single_status_where(---------------把快照的狀態由reverting更新為available
            fields.SnapshotStatus.AVAILABLE,
            fields.SnapshotStatus.RESTORING)
        if not s_res:
            msg_args = {"id": snapshot.id,
                        "status":
                            fields.SnapshotStatus.AVAILABLE}
            msg = _("Revert finished, but failed to reset "-----------底層恢復完成,但是快照的狀態沒有設置成功,需要手動設置
                    "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")
        try:
            self.driver.revert_to_snapshot(context, volume, snapshot)------調用相關驅動去恢復快照
        except (NotImplementedError, AttributeError):
            LOG.info("Driver's 'revert_to_snapshot' is not found. "
                     "Try to use copy-snapshot-to-volume method.")
            self._revert_to_snapshot_generic(context, volume, snapshot)----------沒有實現revert_to_snapshot功能,那么走一般路徑ceph調用的就是這個
        self._notify_about_volume_usage(context, volume, "revert.end")
        self._notify_about_snapshot_usage(context, snapshot, "revert.end")  

基於lvm的驅動

cinder/volume/drivers/lvm.py
    def revert_to_snapshot(self, context, volume, snapshot):
        """Revert a volume to a snapshot"""
        # NOTE(tommylikehu): We still can revert the volume because Cinder
        # will try the alternative approach if 'NotImplementedError'
        # is raised here.
        if self.configuration.lvm_type == 'thin':--------如果lvm配置成thin,那么不支持快照恢復
            msg = _("Revert volume to snapshot not implemented for thin LVM.")
            raise NotImplementedError(msg)
        else:
            self.vg.revert(self._escape_snapshot(snapshot.name))-----執行的是lvconvert --merge命令,合並快照到原始卷中,此時這個快照會被銷毀
            self.vg.deactivate_lv(volume.name)------執行的是lvchange -a n命令,表示更改lv卷的狀態為無效,lvchange表示更改lv的活動狀態,y表示lv活動或有效,n表示lv不活動或無效
            self.vg.activate_lv(volume.name)--------執行的是lvchange -a y --yes,表示更改lv卷的狀態為有效狀態,--yes表示不提示確認互動,直接認為是,
            # Recreate the snapshot that was destroyed by the revert
            self.create_snapshot(snapshot)-----------創建快照  

由於ceph目前沒有實現revert_to_snapshot方法,因此調用_revert_to_snapshot_generic接口

cinder/volume/manager.py
    def _revert_to_snapshot_generic(self, ctxt, volume, snapshot):
        """Generic way to revert volume to a snapshot.
        the framework will use the generic way to implement the revert
        to snapshot feature:
        1. create a temporary volume from snapshot-----從快照創建一個臨時的卷
        2. mount two volumes to host--------------掛載兩個卷到主機上
        3. copy data from temporary volume to original volume--------從臨時卷拷貝數據到原始卷中
        4. detach and destroy temporary volume----------卸載並銷毀臨時卷
        """
        temp_vol = None

        try:
            v_options = {'display_name': '[revert] temporary volume created '
                                         'from snapshot %s' % snapshot.id}
            ctxt = context.get_internal_tenant_context() or ctxt
            temp_vol = self.driver._create_temp_volume_from_snapshot(--------通過快照創建臨時卷
                ctxt, volume, snapshot, volume_options=v_options)
            self._copy_volume_data(ctxt, temp_vol, volume)-------從臨時卷拷貝數據到原始卷,這個過程會分三個小步驟:掛載原始卷和臨時卷;從臨時卷拷貝數據到原始卷;
卸載臨時卷和原始卷 self.driver.delete_volume(temp_vol)-----刪除臨時卷 temp_vol.destroy() 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()

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM