冷迁移 cold migration:
不保存虚拟机在线状态的,将主机迁移到其他宿主机, 迁移过程中会有关机的操作
备注: 为了方便梳理流程, 展示关键的流程步骤,过程中展示的源码部分会被删减
冷迁移
一. 入口API:
nova/api/openstack/compute/migrate_server.py
class MigrateServerController(wsgi.Controller):
def __init__(self, *args, **kwargs):
....
@wsgi.action('migrate')
def _migrate(self, req, id, body):
"""Permit admins to migrate a server to a new host."""
context = req.environ['nova.context']
context.can(ms_policies.POLICY_ROOT % 'migrate')
instance = common.get_instance(self.compute_api, context, id)
try:
self.compute_api.resize(req.environ['nova.context'], instance)
except (exception.TooManyInstances, exception.QuotaError) as e:
raise exc.HTTPForbidden(explanation=e.format_message())
instance = common.get_instance(self.compute_api, context, id) 依据instance id 获取instance id 并对instance的存在性做了校验
实际调用函数self.compute_api.resize(req.environ['nova.context'], instance)
二. resize
compute_api.resize定义在 nova/compute/api.py
定义在API类中
@profiler.trace_cls("compute_api")
class API(base.Base):
"""API for interacting with the compute manager."""
@check_instance_lock // 校验虚拟机的状态, 一个虚拟机上同时不进行多个任务
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
// 校验虚拟机状态,active 和stopped状态允许迁移
def resize(self, context, instance, flavor_id=None, clean_shutdown=True,
**extra_instance_updates):
"""Resize (ie, migrate) a running instance.
If flavor_id is None, the process is considered a migration, keeping
the original flavor_id. If flavor_id is not None, the instance should
be migrated to a new host and resized to the new flavor_id.
"""
// 校验磁盘信息
self._check_auto_disk_config(instance, **extra_instance_updates)
// 获取当前flavor 并校验flavor
current_instance_type = instance.get_flavor()
# If flavor_id is not provided, only migrate the instance.
if not flavor_id:
LOG.debug("flavor_id is None. Assuming migration.",
instance=instance)
new_instance_type = current_instance_type
else:
....
if not new_instance_type:
raise exception.FlavorNotFound(flavor_id=flavor_id)
same_instance_type = (current_instance_type['id'] == new_instance_type['id'])
//获取配额信息
if flavor_id:
....
else:
quotas = objects.Quotas(context=context)
// 更新虚拟机状态为RESIZE_PREP, 虚拟机设置为该状态开始,将会锁定,不允许进行其他任务
instance.task_state = task_states.RESIZE_PREP
instance.progress = 0
instance.update(extra_instance_updates)
instance.save(expected_task_state=[None])
// 依据参数 创建调度过滤参数
filter_properties = {'ignore_hosts': []}
// 是否允许迁移到同一台主机, 默认为False
if not CONF.allow_resize_to_same_host:
filter_properties['ignore_hosts'].append(instance.host)
// 发送迁移状态
if not flavor_id:
self._record_action_start(context, instance,
instance_actions.MIGRATE)
else:
.....
// 获取request_spec,该参数为新的调度参数,将ignore host加入调度
try:
request_spec = objects.RequestSpec.get_by_instance_uuid(
context, instance.uuid)
request_spec.ignore_hosts = filter_properties['ignore_hosts']
except exception.RequestSpecNotFound:
# Some old instances can still have no RequestSpec object attached
# to them, we need to support the old way
request_spec = None
scheduler_hint = {'filter_properties': filter_properties}
//resize_instance 任务迁移
self.compute_task_api.resize_instance(context, instance,
extra_instance_updates, scheduler_hint=scheduler_hint,
flavor=new_instance_type,
reservations=quotas.reservations or [],
clean_shutdown=clean_shutdown,
request_spec=request_spec)
该步骤主要是进行参数校验,并获取request_spec 调度参数。 调用resize_instance
resize_instance 定义
def resize_instance(self, context, instance, extra_instance_updates,
scheduler_hint, flavor, reservations,
clean_shutdown=True, request_spec=None):
# NOTE(comstud): 'extra_instance_updates' is not used here but is
# needed for compatibility with the cells_rpcapi version of this
# method.
self.conductor_compute_rpcapi.migrate_server(
context, instance, scheduler_hint, live=False, rebuild=False,
flavor=flavor, block_migration=None, disk_over_commit=None,
reservations=reservations, clean_shutdown=clean_shutdown,
request_spec=request_spec)
三. migrate_server
调用的conductor 层的 migrate_server, 通过rpc远程调用方式进行
定义在nova/conductor/rpcapi.py
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit,
reservations=None, clean_shutdown=True, request_spec=None):
....
cctxt = self.client.prepare(version=version)
return cctxt.call(context, 'migrate_server', **kw)
四. conductor migrate_server
实际调用的conductor的migrate_server
该过程实际通过Manager进行处理
nova/conductor/manager.py
定义:
@profiler.trace_cls("rpc")
class ComputeTaskManager(base.Base):
"""Namespace for compute methods.
@wrap_instance_event(prefix='conductor')
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit, reservations=None,
clean_shutdown=True, request_spec=None):
if instance and not isinstance(instance, nova_object.NovaObject):
# 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 = objects.Instance._from_db_object(
context, objects.Instance(), instance,
expected_attrs=attrs)
# NOTE: Remove this when we drop support for v1 of the RPC API
if flavor and not isinstance(flavor, objects.Flavor):
# Code downstream may expect extra_specs to be populated since it
# is receiving an object, so lookup the flavor to ensure this.
flavor = objects.Flavor.get_by_id(context, flavor['id'])
if live and not rebuild and not