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
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
/etc/cinder/cinder.conf
/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/db/base.py
/cinder/volume/api.py
flow_engine.run()
volume = flow_engine.storage.fetch('volume')
return volume
/cinder/volume/api.py
/cinder/volume/flows/api.py
/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中
#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/flows/api.
注: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/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)
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
/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/scheduler/manager.py
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
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/scheduler/flows/create_volume.py
/cinder/scheduler/manager.py
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
根据配置选择加载需要的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
ExtractVolumeSpecTask NotifyVolumeActionTask
CreateVolumeFromSpecTask CreateVolumeOnFinishTask
创建volume的工作flow
这里注重介绍CreateVolumeFromSpecTask
/cinder/volume/flows/manager/create_volume.py
/cinder/volume/flows/manager/create_volume.py
假设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
LVMISCSIDriver类继承了LVMVolumeDriver、driver.ISCSIDriver
/cinder/volume/driver/lvm.py
/cinder/volume/driver/lvm.py
/cinder/brick/local_dev/lvm.py