nova I版本虚机热迁移流程源码分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LIUSHU427/article/details/83023808

      nova有两种虚机迁移方式,一种是冷迁移,另个一种是热迁移,这段时间项目上碰到一些热迁移的问题,正好错这个机会梳理一下nova虚机热迁移的源码,版本是基于openstack I版本的.

      首先从url入口开始nova/openstack/compute/contrib/admin_actions.py中

    @wsgi.action('os-migrateLive')
    def _migrate_live(self, req, id, body):
        """Permit admins to (live) migrate a server to a new host."""
        context = req.environ["nova.context"]
        authorize(context, 'migrateLive')

        try:
            block_migration = body["os-migrateLive"]["block_migration"]
            disk_over_commit = body["os-migrateLive"]["disk_over_commit"]
            host = body["os-migrateLive"]["host"]
        except (TypeError, KeyError):
            msg = _("host, block_migration and disk_over_commit must "
                    "be specified for live migration.")
            raise exc.HTTPBadRequest(explanation=msg)

        try:
            block_migration = strutils.bool_from_string(block_migration,
                                                        strict=True)
            disk_over_commit = strutils.bool_from_string(disk_over_commit,
                                                         strict=True)
        except ValueError as err:
            raise exc.HTTPBadRequest(explanation=str(err))

        try:
            instance = self.compute_api.get(context, id, want_objects=True)
            # 这里主要处理热迁移
            self.compute_api.live_migrate(context, instance, block_migration,
                                          disk_over_commit, host)
        except (exception.ComputeServiceUnavailable,
                exception.InvalidHypervisorType,
                exception.UnableToMigrateToSelf,
                exception.DestinationHypervisorTooOld,
                exception.NoValidHost,
                exception.InvalidLocalStorage,
                exception.InvalidSharedStorage,
                exception.MigrationPreCheckError) as ex:
            raise exc.HTTPBadRequest(explanation=ex.format_message())
        except exception.InstanceNotFound as e:
            raise exc.HTTPNotFound(explanation=e.format_message())
        except exception.InstanceInvalidState as state_error:
            common.raise_http_conflict_for_instance_invalid_state(state_error,
                    'os-migrateLive')
        except Exception:
            if host is None:
                msg = _("Live migration of instance %s to another host "
                        "failed") % id
            else:
                msg = _("Live migration of instance %(id)s to host %(host)s "
                        "failed") % {'id': id, 'host': host}
            LOG.exception(msg)
            # Return messages from scheduler
            raise exc.HTTPBadRequest(explanation=msg)

        return webob.Response(status_int=202)

 执行到self.compute_api.live_migrate(context, instance, block_migration, disk_over_commit, host)接下来转到nova/compute/api.py中

    @check_instance_cell
    @check_instance_state(vm_state=[vm_states.ACTIVE])
    def live_migrate(self, context, instance, block_migration,
                     disk_over_commit, host_name):
        """Migrate a server lively to a new host."""
        LOG.debug(_("Going to try to live migrate instance to %s"),
                  host_name or "another host", instance=instance)
        
        # 将instance的状态转为 migrating
        instance.task_state = task_states.MIGRATING
        instance.save(expected_task_state=[None])

        # 转到conductorapi进行处理
        self.compute_task_api.live_migrate_instance(context, instance,
                host_name, block_migration=block_migration,
                disk_over_commit=disk_over_commit)

compute/api 又调用了compute_task_api,看一下compute_task_api具体初始化过程

    @property
    def compute_task_api(self):
        if self._compute_task_api is None:
            # TODO(alaski): Remove calls into here from conductor manager so
            # that this isn't necessary. #1180540
            from nova import conductor
            # 返回conductor的ComputeTaskAPI实例
            self._compute_task_api = conductor.ComputeTaskAPI()
        return self._compute_task_api

 compute/api主要是将instance实例标记为migrating,接下来转到nova/conductor/api.py ComputeTaskAPI

    def live_migrate_instance(self, context, instance, host_name,
                              block_migration, disk_over_commit):
        scheduler_hint = {'host': host_name}
        self.conductor_compute_rpcapi.migrate_server(
            context, instance, scheduler_hint, True, False, None,
            block_migration, disk_over_commit, None)
将主机名存入字典scheduler_hint中,然后调用nova/conductor/rpcapi.py方法migrate_server
    def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
                  flavor, block_migration, disk_over_commit,
                  reservations=None):
        # 检测一下支持rpc版本号
        if self.client.can_send_version('1.6'):
            version = '1.6'
        else:
            instance = jsonutils.to_primitive(
                    objects_base.obj_to_primitive(instance))
            version = '1.4'
        flavor_p = jsonutils.to_primitive(flavor)
        cctxt = self.client.prepare(version=version)
        # 发送rpc消息到nova-conductor
        return cctxt.call(context, 'migrate_server',
                          instance=instance, scheduler_hint=scheduler_hint,
                          live=live, rebuild=rebuild, flavor=flavor_p,
                          block_migration=block_migration,
                          disk_over_commit=disk_over_commit,
                          reservations=reservations)

完成动作主要检测一下兼容的api版本号,发送消息给nova-conductor进行处理

看一下nova-conductor处理函数

    @messaging.expected_exceptions(exception.NoValidHost,
                                   exception.ComputeServiceUnavailable,
                                   exception.InvalidHypervisorType,
                                   exception.UnableToMigrateToSelf,
                                   exception.DestinationHypervisorTooOld,
                                   exception.InvalidLocalStorage,
                                   exception.InvalidSharedStorage,
                                   exception.MigrationPreCheckError)
    def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
            flavor, block_migration, disk_over_commit, reservations=None):
        if instance and not isinstance(instance, instance_obj.Instance):
            # NOTE(danms): Until v2 of the RPC API, we need to tolerate
            # old-world instance objects here
            attrs = ['metadata', 'system_metadata', 'info_cache',
                     'security_groups']
            instance = instance_obj.Instance._from_db_object(
                context, instance_obj.Instance(), instance,
                expected_attrs=attrs)
        if live and not rebuild and not flavor:
            self._live_migrate(context, instance, scheduler_hint,
                               block_migration, disk_over_commit)
        elif not live and not rebuild and flavor:
            instance_uuid = instance['uuid']
            with compute_utils.EventReporter(context, self.db,
                                         'cold_migrate', instance_uuid):
                self._cold_migrate(context, instance, flavor,
                                   scheduler_hint['filter_properties'],
                                   reservations)

 nova-conductor接收rpc消息后会执行函数migrate_server,顶层传过来的参数live true rebuild false flavor false所以会执行manager self._live_migrate

    def _live_migrate(self, context, instance, scheduler_hint,
                      block_migration, disk_over_commit):
        destination = scheduler_hint.get("host")
        try:
            live_migrate.execute(context, instance, destination,
                             block_migration, disk_over_commit)
        except (exception.NoValidHost,
                exception.ComputeServiceUnavailable,
                exception.InvalidHypervisorType,
                exception.InvalidCPUInfo,
                exception.UnableToMigrateToSelf,
                exception.DestinationHypervisorTooOld,
                exception.InvalidLocalStorage,
                exception.InvalidSharedStorage,
                exception.HypervisorUnavailable,
                exception.MigrationPreCheckError) as ex:
            with excutils.save_and_reraise_exception():
                #TODO(johngarbutt) - eventually need instance actions here
                request_spec = {'instance_properties': {
                    'uuid': instance['uuid'], },
                }
                scheduler_utils.set_vm_state_and_notify(context,
                        'compute_task', 'migrate_server',
                        dict(vm_state=instance['vm_state'],
                             task_state=None,
                             expected_task_state=task_states.MIGRATING,),
                        ex, request_spec, self.db)
        except Exception as ex:
            LOG.error(_('Migration of instance %(instance_id)s to host'
                       ' %(dest)s unexpectedly failed.'),
                       {'instance_id': instance['uuid'], 'dest': destination},
                       exc_info=True)
            raise exception.MigrationError(reason=ex)

由nova/conductor/tasks/live_migrate.py进行处理

def execute(context, instance, destination,
            block_migration, disk_over_commit):
    task = LiveMigrationTask(context, instance,
                             destination,
                             block_migration,
                             disk_over_commit)
    #TODO(johngarbutt) create a superclass that contains a safe_execute call
    return task.execute()

 将热迁移操作封装成task然后执行

    def execute(self):
        self._check_instance_is_running()
        self._check_host_is_up(self.source)

        if not self.destination:
            self.destination = self._find_destination()
        else:
            self._check_requested_destination()

        #TODO(johngarbutt) need to move complexity out of compute manager
        return self.compute_rpcapi.live_migration(self.context,
                host=self.source,
                instance=self.instance,
                dest=self.destination,
                block_migration=self.block_migration,
                migrate_data=self.migrate_data)
                #TODO(johngarbutt) disk_over_commit?

首先检测虚机是否运行,源host是否up

扫描二维码关注公众号,回复: 5812387 查看本文章

然后检查目的host是否存在,如果不存在,通过调试算法选取一台host,如果存在,检测目的host配置是否满足需求

检查通过后,开始通知nova-compute节点(源host节点)开始执行热迁移

nova/compute/manager.py

    @wrap_exception()
    @wrap_instance_fault
    def live_migration(self, context, dest, instance, block_migration,
                       migrate_data):
        """Executing live migration.

        :param context: security context
        :param instance: instance dict
        :param dest: destination host
        :param block_migration: if true, prepare for block migration
        :param migrate_data: implementation specific params

        """
        # Create a local copy since we'll be modifying the dictionary
        migrate_data = dict(migrate_data or {})
        try:
            if block_migration:
                disk = self.driver.get_instance_disk_info(instance['name'])
            else:
                disk = None
            
            # 在目的host创建要迁移主机配置
            pre_migration_data = self.compute_rpcapi.pre_live_migration(
                context, instance,
                block_migration, disk, dest, migrate_data)
            migrate_data['pre_live_migration_result'] = pre_migration_data

        except Exception:
            with excutils.save_and_reraise_exception():
                LOG.exception(_('Pre live migration failed at %s'),
                              dest, instance=instance)
                # 如果在目的host创建配置失败,回滚
                self._rollback_live_migration(context, instance, dest,
                                              block_migration, migrate_data)

        # Executing live migration
        # live_migration might raises exceptions, but
        # nothing must be recovered in this version.
        # 本节点开始执行迁移
        self.driver.live_migration(context, instance, dest,
                                   self._post_live_migration,
                                   self._rollback_live_migration,
                                   block_migration, migrate_data)

   nova-compute是在每个compute节点上运行的,迁移的时候从a到b的,那么a节点通知b节点执行pre_live_migrate函数,准备好迁移环境

    @object_compat
    @wrap_exception()
    @wrap_instance_fault
    def pre_live_migration(self, context, instance, block_migration, disk,
                           migrate_data):
        """Preparations for live migration at dest host.

        :param context: security context
        :param instance: dict of instance data
        :param block_migration: if true, prepare for block migration
        :param migrate_data : if not None, it is a dict which holds data
        required for live migration without shared storage.

        """
        # 获取instance块设备信息
        block_device_info = self._get_instance_block_device_info(
                            context, instance, refresh_conn_info=True)
        
        # 获取Instance网络信息
        network_info = self._get_instance_nw_info(context, instance)
        # 通知预迁移工作开始
        self._notify_about_instance_usage(
                     context, instance, "live_migration.pre.start",
                     network_info=network_info)

        # driver执行预迁移工作
        pre_live_migration_data = self.driver.pre_live_migration(context,
                                       instance,
                                       block_device_info,
                                       network_info,
                                       disk,
                                       migrate_data)

        # 预迁移工作完成后在目的host创建相关网络设备
        # NOTE(tr3buchet): setup networks on destination host
        self.network_api.setup_networks_on_host(context, instance,
                                                         self.host)

        # Creating filters to hypervisors and firewalls.
        # An example is that nova-instance-instance-xxx,
        # which is written to libvirt.xml(Check "virsh nwfilter-list")
        # This nwfilter is necessary on the destination host.
        # In addition, this method is creating filtering rule
        # onto destination host.
        # 将Instance安全组规则迁移到新host
        self.driver.ensure_filtering_rules_for_instance(instance,
                                            network_info)
        # 通知预迁移结束
        self._notify_about_instance_usage(
                     context, instance, "live_migration.pre.end",
                     network_info=network_info)

        return pre_live_migration_data

nova/compute/manager 的driver是通过配置文件来初始化的

看一下具体的初始化过程 

    def __init__(self, compute_driver=None, *args, **kwargs):
        """Load configuration options and connect to the hypervisor."""
        self.virtapi = ComputeVirtAPI(self)
        self.network_api = network.API()
        self.volume_api = volume.API()
        self._last_host_check = 0
        self._last_bw_usage_poll = 0
        self._bw_usage_supported = True
        self._last_bw_usage_cell_update = 0
        self.compute_api = compute.API()
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()
        self.conductor_api = conductor.API()
        self.compute_task_api = conductor.ComputeTaskAPI()
        self.is_neutron_security_groups = (
            openstack_driver.is_neutron_security_groups())
        self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI()
        self.cells_rpcapi = cells_rpcapi.CellsAPI()
        self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
        self._resource_tracker_dict = {}
        self.instance_events = InstanceEvents()

        super(ComputeManager, self).__init__(service_name="compute",
                                             *args, **kwargs)

        # NOTE(russellb) Load the driver last.  It may call back into the
        # compute manager via the virtapi, so we want it to be fully
        # initialized before that happens.
        # 这个位置初始化driver
        self.driver = driver.load_compute_driver(self.virtapi, compute_driver)
        self.use_legacy_block_device_info = \
                            self.driver.need_legacy_block_device_info

nova/virt/driver.py

def load_compute_driver(virtapi, compute_driver=None):
    """Load a compute driver module.

    Load the compute driver module specified by the compute_driver
    configuration option or, if supplied, the driver name supplied as an
    argument.

    Compute drivers constructors take a VirtAPI object as their first object
    and this must be supplied.

    :param virtapi: a VirtAPI instance
    :param compute_driver: a compute driver name to override the config opt
    :returns: a ComputeDriver instance
    """
    if not compute_driver:
        compute_driver = CONF.compute_driver

    if not compute_driver:
        LOG.error(_("Compute driver option required, but not specified"))
        sys.exit(1)

    LOG.info(_("Loading compute driver '%s'") % compute_driver)
    try:
        driver = importutils.import_object_ns('nova.virt',
                                              compute_driver,
                                              virtapi)
        return utils.check_isinstance(driver, ComputeDriver)
    except ImportError:
        LOG.exception(_("Unable to load the virtualization driver"))
        sys.exit(1)

如果在ComputeManager初始化的时候没有传入compute_driver,会根据配置文件获取指定compute_driver,这里的driver有很多

包括 libvirt.LibvirtDriver, xenapi.XenAPIDriver, fake.FakeDriver, baremetal.BareMetalDriver, vmwareapi.VMwareESXDriver, vmwareapi.VMwareVCDriver, hyperv.HyperVDriver...

主要看一下libvrit的实现

nova/virt/libvirt/driver.py

    def pre_live_migration(self, context, instance, block_device_info,
                           network_info, disk_info, migrate_data=None):
        """Preparation live migration."""
        # Steps for volume backed instance live migration w/o shared storage.
        is_shared_storage = True
        is_volume_backed = False
        is_block_migration = True
        instance_relative_path = None
        if migrate_data:
            is_shared_storage = migrate_data.get('is_shared_storage', True)
            is_volume_backed = migrate_data.get('is_volume_backed', False)
            is_block_migration = migrate_data.get('block_migration', True)
            instance_relative_path = migrate_data.get('instance_relative_path')

        # 如果实例文件的是否是共享,如果不是需要创建相应文件目录以及实例文件
        if not is_shared_storage:
            # NOTE(dims): Using config drive with iso format does not work
            # because of a bug in libvirt with read only devices. However
            # one can use vfat as config_drive_format which works fine.
            # Please see bug/1246201 for details on the libvirt bug.
            if CONF.config_drive_format != 'vfat':
                if configdrive.required_by(instance):
                    raise exception.NoBlockMigrationForConfigDriveInLibVirt()

            # NOTE(mikal): this doesn't use libvirt_utils.get_instance_path
            # because we are ensuring that the same instance directory name
            # is used as was at the source
            if instance_relative_path:
                instance_dir = os.path.join(CONF.instances_path,
                                            instance_relative_path)
            else:
                instance_dir = libvirt_utils.get_instance_path(instance)

            if os.path.exists(instance_dir):
                raise exception.DestinationDiskExists(path=instance_dir)
            os.mkdir(instance_dir)

            # Ensure images and backing files are present.
            self._create_images_and_backing(context, instance, instance_dir,
                                            disk_info)

        if is_volume_backed and not (is_block_migration or is_shared_storage):
            # Touch the console.log file, required by libvirt.
            console_file = self._get_console_log_path(instance)
            libvirt_utils.file_open(console_file, 'a').close()

            # if image has kernel and ramdisk, just download
            # following normal way.
            self._fetch_instance_kernel_ramdisk(context, instance)

        # Establishing connection to volume server.
        # 处理卷设备连接
        block_device_mapping = driver.block_device_info_get_mapping(
            block_device_info)
        for vol in block_device_mapping:
            connection_info = vol['connection_info']
            disk_info = blockinfo.get_info_from_bdm(
                CONF.libvirt.virt_type, vol)
            self.volume_driver_method('connect_volume',
                                      connection_info,
                                      disk_info)

        # We call plug_vifs before the compute manager calls
        # ensure_filtering_rules_for_instance, to ensure bridge is set up
        # Retry operation is necessary because continuously request comes,
        # concurrent request occurs to iptables, then it complains.
        max_retry = CONF.live_migration_retry_count
        # 尝试在目的host创建vif
        for cnt in range(max_retry):
            try:
                self.plug_vifs(instance, network_info)
                break
            except processutils.ProcessExecutionError:
                if cnt == max_retry - 1:
                    raise
                else:
                    LOG.warn(_('plug_vifs() failed %(cnt)d. Retry up to '
                               '%(max_retry)d.'),
                             {'cnt': cnt,
                              'max_retry': max_retry},
                             instance=instance)
                    greenthread.sleep(1)

执行完pre_live_migrate没有问题的话,就开始执行live_migrate,这里注意一下,pre_live_migrate是在目的host执行的,live_migrate是在源host执行的

nova/virt/libvirt/driver.py

    def live_migration(self, context, instance, dest,
                       post_method, recover_method, block_migration=False,
                       migrate_data=None):
        """Spawning live_migration operation for distributing high-load.

        :param context: security context
        :param instance:
            nova.db.sqlalchemy.models.Instance object
            instance object that is migrated.
        :param dest: destination host
        :param post_method:
            post operation method.
            expected nova.compute.manager.post_live_migration.
        :param recover_method:
            recovery method when any exception occurs.
            expected nova.compute.manager.recover_live_migration.
        :param block_migration: if true, do block migration.
        :param migrate_data: implementation specific params

        """
           
        # 异步执行虚机热迁移
        greenthread.spawn(self._live_migration, context, instance, dest,
                          post_method, recover_method, block_migration,
                          migrate_data)

    def _live_migration(self, context, instance, dest, post_method,
                        recover_method, block_migration=False,
                        migrate_data=None):
        """Do live migration.

        :param context: security context
        :param instance:
            nova.db.sqlalchemy.models.Instance object
            instance object that is migrated.
        :param dest: destination host
        :param post_method:
            post operation method.
            expected nova.compute.manager.post_live_migration.
        :param recover_method:
            recovery method when any exception occurs.
            expected nova.compute.manager.recover_live_migration.
        :param block_migration: if true, do block migration.
        :param migrate_data: implementation specific params
        """

        # Do live migration.
        try:
            if block_migration:
                flaglist = CONF.libvirt.block_migration_flag.split(',')
            else:
                flaglist = CONF.libvirt.live_migration_flag.split(',')
            flagvals = [getattr(libvirt, x.strip()) for x in flaglist]
            logical_sum = reduce(lambda x, y: x | y, flagvals)
            
            # 开始迁移工作
            dom = self._lookup_by_name(instance["name"])
            dom.migrateToURI(CONF.libvirt.live_migration_uri % dest,
                             logical_sum,
                             None,
                             CONF.libvirt.live_migration_bandwidth)

        except Exception as e:
            with excutils.save_and_reraise_exception():
                LOG.error(_("Live Migration failure: %s"), e,
                          instance=instance)
                recover_method(context, instance, dest, block_migration)

        # Waiting for completion of live_migration.
        timer = loopingcall.FixedIntervalLoopingCall(f=None)

        def wait_for_live_migration():
            """waiting for live migration completion."""
            try:
                self.get_info(instance)['state']
            except exception.InstanceNotFound:
                timer.stop()
                # 迁移完成,执行callback函数
                post_method(context, instance, dest, block_migration,
                            migrate_data)
        
        timer.f = wait_for_live_migration
        # 轮循检测instance迁移状态,完成后退出
        timer.start(interval=0.5).wait()

根据

        # Executing live migration
        # live_migration might raises exceptions, but
        # nothing must be recovered in this version.
        self.driver.live_migration(context, instance, dest,
                                   self._post_live_migration,
                                   self._rollback_live_migration,
                                   block_migration, migrate_data)

可以知道live_migrate执行完成后的回调函数是_post_live_migration

    @wrap_exception()
    @wrap_instance_fault
    def _post_live_migration(self, ctxt, instance,
                            dest, block_migration=False, migrate_data=None):
        """Post operations for live migration.

        This method is called from live_migration
        and mainly updating database record.

        :param ctxt: security context
        :param instance: instance dict
        :param dest: destination host
        :param block_migration: if true, prepare for block migration
        :param migrate_data: if not None, it is a dict which has data
        required for live migration without shared storage

        """
        LOG.info(_('_post_live_migration() is started..'),
                 instance=instance)

        bdms = block_device_obj.BlockDeviceMappingList.get_by_instance_uuid(
                ctxt, instance['uuid'])

        # Cleanup source host post live-migration
        block_device_info = self._get_instance_block_device_info(
                            ctxt, instance, bdms=bdms)
        
        # 迁移完成后执行driver善后工作
        self.driver.post_live_migration(ctxt, instance, block_device_info,
                                        migrate_data)

        # Detaching volumes.
        # 清除卷设备关联
        connector = self.driver.get_volume_connector(instance)
        for bdm in bdms:
            # NOTE(vish): We don't want to actually mark the volume
            #             detached, or delete the bdm, just remove the
            #             connection from this host.

            # remove the volume connection without detaching from hypervisor
            # because the instance is not running anymore on the current host
            if bdm.is_volume:
                self.volume_api.terminate_connection(ctxt, bdm.volume_id,
                                                     connector)

        # Releasing vlan.
        # (not necessary in current implementation?)

        network_info = self._get_instance_nw_info(ctxt, instance)

        self._notify_about_instance_usage(ctxt, instance,
                                          "live_migration._post.start",
                                          network_info=network_info)
        # Releasing security group ingress rule.
        # 清除和instance相关安全组规则
        self.driver.unfilter_instance(instance,
                                      network_info)

        migration = {'source_compute': self.host,
                     'dest_compute': dest, }
        # 开始迁移网络,主要是目的节点创建相应的vif并加到bridge上或者ovs
        self.conductor_api.network_migrate_instance_start(ctxt,
                                                          instance,
                                                          migration)

        # Define domain at destination host, without doing it,
        # pause/suspend/terminate do not work.
        # 通知目的host我已经干完活了,该你了
        self.compute_rpcapi.post_live_migration_at_destination(ctxt,
                instance, block_migration, dest)

        # No instance booting at source host, but instance dir
        # must be deleted for preparing next block migration
        # must be deleted for preparing next live migration w/o shared storage
        is_shared_storage = True
        if migrate_data:
            is_shared_storage = migrate_data.get('is_shared_storage', True)
        if block_migration or not is_shared_storage:
            self.driver.cleanup(ctxt, instance, network_info)
        else:
            # self.driver.destroy() usually performs  vif unplugging
            # but we must do it explicitly here when block_migration
            # is false, as the network devices at the source must be
            # torn down
            try:
                # 删除instance vif
                self.driver.unplug_vifs(instance, network_info)
            except NotImplementedError as e:
                LOG.debug(e, instance=instance)
        # 清理网络
        # NOTE(tr3buchet): tear down networks on source host
        self.network_api.setup_networks_on_host(ctxt, instance,
                                                self.host, teardown=True)
        # 清除instance事件列表
        self.instance_events.clear_events_for_instance(instance)

        self._notify_about_instance_usage(ctxt, instance,
                                          "live_migration._post.end",
                                          network_info=network_info)
        LOG.info(_('Migrating instance to %s finished successfully.'),
                 dest, instance=instance)
        LOG.info(_("You may see the error \"libvirt: QEMU error: "
                   "Domain not found: no domain with matching name.\" "
                   "This error can be safely ignored."),
                 instance=instance)

        if CONF.vnc_enabled or CONF.spice.enabled or CONF.rdp.enabled:
            if CONF.cells.enable:
                self.cells_rpcapi.consoleauth_delete_tokens(ctxt,
                        instance['uuid'])
            else:
                self.consoleauth_rpcapi.delete_tokens_for_instance(ctxt,
                        instance['uuid'])

再来看一下目的host都做了哪些事

nova/compute/manager

   @object_compat
    @wrap_exception()
    @wrap_instance_fault
    def post_live_migration_at_destination(self, context, instance,
                                           block_migration):
        """Post operations for live migration .

        :param context: security context
        :param instance: Instance dict
        :param block_migration: if true, prepare for block migration

        """
        LOG.info(_('Post operation of migration started'),
                 instance=instance)

        # NOTE(tr3buchet): setup networks on destination host
        #                  this is called a second time because
        #                  multi_host does not create the bridge in
        #                  plug_vifs
        self.network_api.setup_networks_on_host(context, instance,
                                                         self.host)
        migration = {'source_compute': instance['host'],
                     'dest_compute': self.host, }
        self.conductor_api.network_migrate_instance_finish(context,
                                                           instance,
                                                           migration)

        network_info = self._get_instance_nw_info(context, instance)
        self._notify_about_instance_usage(
                     context, instance, "live_migration.post.dest.start",
                     network_info=network_info)
        block_device_info = self._get_instance_block_device_info(context,
                                                                 instance)

        self.driver.post_live_migration_at_destination(context, instance,
                                            network_info,
                                            block_migration, block_device_info)
        # Restore instance state
        current_power_state = self._get_power_state(context, instance)
        node_name = None
        try:
            compute_node = self._get_compute_info(context, self.host)
            node_name = compute_node['hypervisor_hostname']
        except exception.NotFound:
            LOG.exception(_('Failed to get compute_info for %s') % self.host)
        finally:
            # 更新instance信息
            instance.host = self.host
            instance.power_state = current_power_state
            instance.vm_state = vm_states.ACTIVE
            instance.task_state = None
            instance.node = node_name
            instance.save(expected_task_state=task_states.MIGRATING)

        # NOTE(vish): this is necessary to update dhcp
        self.network_api.setup_networks_on_host(context, instance, self.host)
        self._notify_about_instance_usage(
                     context, instance, "live_migration.post.dest.end",
                     network_info=network_info)

ok,迁移完成 

猜你喜欢

转载自blog.csdn.net/LIUSHU427/article/details/83023808