创建卷快照的源码分析

基于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]#

  

  

  

  

  

  

  

  

 

 

原文地址:https://www.cnblogs.com/potato-chip/p/11017463.html