当前位置:   article > 正文

Openstack Cinder中建立volume过程的源码解析(9)_openstack drivernotinitialized

openstack drivernotinitialized

感谢朋友支持本博客,欢迎共同探讨交流,由于能力和时间有限,错误之处在所难免,欢迎指正!
如果转载,请保留作者信息。
博客地址:http://blog.csdn.net/gaoxingnengjisuan
邮箱地址:dong.liu@siat.ac.cn


我们在上一篇博客中可以看到,在建立新卷之前,需要获取建立卷的目标主机,经过分析源码我们可以知道,如果没有指定建立新卷的目标主机,需要通过调度器算法实现目标主机的确定,如果指定了建立新卷的目标主机,则直接获取目标主机,无论是哪种情况,都需要调用方法self.volume_rpcapi.create_volume来实现在目标主机上新卷的建立。在这篇博客中,我们就具体来分析这个方法的实现过程。

我们先来看方法create_volume的源码实现:

  1. def create_volume(self, ctxt, volume, host,
  2. request_spec, filter_properties,
  3. allow_reschedule=True,
  4. snapshot_id=None, image_id=None,
  5. source_volid=None):
  6. """
  7. 远程调用实现建立并导出卷;
  8. """
  9. request_spec_p = jsonutils.to_primitive(request_spec)
  10. self.cast(ctxt,
  11. self.make_msg('create_volume',
  12. volume_id=volume['id'],
  13. request_spec=request_spec_p,
  14. filter_properties=filter_properties,
  15. allow_reschedule=allow_reschedule,
  16. snapshot_id=snapshot_id,
  17. image_id=image_id,
  18. source_volid=source_volid),
  19. # queue_get_for:根据给定的topic和host获取对应的队列名称;
  20. topic=rpc.queue_get_for(ctxt,
  21. self.topic,
  22. host),
  23. version='1.4')
我们可以看到,这里也是应用了广播方法cast实现远程调用方法create_volume,即/cinder/volume/manager.py----class VolumeManager----def create_volume,我们具体来看这个方法的实现源码:

  1. @utils.require_driver_initialized
  2. def create_volume(self, context, volume_id, request_spec=None,
  3. filter_properties=None, allow_reschedule=True,
  4. snapshot_id=None, image_id=None, source_volid=None):
  5. """
  6. Creates and exports the volume.
  7. 建立并导出卷;
  8. """
  9. # 构建并返回用于通过管理器建立卷的flow;
  10. flow = create_volume.get_manager_flow(
  11. self.db,
  12. self.driver,
  13. self.scheduler_rpcapi,
  14. self.host,
  15. volume_id,
  16. request_spec=request_spec,
  17. filter_properties=filter_properties,
  18. allow_reschedule=allow_reschedule,
  19. snapshot_id=snapshot_id,
  20. image_id=image_id,
  21. source_volid=source_volid,
  22. reschedule_context=context.deepcopy())
  23. assert flow, _('Manager volume flow not retrieved')
  24. # 进行flow的运行操作;
  25. flow.run(context.elevated())
  26. if flow.state != states.SUCCESS:
  27. raise exception.CinderException(_("Failed to successfully complete"
  28. " manager volume workflow"))
  29. self._reset_stats()
  30. return volume_id
可见,这里再一次应用taskflow模式来实现建立并导出卷的操作。我们具体来看方法get_manager_flow的源码实现:

  1. def get_manager_flow(db, driver, scheduler_rpcapi, host, volume_id,
  2. request_spec=None, filter_properties=None,
  3. allow_reschedule=True,
  4. snapshot_id=None, image_id=None, source_volid=None,
  5. reschedule_context=None):
  6. """
  7. Constructs and returns the manager entrypoint flow.
  8. 构建并返回用于通过管理器建立卷的flow;
  9. flow将会做以下的事情:
  10. 1. 首先要确定我们是否允许进行重新调度,因为这影响了我们如何对出现错误的情况进行处理;
  11. 2. 为相关的task注入keys和values;
  12. 3. 对于出错的task进行处理,发送错误通知,记录错误信息等;
  13. 4. 实现了从输入的参数中提取建立卷的规范信息的操作;
  14. 5. 通知已经开始进行卷的建立操作;
  15. 6. 根据所获取的建立卷的规范信息实现卷的建立操作;
  16. 7. 当成功的建立卷之后,完成卷建立之后的通知操作;
  17. """
  18. # flow_name:volume_create_manager;
  19. flow_name = ACTION.replace(":", "_") + "_manager"
  20. # 获取类Flow的实例化对象;
  21. volume_flow = linear_flow.Flow(flow_name)
  22. # Determine if we are allowed to reschedule since this affects how
  23. # failures will be handled.
  24. # 首先要确定我们是否允许进行重新调度,因为这影响了我们如何对出现错误的情况进行处理;
  25. if not filter_properties:
  26. filter_properties = {}
  27. if not request_spec and allow_reschedule:
  28. LOG.debug(_("No request spec, will not reschedule"))
  29. allow_reschedule = False
  30. if not filter_properties.get('retry', None) and allow_reschedule:
  31. LOG.debug(_("No retry filter property or associated "
  32. "retry info, will not reschedule"))
  33. allow_reschedule = False
  34. # 添加一个给定的task到flow;
  35. # 这个类实现了注入字典信息到flow中;
  36. volume_flow.add(base.InjectTask({
  37. 'filter_properties': filter_properties,
  38. 'image_id': image_id,
  39. 'request_spec': request_spec,
  40. 'snapshot_id': snapshot_id,
  41. 'source_volid': source_volid,
  42. 'volume_id': volume_id,
  43. }, addons=[ACTION]))
  44. # 如果不允许进行重新调度的操作;
  45. if not allow_reschedule:
  46. # On failure ensure that we just set the volume status to error.
  47. LOG.debug(_("Retry info not present, will not reschedule"))
  48. # 添加一个给定的task到flow;
  49. # 这个task实现了当出现错误时,设置指定id的卷的状态为ERROR;
  50. volume_flow.add(OnFailureChangeStatusTask(db))
  51. # 如果允许进行重新调度的操作;
  52. else:
  53. # 添加一个给定的task到flow;
  54. # 触发一个发送进行重新调度的请求,当进行task恢复回滚操作的时候;
  55. volume_flow.add(OnFailureRescheduleTask(reschedule_context, db, scheduler_rpcapi))
  56. # 添加一个给定的task到flow;
  57. # 提取一个用于建立卷的通用结构规范;
  58. volume_flow.add(ExtractVolumeSpecTask(db))
  59. # 添加一个给定的task到flow;
  60. # 执行关于给定卷的相关通知操作,获取指定卷的使用率信息,并进行通知操作;
  61. volume_flow.add(NotifyVolumeActionTask(db, host, "create.start"))
  62. # 添加一个给定的task到flow;
  63. # 根据所提供的规范要求实现卷的建立操作;
  64. volume_flow.add(CreateVolumeFromSpecTask(db, host, driver))
  65. # 添加一个给定的task到flow;
  66. # 当成功的建立卷之后,完成卷建立之后的通知操作;
  67. volume_flow.add(CreateVolumeOnFinishTask(db, host, "create.end"))
  68. # 获取flow的调试信息;
  69. return flow_utils.attach_debug_listeners(volume_flow)
这里最重要的一个task就是CreateVolumeFromSpecTask,它所实现的操作就是根据所提供的规范要求实现卷的建立。我们具体来看这个类的源码实现:

  1. class CreateVolumeFromSpecTask(base.CinderTask):
  2. """
  3. 根据所提供的规范要求实现卷的建立操作;
  4. """
  5. def __init__(self, db, host, driver):
  6. super(CreateVolumeFromSpecTask, self).__init__(addons=[ACTION])
  7. self.db = db
  8. self.driver = driver
  9. self.requires.update(['volume_spec', 'volume_ref'])
  10. self._create_func_mapping = {
  11. 'raw': self._create_raw_volume,
  12. 'snap': self._create_from_snapshot,
  13. 'source_vol': self._create_from_source_volume,
  14. 'image': self._create_from_image,
  15. }
  16. self.host = host
  17. def __call__(self, context, volume_ref, volume_spec):
  18. """
  19. 根据所提供的规范要求实现卷的建立操作;
  20. """
  21. if not self.driver.initialized:
  22. LOG.error(_("Unable to create volume, driver not initialized"))
  23. driver_name = self.driver.__class__.__name__
  24. raise exception.DriverNotInitialized(driver=driver_name)
  25. # 获取建立卷的类型信息;
  26. create_type = volume_spec.pop('type', None)
  27. # 根据具体的建立卷的类型,获取对应的建立卷的方法;
  28. # self._create_func_mapping = {
  29. # 'raw': self._create_raw_volume,
  30. # 'snap': self._create_from_snapshot,
  31. # 'source_vol': self._create_from_source_volume,
  32. # 'image': self._create_from_image,
  33. # }
  34. create_functor = self._create_func_mapping.get(create_type)
  35. if not create_functor:
  36. raise exception.VolumeTypeNotFound(volume_type_id=create_type)
  37. volume_spec = dict(volume_spec)
  38. volume_id = volume_spec.pop('volume_id', None)
  39. if not volume_id:
  40. volume_id = volume_ref['id']
  41. LOG.info(_("Volume %(volume_id)s: being created using %(functor)s "
  42. "with specification: %(volume_spec)s") %
  43. {'volume_spec': volume_spec, 'volume_id': volume_id,
  44. 'functor': _make_pretty_name(create_functor)})
  45. volume_ref['host'] = self.host
  46. # 根据确定的要调用的建立卷的方法,调用这个方法实现指定类型的卷的建立操作;
  47. model_update = create_functor(context, volume_ref=volume_ref,
  48. **volume_spec)
  49. try:
  50. if model_update:
  51. volume_ref = self.db.volume_update(context, volume_ref['id'], model_update)
  52. except exception.CinderException as ex:
  53. if model_update:
  54. LOG.exception(_("Failed updating model of volume %(volume_id)s"
  55. " with creation provided model %(model)s") %
  56. {'volume_id': volume_id, 'model': model_update})
  57. raise exception.ExportFailure(reason=ex)
  58. model_update = None
  59. try:
  60. LOG.debug(_("Volume %s: creating export"), volume_ref['id'])
  61. # 为逻辑卷创建导出接口;
  62. model_update = self.driver.create_export(context, volume_ref)
  63. if model_update:
  64. self.db.volume_update(context, volume_ref['id'], model_update)
  65. except exception.CinderException as ex:
  66. if model_update:
  67. LOG.exception(_("Failed updating model of volume %(volume_id)s"
  68. " with driver provided model %(model)s") %
  69. {'volume_id': volume_id, 'model': model_update})
  70. raise exception.ExportFailure(reason=ex)
我们在这个类的初始化方法中可以看到:

        self._create_func_mapping = {
            'raw': self._create_raw_volume,
            'snap': self._create_from_snapshot,
            'source_vol': self._create_from_source_volume,
            'image': self._create_from_image,
        }

这里就指明了建立新卷的四种途径,即直接建立raw格式的新卷、从快照建立新卷、从已有的卷建立新卷和从镜像建立新卷。在上述类的__call__方法中,根据具体情况分别调用了不用的方法实现了新卷的建立,我们来看看这几个建立新卷的方法的源码:

  1. def _create_raw_volume(self, context, volume_ref, **kwargs):
  2. """
  3. 实现raw格式卷的建立;
  4. """
  5. return self.driver.create_volume(volume_ref)
  6. def _create_from_snapshot(self, context, volume_ref, snapshot_id,
  7. **kwargs):
  8. """
  9. 实现从快照建立卷的操作,并根据具体情况实现对指定卷的glance元数据进行更新操作;
  10. """
  11. volume_id = volume_ref['id']
  12. # 获取指定卷的快照;
  13. snapshot_ref = self.db.snapshot_get(context, snapshot_id)
  14. # 调用具体驱动中的create_volume_from_snapshot方法,实现从快照建立卷;
  15. model_update = self.driver.create_volume_from_snapshot(volume_ref,
  16. snapshot_ref)
  17. make_bootable = False
  18. try:
  19. # 根据volume_id获取volume;
  20. originating_vref = self.db.volume_get(context, snapshot_ref['volume_id'])
  21. make_bootable = originating_vref.bootable
  22. except exception.CinderException as ex:
  23. LOG.exception(_("Failed fetching snapshot %(snapshot_id)s bootable"
  24. " flag using the provided glance snapshot "
  25. "%(snapshot_ref_id)s volume reference") %
  26. {'snapshot_id': snapshot_id,
  27. 'snapshot_ref_id': snapshot_ref['volume_id']})
  28. raise exception.MetadataUpdateFailure(reason=ex)
  29. if make_bootable:
  30. # 根据具体情况实现对指定卷的glance元数据进行更新操作;
  31. self._handle_bootable_volume_glance_meta(context, volume_id,
  32. snapshot_id=snapshot_id)
  33. return model_update
  34. def _create_from_source_volume(self, context, volume_ref,
  35. source_volid, **kwargs):
  36. """
  37. 实现从源卷建立(实际上就是直接拷贝)卷的操作;
  38. """
  39. # 根据source_volid获取卷的信息;
  40. srcvol_ref = self.db.volume_get(context, source_volid)
  41. # 创建指定卷的克隆;
  42. model_update = self.driver.create_cloned_volume(volume_ref, srcvol_ref)
  43. # 根据具体情况实现对指定卷的glance元数据进行更新操作;
  44. if srcvol_ref.bootable:
  45. self._handle_bootable_volume_glance_meta(context, volume_ref['id'],
  46. source_volid=source_volid)
  47. return model_update
  48. def _create_from_image(self, context, volume_ref,
  49. image_location, image_id, image_meta,
  50. image_service, **kwargs):
  51. """
  52. 从镜像实现卷的建立;
  53. """
  54. LOG.debug(_("Cloning %(volume_id)s from image %(image_id)s "
  55. " at location %(image_location)s") %
  56. {'volume_id': volume_ref['id'],
  57. 'image_location': image_location, 'image_id': image_id})
  58. # 从现有的镜像有效的建立一个卷;
  59. model_update, cloned = self.driver.clone_image(volume_ref, image_location, image_id)
  60. # 如果没有实现克隆,说明没有指定的镜像;
  61. # 实现建立卷,并下载镜像数据到卷中;
  62. if not cloned:
  63. # 实现建立卷,并下载镜像数据到卷中;
  64. model_update = self.driver.create_volume(volume_ref)
  65. updates = dict(model_update or dict(), status='downloading')
  66. # 更新卷的状态;
  67. try:
  68. volume_ref = self.db.volume_update(context,
  69. volume_ref['id'], updates)
  70. except exception.CinderException:
  71. LOG.exception(_("Failed updating volume %(volume_id)s with "
  72. "%(updates)s") %
  73. {'volume_id': volume_ref['id'],
  74. 'updates': updates})
  75. # 下载glance镜像数据到指定的卷;
  76. self._copy_image_to_volume(context, volume_ref, image_id, image_location, image_service)
  77. # 根据具体情况实现对指定卷的glance元数据进行更新操作;
  78. self._handle_bootable_volume_glance_meta(context, volume_ref['id'],
  79. image_id=image_id,
  80. image_meta=image_meta)
  81. return model_update
再来看这几个方法的源码,不同的方法中会进一步调用不同的方法来实现新卷的建立,这就直接与/cinder/volume/drivers中的不同的块存储后端实现直接联系到一起了,具体调用的是那一种块存储器中的建立卷的方法,就是由self.driver所确定的。


OK!到此为止,cinder中建立新卷的整体流程的源码分析已经全部完成,其实我想说的一句话就是,如果真的把这个流程的实现过程搞清楚,那么cinder模块的源码也就基本掌握了。

谢谢大家的支持!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/217513
推荐阅读
相关标签
  

闽ICP备14008679号