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

    openstack基于卷快照恢复卷

    基于P版本,对卷基于快照进行恢复的源码分析

    1、特性描述

    在pike版本中,openstack官网增加了一个新特性,Cinder volume revert to snapshot,该特性支持将卷恢复到最近的快照。还原过程将覆盖卷的当前状态和数据。如果在快照之后对卷进行了扩展,那么请求将被拒绝。该特性的目的是让用户能够更方便地恢复实例和卷,并减少停机时间。

    目前只提供api调用,cli无此命令

    2、api接口参数样例

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

    Request

    Name In Type Description
    project_id path string The UUID of the project in a multi-tenancy cloud.
    volume_id path string The UUID of the volume.
    revert body object The revert action.
    snapshot_id body string The UUID of the snapshot. The API reverts the volume with this snapshot.

    恢复一个卷到它自己最新的快照,这个接口仅仅支持恢复卸载的卷,并且卷的状态是有效的

    {
        "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)-------从临时卷拷贝数据到原始卷,这个过程会分三个小步骤:挂载原始卷和临时卷;从临时卷拷贝数据到原始卷;<br>卸载临时卷和原始卷
                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)
    

    本文来自:https://www.cnblogs.com/potato-chip/p/11535166.html

  • 相关阅读:
    GIT(6)----fork和clone的区别,fetch与pull的区别
    Linux的本地时间和网络时间同步
    ROS知识(19)----写一个简单的pluginlib例子
    JS中关于闭包和this的指向
    hasOwnProperty()与in操作符的区别
    js判断对象的属性是原型的还是实例的
    JavaScript函数的中实参个数和形参个数的获取
    justify-content 定义子元素在父元素水平位置排列的顺序
    flex弹性盒模型
    让子元素在父元素中水平居中align-items
  • 原文地址:https://www.cnblogs.com/mrwuzs/p/11688141.html
Copyright © 2011-2022 走看看