• 创建卷快照的源码分析


    基于ocata版本,创建快照的代码流程梳理

    1、用户下发创建快照的命令

    cinder --debug snapshot-create ee514adb-4e05-4ede-876a-c9a9d95cc1da

    curl -g -i -X POST http://10.27.244.33:8776/v2/ec5405bdbcd243bbaf5b40093181ff70/snapshots 
    -H "User-Agent: python-cinderclient" 
    -H "Content-Type: application/json" 
    -H "Accept: application/json" 
    -H "X-Auth-Token: {SHA1}72f3a32d5a1929eca8d181f9c270545f9bf83dfa" 
    -d 
    {
    	"snapshot": {
    		"description": null,
    		"metadata": {},
    		"force": false,
    		"name": null,
    		"volume_id": "ee514adb-4e05-4ede-876a-c9a9d95cc1da"
    	}
    }
    

     响应体

    {
    	"snapshot": {
    		"status": "creating",
    		"size": 1,
    		"metadata": {},
    		"name": null,
    		"volume_id": "ee514adb-4e05-4ede-876a-c9a9d95cc1da",
    		"created_at": "2019-06-13T06:39:42.076207",
    		"description": null,
    		"id": "9d5dead2-4aa3-4178-a08b-c695598b683d",
    		"updated_at": null
    	}
    }
    

     创建快照时,给快照起一个名字并且加描述信息

    cinder --debug snapshot-create ee514adb-4e05-4ede-876a-c9a9d95cc1da --name=chenwei --description=test2

    cinder --debug snapshot-create ee514adb-4e05-4ede-876a-c9a9d95cc1da --name=chenwei --description=test2
    -H "User-Agent: python-cinderclient" 
    -H "Content-Type: application/json" 
    -H "Accept: application/json" 
    -H "X-Auth-Token: {SHA1}7a1799ec02c94f5245b657cac274acee68886c05"
    -d
    {
    	"snapshot": {
    		"description": "test2",
    		"metadata": {},
    		"force": false,
    		"name": "chenwei",
    		"volume_id": "ee514adb-4e05-4ede-876a-c9a9d95cc1da"
    	}
    }
    响应体
    {
    	"snapshot": {
    		"status": "creating",
    		"size": 1,
    		"metadata": {},
    		"name": "chenwei",
    		"volume_id": "ee514adb-4e05-4ede-876a-c9a9d95cc1da",
    		"created_at": "2019-06-13T06:46:52.599300",
    		"description": "test2",
    		"id": "253fface-6400-4520-8d2f-237d96e614e4",
    		"updated_at": null
    	}
    }
    

    查看cinder数据库中snapshot表的内容

    mysql> select * from snapshots where id="253fface-6400-4520-8d2f-237d96e614e4"G;
    *************************** 1. row ***************************
             created_at: 2019-06-13 06:46:53
             updated_at: 2019-06-13 06:46:53
             deleted_at: NULL
                deleted: 0
                     id: 253fface-6400-4520-8d2f-237d96e614e4
              volume_id: ee514adb-4e05-4ede-876a-c9a9d95cc1da
                user_id: 6a053d95688846a0823dfae9648a05f4
             project_id: ec5405bdbcd243bbaf5b40093181ff70
                 status: available
               progress: 100%
            volume_size: 1
           scheduled_at: NULL
           display_name: chenwei
    display_description: test2
      provider_location: NULL
      encryption_key_id: NULL
         volume_type_id: NULL
          cgsnapshot_id: NULL
            provider_id: NULL
          provider_auth: NULL
      group_snapshot_id: NULL
    1 row in set (0.00 sec)
    
    [root@xxx glance]# cinder snapshot-list
    +--------------------------------------+--------------------------------------+-----------+---------+------+
    | ID                                   | Volume ID                            | Status    | Name    | Size |
    +--------------------------------------+--------------------------------------+-----------+---------+------+
    | 253fface-6400-4520-8d2f-237d96e614e4 | ee514adb-4e05-4ede-876a-c9a9d95cc1da | available | chenwei | 1    |
    | 9d5dead2-4aa3-4178-a08b-c695598b683d | ee514adb-4e05-4ede-876a-c9a9d95cc1da | available | -       | 1    |
    +--------------------------------------+--------------------------------------+-----------+---------+------+
    

    1、用户发送创建快照的HTTP请求,对应的api接口如下

    POST /v2/{project_id}/snapshots
    Request样例:
    {
        "snapshot": {
            "name": "snap-001",
            "description": "Daily backup",
            "volume_id": "5aa119a8-d25b-45a7-8d1b-88e127885635",
            "force": true,
            "metadata": {
                "key": "v3"
            }
        }
    }
    

    2、cinder api接受该请求,并处理的入口函数cinder/api/v2/snapshots.py:class SnapshotsController(wsgi.Controller):create

       def create(self, req, body):
            """Creates a new snapshot."""
    		.......
    		snapshot = body['snapshot']
            try:
                volume_id = snapshot['volume_id']-----------------从请求体中获取卷的id
            except KeyError:
                msg = _("'volume_id' must be specified")
                raise exc.HTTPBadRequest(explanation=msg)
    
            volume = self.volume_api.get(context, volume_id)--------- 调用volume接口,获取卷的信息
            force = snapshot.get('force', False)----------------默认为false
    		.....
            # NOTE(thingee): v2 API allows name instead of display_name
            if 'name' in snapshot:
                snapshot['display_name'] = snapshot.pop('name')------给snapshot的display_name参数赋值
    
    		.....
            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(--------------调用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)
    

    3、cinder/volume/api.py:class API(base.Base):create_snapshot函数

    	self.volume_api = volume.API()
        def create_snapshot(self, context,
                            volume, 
    						name, -------------卷的名字
    						description,-------卷的描述信息
                            metadata=None,
    						cgsnapshot_id=None,
                            group_snapshot_id=None):
            result = self._create_snapshot(context, volume, name, description,
                                           False, metadata, cgsnapshot_id,
                                           group_snapshot_id)
            LOG.info(_LI("Snapshot create request issued successfully."),
                     resource=result)
            return result
    
        def _create_snapshot(self, context,
                             volume, name, description,
                             force=False, metadata=None,
                             cgsnapshot_id=None,
                             group_snapshot_id=None):
            volume.assert_not_frozen()
            snapshot = self.create_snapshot_in_db(---------------s1先预留配额,然后在数据库中添加快照entry,
                context, volume, name,
                description, force, metadata, cgsnapshot_id,
                True, group_snapshot_id)
            self.volume_rpcapi.create_snapshot(context, volume, snapshot)------发送rpc请求,创建snapshot
    

    s1步,对create_snapshot_in_db函数中详解  

    def create_snapshot_in_db()
    	.....
    	if commit_quota:
    	try:
    		if CONF.no_snapshot_gb_quota:----默认值为false
    			reserve_opts = {'snapshots': 1}
    		else:
    			reserve_opts = {'snapshots': 1,
    							'gigabytes': volume['size']}
    		QUOTAS.add_volume_type_opts(context,
    									reserve_opts,
    									volume.get('volume_type_id'))
    		reservations = QUOTAS.reserve(context, **reserve_opts)
    	
    	snapshot = objects.Snapshot(context=context, **kwargs)
    	snapshot.create()
    
    	if commit_quota:
    		QUOTAS.commit(context, reservations)
    

    4、发送rpc请求,给cinder-manager,进行创建快照

    cinder/volume/rpcapi.py:class VolumeAPI(rpc.RPCAPI):create_snapshot
        def create_snapshot(self, ctxt, volume, snapshot):
            snapshot.create_worker()
            cctxt = self._get_cctxt(volume.service_topic_queue)----传入volume参数,为了寻找消息队列
            cctxt.cast(ctxt, 'create_snapshot', snapshot=snapshot)
    cinder/volume/manager.py:class VolumeManager:create_snapshot(self, context, snapshot):
    
       def create_snapshot(self, context, snapshot):
            """Creates and exports the snapshot."""
            context = context.elevated()
    
            self._notify_about_snapshot_usage(
                context, snapshot, "create.start")
    
            try:
                # NOTE(flaper87): Verify the driver is enabled
                # before going forward. The exception will be caught
                # and the snapshot status updated.
                utils.require_driver_initialized(self.driver)-------s1初始化相关驱动,debug调试self.driver=<cinder.volume.drivers.lvm.LVMVolumeDriver object at 0x71e34d0>
    
                # Pass context so that drivers that want to use it, can,
                # but it is not a requirement for all drivers.
                snapshot.context = context
    
                model_update = self.driver.create_snapshot(snapshot)------s2调用对应驱动程序的create_snapshot
                if model_update:
                    snapshot.update(model_update)------------------s3更新数据库snapshots表
                    snapshot.save()
    		........
    
            vol_ref = self.db.volume_get(context, snapshot.volume_id)--------s3从数据库获取快照对应卷的信息
            if vol_ref.bootable:-------------s4如果卷是可启动的,同步glance metadata信息
                try:
                    self.db.volume_glance_metadata_copy_to_snapshot(
                        context, snapshot.id, snapshot.volume_id)
    		.........
    
            snapshot.status = fields.SnapshotStatus.AVAILABLE
            snapshot.progress = '100%'
            snapshot.save()
    
            self._notify_about_snapshot_usage(context, snapshot, "create.end")
            LOG.info(_LI("Create snapshot completed successfully"),
                     resource=snapshot)
            return snapshot.id
    		
    s1 	self.driver=<cinder.volume.drivers.lvm.LVMVolumeDriver object at 0x71e34d0>	
    s2  snapshot的值
    Snapshot(cgsnapshot=<?>,
    cgsnapshot_id=None,
    created_at=2019-06-13T07:40:09Z,
    deleted=False,
    deleted_at=None,
    display_description='test3',
    display_name='chenwei1',
    encryption_key_id=None,
    group_snapshot=<?>,
    group_snapshot_id=None,
    id=d580f2e9-03de-4ffa-833d-366785ad77fb,
    metadata={},
    progress='0%',
    project_id='ec5405bdbcd243bbaf5b40093181ff70',
    provider_auth=None,
    provider_id=None,
    provider_location=None,
    status='creating',
    updated_at=None,
    user_id='6a053d95688846a0823dfae9648a05f4',
    volume=<?>,
    volume_id=ee514adb-4e05-4ede-876a-c9a9d95cc1da,
    volume_size=1,
    volume_type_id=None)
    

    5、以lvm驱动为例

    cinder/volume/drivers/lvm.py
        def create_snapshot(self, snapshot):
            """Creates a snapshot."""
            self.vg.create_lv_snapshot(self._escape_snapshot(snapshot['name']),
                                       snapshot['volume_name'],
                                       self.configuration.lvm_type)
    
    调试信息
    self.vg=<cinder.brick.local_dev.lvm.LVM object at 0x62addd0>	
    snapshot['name']='snapshot-95ed2e2e-904a-4d42-a8e6-cade527e8812',没搞明白这两个值是怎么传过来的,5步打印的值,并没有这个name
    snapshot['volume_name']='volume-ee514adb-4e05-4ede-876a-c9a9d95cc1da'
    

     6、最终代码会调用到cinder/brick/local_dev/lvm.py中的create_lv_snapshot函数,执行lvm的相关命令行 

       @utils.retry(putils.ProcessExecutionError)
        def create_lv_snapshot(self, name, source_lv_name, lv_type='default'):
            """Creates a snapshot of a logical volume.
    
            :param name: Name to assign to new snapshot
            :param source_lv_name: Name of Logical Volume to snapshot
            :param lv_type: Type of LV (default or thin)
    
            """
            source_lvref = self.get_volume(source_lv_name)
            if source_lvref is None:
                LOG.error(_LE("Trying to create snapshot by non-existent LV: %s"),
                          source_lv_name)
                raise exception.VolumeDeviceNotFound(device=source_lv_name)
            cmd = LVM.LVM_CMD_PREFIX + ['lvcreate', '--name', name, '--snapshot',
                                        '%s/%s' % (self.vg_name, source_lv_name)]
            if lv_type != 'thin':
                size = source_lvref['size']
                cmd.extend(['-L', '%sg' % (size)])
    
            try:
                self._execute(*cmd,
                              root_helper=self._root_helper,
                              run_as_root=True)
            except putils.ProcessExecutionError as err:
                LOG.exception(_LE('Error creating snapshot'))
                LOG.error(_LE('Cmd     :%s'), err.cmd)
                LOG.error(_LE('StdOut  :%s'), err.stdout)
                LOG.error(_LE('StdErr  :%s'), err.stderr)
                raise	
    

    7、在计算节点上执行相关的lvs命令行,本质上对于Lvm驱动来说,快照(在某一个时间点上,卷数据的完全拷贝)就是一个lvm卷

    [root@xxx site-packages]# lvs
    LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync Convert
    _snapshot-1ee9b7f6-65a5-4d1c-9a27-126082b28933 vg_os swi-a-s--- 1.00g volume-ee514adb-4e05-4ede-876a-c9a9d95cc1da 0.00
    _snapshot-253fface-6400-4520-8d2f-237d96e614e4 vg_os swi-a-s--- 1.00g volume-ee514adb-4e05-4ede-876a-c9a9d95cc1da 0.00
    _snapshot-9d5dead2-4aa3-4178-a08b-c695598b683d vg_os swi-a-s--- 1.00g volume-ee514adb-4e05-4ede-876a-c9a9d95cc1da 0.00
    _snapshot-d580f2e9-03de-4ffa-833d-366785ad77fb vg_os swi-a-s--- 1.00g volume-ee514adb-4e05-4ede-876a-c9a9d95cc1da 0.00
    _snapshot-eec80f33-97a5-445f-8282-57720f475fc5 vg_os swi-a-s--- 1.00g volume-ee514adb-4e05-4ede-876a-c9a9d95cc1da 0.00
    lv_os_data vg_os -wi-ao---- 300.00g
    lv_os_instance vg_os -wi-ao---- 400.00g
    volume-ee514adb-4e05-4ede-876a-c9a9d95cc1da vg_os owi-aos--- 1.00g
    [root@xgto02n010027244153 site-packages]#

      

      

      

      

      

      

      

      

     

     

  • 相关阅读:
    [苹果maccms] MACCMS苹果cms宝塔定时任务添加教程说明
    [苹果cmsV10]新版本演员库分类报无权限问题和解决方法!
    CentOS 6.8安装Python2.7.13
    [HOWTO] Install Sphinx for A Script Pro
    A Script Pro nginx URL重写规则无法播放MP4解决方法
    随机跳转
    UI库
    vuex
    vue 数据请求
    vue守卫、储存与路由模式
  • 原文地址:https://www.cnblogs.com/potato-chip/p/11017463.html
Copyright © 2020-2023  润新知