• cinder 卷迁移进度的代码分析


    一、cinder-api服务的入口函数

    D:code-programcinder-ocata_cindercinderapicontribadmin_actions.py
    from cinder import volume
    self.volume_api = volume.API()
    class VolumeAdminController(AdminController):
        """AdminController for Volumes."""
        @wsgi.Controller.api_version('3.28')
        @wsgi.action('os-migrate_volume_progress')
        def _get_migrate_volume_progress(self, req, id, body):
            """Get the migration progress of a volume."""
            context = req.environ['cinder.context']
            self.authorize(context, 'get_migrate_volume_progress')daim
            volume = self._get(context, id)-----根据卷的uuid获取卷的信息
            return self.volume_api.get_migrate_volume_progress(context, volume)---调用volume.api里面的接口
    
    D:code-programcinder-ocata_cindercindervolumeapi.py
    from cinder.volume import rpcapi as volume_rpcapi
    self.volume_rpcapi = volume_rpcapi.VolumeAPI()
    class API(base.Base):
        """API for interacting with the volume manager."""
        AVAILABLE_MIGRATION_STATUS = (None, 'deleting', 'error', 'success')
    	#返回一个特定卷的迁移进度,入参为将被迁移或者正在被迁移的源卷信息,
    	#返回值包括卷uuid,迁移的状态、及迁移的进度
    	#如果卷的迁移状态migration_status为migrating,则获取卷的实际迁移进度
    	#如果卷的迁移状态migration_status为starting,则返回卷的迁移进度为0%,在源卷所在的存储节点cinder-volume服务没有接受到发送的
    	#卷迁移rpc.cast请求以前,此时调用获取卷的迁移进度,会一直是0%的状态
    	#如果卷的迁移进度migration_status为success',completing',则返回卷的迁移进度为100%,在卷成功迁移完成之后,获取卷的迁移进度,
    	#那么它是100%
    	#对于获取到的卷的迁移进度为NA,这个值,那么一种可能情况是,卷的迁移消息还没被cinder.volume接口migrate_volume及时的处理,就调用
    	#获取卷的迁移进度api接口
       @wrap_check_policy
        def get_migrate_volume_progress(self, context, volume):
            """Return the migration progress for a specific volume.
            
            :param context: the context of the caller
            :param volume: the source volume to be migrated or being migrated
            :return the volume migration progress including volume id, migration
                    status and migration progress
            """
            volume_progress = {'volume_id': volume['id'],
                               'migration_status': volume['migration_status'],
                               'migration_progress': 'NA'}---初始值为NA
            if volume['migration_status'] == 'migrating':
                progress = self.volume_rpcapi.get_migrate_volume_progress(------对步骤1的详解
                    context, volume)
                volume_progress['migration_progress'] = progress + "%"
            elif volume['migration_status'] == 'starting':
                volume_progress['migration_progress'] = '0%'
            elif volume['migration_status'] in ('success', 'completing'):
                volume_progress['migration_progress'] = '100%'
            LOG.info(_LI('Volume %(vol_id)s has been migrated %(mig_progress)s.'),
                     {'vol_id': volume['id'],
                      'mig_progress': volume_progress['migration_progress']})
            return {'volume': volume_progress}
    
    对步骤1的详解
    给源卷所在的cinder-volume服务的存储节点,发送一个rpc.call请求,来获取迁移的进度
    D:code-programcinder-ocata_cindercindervolume
    pcapi.py
    class VolumeAPI(rpc.RPCAPI):
        """Client side of the volume rpc API.
        @rpc.assert_min_rpc_version('3.11')
        def get_migrate_volume_progress(self, ctxt, volume):
            version = self._compat_ver('3.11')
            cctxt = self._get_cctxt(volume['host'], version=version)
            return cctxt.call(ctxt, 'get_migrate_volume_progress',
                              volume=volume)
    

    二、源卷所在的cinder-volume服务存储节点,接受rpc.call请求,并处理,虽然配置了cinder-volume对应的驱动,volume_driver = cinder.volume.drivers.lvm.LVMVolumeDriver

    但是最终调用的是BaseVD里面的方法

    D:code-programcinder-ocata_cindercindervolumedriver.py
    @six.add_metaclass(abc.ABCMeta)
    class BaseVD(object):
        def get_migrate_volume_progress(self, context, volume, src_path=None,
                                        dest_path=None,
                                        migration_mode='HOST_BASED'):
            if migration_mode is None and volume['id'] in self._migration_info:
                migration_mode = self._migration_info[volume['id']]
            # NOTE(fengzy): If the volume is migrated using dd command,
            # e.g. volume migration from LVM to LVM, mode is HOST_BASED.
            # For the volume is migrated using file I/O, mode is FILE_BASED,
            # DRIVER_BASED need driver support. And now our mode only
            # HOST_BASED and FILE_BASED, no DRIVER_BASED.
    		#由于是lvm到lvm的迁移,因此mode是基于主机的 HOST_BASED
            if migration_mode == self.HOST_BASED:-----走该分支
                return self.get_host_based_volume_progress(volume, src_path,-----对步骤二进行详解
                                                           dest_path)
            elif migration_mode == self.DRIVER_BASED:
                progress = self.get_migration_progress(
                    context, volume)
                if progress:
                    return six.text_type(progress)
                else:
                    return self.UNKNOWN_PROGRESS
            elif migration_mode == self.FILE_BASED:
                key_handle = volume['id'] + 'dest_handle'
                if key_handle in self._migration_info:
                    handle = self._migration_info[key_handle]
                    if handle:
                        try:
                            size_in_bytes = int(volume['size']) * units.Gi
                            if not handle.closed:
                                pro = six.text_type(
                                    handle.tell() * 100 / size_in_bytes)
                                self._migration_info[volume['id'] +
                                                     'previous_progress'] = pro
                                return pro
                            else:
                                return '100'
                        except ValueError:
                            return self._migration_info[volume['id'] +
                                                        'previous_progress']
                    else:
                        return self._migration_info[volume['id'] +
                                                    'previous_progress']
            return self.UNKNOWN_PROGRESS
    
    对步骤二进行详解
        def get_host_based_volume_progress(self, volume, src_path=None,
                                           dest_path=None):
            key_source = volume['id'] + 'source'
            key_dest = volume['id'] + 'dest'
            if dest_path is None:
                dest_path = self._migration_info.get(key_dest, None)
            if not dest_path:
                return '0'
            if src_path is None:
                src_path = self._migration_info.get(key_source, None)
            if not src_path:
                return '0'
    
            pid = self.get_pid_for_host_based_migration(volume['id'], src_path,--获取dd拷贝命令的pid信息
                                                        dest_path)
            if pid:
    		#迁移进度的算法,按照文件大小的相除进行计算的
                self._migration_info[volume['id'] + 'pid'] = pid
                size_in_bytes = int(volume['size']) * units.Gi
                position_path = ('/proc/%s/fdinfo/1') % pid
                try:
                    output = utils.read_file_as_root(position_path)
                    position = self._get_position(output)
                    progress = six.text_type(position * 100 / size_in_bytes)
                    self._migration_info[volume['id'] +
                                         'previous_progress'] = progress
                    return progress
                except exception.FileNotFound:
                    return self._migration_info.get(
                        volume['id'] + 'previous_progress', '0')
            else:
                return self._migration_info.get(
                    volume['id'] + 'previous_progress', '0')
    
        def get_pid_for_host_based_migration(self, volume_id, src_path,
                                             dest_path):
            # Get the PID for the block copy command for the source volume.
            pid = self._migration_info.get(volume_id + 'pid', None)
            if pid is None:
                pid = volume_utils.get_process_pid('dd', src_path,
                                                   dest_path)
            return pid
    D:code-programcinder-ocata_cindercindervolumeutils.py
    def get_process_pid(command_name, src_path, dest_path):
        for proc in psutil.process_iter():
            try:
                p_info = proc.as_dict(attrs=['pid', 'name', 'cmdline'])
            except psutil.NoSuchProcess:
                pass
            else:
                is_in_path = "if=" + src_path in p_info['cmdline'] and
                             "of=" + dest_path in p_info['cmdline']
                if command_name == p_info['name'] and is_in_path:
                    return p_info['pid']
        return None
    

     三、迁移的例子

    [root@test network-scripts]# ps -ef |grep dd
    root         2     0  0  2015 ?        00:00:00 [kthreadd]
    root       516     1 86 08:42 ?        08:51:10 /usr/libexec/qemu-kvm -name instance-0000dcaf -S -M rhel6.5.0 -cpu SandyBridge,+erms,+smep,+fsgsbase,+pdpe1gb,+rdrand,+f16c,+osxsave,+dca,+pcid,+pdcm,+xtpr,+tm2,+est,+smx,+vmx,+ds_cpl,+monitor,+dtes64,+pbe,+tm,+ht,+ss,+acpi,+ds,+vme -enable-kvm -m 32768 -realtime mlock=off -smp 16,sockets=16,cores=1,threads=1 -uuid e664cce7-a4ac-410b-a775-38c19b576836 -smbios type=1,manufacturer=Red Hat Inc.,product=OpenStack Nova,version=2014.1.1-5.el6,serial=6d3753e6-3570-415a-89e3-ea2bd6e4559d,uuid=e664cce7-a4ac-410b-a775-38c19b576836 -nodefconfig -nodefaults -chardev socket,id=charmonitor,path=/var/lib/libvirt/qemu/instance-0000dcaf.monitor,server,nowait -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc,driftfix=slew -no-kvm-pit-reinjection -no-shutdown -device piix3-usb-uhci,id=usb,bus=pci.0,addr=0x1.0x2 -device virtio-serial-pci,id=virtio-serial0,bus=pci.0,addr=0x4 -drive file=/os_instance/e664cce7-a4ac-410b-a775-38c19b576836/disk,if=none,id=drive-virtio-disk0,format=qcow2,cache=writethrough -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x5,drive=drive-virtio-disk0,id=virtio-disk0,bootindex=1 -drive file=/dev/mapper/vg_os-volume--54549594--66ac--4edd--97a6--410071d7798e,if=none,id=drive-virtio-disk1,format=raw,serial=54549594-66ac-4edd-97a6-410071d7798e,cache=none -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x6,drive=drive-virtio-disk1,id=virtio-disk1 -drive file=/dev/mapper/vg_os-volume--c9945a36--ac4b--4a55--92e6--4a7c361b7649,if=none,id=drive-virtio-disk2,format=raw,serial=c9945a36-ac4b-4a55-92e6-4a7c361b7649,cache=none -device virtio-blk-pci,scsi=off,bus=pci.0,addr=0x7,drive=drive-virtio-disk2,id=virtio-disk2 -drive file=/os_instance/e664cce7-a4ac-410b-a775-38c19b576836/disk.config,if=none,media=cdrom,id=drive-ide0-1-1,readonly=on,format=raw,cache=writethrough -device ide-drive,bus=ide.1,unit=1,drive=drive-ide0-1-1,id=ide0-1-1 -netdev tap,fd=24,id=hostnet0,vhost=on,vhostfd=25 -device virtio-net-pci,netdev=hostnet0,id=net0,mac=fa:16:3e:6e:ba:a3,bus=pci.0,addr=0x3 -chardev file,id=charserial0,path=/os_instance/e664cce7-a4ac-410b-a775-38c19b576836/console.log -device isa-serial,chardev=charserial0,id=serial0 -chardev pty,id=charserial1 -device isa-serial,chardev=charserial1,id=serial1 -chardev pty,id=charchannel0 -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=com.redhat.spice.0 -spice port=5900,addr=0.0.0.0,seamless-migration=on -k en-us -vga qxl -global qxl-vga.ram_size=67108864 -global qxl-vga.vram_size=67108864 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x8
    root      3157  1876  0 01:38 ?        00:00:00 sudo cinder-rootwrap /etc/cinder/rootwrap.conf dd if=/dev/mapper/vg_os-volume--54549594--66ac--4edd--97a6--410071d7798e of=/dev/disk/by-path/ip-192.168.0.1:3260-iscsi-iqn.2010-10.org.openstack:volume-d39b3037-893d-4590-8fc3-ecfbc476d8b8-lun-1 count=1048576 bs=1M iflag=direct oflag=direct
    root      3158  3157  0 01:38 ?        00:00:00 /usr/bin/python /usr/bin/cinder-rootwrap /etc/cinder/rootwrap.conf dd if=/dev/mapper/vg_os-volume--54549594--66ac--4edd--97a6--410071d7798e of=/dev/disk/by-path/ip-192.168.0.1:3260-iscsi-iqn.2010-10.org.openstack:volume-d39b3037-893d-4590-8fc3-ecfbc476d8b8-lun-1 count=1048576 bs=1M iflag=direct oflag=direct
    root      3160  3158  0 01:38 ?        00:02:50 /bin/dd if=/dev/mapper/vg_os-volume--54549594--66ac--4edd--97a6--410071d7798e of=/dev/disk/by-path/ip-192.168.0.1:3260-iscsi-iqn.2010-10.org.openstack:volume-d39b3037-893d-4590-8fc3-ecfbc476d8b8-lun-1 count=1048576 bs=1M iflag=direct oflag=direct
    root     17245     1  0  2019 ?        12:49:45 /opt/promes/exporter/node_exporter_p18/node_exporter-0.18.1.linux-amd64/node_exporter --web.listen-address=:19100
    root     26012 12253  0 18:55 pts/3    00:00:00 grep dd
    root     27426     2  0  2016 ?        00:00:29 [ib_addr]
    [root@testnetwork-scripts]# 
    
    [root@test by-path]# ls
    ip-192.168.0.2:3260-iscsi-iqn.2010-10.org.openstack:volume-d39b3037-893d-4590-8fc3-ecfbc476d8b8-lun-1  pci-0000:01:00.0-scsi-0:2:0:0-part2
    pci-0000:01:00.0-scsi-0:2:0:0                                                                             pci-0000:01:00.0-scsi-0:2:1:0
    pci-0000:01:00.0-scsi-0:2:0:0-part1                                                                       pci-0000:01:00.0-scsi-0:2:1:0-part1
    

      

     

      

  • 相关阅读:
    javascript深入理解js闭包
    【STL源码学习】STL算法学习之一
    【STL源码学习】细品vector
    【C++深入浅出】智能指针之auto_ptr学习
    【字典树应用】联想用户最想输入的词汇
    【数据结构】非常有用的hash表
    【WIN32进阶之路】:内存映射文件
    【WIN32进阶之路】:线程同步技术纲要
    win32进阶必备:多线程同步之互斥锁
    【C++深入浅出】设计模式学习之单例模式
  • 原文地址:https://www.cnblogs.com/potato-chip/p/12660669.html
Copyright © 2020-2023  润新知