Cinder 的设计遵循 service -> manager -> driver 的三层架构。
主要包括 cinder-api, cinder-scheduler, cinder-volume 三大核心组件(进程),组件之间通过 RPC 进行通信。
RESTful 入口 cinder-api
1 2 3 4
|
[app:apiv3] paste.app_factory = cinder.api.v3.router:APIRouter.factory
|
1 2 3 4 5 6 7 8
|
class APIRouter(base_wsgi.Router): def __init__ ext_mgr = self.ExtensionManager() self._setup_routes(mapper, ext_mgr) self._setup_ext_routes(mapper, ext_mgr) self._setup_extensions(ext_mgr)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
class APIRouter(cinder.api.openstack.APIRouter): ExtensionManager = extensions.ExtensionManager
def _setup_routes(self, mapper, ext_mgr): self.resources['volumes'] = volumes.create_resource(ext_mgr) mapper.resource("volume", "volumes", controller=self.resources['volumes'], collection={'detail': 'GET', 'summary': 'GET'}, member={'action': 'POST'}) self.resources['attachments'] = attachments.create_resource(ext_mgr) mapper.resource("attachment", "attachments", controller=self.resources['attachments'], collection={'detail': 'GET', 'summary': 'GET'}, member={'action': 'POST'})
|
云盘创建
1 2 3 4 5
|
class VolumeController(volumes_v2.VolumeController): def create self.volume_api.create
|
1 2 3 4 5 6
|
class API(base.Base): def create flow_engine = create_volume.get_flow flow_engine.run()
|
1 2 3 4 5 6 7 8 9 10 11
|
def get_flow api_flow.add(VolumeCastTask(scheduler_rpcapi, volume_rpcapi, db_api))
class VolumeCastTask(flow_utils.CinderTask): def execute self._cast_create_volume
def _cast_create_volume self.scheduler_rpcapi.create_volume
|
Cinder 的 scheduler 没有像 Nova 一样提供 api 对 rpcapi 进行封装,整个组件间的交互流程也比 Nova 要简单清晰的很多
1 2 3 4 5
|
class SchedulerAPI(rpc.RPCAPI): def create_volume cctxt.cast(ctxt, 'create_volume', **msg_args)
|
1 2 3 4 5 6 7 8 9 10 11 12 13
|
scheduler_manager_opts = [ cfg.StrOpt('scheduler_driver', default='cinder.scheduler.filter_scheduler.FilterScheduler', help='Default scheduler driver to use'), ]
class SchedulerManager(manager.CleanableManager, manager.Manager): def create_volume flow_engine = create_volume.get_flow scheduler_flow.add(ScheduleCreateVolumeTask(driver_api)) flow_engine.run()
|
1 2 3 4 5 6 7 8 9
|
def get_flow scheduler_flow.add(ScheduleCreateVolumeTask(driver_api))
class ScheduleCreateVolumeTask(flow_utils.CinderTask): def execute self.driver_api.schedule_create_volume
|
1 2 3 4 5 6 7 8
|
class FilterScheduler(driver.Scheduler): def schedule_create_volume self._schedule self._get_weighted_candidates self._choose_top_backend self.volume_rpcapi.create_volume
|
从 cinder-api 到 cinder-scheduler,现在终于到了 cinder-volume,需要注意的是,每个不同的后端,会创建一个对应的 manager,其中 class VolumeManager
的 service_name
初始化参数决定了 manager 对应的后端卷驱动(请参考 cinder/cmd/volume.py 对 CONF.enabled_backends 选项的处理)
1 2 3 4 5 6 7 8 9
|
class VolumeAPI(rpc.RPCAPI): def create_volume cctxt.cast(ctxt, 'create_volume', request_spec=request_spec, filter_properties=filter_properties, allow_reschedule=allow_reschedule, volume=volume)
|
1 2 3 4 5 6 7
|
class VolumeManager(manager.CleanableManager, manager.SchedulerDependentManager): def create_volume flow_engine = create_volume.get_flow flow_engine.run()
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
def get_flow volume_flow.add(ExtractVolumeSpecTask(db), NotifyVolumeActionTask(db, "create.start"), CreateVolumeFromSpecTask(manager, db, driver, image_volume_cache), CreateVolumeOnFinishTask(db, "create.end"))
class CreateVolumeFromSpecTask(flow_utils.CinderTask): def execute if create_type == 'raw': self._create_raw_volume self.driver.create_volume elif create_type == 'snap': self._create_from_snapshot self.driver.create_volume_from_snapshot elif create_type == 'source_vol': self._create_from_source_volume self.driver.create_cloned_volume elif create_type == 'image': elf._create_from_image elif create_type == 'backup': self._create_from_backup
|
根据不同的后端类型,不同的驱动有不同的处理逻辑
1 2 3 4 5 6 7
|
class RBDDriver(driver.CloneableImageVD, driver.MigrateVD, driver.ManageableVD, driver.ManageableSnapshotsVD, driver.BaseVD): def create_volume self.RBDProxy().create
|
云盘挂载
Nova 进行云盘挂载时会调用 Cinder 的 attachments RESTful 接口返回 os_brick 挂载所需要的信息。
1 2 3 4 5
|
class AttachmentsController(wsgi.Controller): def update self.volume_api.attachment_update
|
1 2 3 4 5
|
class API(base.Base): def attachment_update self.volume_rpcapi.attachment_update
|
1 2 3 4 5 6 7 8 9
|
class VolumeAPI(rpc.RPCAPI): def attachment_update cctxt.call(ctxt, 'attachment_update', vref=vref, connector=connector, attachment_id=attachment_id)
|
1 2 3 4 5 6 7 8 9
|
class VolumeManager(manager.CleanableManager, manager.SchedulerDependentManager): def attachment_update self._connection_create self.driver.initialize_connection self.driver.attach_volume
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
|
def initialize_connection(self, volume, connector): hosts, ports = self._get_mon_addrs() data = { 'driver_volume_type': 'rbd', 'data': { 'name': '%s/%s' % (self.configuration.rbd_pool, volume.name), 'hosts': hosts, 'ports': ports, 'cluster_name': self.configuration.rbd_cluster_name, 'auth_enabled': (self.configuration.rbd_user is not None), 'auth_username': self.configuration.rbd_user, 'secret_type': 'ceph', 'secret_uuid': self.configuration.rbd_secret_uuid, 'volume_id': volume.id, "discard": True, 'keyring': self._get_keyring_contents(), } } LOG.debug('connection data: %s', data) return data
|