Openstack liberty 创建实例快照源码分析2
来源:互联网 发布:朝鲜族吃里扒外知乎 编辑:程序博客网 时间:2024/04/20 21:19
这是创建云主机实例快照源码分析系列的最后一篇,在第一篇文章中分析了从镜像启动云主机,创建在线/离线快照的过程;本篇将分析从启动盘启动的云主机创建快照的过程,下面请看正文:
磁盘启动云主机,离线(在线)快照
nova-api处理过程
函数入口和前述一样,还是 nova/api/openstack/compute/servers.py/ServersController._action_create_image
,下面一起来看看:
def _action_create_image(self, req, id, body): """省略了与'镜像启动云主机,做快照'的相关代码,具体分析可以看上一篇 博文的分析,另外下文的分析中省略了异常处理部分,输入参数如下: req = Request对象,包含本地请求的上下文信息 id = u'85972ed5-f670-4790-b158-2c72c0b7bde5',实例id body = {u'createImage': {u'name': u'snapshot1', u'metadata': {}}} """ #从req中获取请求上下文并执行权限认证 context = req.environ['nova.context'] authorize(context, action='create_image') 从body中解析出快照名及属性 entity = body["createImage"] image_name = common.normalize_name(entity["name"]) metadata = entity.get('metadata', {}) #检查属性配额 common.check_img_metadata_properties_quota(context, metadata) #从数据库中获取实例对象(InstanceV2)及块设备映射列表 #(BlockDeviceMappingList) instance = self._get_server(context, req, id) bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) #通过系统盘是否是volume来判断,系统是否从磁盘启动 if self.compute_api.is_volume_backed_instance(context, instance, bdms): authorize(context, action="create_image:allow_volume_backed") #instance是InstanceV2对象,通过id获得 #image_name = snapshot1,快照名 #调用nova/compute/api.py/API.snapshot_volume_backed方法 #执行快照,下文具体分析 image = self.compute_api.snapshot_volume_backed( context, instance, image_name, extra_properties= metadata) ...... -------------------------------------------------------------接上文: nova/compute/api.py/API.snapshot_volume_backeddef snapshot_volume_backed(self, context, instance, name, extra_properties=None): """从实例的system_metadata生成镜像属性(排除不可继承属性),如下: {u'min_disk': u'20', 'is_public': False, u'min_ram': u'0', 'properties': {u'base_image_ref': u''}, 'name': u'snapshot1'} """ image_meta = self._initialize_instance_snapshot_metadata( instance, name, extra_properties) """ the new image is simply a bucket of properties (particularly the block device mapping, kernel and ramdisk IDs) with no image data, hence the zero size """ image_meta['size'] = 0 #下面的代码清除了container_format,disk_format属性 for attr in ('container_format', 'disk_format'): image_meta.pop(attr, None) properties = image_meta['properties'] #下面的代码清除了block_device_mapping,bdm_v2, #root_device_name属性 for key in ('block_device_mapping', 'bdm_v2', 'root_device_name'): properties.pop(key, None) """添加root_device_name属性到properties,所以最终的快照属性字典如 下: {'name': u'snapshot1', u'min_ram': u'0', u'min_disk': u'20', 'is_public': False, 'properties': {u'base_image_ref': u'', 'root_device_name': u'/dev/vda'}, 'size': 0} """ if instance.root_device_name: properties['root_device_name'] = instance.root_device_name #省略异常处理代码 #如果云主机处于运行状态(在线快照),则快照前需要静默文件系统 #这需要agent的支持,如果没有安装或者静默失败,则抛异常 quiesced = False if instance.vm_state == vm_states.ACTIVE: #通过rpc.call发送quiesce_instance请求给`nova-compute` #进而借助libvirt发送请求给虚拟机内的agent实现文件系统的静默 self.compute_rpcapi.quiesce_instance(context, instance) quiesced = True #从数据库中获取该云主机关联的所有块设备,返回一个 #BlockDeviceMappingList对象 bdms = objects.BlockDeviceMappingList.get_by_instance_uuid( context, instance.uuid) #准备执行快照(如果有多个volume设备,则需要执行多次) mapping = [] for bdm in bdms: if bdm.no_device: continue #只对volume类型的设备执行快照 if bdm.is_volume: # create snapshot based on volume_id #根据volume_id,从数据库获取卷信息字典 volume = self.volume_api.get(context, bdm.volume_id) """ NOTE(yamahata): Should we wait for snapshot creation? Linux LVM snapshot creation completes in short time, it doesn't matter for now. """ #根据我们的输入,name = 'snapshot for snapshot1' name = _('snapshot for %s') % image_meta['name'] """通过cinderclient发起创建快照的请求,由cinder-volume完 成卷快照,系统卷快照信息字典如下: { 'status': u'creating', 'display_name': u'snapshot for snapshot1', 'created_at': u'2016-06-24T09:23:00.517279', 'display_description': u'', 'volume_size': 20, 'volume_id': u'60e16af2-0684-433c-a1b6- c1af1c2523fc', 'progress': None, 'project_id': u'25520b29dce346d38bc4b055c5ffbfcb', 'id': u'cede2421-ea68-4a8e-937d-c27074b9024b', 'size': 20 } 具体执行过程,下文分析 """ snapshot = self.volume_api.create_snapshot_force( context, volume['id'], name, volume['display_description']) """根据bdm构建快照属性字典,并生成BlockDeviceDict对象, 系统卷快照属性字典如下: { 'guest_format': None, 'boot_index': 0, 'no_device': None, 'connection_info': None, 'snapshot_id': u'cede2421-ea68-4a8e-937d- c27074b9024b', 'volume_size': 20, 'device_name': u'/dev/vda', 'disk_bus': u'virtio', 'image_id': None, 'source_type': 'snapshot', 'device_type': u'disk', 'volume_id': None, 'destination_type': 'volume', 'delete_on_termination': False } """ mapping_dict = block_device.snapshot_from_bdm(snapshot['id'], bdm) #排除那些只在数据库中显示的字段(如:created_at等)的字典 mapping_dict = mapping_dict.get_image_mapping() #对于非volume设备,直接从dbm中获取映射字典 else: mapping_dict = bdm.get_image_mapping() #将所有的设备映射字典添加到mapping列表,作为快照属性的 #一部分上传到glance数据库 mapping.append(mapping_dict) #如果之前静默了文件系统,这里就要解冻; #由rpc.cast发送异步请求给nova-compute处理,nova-compute处理该请 #求时会等到快照完成后才解冻文件系统,解冻请求也需要agent的支持 if quiesced: self.compute_rpcapi.unquiesce_instance(context, instance, mapping) #更新快照属性字典 if mapping: properties['block_device_mapping'] = mapping properties['bdm_v2'] = True """添加glance快照(镜像)数据库条目(会在Dashboard的镜像面板显示一 条名为snapshot1的快照记录),我的例子中信息如下: 大部分信息都拷贝至系统盘属性,这是因为卷快照是可以直接用来启动云主机的 另外'block_device_mapping'属性中包含所有的volume设备快照信息 (如果有的话),每个volume设备快照信息作为一条记录,记录在 image_properties数据表; { 'name': u'snapshot1', u'min_ram': u'0', u'min_disk': u'20', 'is_public': False, 'properties': { 'bdm_v2': True, 'block_device_mapping': [{'guest_format': None, 'boot_index': 0, 'no_device': None, 'image_id': None, 'volume_id': None, 'device_name': u'/dev/vda', 'disk_bus':u'virtio', 'volume_size': 20, 'source_type': 'snapshot', 'device_type': u'disk', 'snapshot_id': u'cede2421-ea68-4a8e-937d- c27074b9024b', 'destination_type': 'volume', 'delete_on_termination': False}], u'base_image_ref': u'', 'root_device_name': u'/dev/vda' }, 'size': 0 } """ return self.image_api.create(context, image_meta)
小结:nova-api
主要完成了如下的功能:
- 如果是在线快照,则冻结/解冻结文件系统
- 创建glance数据库镜像记录(包含所有卷的快照信息)
cinder创建磁盘快照
cinder-api处理过程
上文中cinderclient通过http发送快照请求后,cinder-api
会接受到该请求,处理函数如下:
#cinder/api/v2/snapshots.py/SnapshotsController.createdef create(self, req, body): """根据上文的分析,我们得到如下的输入参数 req = Request对象,包含本次请求的上下文 body = {u'snapshot': {u'volume_id': u'60e16af2-0684-433c- a1b6-c1af1c2523fc', u'force': True, u'description': u'', u'name': u'snapshot for snapshot1', u'metadata': {}}}, 这个 是快照属性信息 """ kwargs = {} #获得请求上下文 context = req.environ['cinder.context'] #输入参数是否合法 self.assert_valid_body(body, 'snapshot') #从body中提取参数 snapshot = body['snapshot'] kwargs['metadata'] = snapshot.get('metadata', None) #省略异常处理,如果不包含volume_id则抛异常 volume_id = snapshot['volume_id'] #从数据库中提取卷信息;省略异常处理,如果找不到卷则抛异常 volume = self.volume_api.get(context, volume_id) #是否是强制快照,我们这里force = True,强制与非强制快照的区别体现在 #非可以用(available)状态卷快照的处理上,请看后文的分析 force = snapshot.get('force', False) msg = _LI("Create snapshot from volume %s") LOG.info(msg, volume_id, context=context) #验证快照名及快照描述是否合法,长度不能超过256 self.validate_name_and_description(snapshot) #用快照名做快照描述 if 'name' in snapshot: snapshot['display_name'] = snapshot.pop('name') #参数类型转换,如果是非True/False的值,则抛异常 force = strutils.bool_from_string(force, strict=True) #force = True,走这个分支,否则走else分支;下述两个方法都是对 _create_snapshot的封装,请看下文的分析 if force: new_snapshot = self.volume_api.create_snapshot_force( context, volume, snapshot.get('display_name'), snapshot.get('description'), **kwargs) else: new_snapshot = self.volume_api.create_snapshot( context, volume, snapshot.get('display_name'), snapshot.get('description'), **kwargs) #将快照信息条件到请求体中,应答的时候要用 req.cache_db_snapshot(new_snapshot) #返回快照描述信息,应答的时候需要 return self._view_builder.detail(req, new_snapshot)-------------------------------------------------------------#接上文def _create_snapshot(self, context, volume, name, description, force=False, metadata=None, cgsnapshot_id=None): """根据上文的分析:force = True""" """该方法完成如下功能: 1. 执行卷状态条件判断,如果卷处于维护状态,迁移过程中,副本卷, force=False且不是可用状态,则抛异常 2. 执行用户快照配额管理,用户可以为不同的卷类型设置配额信息,如: volumes, gigabytes,snapshots,我这里使用的是ceph rbd,例子如下: {'gigabytes': 20, u'snapshots_ceph': 1, u'gigabytes_ceph': 20, 'snapshots': 1}, 用户默认配额如下: {'gigabytes': 1000, u'snapshots_ceph': -1, 'snapshots': 10, u'gigabytes_ceph': -1} 如果配额不足则抛异常 3. 创建快照条目,我的例子中是(创建卷快照要先创建cinder数据库条 目): {'status': u'creating', 'volume_type_id': 'd494e240-17b3- 4d35-a5a1-2923d8677d79', 'display_name': u'snapshot for snapshot1', 'user_id': 'b652f9bd65844f739684a20ed77e9a0f', 'display_description': u'', 'cgsnapshot_id': None, 'volume_size': 20, 'encryption_key_id': None, 'volume_id': '60e16af2-0684-433c-a1b6-c1af1c2523fc', 'progress': u'0%', 'project_id': '25520b29dce346d38bc4b055c5ffbfcb', 'metadata': {} } 卷快照完成后,会在Dashboard的云硬盘快照面板显示一条名为' snapshot for snapshot1'的卷快照记录 """ snapshot = self.create_snapshot_in_db( context, volume, name, description, force, metadata, cgsnapshot_id) #调用rpc.cast将create_snapshot消息投递到消息队列,由cinder- #volume完成快照 self.volume_rpcapi.create_snapshot(context, volume, snapshot) return snapshot
小结:卷快照过程中,cinder-api的操作总结为如下两个方面:
- 卷状态条件检查及配额检查
- 创建glance数据库快照记录(记录的是单个卷快照的信息)
cinder-volume处理过程
从消息队列拿到来自cinder-api
的请求后,cinder-volume
调用 VolumeManager.create_snapshot
方法处理该请求,如下:
#cinder/volume/manager.py/VolumeManager.create_snapshotdef create_snapshot(self, context, volume_id, snapshot): """输入参数说明如下: context 请求上下文 volume_id 执行快照的卷id snapshot 包含该次快照的详细信息 """ #获取请求上下文的一个拷贝(设置了admin) context = context.elevated() #发送通知,给ceilometer用的 self._notify_about_snapshot_usage( context, snapshot, "create.start") """省略异常处理代码,有任何异常则退出并设置快照状态为error """ #确保存储驱动已经初始化,否则抛异常 utils.require_driver_initialized(self.driver) # Pass context so that drivers that want to use it, can, # but it is not a requirement for all drivers. snapshot.context = context #调用后端存储驱动执行快照,我的例子中是RBDDriver,下文具体分析 model_update = self.driver.create_snapshot(snapshot) #更新数据库条目信息, 我这里返回的是None,所以不执行该次更新 if model_update: snapshot.update(model_update) snapshot.save() #从cinder数据库获取卷信息 vol_ref = self.db.volume_get(context, volume_id) #是否是启动卷,我们是通过卷启动的,系统盘自然就是启动卷 #如果是非系统盘启动卷,则跳过该过程 if vol_ref.bootable: #这里省略异常处理,如果异常则退出并设置快照状态为error #用卷的metadata信息更新快照的metadata信息,毕竟启动卷是用来 #启动系统的,需要保证快照与原卷信息一致 self.db.volume_glance_metadata_copy_to_snapshot( context, snapshot.id, volume_id) #快照完成了,标记快照为可用 snapshot.status = 'available' snapshot.progress = '100%' snapshot.save() #发送通知,给ceilometer用的 self._notify_about_snapshot_usage(context, snapshot, "create.end") #日志 LOG.info(_LI("Create snapshot completed successfully"), resource=snapshot) #返回快照id return snapshot.id--------------------------------------------------------------#接上文:#cinder/volume/drivers/rbd.py/RBDDriver.create_snapshotdef create_snapshot(self, snapshot): """Creates an rbd snapshot.""" """创建一个Image对象,然后直接调用librbd相关的方法执行秒级快照""" with RBDVolumeProxy(self, snapshot['volume_name']) as volume: snap = utils.convert_str(snapshot['name']) volume.create_snap(snap) volume.protect_snap(snap)
小结:cinder-volume
快照功能很简单:调用后端存储执行快照,然后更新glance数据库快照记录
阅读完上面的分析,相信读者会发现上面的快照过程中cinder
执行的就是卷的快照,nova
实现的是云主机信息及其镜像记录的处理。事实确实也如此:快照执行完成后,会在Dashboard的镜像面板显示一条镜像记录,在卷快照面板显示一条或者多条(如果有多个卷的话)卷快照记录。
1 0
- Openstack liberty 创建实例快照源码分析2
- Openstack liberty 创建实例快照源码分析1
- Openstack liberty 中Cinder-api启动过程源码分析2
- OpenStack 创建快照(Create Snapshot)过程源码分析
- Openstack liberty源码分析 之 云主机的启动过程2
- Openstack liberty 云主机迁移源码分析之静态迁移2
- Openstack liberty 云主机迁移源码分析之在线迁移2
- Openstack liberty Glance上传镜像源码分析
- Openstack liberty 中Cinder-api启动过程源码分析1
- Openstack liberty及mitaka中云主机快照实现对比分析
- Openstack快照现状分析
- openstack快照现状分析
- openstack 快照分析
- openstack快照分析
- openstack 快照分析
- openstack nova 创建虚拟机流程 liberty版本
- Openstack liberty源码分析 之 云主机的启动过程1
- Openstack liberty源码分析 之 云主机的启动过程3
- 阿拉伯语、波斯语等RTL语言下部分控件位置不对
- Android 用Animation-list实现逐帧动画
- 一维数组的初始化问题
- 1CISC和RISC的区别联系是什么?2哈佛结构和冯·诺依曼结构的区别
- 先说说我自己
- Openstack liberty 创建实例快照源码分析2
- 类似问题 duplicate entry: com/example/apputils/BuildConfig.class
- Elasticsearch java API (10)Multi Get API
- php新手来搭建Web服务器(Apache+PHP+MysQL)
- Java - 基本类的使用(Map集合类)
- 剑指offer-面试1:赋值运算符函数(C++ operator重构)
- yum 指定版本安装
- 正态分布
- 常用工具类的积累