• cinder侧卸载卷流程分析


    cinder侧卸载卷分析,存储类型以lvm+iscsi的方式为分析基础
    在虚机卸载卷的过程中,主要涉及如下三个函数
    1)cinder.volume.api.begin_detaching 把volume的状态改为detaching,阻止其它节点执行挂载操作
    2)cinder.volume.api.terminate_connection 进行target,lun等信息的清理
    3)cinder.volume.api.detach 更新cinder数据库,设置卷的状态为available

    1、nova侧调用cinderclient的begin_detaching方法,

    nova/compute/api.py:API
        def _check_and_begin_detach(self, context, volume, instance):
            self.volume_api.check_detach(context, volume, instance=instance)
            self.volume_api.begin_detaching(context, volume['id'])

    1)cinderclient端接受到nova发送的begin_detaching操作的http请求,其入口处理函数为

    cinder/api/contrib/volume_actions.py:VolumeActionsController
        def _begin_detaching(self, req, id, body):
            """Update volume status to 'detaching'."""
            context = req.environ['cinder.context']
            # Not found exception will be handled at the wsgi level
            volume = self.volume_api.get(context, id)
    
            self.volume_api.begin_detaching(context, volume)
            return webob.Response(status_int=http_client.ACCEPTED)    
    该函数的主要作用是通过volume 的uuid,获取volume实例信息,并调用volume目录下的api模块

    2)进一步调用cinder volume的api模块的begin_detaching函数,进行数据库的操作,更新卷的状态为detaching,防止其他api对这个卷操作

        @wrap_check_policy
        def begin_detaching(self, context, volume):
            # If we are in the middle of a volume migration, we don't want the
            # user to see that the volume is 'detaching'. Having
            # 'migration_status' set will have the same effect internally.
            expected = {'status': 'in-use',
                        'attach_status': fields.VolumeAttachStatus.ATTACHED,
                        'migration_status': self.AVAILABLE_MIGRATION_STATUS}
    
            result = volume.conditional_update({'status': 'detaching'}, expected)
    
            if not (result or self._is_volume_migrating(volume)):
                msg = _("Unable to detach volume. Volume status must be 'in-use' "
                        "and attach_status must be 'attached' to detach.")
                LOG.error(msg)
                raise exception.InvalidVolume(reason=msg)
    
            LOG.info(_LI("Begin detaching volume completed successfully."),
                     resource=volume)

    2、nova侧向cinder发送terminate_connection请求,请求删除卷的连接信息

    def _detach_volume(self, context, volume_id, instance, destroy_bdm=True,attachment_id=None):
         ......
         self.volume_api.terminate_connection(context, volume_id, connector)
         ......
         self.volume_api.detach(context.elevated(), volume_id, instance.uuid,attachment_id)
         

    1)cinderclient接受nova发送过来的os-terminate_connection请求

       @wsgi.action('os-terminate_connection')
        def _terminate_connection(self, req, id, body):
            """Terminate volume attachment."""
            context = req.environ['cinder.context']
            # Not found exception will be handled at the wsgi level
            volume = self.volume_api.get(context, id)
            try:
                connector = body['os-terminate_connection']['connector']
            except KeyError:
                raise webob.exc.HTTPBadRequest(
                    explanation=_("Must specify 'connector'"))
            try:
                self.volume_api.terminate_connection(context, volume, connector)
            except exception.VolumeBackendAPIException:
                msg = _("Unable to terminate volume connection from backend.")
                raise webob.exc.HTTPInternalServerError(explanation=msg)
            return webob.Response(status_int=http_client.ACCEPTED)
            

    2)进一步调用volume目录下api模块的 terminate_connection 函数,对该请求进行处理

    cinder/volume/api.py:API类        
        @wrap_check_policy
        def terminate_connection(self, context, volume, connector, force=False):
           step 1: self.volume_rpcapi.terminate_connection(context,volume,connector,force)
           step 2 :self.unreserve_volume(context, volume)

    step 1:cinder api进一步发送RPC请求给volume所在的cinder-volume服务节点,最终在cinder-volume节点,
    由cinder/volume/manager.py:VolumeManager的terminate_connection处理,该函数的处理,主要包括如下内容

       def terminate_connection(self, context, volume_id, connector, force=False):
            utils.require_driver_initialized(self.driver)----获取对应的驱动信息
            volume_ref = self.db.volume_get(context, volume_id)-----从数据库中获取卷的信息
            try:
                step 1.1 self.driver.terminate_connection(volume_ref, connector, force=force)-----调用对应的驱动的terminate_connection函数

    step 1.1 :
    使用lvm+lio的方式,代码跳转过程如下:drivers/lvm.py -> targets/lio.py,从target 的acl中删除initiator,从而有效的在 target侧关闭iscis session连接

        def terminate_connection(self, volume, connector, **kwargs):
            volume_iqn = volume['provider_location'].split(' ')[1]
            # Delete initiator iqns from target ACL
            try:
                self._execute('cinder-rtstool', 'delete-initiator',
                              volume_iqn,
                              connector['initiator'],
                              run_as_root=True)
            except putils.ProcessExecutionError:
                LOG.exception(
                    _LE("Failed to delete initiator iqn %s from target."),
                    connector['initiator'])
                raise exception.ISCSITargetDetachFailed(volume_id=volume['id'])
    
            # We make changes persistent
            self._persist_configuration(volume['id'])

    3、nova给cinderclient发送os-detach命令,更改cinder数据库

    1)cinder侧接受nova更新cinder数据库的入口函数

    cinder/api/contrib/volume_actions.py
        @wsgi.action('os-detach')
        def _detach(self, req, id, body):
            volume = self.volume_api.get(context, id)
            attachment_id = None
            if body['os-detach']:
                attachment_id = body['os-detach'].get('attachment_id', None)
            try:
                self.volume_api.detach(context, volume, attachment_id)

    2)最后cinder-api通过RPC请求到cinder-volume节点,调用remove_export,移除target信息,更新数据库,把volume状态改为available,attach_status状态为detached

    cindervolumeapi.py
        @wrap_check_policy
        def detach(self, context, volume, attachment_id):
            if volume['status'] == 'maintenance':
                LOG.info(_LI('Unable to detach volume, '
                             'because it is in maintenance.'), resource=volume)
                msg = _("The volume cannot be detached in maintenance mode.")
                raise exception.InvalidVolume(reason=msg)
            detach_results = self.volume_rpcapi.detach_volume(context, volume,
                                                              attachment_id)
            LOG.info(_LI("Detach volume completed successfully."),
                     resource=volume)
            return detach_results

    最终调用cinder-volume服务的manage.py文件中的detach_volume接口

        @coordination.synchronized('{volume_id}-{f_name}')
        def detach_volume(self, context, volume_id, attachment_id=None,
                          volume=None):
            """Updates db to show volume is detached."""
            # TODO(vish): refactor this into a more general "unreserve"
            # FIXME(lixiaoy1): Remove this in v4.0 of RPC API.
            if volume is None:
                # For older clients, mimic the old behavior and look up the volume
                # by its volume_id.
                volume = objects.Volume.get_by_id(context, volume_id)
    
            if attachment_id:
                try:
                    attachment = objects.VolumeAttachment.get_by_id(context,
                                                                    attachment_id)
                except exception.VolumeAttachmentNotFound:
                    LOG.info(_LI("Volume detach called, but volume not attached."),
                             resource=volume)
                    # We need to make sure the volume status is set to the correct
                    # status.  It could be in detaching status now, and we don't
                    # want to leave it there.
                    volume.finish_detach(attachment_id)
                    return
            else:
                # We can try and degrade gracefully here by trying to detach
                # a volume without the attachment_id here if the volume only has
                # one attachment.  This is for backwards compatibility.
                attachments = volume.volume_attachment
                if len(attachments) > 1:
                    # There are more than 1 attachments for this volume
                    # we have to have an attachment id.
                    msg = _("Detach volume failed: More than one attachment, "
                            "but no attachment_id provided.")
                    LOG.error(msg, resource=volume)
                    raise exception.InvalidVolume(reason=msg)
                elif len(attachments) == 1:
                    attachment = attachments[0]
                else:
                    # there aren't any attachments for this volume.
                    # so set the status to available and move on.
                    LOG.info(_LI("Volume detach called, but volume not attached."),
                             resource=volume)
                    volume.status = 'available'
                    volume.attach_status = fields.VolumeAttachStatus.DETACHED
                    volume.save()
                    return
    
            self._notify_about_volume_usage(context, volume, "detach.start")
            try:
                # NOTE(flaper87): Verify the driver is enabled
                # before going forward. The exception will be caught
                # and the volume status updated.
                utils.require_driver_initialized(self.driver)
    
                LOG.info(_LI('Detaching volume %(volume_id)s from instance '
                             '%(instance)s.'),
                         {'volume_id': volume_id,
                          'instance': attachment.get('instance_uuid')},
                         resource=volume)
                self.driver.detach_volume(context, volume, attachment)
            except Exception as ex:
                with excutils.save_and_reraise_exception():
                    self.db.volume_attachment_update(
                        context, attachment.get('id'), {
                            'attach_status':
                                fields.VolumeAttachStatus.ERROR_DETACHING})
    
                self.db.volume_metadata_update(context,
                                               volume_id,
                                               {'error': six.text_type(ex)},
                                               False)
    
            # NOTE(jdg): We used to do an ensure export here to
            # catch upgrades while volumes were attached (E->F)
            # this was necessary to convert in-use volumes from
            # int ID's to UUID's.  Don't need this any longer
    
            # We're going to remove the export here
            # (delete the iscsi target)
            try:
                utils.require_driver_initialized(self.driver)
                self.driver.remove_export(context.elevated(), volume)
            except exception.DriverNotInitialized:
                with excutils.save_and_reraise_exception():
                    LOG.exception(_LE("Detach volume failed, due to "
                                      "uninitialized driver."),
                                  resource=volume)
            except Exception as ex:
                LOG.exception(_LE("Detach volume failed, due to "
                                  "remove-export failure."),
                              resource=volume)
                raise exception.RemoveExportException(volume=volume_id,
                                                      reason=six.text_type(ex))
    
            volume.finish_detach(attachment.id)
            self._notify_about_volume_usage(context, volume, "detach.end")
            LOG.info(_LI("Detach volume completed successfully."), resource=volume)
  • 相关阅读:
    出版文字作品报酬规定(收藏)
    关于Delphi7 的XML说明
    我做的XML验证的测试记录
    印刷常用名词
    验证XLM数据合法性(收藏)
    关于.Net操作XML相关类
    我的性格
    webpack学习笔记一
    汇编语言内存变量的地址
    Linux 汇编语言(GNU GAS汇编)开发指南
  • 原文地址:https://www.cnblogs.com/potato-chip/p/10794773.html
Copyright © 2020-2023  润新知