zoukankan      html  css  js  c++  java
  • 卷基于快照进行恢复

    基于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()

      

  • 相关阅读:
    461. Hamming Distance
    342. Power of Four
    326. Power of Three
    368. Largest Divisible Subset java solutions
    95. Unique Binary Search Trees II java solutions
    303. Range Sum Query
    160. Intersection of Two Linked Lists java solutions
    88. Merge Sorted Array java solutions
    67. Add Binary java solutions
    14. Longest Common Prefix java solutions
  • 原文地址:https://www.cnblogs.com/potato-chip/p/11535166.html
Copyright © 2011-2022 走看看