• Nova组件源码分析之冷迁移与Resize


    冷迁移Resize

    1、迁移是指将虚拟机从一个计算节点迁移到另外一个节点上冷迁移过程中虚拟机是关机或是处于不可用的状态,而热迁移则需要保证虚拟机时刻运行。

    2、Resize则是指根据需求调整虚拟机的计算能力和资源Resize和冷迁移的工作流程相同,区别只是Resize时必须保持新的Flavor配置大于旧的配置,而冷迁移则要求两者相同

    3、resize和冷迁移的本质区别:

    同一个接口,根据传参不同,有flavor就执行升配,无flavor就走的是冷迁移。

    1. 迁移一般都是从一个节点迁移到另一个节点上,升配的时候一般默认也是如此,但可以配置resize_to_same_host,升配的时候,不迁移到别的节点。

    1、 入口API:nova/api/openstack/compute/migrate_server.py

    1. class MigrateServerController(wsgi.Controller):
    2.   ................
    3. def _migrate(self, req, id, body):
    4.     """允许管理员将服务器迁移到新主机。"""
    5.     context = req.environ['nova.context']
    6.     instance = common.get_instance(self.compute_api, context, id,
    7.                                    expected_attrs=['flavor', 'services'])
    8.     context.can(ms_policies.POLICY_ROOT % 'migrate',
    9.                 target={'project_id': instance.project_id})
    10.     host_name = None
    11.     if (api_version_request.is_supported(req, min_version='2.56') and
    12.         body['migrate'] is not None):
    13.         host_name = body['migrate'].get('host')
    14. try:
    15.     self.compute_api.resize(req.environ['nova.context'], instance,
    16.                             host_name=host_name)
    17.   ................

    instance = common.get_instance(self.compute_api, context, id,expected_attrs=['flavor', 'services'])依据instance id 获取instance id 并对instance的存在性做了校验

    实际调用函数self.compute_api.resize(req.environ['nova.context'], instance,host_name=host_name)。compute_api.resize定义在 nova/compute/api.py

    2、nova/compute/api.py

    1. class API:
    2.     """用于与计算管理器交互的API."""
    3. @check_instance_lock
    4. @check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
    5. @check_instance_host(check_is_up=True)
    6. def resize(self, context, instance, flavor_id=None, clean_shutdown=True,
    7.            host_name=None, auto_disk_config=None):
    8.  """调整(即,迁移)一个正在运行的实例的大小。
    9. 如果flavor_id为None,则认为该进程是迁移,保持
    10. 原flavor_id。如果flavor_id不是None,实例应该
    11. 迁移到新的主机并调整大小为新的flavor_id。
    12. 在调整大小的情况下,host_name总是None。
    13. Host_name仅在冷迁移情况下可以设置。
    14.     """
    15. allow_cross_cell_resize = self._allow_cross_cell_resize(
    16.     context, instance)
    17. if host_name is not None:
    18.     node = self._validate_host_for_cold_migrate(
    19.         context, instance, host_name, allow_cross_cell_resize)
    20. # 校验磁盘信息
    21. self._check_auto_disk_config(
    22.     instance, auto_disk_config=auto_disk_config)
    23. # 获取当前flavor 并校验flavor
    24. current_flavor = instance.get_flavor()
    25. ’’’
    26. 注意(aarents):确保image_base_image_ref存在,因为在finish_resize/cross_cell_resize期间需要它。实例升级
    27. 从一个较老的新星释放可能没有这个属性,因为
    28. a rebuild bug bug /1893618
    29. ‘’’
    30. instance.system_metadata.update(
    31.     {'image_base_image_ref': instance.image_ref}
    32. )
    33. volume_backed = None
    34. # 如果没有提供flavor_id,则只迁移实例。
    35. if not flavor_id:
    36.     LOG.debug("flavor_id is None. Assuming migration.",
    37.               instance=instance)
    38.  # 保证迁移前后虚拟机 Flavor 不会发生改变
    39.     new_flavor = current_flavor
    40. else:
    41.   ................
    42. filter_properties = {'ignore_hosts': []}
    43. “””通过配置项 allow_resize_to_same_host 来决定是否会 resize 到同一个计算节点
    44. 实际上,当 Migrate 到同一个计算节点时,nova-compute 会触发 UnableToMigrateToSelf 异常,
    45. 再继续 Retry Scheduler,直至调度到合适的计算节点或异常退出,前提是 nova-scheduler 启用了 RetryFilter
    46. “””
    47. if not self._allow_resize_to_same_host(same_flavor, instance):
    48.     filter_properties['ignore_hosts'].append(instance.host)
    49. # 获取request_spec,该参数为新的调度参数,将ignore host加入调度
    50. request_spec = objects.RequestSpec.get_by_instance_uuid(
    51.     context, instance.uuid)
    52. request_spec.ignore_hosts = filter_properties['ignore_hosts']
    53.   ................
    54. # 更新虚拟机状态为RESIZE_PREP, 虚拟机设置为该状态开始,将会锁定,不允许进行其他任务
    55. instance.task_state = task_states.RESIZE_PREP
    56. instance.progress = 0
    57. instance.auto_disk_config = auto_disk_config or False
    58. instance.save(expected_task_state=[None])
    59. # 发送迁移状态
    60. if not flavor_id:
    61.     self._record_action_start(context, instance, instance_actions.MIGRATE)
    62. else:
    63.     self._record_action_start(context, instance,instance_actions.RESIZE)
    64. scheduler_hint = {'filter_properties': filter_properties}
    65.   ................
    66. # resize_instance 任务迁移
    67. self.compute_task_api.resize_instance(
    68.     context, instance,
    69.     scheduler_hint=scheduler_hint,
    70.     flavor=new_flavor,
    71.     clean_shutdown=clean_shutdown,
    72.     request_spec=request_spec,
    73.     do_cast=True)

    该步骤主要是进行参数校验,并获取request_spec 调度参数。 调用nova/conductor/api的resize_instance函数

    3、nova/conductor/api

    1. def resize_instance(self, context, instance, scheduler_hint, flavor,
    2.                     reservations=None, clean_shutdown=True,
    3.                     request_spec=None, host_list=None, do_cast=False):
    4.     self.conductor_compute_rpcapi.migrate_server(
    5.         context, instance, scheduler_hint, live=False, rebuild=False,
    6.         flavor=flavor, block_migration=None, disk_over_commit=None,
    7.         reservations=reservations, clean_shutdown=clean_shutdown,
    8.         request_spec=request_spec, host_list=host_list,
    9.         do_cast=do_cast)

    调用的conductor 层的 migrate_server, 通过rpc远程调用方式进行。

    4、nova/conductor/rcpapi.py

    1. def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
    2.               flavor, block_migration, disk_over_commit,
    3.               reservations=None, clean_shutdown=True, request_spec=None,
    4.               host_list=None, do_cast=False):
    5. cctxt = self.client.prepare(
    6.      version=version,
    7.      call_monitor_timeout=CONF.rpc_response_timeout,
    8.      timeout=CONF.long_rpc_timeout)
    9. if do_cast:
    10.      return cctxt.cast(context, 'migrate_server', **kw)
    11. return cctxt.call(context, 'migrate_server', **kw)

    实际调用的conductor的migrate_server该过程实际通过Manager进行处理。

    5、nova/conductor/manager.py

    1. @targets_cell
    2. @wrap_instance_event(prefix='conductor')
    3. def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
    4.         flavor, block_migration, disk_over_commit, reservations=None,
    5.         clean_shutdown=True, request_spec=None, host_list=None):
    6.     if instance and not isinstance(instance, nova_object.NovaObject):
    7.         # NOTE(danms): Until v2 of the RPC API, we need to tolerate
    8.         # old-world instance objects here
    9.         attrs = ['metadata', 'system_metadata', 'info_cache',
    10.                  'security_groups']
    11.         instance = objects.Instance._from_db_object(
    12.             context, objects.Instance(), instance,
    13.             expected_attrs=attrs)
    14.     # NOTE: Remove this when we drop support for v1 of the RPC API
    15.     if flavor and not isinstance(flavor, objects.Flavor):
    16.         # Code downstream may expect extra_specs to be populated since it
    17.         # is receiving an object, so lookup the flavor to ensure this.
    18.         flavor = objects.Flavor.get_by_id(context, flavor['id'])
    19.     if live and not rebuild and not flavor:
    20.         self._live_migrate(context, instance, scheduler_hint,
    21.                            block_migration, disk_over_commit, request_spec)
    22.     elif not live and not rebuild and flavor:
    23.         instance_uuid = instance.uuid
    24.         with compute_utils.EventReporter(context, 'cold_migrate',
    25.                                          self.host, instance_uuid):
    26.             self._cold_migrate(context, instance, flavor,
    27.                                scheduler_hint['filter_properties'],
    28.                                clean_shutdown, request_spec,
    29.                                host_list)
    30.     else:
    31.         raise NotImplementedError()

    migrate_server 是一个公用的函数, 冷迁移, resiez, 以及热迁移都经过该函数。这里冷迁移实际使用_cold_migrate 进行处理。

    1. def _cold_migrate(self, context, instance, flavor, filter_properties,
    2.                   clean_shutdown, request_spec, host_list):
    3. #  更新request_spec
    4. request_spec = self._get_request_spec_for_cold_migrate(
    5.     context, instance, flavor, filter_properties, request_spec)
    6. # 创建冷迁移任务 并执行
    7. task = self._build_cold_migrate_task(context, instance, flavor,
    8.         request_spec, clean_shutdown, host_list)
    9. try:
    10.     task.execute()
    11. except exception.NoValidHost as ex:
    12.   ..................
    13. # 保存request_spec 调度参数
    14.  if request_spec.obj_what_changed():
    15.     request_spec.save()

    该过程实际就是做异常处理,并保存请求参数。task.execute() 实际处理由_execute进行定义在nova/conductor/tasks/migrate.py

    6、nova/conductor/task/migrate.py

    1. def _execute(self):
    2. # 将forced host 重置掉,以免影响调度
    3. self.request_spec.reset_forced_destinations()
    4. legacy_props = self.request_spec.to_legacy_filter_properties_dict()
    5. scheduler_utils.setup_instance_group(self.context, self.request_spec)
    6. if not ('requested_destination' in self.request_spec and self.request_spec.requested_destination and 'host' in self.request_spec.requested_destination):
    7. scheduler_utils.populate_retry(legacy_props,self.instance.uuid)
    8. port_res_req, req_lvl_params = (self.network_api.get_requested_resource_for_instance(
    9. self.context, self.instance.uuid))
    10. # 重新调度生成request_spec
    11. self.request_spec.requested_resources = port_res_req
    12. self.request_spec.request_level_params = req_lvl_params
    13. self._set_requested_destination_cell(legacy_props)
    14.    .................
    15. if self.host_list is None:
    16. # 调度, 获取可用宿主机,并选用一台
    17.     selection = self._schedule()
    18.    .................
    19. else:
    20.  # 这是一个重新调度,将使用提供的备用主机,在host_list中作为目的主机。
    21.     selection = self._reschedule()
    22. scheduler_utils.populate_filter_properties(legacy_props, selection)
    23. (host, node) = (selection.service_host, selection.nodename)
    24. # rpc 调用目的节点prep_resize
    25. self.compute_rpcapi.prep_resize(
    26.     self.context, self.instance, self.request_spec.image,
    27.     self.flavor, host, migration,
    28.     request_spec=self.request_spec, filter_properties=legacy_props,
    29.     node=node, clean_shutdown=self.clean_shutdown,
    30.     host_list=self.host_list)

    该步骤主要进行调度, 选取目标宿主机, 发送任务, 进行迁移。Nova-compute发起RPC请求后,Nova-compute的manager响应对应的方法

    7、nova/compute/manager.py

    1. def prep_resize(self, context, image, instance, flavor,
    2.                 request_spec, filter_properties, node,
    3.                 clean_shutdown, migration, host_list):
    4. # 校验参数
    5. if node is None:
    6.     node = self._get_nodename(instance, refresh=True)
    7. instance_state = instance.vm_state
    8. with self._error_out_instance_on_exception(
    9.         context, instance, instance_state=instance_state),\
    10.         errors_out_migration_ctxt(migration):
    11. # 通知状态
    12.     self._send_prep_resize_notifications(
    13.         context, instance, fields.NotificationPhase.START,
    14.         flavor)
    15.     try:
    16.         scheduler_hints = self._get_scheduler_hints(filter_properties,
    17.                                                     request_spec)
    18. try:
    19.     self._validate_instance_group_policy(context, instance,
    20.                                          scheduler_hints)
    21. except exception.RescheduledException as e:
    22.     raise exception.InstanceFaultRollback(inner_exception=e)
    23. self._prep_resize(context, image, instance,
    24.                   flavor, filter_properties,
    25.                   node, migration, request_spec,
    26.                   clean_shutdown)
    27.   ..............
    28. finally:
    29.     self._send_prep_resize_notifications(
    30.         context, instance, fields.NotificationPhase.END,
    31.         flavor)

    由_prep_resize 进行处理

    1. def _prep_resize(
    2.     self, context, image, instance, flavor, filter_properties, node,
    3.     migration, request_spec, clean_shutdown=True,
    4. ):
    5. if not filter_properties:
    6.     filter_properties = {}
    7. if not instance.host:
    8.     self._set_instance_obj_error_state(instance)
    9.     msg = _('Instance has no source host')
    10.     raise exception.MigrationError(reason=msg)
    11. “””
    12. 判断是否为同一台主机,
    13. 并检查 supports_migrate_to_same_host 是否支持迁移到同一台主机上
    14. “””
    15. same_host = instance.host == self.host
    16. # 如果flavor id匹配,它就是迁移;否则调整
    17. if same_host and flavor.id == instance['instance_type_id']:
    18.     if not self.driver.capabilities.get(
    19.             'supports_migrate_to_same_host', False):
    20.                 raise exception.InstanceFaultRollback(
    21.             inner_exception=exception.UnableToMigrateToSelf(
    22.                 instance_id=instance.uuid, host=self.host))
    23. # 更新instance 信息 并保存
    24. instance.new_flavor = flavor
    25. vm_state = instance.vm_state
    26. LOG.debug('Stashing vm_state: %s', vm_state, instance=instance)
    27. instance.system_metadata['old_vm_state'] = vm_state
    28. instance.save()
    29.   ............
    30. limits = filter_properties.get('limits', {})
    31. allocs = self.reportclient.get_allocations_for_consumer(
    32.     context, instance.uuid)
    33. # 开始迁移
    34. with self.rt.resize_claim(
    35.     context, instance, flavor, node, migration, allocs,
    36.     image_meta=image, limits=limits,
    37. ) as claim:
    38.     LOG.info('Migrating', instance=instance)
    39. # RPC转换到源主机以启动实际的大小调整/迁移。
    40.     self.compute_rpcapi.resize_instance(
    41.         context, instance, claim.migration, image,
    42.         flavor, request_spec, clean_shutdown)

    通过RPC回到源主机上由源主机的nova-compute服务完成迁移。

    8、nova/compure/rpcapi.py

    1. def resize_instance(self, ctxt, instance, migration, image, flavor,
    2.                     request_spec, clean_shutdown=True):
    3.     version = '6.0'
    4.     msg_args = {'instance': instance, 'migration': migration,
    5.                 'image': image,
    6.                 'flavor': flavor,
    7.                 'clean_shutdown': clean_shutdown,
    8.                 'request_spec': request_spec,
    9.     }
    10.     client = self.router.client(ctxt)
    11.     if not client.can_send_version(version):
    12.         version = self._ver(ctxt, '5.2')
    13.         del msg_args['flavor']
    14.         msg_args['instance_type'] = flavor
    15.         if not client.can_send_version(version):
    16.             msg_args.pop('request_spec')
    17.             version = '5.0'
    18.     cctxt = client.prepare(server=_compute_host(None, instance),
    19.             version=version)
    20.     cctxt.cast(ctxt, 'resize_instance', **msg_args)

    通过源码分析,得到 compute_rpcapi.resize_instance 其实是调用instance原来主机上的过程,
    进行正式的迁移。

    9、nova/compute/manager.py

    1. def resize_instance(self, context, instance, image,
    2.                     migration, flavor, clean_shutdown,
    3.                     request_spec):
    4.  """启动一个正在运行的实例迁移到另一个主机。
    5. 这是从目标主机的' ' prep_resize ' '例程启动的并在源主机上运行。
    6.  """
    7.     try:
    8.         self._resize_instance(context, instance, image, migration,
    9.                               flavor, clean_shutdown, request_spec)
    10.     except Exception:
    11.         with excutils.save_and_reraise_exception():
    12.             self._revert_allocation(context, instance, migration)

    主要调用_resize_instance函数。

    1. def _resize_instance(
    2.     self, context, instance, image, migration, flavor,
    3.     clean_shutdown, request_spec,
    4. ):
    5. instance_state = instance.vm_state
    6. with self._error_out_instance_on_exception(
    7.         context, instance, instance_state=instance_state), \
    8.      errors_out_migration_ctxt(migration):
    9. # 获取instance 网络信息 该部分现在由NEUTRON 处理
    10.     network_info = self.network_api.get_instance_nw_info(context, instance)
    11. # 更新状态
    12.     migration.status = 'migrating'
    13.     migration.save()
    14.     instance.task_state = task_states.RESIZE_MIGRATING
    15.     instance.save(expected_task_state=task_states.RESIZE_PREP)
    16.     bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
    17.             context, instance.uuid)
    18. # 通知instance状态
    19.     self._send_resize_instance_notifications(
    20.         context, instance, bdms, network_info,
    21.         fields.NotificationPhase.START)
    22.  # 获取虚拟机的块设备信息
    23. block_device_info = self._get_instance_block_device_info(
    24.                         context, instance, bdms=bdms)
    25.     timeout, retry_interval = self._get_power_off_values(
    26.         instance, clean_shutdown)
    27. # 关闭并进行C盘迁移
    28.     disk_info = self.driver.migrate_disk_and_power_off(
    29.         context, instance, migration.dest_host,
    30.         flavor, network_info,
    31.         block_device_info,
    32.         timeout, retry_interval)
    33. # 中断磁盘映射关系
    34.     self._terminate_volume_connections(context, instance, bdms)
    35. # 迁移虚拟机网络
    36.     self.network_api.migrate_instance_start(context,
    37.                                             instance,
    38.                                             migration)
    39. # 保存虚拟机状态
    40.     migration.status = 'post-migrating'
    41.     migration.save()
    42. # 迁移完成
    43.     instance.host = migration.dest_compute
    44.     instance.node = migration.dest_node
    45.     instance.old_flavor = instance.flavor
    46.     instance.task_state = task_states.RESIZE_MIGRATED
    47.     instance.save(expected_task_state=task_states.RESIZE_MIGRATING)
    48. # 完成迁移, 完成迁移的收尾工作, 实例创建, 启动等
    49.     self.compute_rpcapi.finish_resize(context, instance,
    50.         migration, image, disk_info, migration.dest_compute,
    51.         request_spec)
    52. self._send_resize_instance_notifications(
    53.     context, instance, bdms, network_info,
    54.     fields.NotificationPhase.END)
    55. self.instance_events.clear_events_for_instance(instance)

    关键步骤:

    1、关闭并进行磁盘迁移

    2、网络迁移

    3、迁移收尾工作, 创建实例, 启动实例等

    核心步骤:

    1. 磁盘迁移:
    1. disk_info = self.driver.migrate_disk_and_power_off(
    2.     context, instance, migration.dest_host,
    3.     flavor, network_info,
    4.     block_device_info,
    5.     timeout, retry_interval)

    driver.migrate_disk_and_power_off过程·定义在nova/virt/libvirt/driver.py

    1. def migrate_disk_and_power_off(self, context, instance, dest,
    2.                                flavor, network_info,
    3.                                block_device_info=None,
    4.                                timeout=0, retry_interval=0):
    5. LOG.debug("Starting migrate_disk_and_power_off",
    6.            instance=instance)
    7. ephemerals = driver.block_device_info_get_ephemerals(block_device_info)
    8. eph_size = (block_device.get_bdm_ephemeral_disk_size(ephemerals) or
    9.             instance.flavor.ephemeral_gb)
    10. # 磁盘参数校验
    11. root_down = flavor.root_gb < instance.flavor.root_gb
    12. ephemeral_down = flavor.ephemeral_gb < eph_size
    13. booted_from_volume = self._is_booted_from_volume(block_device_info)
    14. if (root_down and not booted_from_volume) or ephemeral_down:
    15.     reason = _("Unable to resize disk down.")
    16.     raise exception.InstanceFaultRollback(
    17.         exception.ResizeError(reason=reason))
    18.    .................
    19. # 是否为共享存储, 如果不是则在目标宿主机上创建对应的目录
    20. if not shared_instance_path:
    21.     try:
    22.         self._remotefs.create_dir(dest, inst_base)
    23.     except processutils.ProcessExecutionError as e:
    24.         reason = _("not able to execute ssh command: %s") % e
    25.         raise exception.InstanceFaultRollback(
    26.             exception.ResizeError(reason=reason))
    27. # 关闭虚拟机
    28. self.power_off(instance, timeout, retry_interval)
    29. self.unplug_vifs(instance, network_info)
    30. # 获取磁盘映射关系
    31. block_device_mapping = driver.block_device_info_get_mapping(
    32.     block_device_info)
    33. for vol in block_device_mapping:
    34.     connection_info = vol['connection_info']
    35.     self._disconnect_volume(context, connection_info, instance)
    36. disk_info = self._get_instance_disk_info(instance, block_device_info)
    37. # 进行目录操作, 磁盘设置等
    38. try:
    39. self._cleanup_failed_instance_base(inst_base_resize)
    40. os.rename(inst_base, inst_base_resize)
    41. if shared_instance_path:
    42.     dest = None
    43.     fileutils.ensure_tree(inst_base)
    44. on_execute = lambda process: \
    45.     self.job_tracker.add_job(instance, process.pid)
    46. on_completion = lambda process: \
    47.     self.job_tracker.remove_job(instance, process.pid)
    48.    ...............
    49. # 将磁盘复制到目标节点
    50. compression = info['type'] not in NO_COMPRESSION_TYPES
    51. libvirt_utils.copy_image(from_path, img_path, host=dest,
    52.                          on_execute=on_execute,
    53.                          on_completion=on_completion,
    54.                          compression=compression)
    55. # 迁移disk.info 磁盘信息, 例如注入信息等
    56. src_disk_info_path = os.path.join(inst_base_resize, 'disk.info')
    57. if os.path.exists(src_disk_info_path):
    58.     dst_disk_info_path = os.path.join(inst_base, 'disk.info')
    59.     libvirt_utils.copy_image(src_disk_info_path,
    60.                              dst_disk_info_path,
    61.                              host=dest, on_execute=on_execute,
    62.                              on_completion=on_completion)
    63. libvirt_utils.save_and_migrate_vtpm_dir(
    64.         instance.uuid, inst_base_resize, inst_base, dest,
    65.         on_execute, on_completion)
    66. except Exception:
    67.     with excutils.save_and_reraise_exception():
    68.         self._cleanup_remote_migration(dest, inst_base,
    69.                                        inst_base_resize,
    70.                                        shared_instance_path)
    71. return jsonutils.dumps(disk_info)
    1. 磁盘迁移完成后, 进行网络迁移。
    2. 网络迁移完成后,finish_resize。通过RPC到目标主机上完成虚拟机的Resize

    10、nova/compute/rcpapi.py

    1. def finish_resize(self, ctxt, instance, migration, image, disk_info, host,
    2.                   request_spec):
    3.   .............
    4. cctxt = client.prepare(
    5.         server=host, version=version)
    6. cctxt.cast(ctxt, 'finish_resize', **msg_args)

    1. nova/compute/manager.py
    1. def finish_resize(self, context, disk_info, image, instance,
    2.                   migration, request_spec):
    3. “””
    4. 完成迁移过程。
    5. 设置新传输的磁盘,并在其新的主机上打开实例。
    6. “””
    7. try:
    8.     self._finish_resize_helper(context, disk_info, image, instance,migration, request_spec)
    9. except Exception:
    10.     with excutils.save_and_reraise_exception():
    11. LOG.info('Deleting allocations for old flavor on source node '
    12.          '%s after finish_resize failure. You may be able to '
    13.          'recover the instance by hard rebooting it.',
    14.          migration.source_compute, instance=instance)
    15. self._delete_allocation_after_move(
    16.     context, instance, migration)

    通过调用._finish_resize_helper函数调用_finish_resize 。

    1. def _finish_resize(self, context, instance, migration, disk_info,
    2.                    image_meta, bdms, request_spec):
    3.     resize_instance = False
    4.     old_instance_type_id = migration.old_instance_type_id
    5.     new_instance_type_id = migration.new_instance_type_id
    6.     old_flavor = instance.flavor
    7.     old_vm_state = instance.system_metadata.get('old_vm_state',vm_states.ACTIVE)
    8. if old_instance_type_id != new_instance_type_id:
    9.     new_flavor = instance.new_flavor  # this is set in _prep_resize
    10.     self._set_instance_info(instance, new_flavor)
    11.     for key in ('root_gb', 'swap', 'ephemeral_gb'):
    12.         if old_flavor[key] != new_flavor[key]:
    13.             resize_instance = True
    14.             break
    15.     instance.apply_migration_context()
    16. # 创建设置网络, 并完成网络迁移
    17. self.network_api.setup_networks_on_host(context, instance,
    18.                                         migration.dest_compute)
    19. provider_mappings = self._get_request_group_mapping(request_spec)
    20.     self.network_api.migrate_instance_finish(
    21.     context, instance, migration, provider_mappings)
    22.     network_info = self.network_api.get_instance_nw_info(context, instance)
    23.     instance.task_state = task_states.RESIZE_FINISH
    24.     instance.save(expected_task_state=task_states.RESIZE_MIGRATED)
    25.     self._send_finish_resize_notifications(
    26.     context, instance, bdms, network_info,
    27.     fields.NotificationPhase.START)
    28.    self._update_volume_attachments(context, instance, bdms)
    29. #  获取磁盘信息
    30.    block_device_info = self._get_instance_block_device_info(
    31.     context, instance, refresh_conn_info=True, bdms=bdms)
    32.     power_on = old_vm_state != vm_states.STOPPED
    33.     allocations = self.reportclient.get_allocs_for_consumer(
    34.     context, instance.uuid)['allocations']
    35.     try:
    36. # 完成迁移
    37.         self.driver.finish_migration(context, migration, instance,disk_info,  
    38.            network_info,image_meta, resize_instance,allocations, block_device_info, power_on)
    39.     except Exception:
    40.         with excutils.save_and_reraise_exception():
    41.             if old_instance_type_id != new_instance_type_id:
    42.                 self._set_instance_info(instance, old_flavor)
    43.     self._complete_volume_attachments(context, bdms)
    44.     migration.status = 'finished'
    45.     migration.save()
    46. # 更新状态
    47.     instance.vm_state = vm_states.RESIZED
    48.     instance.task_state = None
    49.     instance.launched_at = timeutils.utcnow()
    50.     instance.save(expected_task_state=task_states.RESIZE_FINISH)
    51.     return network_info

    finish_migration 定义在 virt/libvirt/driver.py

    12、nova/virt/libvirt/driver.py

    1. def finish_migration(
    2.     self,
    3.     context: nova_context.RequestContext,
    4.     migration: 'objects.Migration',
    5.     instance: 'objects.Instance',
    6.     disk_info: str,
    7.     network_info: network_model.NetworkInfo,
    8.     image_meta: 'objects.ImageMeta',
    9.     resize_instance: bool,
    10.     allocations: ty.Dict[str, ty.Any],
    11.     block_device_info: ty.Optional[ty.Dict[str, ty.Any]] = None,
    12.     power_on: bool = True,
    13. ) -> None:
    14. # 在目标主机上完成迁移过程
    15. # 获取磁盘信息,并创建磁盘镜像
    16. block_disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
    17.                                           instance,
    18.                                           image_meta,
    19.                                           block_device_info)
    20. self._create_image(context, instance, block_disk_info['mapping'],
    21.                    block_device_info=block_device_info,
    22.                    ignore_bdi_for_swap=True,
    23.                    fallback_from_host=migration.source_compute)
    24. self._ensure_console_log_for_instance(instance)
    25. gen_confdrive = functools.partial(
    26.     self._create_configdrive, context, instance,
    27.     InjectionInfo(admin_pass=None, network_info=network_info,
    28.                   files=None))
    29. for info in jsonutils.loads(disk_info):
    30.     path = info['path']
    31.     disk_name = os.path.basename(path)
    32. if (disk_name != 'disk.config' and
    33.             info['type'] == 'raw' and CONF.use_cow_images):
    34.     self._disk_raw_to_qcow2(info['path'])
    35. mdevs = self._allocate_mdevs(allocations)
    36. self._finish_migration_vtpm(context, instance)
    37. # 依据instance 信息生成 xml 文件
    38. xml = self._get_guest_xml(context, instance, network_info,
    39.                           block_disk_info, image_meta,
    40.                           block_device_info=block_device_info,
    41.                           mdevs=mdevs)
    42. # 创建虚拟机
    43. guest = self._create_guest_with_network(
    44.     context, xml, instance, network_info, block_device_info,
    45.     power_on=power_on, vifs_already_plugged=True,
    46.     post_xml_callback=gen_confdrive)
    47. if power_on:
    48.     timer = loopingcall.FixedIntervalLoopingCall(
    49.                                             self._wait_for_running,
    50.                                             instance)
    51.     timer.start(interval=0.5).wait()
    52. guest.sync_guest_time()
    53. LOG.debug("finish_migration finished successfully.", instance=instance)

    13、_create_guest_with_network创建虚拟机

    _create_guest_and_network 在目标宿主机创建虚拟机, 如果原始状态为active则启动
    该步骤在虚拟机创建中分析。

  • 相关阅读:
    2023版:深度比较几种.NET Excel导出库的性能差异
    分享一个Redis自带的压测工具:redis-benchmark
    PS 绘画临摹简单记录
    计算机毕业设计Java大学生家教平台(源码+系统+mysql数据库+lw文档)
    物无定味适口者珍,Python3并发场景(CPU密集/IO密集)任务的并发方式的场景抉择(多线程threading/多进程multiprocessing/协程asyncio)
    为什么刀具数据库无法打开?
    Lua01 基本语法+数据类型+流程控制+运算符
    Qlik Sense : Fetching data with Qlik Web Connectors
    【多线程】信号量 Semaphore
    vue课程75 axios是只专注于网络请求的库
  • 原文地址:https://blog.csdn.net/m0_51734025/article/details/125422989