• 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
    

      

     

      

  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 黑色星期五
    Java实现 蓝桥杯VIP 算法训练 比赛安排
    Java实现 蓝桥杯VIP 算法训练 比赛安排
    Java实现 蓝桥杯VIP 算法训练 斜率计算
    Java实现 蓝桥杯VIP 算法训练 斜率计算
    Java实现 蓝桥杯VIP 算法训练 整数平均值
    Java实现 蓝桥杯VIP 算法训练 整数平均值
    控件动态产生器(使用RegisterClasses提前进行注册)
    Delphi编写自定义控件以及接口的使用(做了一个TpgDbEdit)
    Log4delphi使用心得
  • 原文地址:https://www.cnblogs.com/potato-chip/p/12660669.html
Copyright © 2020-2023  润新知