cinder侧挂载卷流程分析,存储类型以lvm+iscsi的方式为分析基础
cinder侧主要调用了三个接口
1)reserve_volume: 把volume的状态改为attaching,阻止其它节点执行挂载操作。
2)initialize_connection: 这个方法负责构建和返回nova调用者需要的所有信息。返回的信息中包括CHAP credential, target-iqn 和lun 信息。
3)attach_volume: 把volume状态改为in-use,挂载成功,并创建对应的attach记录。

1、nova侧调用cinder的reserve_volume方法

nova/volume/cinder.py
@translate_volume_exception
def reserve_volume(self, context, volume_id):
cinderclient(context).volumes.reserve(volume_id)

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

cinder/api/contrib/volume_actions.py:VolumeActionsController
@wsgi.action('os-reserve')
def _reserve(self, req, id, body):
"""Mark volume as reserved."""
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.reserve_volume(context, volume)
return webob.Response(status_int=http_client.ACCEPTED)

该函数的主要作用是通过volume 的uuid,获取volume实例信息,并调用volume目录下的api模块

2)进一步调用cinder volume的api模块的reserve_volume函数,进行数据库的操作,更新卷的状态为attaching
该函数的主要作用是检查指定的卷是否为available,如果卷的状态是available,更新cinder数据库,把卷的状态标记为attaching来预留这个卷,防止其他api在别的地方使用这个卷
对于支持多路挂载的卷,有效状态包括in-use

def reserve_volume(self, context, volume):
expected = {'multiattach': volume.multiattach,
'status': (('available', 'in-use') if volume.multiattach
else 'available')} result = volume.conditional_update({'status': 'attaching'}, expected) if not result:
expected_status = utils.build_or_str(expected['status'])
msg = _('Volume status must be %(expected)s to reserve, but the '
'status is %(current)s.') % {'expected': expected_status,
'current': volume.status}
LOG.error(msg)
raise exception.InvalidVolume(reason=msg) LOG.info(_LI("Reserve volume completed successfully."),
resource=volume)

2、nova侧向cinder发送initialize_connection请求,请求获取卷的所有连接信息

nova/virt/block_device.py:DriverVolumeBlockDevice
def attach(self, context, instance, volume_api, virt_driver,
do_check_attach=True, do_driver_attach=False, **kwargs):
volume = volume_api.get(context, self.volume_id)
if do_check_attach:
volume_api.check_attach(context, volume, instance=instance) volume_id = volume['id']
context = context.elevated() connector = virt_driver.get_volume_connector(instance)
connection_info = volume_api.initialize_connection(context,
volume_id,
connector)
if 'serial' not in connection_info:
connection_info['serial'] = self.volume_id
self._preserve_multipath_id(connection_info)
........

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

@wsgi.action('os-initialize_connection')
def _initialize_connection(self, req, id, body):
"""Initialize 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-initialize_connection']['connector']
except KeyError:
raise webob.exc.HTTPBadRequest(
explanation=_("Must specify 'connector'"))
try:
info = self.volume_api.initialize_connection(context,volume,connector)
....

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

cinder/volume/api.py:API类
@wrap_check_policy
def initialize_connection(self, context, volume, connector):
if volume.status == 'maintenance':
LOG.info(_LI('Unable to initialize the connection for '
'volume, because it is in '
'maintenance.'), resource=volume)
msg = _("The volume connection cannot be initialized in "
"maintenance mode.")
raise exception.InvalidVolume(reason=msg)
init_results = self.volume_rpcapi.initialize_connection(context,
volume,
connector)
LOG.info(_LI("Initialize volume connection completed successfully."),
resource=volume)
return init_results

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

   def initialize_connection(self, context, volume, connector):
....
utils.require_driver_initialized(self.driver)
step : self.driver.validate_connector(connector)
step : model_update = self.driver.create_export(context.elevated(),volume, connector)
step : volume.update(model_update)
setp : conn_info = self.driver.initialize_connection(volume, connector)
return conn_info

step 1:
对于LVM + iSCSI方式,validate_connector就是检查有没有initiator字段,即nova-compute节点的initiator信息
代码跳转过程如下:drivers/lvm.py -> targets/lio.py -> targets/iscsi.py。

cinder/volume/targets/iscsi.py:ISCSITarget
def validate_connector(self, connector):
# NOTE(jdg): api passes in connector which is initiator info
if 'initiator' not in connector:
err_msg = (_LE('The volume driver requires the iSCSI initiator '
'name in the connector.'))
LOG.error(err_msg)
raise exception.InvalidConnectorException(missing='initiator')
return True

step 2 :调用cinder-rtstool工具创建target,并把卷volume添加到target中创建出lun,认证信息。

def create_export(self, context, volume, connector, vg=None):
if vg is None:
vg = self.configuration.volume_group
volume_path = "/dev/%s/%s" % (vg, volume['name'])
export_info = self.target_driver.create_export(
context,
volume,
volume_path)
return {'provider_location': export_info['location'],
'provider_auth': export_info['auth'], }

最终调用的是cinder/volume/targets/iscsi.py:ISCSITarget类

    def create_export(self, context, volume, volume_path):
"""Creates an export for a logical volume."""
# 'iscsi_name': 'iqn.2010-10.org.openstack:volume-00000001'
iscsi_name = "%s%s" % (self.configuration.iscsi_target_prefix,------设置iscsi name,形式为iqn.-.org.openstack:volume-uuid
volume['name'])
iscsi_target, lun = self._get_target_and_lun(context, volume)---返回target,和lun的编号,值为(,) # Verify we haven't setup a CHAP creds file already
# if DNE no big deal, we'll just create it
chap_auth = self._get_target_chap_auth(context, volume)------从数据库volumes表中,读取该卷的provider_auth字段,获取认证信息,若没有,则创建
if not chap_auth:
chap_auth = (vutils.generate_username(),-----创建auth认证信息
vutils.generate_password()) # Get portals ips and port
portals_config = self._get_portals_config()-------获取portals配置,该函数返回的字典格式如下 {'portals_ips': portals_ips,'portals_port': self.configuration.iscsi_port} # NOTE(jdg): For TgtAdm case iscsi_name is the ONLY param we need
# should clean this all up at some point in the future
tid = self.create_iscsi_target(iscsi_name,-----------------创建target
iscsi_target,
lun,
volume_path,
chap_auth,
**portals_config)
data = {}
data['location'] = self._iscsi_location(
self.configuration.iscsi_ip_address, tid, iscsi_name, lun,
self.configuration.iscsi_secondary_ip_addresses)
LOG.debug('Set provider_location to: %s', data['location'])
data['auth'] = self._iscsi_authentication(
'CHAP', *chap_auth)
return data

创建target操作,调用的是cinder/volume/targets/lio.py中的create_iscsi_target方法
这个函数下发的参数为
name:iqn.2010-10.org.openstack:volume-uuid
tid:0
lun:0
path:卷的路径
chap_auth:{username,passowrd}
kwargs:{'portals_ips': portals_ips,存储服务器的ip
'portals_port': self.configuration.iscsi_port,一般是3260
}

def create_iscsi_target(self, name, tid, lun, path,chap_auth=None, **kwargs):
# tid and lun are not used
vol_id = name.split(':')[]
LOG.info(_LI('Creating iscsi_target for volume: %s'), vol_id)
chap_auth_userid = ""
chap_auth_password = ""
if chap_auth is not None:
(chap_auth_userid, chap_auth_password) = chap_auth optional_args = []
if 'portals_port' in kwargs:
optional_args.append('-p%s' % kwargs['portals_port']) if 'portals_ips' in kwargs:
optional_args.append('-a' + ','.join(kwargs['portals_ips'])) try:
command_args = ['cinder-rtstool',
'create',
path,
name,
chap_auth_userid,
chap_auth_password,
self.iscsi_protocol == 'iser'] + optional_args
self._execute(*command_args, run_as_root=True)
except putils.ProcessExecutionError:
LOG.exception(_LE("Failed to create iscsi target for volume "
"id:%s."), vol_id) raise exception.ISCSITargetCreateFailed(volume_id=vol_id) iqn = '%s%s' % (self.iscsi_target_prefix, vol_id)
tid = self._get_target(iqn)
if tid is None:
LOG.error(_LE("Failed to create iscsi target for volume "
"id:%s."), vol_id)
raise exception.NotFound() # We make changes persistent
self._persist_configuration(vol_id)
return tid def _iscsi_location(self, ip, target, iqn, lun=None, ip_secondary=None):
ip_secondary = ip_secondary or []
port = self.configuration.iscsi_port
portals = map(lambda x: "%s:%s" % (x, port), [ip] + ip_secondary)
return ("%(portals)s,%(target)s %(iqn)s %(lun)s"
% ({'portals': ";".join(portals),
'target': target, 'iqn': iqn, 'lun': lun}))

step 3:创建完target以后,更新cinder数据库volumes表中,该volume的provider_location,provider_auth两个字段的值

step 4:调用cinder-rtstool的add-initiator子命令,把计算节点的initiator增加到刚刚创建的target acls中,并把所有的信息拼装返回给nova使用。

cinder/volume/targets/lio.py:
def initialize_connection(self, volume, connector):
volume_iqn = volume['provider_location'].split(' ')[]
(auth_method, auth_user, auth_pass) = \
volume['provider_auth'].split(' ', )
# Add initiator iqns to target ACL
try:
self._execute('cinder-rtstool', 'add-initiator',
volume_iqn,
auth_user,
auth_pass,
connector['initiator'],
run_as_root=True)
......
return super(LioAdm, self).initialize_connection(volume, connector)

3、nova给cinderclient发送attach_volume命令,更改cinder数据库中,volume状态

nova/virt/block_device.py:API
@translate_volume_exception
def attach(self, context, volume_id, instance_uuid, mountpoint, mode='rw'):
cinderclient(context).volumes.attach(volume_id, instance_uuid,
mountpoint, mode=mode)

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

cinder/api/contrib/volume_actions.py
@wsgi.action('os-attach')
def _attach(self, req, id, body):
.....
self.volume_api.attach(context, volume,instance_uuid, host_name, mountpoint, mode)
....

2)最后cinder-api通过RPC请求到cinder-volume节点,更新数据库,把volume状态改为in-use,并创建对应的attach记录。

cinder/volume/manager.py:VolumeManager
def attach_volume(self, context, volume_id, instance_uuid, host_name,
mountpoint, mode, volume=None):
"""Updates db to show volume is attached.""
......
attachment = volume.begin_attach(mode
......

cinder侧挂载卷流程分析的更多相关文章

  1. cinder侧卸载卷流程分析

    cinder侧卸载卷分析,存储类型以lvm+iscsi的方式为分析基础在虚机卸载卷的过程中,主要涉及如下三个函数1)cinder.volume.api.begin_detaching 把volume的 ...

  2. Cinder Volume 服务启动流程分析和周期性任务分析

    1.cinder-volume服务的程序入口 #!/usr/bin/python2 # PBR Generated from u'console_scripts' import sys from ci ...

  3. cinder创建volume的流程-简单梳理

    1. cinder-api接收到创建的请求,入口:cinder.api.v2.volumes.VolumeController#create,该方法主要负责一些参数的重新封装和校验,然后调用cinde ...

  4. 【转】linux文件系统之mount流程分析

    本质上,Ext3 mount的过程实际上是inode被替代的过程. 例如,/dev/sdb块设备被mount到/mnt/alan目录.命令:mount -t ext3 /dev/sdb /mnt/al ...

  5. Android SDCard Mount 流程分析

    前段时间对Android 的SDCard unmount 流程进行了几篇简短的分析,由于当时只是纸上谈兵,没有实际上的跟进,可能会有一些误导人或者小错误.今天重新梳理了头绪,针对mount的流程再重新 ...

  6. Android之 MTP框架和流程分析

    概要 本文的目的是介绍Android系统中MTP的一些相关知识.主要的内容包括:第1部分 MTP简介            对Mtp协议进行简单的介绍.第2部分 MTP框架            介绍 ...

  7. Linux文件系统之Mount流程分析

    转载:原文地址http://www.linuxeye.com/linuxrumen/1121.html 本质上,Ext3 mount的过程实际上是inode被替代的过程.例如,/dev/sdb块设备被 ...

  8. openstack之虚拟机创建流程分析

    这篇博文静静的呆在草稿箱大半年了.假设不是由于某些原因被问到,以及由于忽略它而导致的损失,否则我也不知道什么时候会将它完毕.感谢这段时间经历的挫折,让我知道不足.希望你能给我更大的决心! 本文试图具体 ...

  9. GlusterFs卷类型分析及创建、使用(结合kubernetes集群分析)

    引言 本文通过对卷类型的分析对比,来帮助读者选取生产环境最符合服务的挂载存储,命令可结合<glusterfs详解及kubernetes 搭建heketi-glusterfs>进行实验,下面 ...

随机推荐

  1. jQueryUI Sortable 应用Demo

    最近工作用需要设计一个自由布局的页面设计.我选了jQuery UI 的 sortable ,可以拖拽,自由排序 使用很方便,写一个demo,做个记录. 第一.单项目自由排序 下图效果 代码段: < ...

  2. Python多线程-信号量

    信号量就是一个线程中有多个线程 # -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import threading import ...

  3. Unable to correct problems, you have held broken package

    其实这篇接着上文(一),主要是解决samba安装的问题,中间又是一路曲折.不过这个问题也算是比较典型,有必要记录一下. #apt-get install smb* 安装失败.其实顺利的话,直接一条这样 ...

  4. javascript原型继承中的两种方法对比

    在实际的项目中,我们通常都是用构造函数来创建一个对象,再将一些常用的方法添加到其原型对象上.最后要么直接实例化该对象,要么将它作为父类,再申明一个对象,继承该父类. 而在继承的时候有两种常用方式,今天 ...

  5. 使用Selenium对付一个点击游戏

    继续来熟悉Selenium的使用,这次来玩一个Html5游戏.原网址在这:http://tianmaying.com/app/clicking/# 游戏是这样的,5秒内你能点击这个按钮几次.一般人都只 ...

  6. Internet Explorer 无法打开该 Internet 站点,请求的站点不可用或无法找到

    笔者最近遇见一个神奇的问题,同事在开发时用的谷歌浏览器,实现了一个下载功能,测试也没问题:但测试人员反馈说他那边没法下载,报异常.弹出框 同事跑过来和我商讨这个问题,笔者当时就懵了,于是赶紧查找相关资 ...

  7. 如何在Less中使用使用calc

    文章转载自  琼台博客:http://www.qttc.net/201409448.html Less的好处不用说大家都知道,确实让写CSS的人不在痛苦了,最近我在Less里加入calc时确发现了有点 ...

  8. Java多线程-线程的同步(同步代码块)

    对于同步,除了同步方法外,还可以使用同步代码块,有时候同步代码块会带来比同步方法更好的效果. 追其同步的根本的目的,是控制竞争资源的正确的访问,因此只要在访问竞争资源的时候保证同一时刻只能一个线程访问 ...

  9. 因采用 Flask 原生 WSGI 出现 "Broken pipe" 报错的故障处理

    :first-child { margin-top: 0; } blockquote > :last-child { margin-bottom: 0; } img { border: 0; m ...

  10. BeanUtils简单应用

    <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http:// ...