cinder创建volume流程简介

cinder创建volume流程简介

Openstack icehouse
Cinder-api启动脚本

/etc/init.d/openstack-cinder-api
Vim /etc/init.d/openstack-cinder-api

. /etc/rc.d/init.d/functions

suffix=api
prog=openstack-cinder-$suffix
exec="/usr/bin/cinder-$suffix" #cinder执行文件
config="/etc/cinder/cinder.conf" #cinder主要配置文件
distconfig="/usr/share/cinder/cinder-dist.conf" #主要配置文件
pidfile="/var/run/cinder/cinder-$suffix.pid"  #进程文件
logfile="/var/log/cinder/$suffix.log"  #cinder-api日志文件

[ -e /etc/sysconfig/$prog ] && . /etc/sysconfig/$prog

lockfile=/var/lock/subsys/$prog #lock

start() {
    [ -x $exec ] || exit 5
    [ -f $config ] || exit 6
    echo -n $"Starting $prog: "
    daemon --user cinder --pidfile $pidfile "$exec --config-file $distconfig --config-file $config --logfile $logfile &>/dev/null & echo \$! > $pidfile"
    retval=$?
    echo
    [ $retval -eq 0 ] && touch $lockfile
    return $retval
}
省略。。。

查看openstack-cinder-api进程
ps -ef | grep cinder-api 
这里写图片描述

"""Starter script for Cinder OS API."""

import eventlet #线程管理模块
eventlet.monkey_patch()

import os
import sys

from oslo.config import cfg #负责CLI和CONF配置项解析的组件

possible_topdir = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
                                   os.pardir,
                                   os.pardir))  # possible_topdir = /usr
if os.path.exists(os.path.join(possible_topdir, "cinder", "__init__.py")):  #false
    sys.path.insert(0, possible_topdir)

from cinder.openstack.common import gettextutils
gettextutils.install('cinder', lazy=False)

# Need to register global_opts
from cinder.common import config  # noqa
from cinder.openstack.common import log as logging
from cinder import rpc
from cinder import service
from cinder import utils
from cinder import version


CONF = cfg.CONF


if __name__ == '__main__':
    CONF(sys.argv[1:], project='cinder',
         version=version.version_string())
    logging.setup("cinder")
    utils.monkey_patch()

    rpc.init(CONF) #初始化rpc服务
    launcher = service.process_launcher()
    server = service.WSGIService('osapi_volume') #调用osapi_volume组件的工厂程序
    launcher.launch_service(server, workers=server.workers or 1)
launcher.wait()

一.创建一个cinder volume
Cinder.api.v2.api
cinder创建volume流程简介_第1张图片

 def create(self, req, body):
        """Creates a new volume."""
        if not self.is_valid_body(body, 'volume'): 1.判断请求体是否合法
            msg = _("Missing required element '%s' in request body") % 'volume'
            raise exc.HTTPBadRequest(explanation=msg)

        LOG.debug('Create volume request body: %s', body)
        context = req.environ['cinder.context']
        volume = body['volume']

        kwargs = {}

        # NOTE(thingee): v2 API allows name instead of display_name
        if volume.get('name'):  2.使用display_name 替换 name
            volume['display_name'] = volume.get('name')
            del volume['name']

        # NOTE(thingee): v2 API allows description instead of
        # display_description
        if volume.get('description'):3.使用display_description 替换 description
            volume['display_description'] = volume.get('description')
            del volume['description']
  4.检查是否有所选的volume类型  
  req_volume_type = volume.get('volume_type', None)
        if req_volume_type:
            try:
                if not uuidutils.is_uuid_like(req_volume_type):
                    kwargs['volume_type'] = \
                        volume_types.get_volume_type_by_name(
                            context, req_volume_type)
                else:
                    kwargs['volume_type'] = volume_types.get_volume_type(
                        context, req_volume_type)
            except exception.VolumeTypeNotFound:
                msg = _("Volume type not found.")
                raise exc.HTTPNotFound(explanation=msg)
        5.获取请求元信息
        kwargs['metadata'] = volume.get('metadata', None)
        6.判断是否使用指定的snapshot来创建volume
        snapshot_id = volume.get('snapshot_id')
        if snapshot_id is not None:
            try:
                kwargs['snapshot'] = self.volume_api.get_snapshot(context,
                                                                  snapshot_id)
            except exception.NotFound:
                explanation = _('snapshot id:%s not found') % snapshot_id
                raise exc.HTTPNotFound(explanation=explanation)
        else:
            kwargs['snapshot'] = None
        7.判断是否从已有的设备中创建volume
        source_volid = volume.get('source_volid')
        if source_volid is not None:
            try:
                kwargs['source_volume'] = \
                    self.volume_api.get_volume(context,
                                               source_volid)
            except exception.NotFound:
                explanation = _('source volume id:%s not found') % source_volid
                raise exc.HTTPNotFound(explanation=explanation)
        else:
            kwargs['source_volume'] = None
        8.设置将要创建volume的大小
        size = volume.get('size', None)
        if size is None and kwargs['snapshot'] is not None:
            size = kwargs['snapshot']['volume_size']
        elif size is None and kwargs['source_volume'] is not None:
            size = kwargs['source_volume']['size']

        LOG.audit(_("Create volume of %s GB"), size, context=context)
        9.判断是否加载了os-image-create服务
        if self.ext_mgr.is_loaded('os-image-create'):
            image_href = volume.get('imageRef')
            if image_href: 如果imageref被nova API产生,则从imageref中提取image_uuid
                image_uuid = self._image_uuid_from_href(image_href)
                kwargs['image_id'] = image_uuid
        10.设置volume的可用域
        kwargs['availability_zone'] = volume.get('availability_zone', None)                  11.设置scheduler_hints volume创建参数
        kwargs['scheduler_hints'] = volume.get('scheduler_hints', None)

        new_volume = self.volume_api.create(context,
                                            size,
                                            volume.get('display_name'),
                                            volume.get('display_description'),
                                            **kwargs)

        # TODO(vish): Instance should be None at db layer instead of
        # trying to lazy load, but for now we turn it into
        # a dict to avoid an error.
        new_volume = dict(new_volume.iteritems())

        utils.add_visible_admin_metadata(context, new_volume, self.volume_api)

        retval = self._view_builder.detail(req, new_volume)

        Return  retval

cinder创建volume流程简介_第2张图片
cinder创建volume流程简介_第3张图片

/etc/cinder/cinder.conf
这里写图片描述
cinder创建volume流程简介_第4张图片
/cinder/volume/api.API()

def create(self, context, size, name, description, snapshot=None, image_id=None, volume_type=None, metadata=None, availability_zone=None, source_volume=None, scheduler_hints=None, backup_source_volume=None):
1.如果是通过已有的volume创建新volume则新的volume类型必须与源volume类型一致
        if source_volume and volume_type:
            if volume_type['id'] != source_volume['volume_type_id']:
                msg = _("Invalid volume_type provided (requested type "
                        "must match source volume, or be omitted). "
                        "You should omit the argument.")
                raise exception.InvalidInput(reason=msg)
2.如果是通过已有的snapshot创建新volume则新的volume类型必须与源snapshot类型一致
        if snapshot and volume_type:
            if volume_type['id'] != snapshot['volume_type_id']:
                msg = _("Invalid volume_type provided (requested type "
                        "must match source snapshot, or be omitted). "
                        "You should omit the argument.")
                raise exception.InvalidInput(reason=msg)
3.内置函数,用于判断选择的域是否在可用域范围内
    def check_volume_az_zone(availability_zone):
            try:
                return self._valid_availability_zone(availability_zone)
            except exception.CinderException:
                LOG.exception(_("Unable to query if %s is in the "
                                "availability zone set"), availability_zone)
                return False

        create_what = {
            'context': context,
            'raw_size': size,
            'name': name,
            'description': description,
            'snapshot': snapshot,
            'image_id': image_id,
            'raw_volume_type': volume_type,
            'metadata': metadata,
            'raw_availability_zone': availability_zone,
            'source_volume': source_volume,
            'scheduler_hints': scheduler_hints,
            'key_manager': self.key_manager,
            'backup_source_volume': backup_source_volume,
        }
4.创建vpi volume flow
        try:
            flow_engine = 
create_volume.get_flow(self.scheduler_rpcapi,
                                                 self.volume_rpcapi,
                                                 self.db,
                                                 self.image_service,
                                                 check_volume_az_zone,
                                                 create_what)
        except Exception:
            LOG.exception(_("Failed to create api volume flow"))
            raise exception.CinderException(
                _("Failed to create api volume flow"))

/cinder/volume/api.py

/cinder/volume/api.py
cinder创建volume流程简介_第5张图片
/cinder/db/base.py
cinder创建volume流程简介_第6张图片
/cinder/volume/api.py
cinder创建volume流程简介_第7张图片

        flow_engine.run()
        volume = flow_engine.storage.fetch('volume')
        return volume

/cinder/volume/api.py

/cinder/volume/flows/api.py
cinder创建volume流程简介_第8张图片
/cinder/volume/flows/api.get_flow()

def get_flow(scheduler_rpcapi, volume_rpcapi, db, image_service, az_check_functor, create_what):
    """Constructs and returns the api entrypoint flow. This flow will do the following: 1. Inject keys & values for dependent tasks.为任务注入keys 和values 2. Extracts and validates the input keys & values.提前和确认keys和values 3. Reserves the quota (reverts quota on any failures).存储配额 4. Creates the database entry.创建数据库实体记录 5. Commits the quota.提交配额 6. Casts to volume manager or scheduler for further processing.卷管理或者调度,进一步处理 """
    #flow_name=volume_create_api
flow_name = ACTION.replace(":", "_") + "_api"
1.创建一个线性流
    api_flow = linear_flow.Flow(flow_name)
2.把提供的task/tasks/flow/flows添加到当前flow中

cinder创建volume流程简介_第9张图片

#ExtractVolumeRequestTask接收一个集合,返回一个经过验证的参数集,或者经转换的参数集提供给其他task使用
    api_flow.add(ExtractVolumeRequestTask(
        image_service,
        az_check_functor,
        rebind={'size': 'raw_size',
                'availability_zone': 'raw_availability_zone',
                'volume_type': 'raw_volume_type'}))
#QuotaReserveTask预留一个指定大小的volume,和volume type
api_flow.add(QuotaReserveTask(),
#EntryCreateTask 在数据库中创建一条创建volume的记录
                 EntryCreateTask(db),
    #QuotaCommitTask提交预留信息
                 QuotaCommitTask())

    # This will cast it out to either the scheduler or volume manager via
# the rpc apis provided.
#VolumeCastTask执行volume cast操作到scheduler或者volume manager
    api_flow.add(VolumeCastTask(scheduler_rpcapi, volume_rpcapi, db))

# Now load (but do not run) the flow using the provided initial data.
3.使用提供的初始化数据来加载该flow
    #flow: flow to load
    #store: dict -- data to put to storage to satisfy flow requirements
    return taskflow.engines.load(api_flow, store=create_what)

/cinder/volume/flows/api.
cinder创建volume流程简介_第10张图片
/cinder/volume/flows/api.
cinder创建volume流程简介_第11张图片
注:task任务类详解
ExtractVolumeRequestTask
QuotaReserveTask
EntryCreateTask
QuotaCommitTask
VolumeCastTask

class VolumeCastTask(flow_utils.CinderTask):
    """Performs a volume create cast to the scheduler or to the volume manager. This which will signal a transition of the api workflow to another child and/or related workflow on another component. Reversion strategy: N/A """

    def __init__(self, scheduler_rpcapi, volume_rpcapi, db):
        requires = ['image_id', 'scheduler_hints', 'snapshot_id',
                    'source_volid', 'volume_id', 'volume_type',
                    'volume_properties']
        super(VolumeCastTask, self).__init__(addons=[ACTION],
                                             requires=requires)
        self.volume_rpcapi = volume_rpcapi
        self.scheduler_rpcapi = scheduler_rpcapi
        self.db = db
#远程调用建立卷的方法,实现卷的建立操作;
    def _cast_create_volume(self, context, request_spec, filter_properties):
        source_volid = request_spec['source_volid']
        volume_id = request_spec['volume_id']
        snapshot_id = request_spec['snapshot_id']
        image_id = request_spec['image_id']
        host = None
#如果是从snapshot来创建volume则要获取该snapshot的所在的host
        if snapshot_id and CONF.snapshot_same_host:
            # NOTE(Rongze Zhu): A simple solution for bug 1008866.
            #
            # If snapshot_id is set, make the call create volume directly to
            # the volume host where the snapshot resides instead of passing it
            # through the scheduler. So snapshot can be copy to new volume.
            snapshot_ref = self.db.snapshot_get(context, snapshot_id)
            source_volume_ref = self.db.volume_get(context,
                                                   snapshot_ref['volume_id'])
            host = source_volume_ref['host']
  #如果是从snapshot来创建新volume,则要获取该volume所在的host
        elif source_volid:
            source_volume_ref = self.db.volume_get(context, source_volid)
            host = source_volume_ref['host']
#如果没有提供host参数时执行以下代码,进行调度选择合适的host执行创建操作
        if not host:
            # Cast to the scheduler and let it handle whatever is needed
            # to select the target host for this volume.
            self.scheduler_rpcapi.create_volume(
                context,
                CONF.volume_topic,
                volume_id,
                snapshot_id=snapshot_id,
                image_id=image_id,
                request_spec=request_spec,
                filter_properties=filter_properties)
 #否则执行以下代码,rpc到指定的host上执行操作
        else:
            # Bypass the scheduler and send the request directly to the volume
            # manager.
            now = timeutils.utcnow()
            values = {'host': host, 'scheduled_at': now}
            volume_ref = self.db.volume_update(context, volume_id, values)#更新数据库的接口
            self.volume_rpcapi.create_volume(
                context,
                volume_ref,
                volume_ref['host'],
                request_spec,
                filter_properties,
                allow_reschedule=False,
                snapshot_id=snapshot_id,
                image_id=image_id,
                source_volid=source_volid)
  #flow_engine.run()会调用该函数
    def execute(self, context, **kwargs):
        scheduler_hints = kwargs.pop('scheduler_hints', None)
        request_spec = kwargs.copy()
        filter_properties = {}
        if scheduler_hints:
            filter_properties['scheduler_hints'] = scheduler_hints
        self._cast_create_volume(context, request_spec, filter_properties)

执行execute()函数执行volume创建工作,假设host为空执行self.scheduler_rpcapi.create_volume()

def create_volume(self, ctxt, topic, volume_id, snapshot_id=None, image_id=None, request_spec=None, filter_properties=None):
      1.创建发送RPC消息的Client
        cctxt = self.client.prepare(version='1.2')
        request_spec_p = jsonutils.to_primitive(request_spec)
2.发送RPC消息远程调用对应manager的create_volume方法,并立刻返回
        return cctxt.cast(ctxt, 'create_volume',
                          topic=topic,
                          volume_id=volume_id,
                          snapshot_id=snapshot_id,
                          image_id=image_id,
                          request_spec=request_spec_p,
                          filter_properties=filter_properties)

/cinder/scheduler/rpcapi.py
cinder创建volume流程简介_第12张图片
/cinder/scheduler/rpcapi.py
这里写图片描述
/cinder/rpc.py

def get_client(target, version_cap=None, serializer=None):
    assert TRANSPORT is not None
serializer = RequestContextSerializer(serializer)
    return messaging.RPCClient(TRANSPORT,
                               target,
                               version_cap=version_cap,
/oslo/messaging   /oslo/messaging/rpc *
                               serializer=serializer)

cinder创建volume流程简介_第13张图片
/oslo/messaging/rpc/client.py
cinder创建volume流程简介_第14张图片

def prepare(self, exchange=_marker, topic=_marker, namespace=_marker, version=_marker, server=_marker, fanout=_marker, timeout=_marker, version_cap=_marker):
        """Prepare a method invocation context. Use this method to override client properties for an individual method invocation. For example:: def test(self, ctxt, arg): cctxt = self.prepare(version='2.5') return cctxt.call(ctxt, 'test', arg=arg) :param exchange: see Target.exchange :type exchange: str :param topic: see Target.topic :type topic: str :param namespace: see Target.namespace :type namespace: str :param version: requirement the server must support, see Target.version :type version: str :param server: send to a specific server, see Target.server :type server: str :param fanout: send to all servers on topic, see Target.fanout :type fanout: bool :param timeout: an optional default timeout (in seconds) for call()s :type timeout: int or float :param version_cap: raise a RPCVersionCapError version exceeds this cap :type version_cap: str """
#准备rpc 方法的context
        return _CallContext._prepare(self,
                                     exchange, topic, namespace,
                                     version, server, fanout,
                                     timeout, version_cap)

/oslo/messaging/rpc/clinet.py
cinder创建volume流程简介_第15张图片
/oslo/messaging/rpc/client.py

/oslo/message/rpc/client.py

def cast(self, ctxt, method, **kwargs):
        """Invoke a method and return immediately. See RPCClient.cast()."""
#制作要发送的消息
        msg = self._make_message(ctxt, method, kwargs)
#对ctxt做序列化
        ctxt = self.serializer.serialize_context(ctxt)
#检查版本
        if self.version_cap:
            self._check_version_cap(msg.get('version'))
        try:
#开始发送RPC消息
            self.transport._send(self.target, ctxt, msg)
        except driver_base.TransportDriverError as ex:
            raise ClientSendError(self.target, ex)

RPC后有/cinder/scheduler/manager.SchedulerManager接收
/cinder/scheduler/rpcapi.py
cinder创建volume流程简介_第16张图片
/cinder/scheduler/manager.py
cinder创建volume流程简介_第17张图片

def create_volume(self, context, topic, volume_id, snapshot_id=None, image_id=None, request_spec=None, filter_properties=None):

        try:
            flow_engine = create_volume.get_flow(context,
                                                 db, self.driver,
                                                 request_spec,
                                                 filter_properties,
                                                 volume_id,
                                                 snapshot_id,
                                                 image_id)
        except Exception:
            LOG.exception(_("Failed to create scheduler manager volume flow"))
            raise exception.CinderException(
                _("Failed to create scheduler manager volume flow"))
        flow_engine.run()

/cinder/scheduler/manager.py
这里写图片描述
/cinder/scheduler/flows/create_volume.py

/cinder/scheduler/flows/create_volume.py
cinder创建volume流程简介_第18张图片
ExtractSchedulerSpecTask task.FunctorTask(schedule_create_volume)
ExtractSchedulerSpecTask 主要用于提取scheduler请求的一些指定参数,

class ExtractSchedulerSpecTask(flow_utils.CinderTask):
    """Extracts a spec object from a partial and/or incomplete request spec. Reversion strategy: N/A """

    default_provides = set(['request_spec'])

    def __init__(self, db, **kwargs):
        super(ExtractSchedulerSpecTask, self).__init__(addons=[ACTION],
                                                       **kwargs)
        self.db = db

    def _populate_request_spec(self, context, volume_id, snapshot_id, image_id):
        # Create the full request spec using the volume_id.
        #
        # NOTE(harlowja): this will fetch the volume from the database, if
        # the volume has been deleted before we got here then this should fail.
        #
        # In the future we might want to have a lock on the volume_id so that
        # the volume can not be deleted while its still being created?
        if not volume_id:
            msg = _("No volume_id provided to populate a request_spec from")
            raise exception.InvalidInput(reason=msg)
        volume_ref = self.db.volume_get(context, volume_id)
        volume_type_id = volume_ref.get('volume_type_id')
        vol_type = self.db.volume_type_get(context, volume_type_id)
        return {
            'volume_id': volume_id,
            'snapshot_id': snapshot_id,
            'image_id': image_id,
            'volume_properties': {
                'size': utils.as_int(volume_ref.get('size'), quiet=False),
                'availability_zone': volume_ref.get('availability_zone'),
                'volume_type_id': volume_type_id,
            },
            'volume_type': list(dict(vol_type).iteritems()),
        }
#提取指定的scheduler请求参数,并返回
    def execute(self, context, request_spec, volume_id, snapshot_id, image_id):
        # For RPC version < 1.2 backward compatibility
        if request_spec is None:
            request_spec = self._populate_request_spec(context, volume_id,
                                                       snapshot_id, image_id)
        return {
            'request_spec': request_spec,
        }

task.FunctorTask(schedule_create_volume)执行scheduler_create_volume()
是get_flow()的内嵌函数,被包装成stask,加载到stask flow中。
def schedule_create_volume(context, request_spec, filter_properties):

        def _log_failure(cause):
            LOG.error(_("Failed to schedule_create_volume: %(cause)s") %
                      {'cause': cause})

        def _notify_failure(cause):
            """When scheduling fails send out a event that it failed."""
            topic = "scheduler.create_volume"
            payload = {
                'request_spec': request_spec,
                'volume_properties': request_spec.get('volume_properties', {}),
                'volume_id': volume_id,
                'state': 'error',
                'method': 'create_volume',
                'reason': cause,
            }
            try:
                rpc.get_notifier('scheduler').error(context, topic, payload)
            except exception.CinderException:
                LOG.exception(_("Failed notifying on %(topic)s "
                                "payload %(payload)s") % {'topic': topic,
                                                          'payload': payload})

        try:
            driver.schedule_create_volume(context, request_spec,
                                          filter_properties)
        except exception.NoValidHost as e:
            # Not host found happened, notify on the scheduler queue and log
            # that this happened and set the volume to errored out and
            # *do not* reraise the error (since whats the point).
            _notify_failure(e)
            _log_failure(e)
            common.error_out_volume(context, db, volume_id, reason=e)
        except Exception as e:
            # Some other error happened, notify on the scheduler queue and log
            # that this happened and set the volume to errored out and
            # *do* reraise the error.
            with excutils.save_and_reraise_exception():
                _notify_failure(e)
                _log_failure(e)
                common.error_out_volume(context, db, volume_id, reason=e)

/cinder/scheduler/flows/create_volume.py
cinder创建volume流程简介_第19张图片
/cinder/scheduler/flows/create_volume.py

/cinder/scheduler/manager.py
cinder创建volume流程简介_第20张图片

ChanceScheduler and SimpleScheduler have been ' 'deprecated due to lack of support for advanced ' 'features like: volume types, volume encryption,' ' QoS etc. These two schedulers can be fully ' 'replaced by FilterScheduler with certain ' 'combination of filters and weighers.

/etc/cinder/cinder.conf
这里写图片描述
/cinder/scheduler/filter_scheduler.FilerScheduler().scheduler_create_volume() (dirver.scheduler_create_volume())

def schedule_create_volume(self, context, request_spec, filter_properties):
        weighed_host = self._schedule(context, request_spec,
                                      filter_properties)

        if not weighed_host:
            raise exception.NoValidHost(reason="")

        host = weighed_host.obj.host
        volume_id = request_spec['volume_id']
        snapshot_id = request_spec['snapshot_id']
        image_id = request_spec['image_id']
1.更新创建volume数据库条目
        updated_volume = driver.volume_update_db(context, volume_id, host)
        self._post_select_populate_filter_properties(filter_properties,
                                                     weighed_host.obj)

        # context is not serializable
        filter_properties.pop('context', None)
2.发送RPC到指定volume driver manager 创建volume
        self.volume_rpcapi.create_volume(context, updated_volume, host,
                                         request_spec, filter_properties,
                                         allow_reschedule=True,
                                         snapshot_id=snapshot_id,
                                         image_id=image_id)

/cinder/scheduler/driver.py

/cinder/volume/rpcapi.py

def create_volume(self, ctxt, volume, host, request_spec, filter_properties, allow_reschedule=True, snapshot_id=None, image_id=None, source_volid=None):

        cctxt = self.client.prepare(server=host, version='1.4')
        request_spec_p = jsonutils.to_primitive(request_spec)
#调用cinder.volume.manager.VolumeManager(create_volume())
        cctxt.cast(ctxt, 'create_volume',
                   volume_id=volume['id'],
                   request_spec=request_spec_p,
                   filter_properties=filter_properties,
                   allow_reschedule=allow_reschedule,
                   snapshot_id=snapshot_id,
                   image_id=image_id,
                   source_volid=source_volid),

/cinder/volume/manager.py

def create_volume(self, context, volume_id, request_spec=None, filter_properties=None, allow_reschedule=True, snapshot_id=None, image_id=None, source_volid=None):

        """Creates the volume."""
        context_saved = context.deepcopy()
        context = context.elevated()
        if filter_properties is None:
            filter_properties = {}

        try:
            # NOTE(flaper87): Driver initialization is
            # verified by the task itself.
#driver 初始化会被task检查,获取create volume工作流
            flow_engine = create_volume.get_flow(
                context,
                self.db,
                self.driver,
                self.scheduler_rpcapi,
                self.host,
                volume_id,
                snapshot_id=snapshot_id,
                image_id=image_id,
                source_volid=source_volid,
                allow_reschedule=allow_reschedule,
                reschedule_context=context_saved,
                request_spec=request_spec,
                filter_properties=filter_properties)
        except Exception:
            LOG.exception(_("Failed to create manager volume flow"))
            raise exception.CinderException(
                _("Failed to create manager volume flow"))

        if snapshot_id is not None:
            # Make sure the snapshot is not deleted until we are done with it.
            locked_action = "%s-%s" % (snapshot_id, 'delete_snapshot')
        elif source_volid is not None:
            # Make sure the volume is not deleted until we are done with it.
            locked_action = "%s-%s" % (source_volid, 'delete_volume')
        else:
            locked_action = None

        def _run_flow():
            # This code executes create volume flow. If something goes wrong,
            # flow reverts all job that was done and reraises an exception.
            # Otherwise, all data that was generated by flow becomes available
            # in flow engine's storage.
            flow_engine.run()
 #确保在使用snapshot或者volume创建volume时,源volume不会被删除。
        @utils.synchronized(locked_action, external=True)
        def _run_flow_locked():
            _run_flow()
 #启动工作流。
        if locked_action is None:
            _run_flow()
        else:
            _run_flow_locked()

        # Fetch created volume from storage
        volume_ref = flow_engine.storage.fetch('volume')
        # Update volume stats
        self.stats['allocated_capacity_gb'] += volume_ref['size']
        return volume_ref['id']

/cinder/volume/manager.py

/cinder/volume/manager.py
cinder创建volume流程简介_第21张图片
根据配置选择加载需要的driver(默认driver
cinder.volume.drivers.lvm.LVMISCSIDriver)
/cinder/volume/manager.py
这里写图片描述
/cinder/volume/flows/manager/create_volume.py

def get_flow(context, db, driver, scheduler_rpcapi, host, volume_id, allow_reschedule, reschedule_context, request_spec, filter_properties, snapshot_id=None, image_id=None, source_volid=None):
    """Constructs and returns the manager entrypoint flow. This flow will do the following: 1. Determines if rescheduling is enabled (ahead of time). 2. Inject keys & values for dependent tasks. 3. Selects 1 of 2 activated only on *failure* tasks (one to update the db status & notify or one to update the db status & notify & *reschedule*). 4. Extracts a volume specification from the provided inputs. 5. Notifies that the volume has start to be created. 6. Creates a volume from the extracted volume specification. 7. Attaches a on-success *only* task that notifies that the volume creation has ended and performs further database status updates. """

    flow_name = ACTION.replace(":", "_") + "_manager"
    volume_flow = linear_flow.Flow(flow_name)

    # This injects the initial starting flow values into the workflow so that
    # the dependency order of the tasks provides/requires can be correctly
    # determined.
    create_what = {
        'context': context,
        'filter_properties': filter_properties,
        'image_id': image_id,
        'request_spec': request_spec,
        'snapshot_id': snapshot_id,
        'source_volid': source_volid,
        'volume_id': volume_id,
    }

    volume_flow.add(ExtractVolumeRefTask(db, host))

    if allow_reschedule and request_spec:
        volume_flow.add(OnFailureRescheduleTask(reschedule_context,
                                                db, scheduler_rpcapi))
    #create volume
    volume_flow.add(ExtractVolumeSpecTask(db),
                    NotifyVolumeActionTask(db, "create.start"),
                    CreateVolumeFromSpecTask(db, driver),
                    CreateVolumeOnFinishTask(db, "create.end"))

    # Now load (but do not run) the flow using the provided initial data.
return taskflow.engines.load(volume_flow, store=create_what)

/cinder/volume/flows/manager/create_volume.py
cinder创建volume流程简介_第22张图片
ExtractVolumeSpecTask NotifyVolumeActionTask
CreateVolumeFromSpecTask CreateVolumeOnFinishTask
创建volume的工作flow
这里注重介绍CreateVolumeFromSpecTask
/cinder/volume/flows/manager/create_volume.py
cinder创建volume流程简介_第23张图片
/cinder/volume/flows/manager/create_volume.py
cinder创建volume流程简介_第24张图片
假设create_type是raw,则调用_create_raw_volume()

def _create_raw_volume(self, context, volume_ref, **kwargs):
#dirver=cinder.volume.drivers.lvm.LVMISCSIDriver
        return self.driver.create_volume(volume_ref)

/cinder/volume/driver/lvm.py
cinder创建volume流程简介_第25张图片
LVMISCSIDriver类继承了LVMVolumeDriver、driver.ISCSIDriver
/cinder/volume/driver/lvm.py
cinder创建volume流程简介_第26张图片
/cinder/volume/driver/lvm.py
cinder创建volume流程简介_第27张图片
/cinder/brick/local_dev/lvm.py
cinder创建volume流程简介_第28张图片

你可能感兴趣的:(openstack,cinder)