虚拟机冷迁移和resize代码分析(三)

  我们接着上周的流程继续讲解,接着看_prep_resize()方法,其代码和注释如下:

def _prep_resize(self, context, image, instance, instance_type,
        quotas, request_spec, filter_properties, node,
        clean_shutdown=True):

    if not filter_properties:
        filter_properties = {}

    if not instance.host:
        self._set_instance_obj_error_state(context, instance)
        msg = _('Instance has no source host')
        raise exception.MigrationError(reason=msg)

    same_host = instance.host == self.host
    # if the flavor IDs match, it's migrate; otherwise resize
    if same_host and instance_type.id == instance['instance_type_id']:
        # check driver whether support migrate to same host
        if not self.driver.capabilities['supports_migrate_to_same_host']:
            raise exception.UnableToMigrateToSelf(
                instance_id=instance.uuid, host=self.host)

    # NOTE(danms): Stash the new instance_type to avoid having to
    # look it up in the database later
    instance.set_flavor(instance_type, 'new')
    # NOTE(mriedem): Stash the old vm_state so we can set the
    # resized/reverted instance back to the same state later.
    vm_state = instance.vm_state
    LOG.debug('Stashing vm_state: %s', vm_state, instance=instance)
    instance.system_metadata['old_vm_state'] = vm_state
    instance.save()

    limits = filter_properties.get('limits', {})
    rt = self._get_resource_tracker(node)
    with rt.resize_claim(context, instance, instance_type,
                         image_meta=image, limits=limits) as claim:
        LOG.info(_LI('Migrating'), context=context, instance=instance)
        self.compute_rpcapi.resize_instance(
                context, instance, claim.migration, image,
                instance_type, quotas.reservations,
                clean_shutdown)

  接着调用/nova/compute/rpcapi.py中的resize_instance()方法,其代码如下:

 def resize_instance(self, ctxt, instance, migration, image, instance_type,
                    reservations=None, clean_shutdown=True):
    msg_args = {'instance': instance, 'migration': migration,
                'image': image, 'reservations': reservations,
                'instance_type': instance_type,
                'clean_shutdown': clean_shutdown,
    }
    version = '4.1'
    if not self.client.can_send_version(version):
        msg_args['instance_type'] = objects_base.obj_to_primitive(
                                        instance_type)
        version = '4.0'
    cctxt = self.client.prepare(server=_compute_host(None, instance),
            version=version)
    cctxt.cast(ctxt, 'resize_instance', **msg_args)

  接着调用/nova/compute/manage.py中的resize_instance()方法,该节点为源计算节点,其代码如下:

@wrap_exception()
@reverts_task_state
@wrap_instance_event
@errors_out_migration
@wrap_instance_fault
def resize_instance(self, context, instance, image,
                    reservations, migration, instance_type,
                    clean_shutdown):
    """Starts the migration of a running instance to another host."""

    quotas = objects.Quotas.from_reservations(context,
                                              reservations,
                                              instance=instance)
    with self._error_out_instance_on_exception(context, instance,
                                               quotas=quotas):
        # TODO(chaochin) Remove this until v5 RPC API
        # Code downstream may expect extra_specs to be populated since it
        # is receiving an object, so lookup the flavor to ensure this.
        if (not instance_type or
            not isinstance(instance_type, objects.Flavor)):
            instance_type = objects.Flavor.get_by_id(
                context, migration['new_instance_type_id'])
		#获取虚拟机网络信息
        network_info = self.network_api.get_instance_nw_info(context,
                                                             instance)
		#设置迁移相关状态,并保存在数据库表migrate中
		#(包括源主机,目的主机,迁移状态,此处为migrating状态)
        migration.status = 'migrating'
        with migration.obj_as_admin():
            migration.save()
		#设置虚拟机状态为RESIZE_PREP
        instance.task_state = task_states.RESIZE_MIGRATING
        instance.save(expected_task_state=task_states.RESIZE_PREP)

        self._notify_about_instance_usage(
            context, instance, "resize.start", network_info=network_info)

        bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
                context, instance.uuid)
		#获取虚拟机磁盘块设备信息(包括卷,快照、镜像磁盘文件)
        block_device_info = self._get_instance_block_device_info(
                            context, instance, bdms=bdms)
		
        timeout, retry_interval = self._get_power_off_values(context,
                                        instance, clean_shutdown)
		#迁移磁盘且关机
        disk_info = self.driver.migrate_disk_and_power_off(
                context, instance, migration.dest_host,
                instance_type, network_info,
                block_device_info,
                timeout, retry_interval)

        self._terminate_volume_connections(context, instance, bdms)

        migration_p = obj_base.obj_to_primitive(migration)
		#开始网络迁移
        self.network_api.migrate_instance_start(context,
                                                instance,
                                                migration_p)
		#migration数据库表状态设置为post-migrating、
        migration.status = 'post-migrating'
        with migration.obj_as_admin():
            migration.save()

        instance.host = migration.dest_compute
        instance.node = migration.dest_node
		#instance数据库表instance.task_state为RESIZE_MIGRATED
        instance.task_state = task_states.RESIZE_MIGRATED
        instance.save(expected_task_state=task_states.RESIZE_MIGRATING)

        self.compute_rpcapi.finish_resize(context, instance,
                migration, image, disk_info,
                migration.dest_compute, reservations=quotas.reservations)

        self._notify_about_instance_usage(context, instance, "resize.end",
                                          network_info=network_info)
        self.instance_events.clear_events_for_instance(instance)

  这里直接看/nova/compute/manage.py中的finish_resize()方法,该节点为目的计算节点,其代码如下:

@wrap_exception()
@reverts_task_state
@wrap_instance_event
@errors_out_migration
@wrap_instance_fault
def finish_resize(self, context, disk_info, image, instance,
                  reservations, migration):
    """Completes the migration process.

    Sets up the newly transferred disk and turns on the instance at its
    new host machine.

    """
    quotas = objects.Quotas.from_reservations(context,
                                              reservations,
                                              instance=instance)
    try:
        self._finish_resize(context, instance, migration,
                            disk_info, image)
        quotas.commit()
    except Exception:
        LOG.exception(_LE('Setting instance vm_state to ERROR'),
                      instance=instance)
        with excutils.save_and_reraise_exception():
            try:
                quotas.rollback()
            except Exception:
                LOG.exception(_LE("Failed to rollback quota for failed "
                                  "finish_resize"),
                              instance=instance)
            self._set_instance_obj_error_state(context, instance)

  接着调用_finish_resize()方法,其代码如下:

def _finish_resize(self, context, instance, migration, disk_info,
                   image):
    resize_instance = False
    old_instance_type_id = migration['old_instance_type_id']
    new_instance_type_id = migration['new_instance_type_id']
    old_instance_type = instance.get_flavor()
    # NOTE(mriedem): Get the old_vm_state so we know if we should
    # power on the instance. If old_vm_state is not set we need to default
    # to ACTIVE for backwards compatibility
	#获得旧虚拟机的状态
    old_vm_state = instance.system_metadata.get('old_vm_state',
                                                vm_states.ACTIVE)
    instance.set_flavor(old_instance_type, 'old')
	#修改虚拟机类型(内存和磁盘等信息)
    if old_instance_type_id != new_instance_type_id:
        instance_type = instance.get_flavor('new')
        self._set_instance_info(instance, instance_type)
        for key in ('root_gb', 'swap', 'ephemeral_gb'):
            if old_instance_type[key] != instance_type[key]:
                resize_instance = True
                break
    instance.apply_migration_context()

    # NOTE(tr3buchet): setup networks on destination host
	#目标主机创建网络
    self.network_api.setup_networks_on_host(context, instance,
                                            migration['dest_compute'])

    migration_p = obj_base.obj_to_primitive(migration)
    self.network_api.migrate_instance_finish(context,
                                             instance,
                                             migration_p)
	#迁移网络
    network_info = self.network_api.get_instance_nw_info(context, instance)
	#数据库实例状态更新为RESIZE_FINISH
    instance.task_state = task_states.RESIZE_FINISH
    instance.save(expected_task_state=task_states.RESIZE_MIGRATED)

    self._notify_about_instance_usage(
        context, instance, "finish_resize.start",
        network_info=network_info)
	#获取磁盘文件信息(包括卷、快照,镜像)
    block_device_info = self._get_instance_block_device_info(
                        context, instance, refresh_conn_info=True)

    # NOTE(mriedem): If the original vm_state was STOPPED, we don't
    # automatically power on the instance after it's migrated
    power_on = old_vm_state != vm_states.STOPPED

    try:
        self.driver.finish_migration(context, migration, instance,
                                     disk_info,
                                     network_info,
                                     image, resize_instance,
                                     block_device_info, power_on)
    except Exception:
        with excutils.save_and_reraise_exception():
            if old_instance_type_id != new_instance_type_id:
                self._set_instance_info(instance,
                                        old_instance_type)
	#更新迁移完毕后虚拟机状态信息
    migration.status = 'finished'
    with migration.obj_as_admin():
        migration.save()

    instance.vm_state = vm_states.RESIZED
    instance.task_state = None
    instance.launched_at = timeutils.utcnow()
    instance.save(expected_task_state=task_states.RESIZE_FINISH)

    self._update_scheduler_instance_info(context, instance)
    self._notify_about_instance_usage(
        context, instance, "finish_resize.end",
        network_info=network_info)

  其中driver.finish_migration()方法,由于compute_driver=libvirt.LibvirtDriver,这个参数也是在nova.conf配置文件得到的。所以这里会去调用nova/virt/libvirt/driver.py.finish_migration()方法,其代码和注释如下:

def finish_migration(self, context, migration, instance, disk_info,
                     network_info, image_meta, resize_instance,
                     block_device_info=None, power_on=True):
    LOG.debug("Starting finish_migration", instance=instance)

    image_meta = objects.ImageMeta.from_dict(image_meta)

    # resize disks. only "disk" and "disk.local" are necessary.
	#迁移磁盘信息(仅仅对于root_gb与ephemeral_gb)
    disk_info = jsonutils.loads(disk_info)
    for info in disk_info:
		#获取该disk类型下的磁盘size
        size = self._disk_size_from_instance(instance, info)
        if resize_instance:
            image = imgmodel.LocalFileImage(info['path'],
                                            info['type'])
            self._disk_resize(image, size)
		#如果磁盘格式为raw,且使用了use_cow_images,则先转化为qcow2
        if info['type'] == 'raw' and CONF.use_cow_images:
            self._disk_raw_to_qcow2(info['path'])
	#获取磁盘信息(例如virio、disk.local、disk.swap)
    disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
                                        instance,
                                        image_meta,
                                        block_device_info)
    # assume _create_image do nothing if a target file exists.
    self._create_image(context, instance, disk_info['mapping'],
                       network_info=network_info,
                       block_device_info=None, inject_files=False,
                       fallback_from_host=migration.source_compute)
	#生成xml文件
    xml = self._get_guest_xml(context, instance, network_info, disk_info,
                              image_meta,
                              block_device_info=block_device_info,
                              write_to_disk=True)
    # NOTE(mriedem): vifs_already_plugged=True here, regardless of whether
    # or not we've migrated to another host, because we unplug VIFs locally
    # and the status change in the port might go undetected by the neutron
    # L2 agent (or neutron server) so neutron may not know that the VIF was
    # unplugged in the first place and never send an event.
	#根据配置和xml文件创建虚拟机
    self._create_domain_and_network(context, xml, instance, network_info,
                                    disk_info,
                                    block_device_info=block_device_info,
                                    power_on=power_on,
                                    vifs_already_plugged=True)
	#如果迁移前为开机状态,则开机(至此迁移全部完成)
    if power_on:
        timer = loopingcall.FixedIntervalLoopingCall(
                                                self._wait_for_running,
                                                instance)
        timer.start(interval=0.5).wait()

    LOG.debug("finish_migration finished successfully.", instance=instance)

转载于:https://my.oschina.net/u/1179767/blog/842098

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值