1. nova-api服务入口
python
#nova/compute/api.py
class API(base.Base):
#检测vm是否有锁,或者在进行其他任务,存在锁则不能resize
@check_instance_lock
#检测vm是否是ACTIVE或者STOPPED状态,该接口不支持其他状态虚拟机
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
#检测vm所在宿主机是否UP,没有up则不能进行resize
@check_instance_host(check_is_up=True)
def resize(self, context, instance, flavor_id=None, clean_shutdown=True,
host_name=None, **extra_instance_updates):
#在resize 虚拟机配置时host_name一直为空,只有在冷迁移虚拟机时可以被指定
if host_name is not None:
#冷迁移时,判断host_naame是否是虚拟机当前所在宿主机,如果是抛异常CannotMigrateToSameHost,如果不是则判断host_name对应的主机是否存在
if host_name == instance.host:
raise exception.CannotMigrateToSameHost()
# Check whether host exists or not.
node = objects.ComputeNode.get_first_node_by_host_for_old_compat(
context, host_name, use_slave=True)
#在resize虚拟机时,可以选择对磁盘自动或者手动进行分区,
# 此时就需要与原来的虚拟机的这个配置进行对比,验证resize时这个分区参数的有效性
# 如果虚拟机原来是关闭自动分区的,此时resize时再开启自动分区就会抛异常 exception.AutoDiskConfigDisabledByImage,结束resize操作
self._check_auto_disk_config(instance, **extra_instance_updates)
current_instance_type = instance.get_flavor()
volume_backed = None
# 如果调用resize接口时没有提供flavor_id,那么就是迁移虚拟机
if not flavor_id:
LOG.debug("flavor_id is None. Assuming migration.",
instance=instance)
new_instance_type = current_instance_type
else:
new_instance_type = flavors.get_flavor_by_flavor_id(
flavor_id, read_deleted="no")
#如果resize时选择的flavor根磁盘为0,并且vm的原来配置的flavor的根磁盘不为0
# 如果vm的根磁盘没有使用卷存储volume_backed,抛异常CannotResizeDisk
if (new_instance_type.get('root_gb') == 0 and
current_instance_type.get('root_gb') != 0):
volume_backed = compute_utils.is_volume_backed_instance(
context, instance)
if not volume_backed:
reason = _('Resize to zero disk flavor is not allowed.')
raise exception.CannotResizeDisk(reason=reason)
current_instance_type_name = current_instance_type['name']
new_instance_type_name = new_instance_type['name']
LOG.debug("Old instance type %(current_instance_type_name)s, "
"new instance type %(new_instance_type_name)s",
{'current_instance_type_name': current_instance_type_name,
'new_instance_type_name': new_instance_type_name},
instance=instance)
#根据flavor的id判断是不是同一个flavor,如果不是且新的flavor disabled就抛异常FlavorNotFound,如果是也抛异常CannotResizeToSameFlavor
same_instance_type = (current_instance_type['id'] ==
new_instance_type['id'])
if not same_instance_type and new_instance_type.get('disabled'):
raise exception.FlavorNotFound(flavor_id=flavor_id)
if same_instance_type and flavor_id:
raise exception.CannotResizeToSameFlavor()
# 如果flavor_id存在,则是进行resize操作,此时需要判断用户是否有足够的配额进行resize操作
# 这里主要判断cpu和内存配额,如果没有足够配额抛出异常TooManyInstances
if flavor_id:
self._check_quota_for_upsize(context, instance,
current_instance_type,
new_instance_type)
if not same_instance_type:
image = utils.get_image_from_system_metadata(
instance.system_metadata)
if volume_backed is None:
volume_backed = compute_utils.is_volume_backed_instance(
context, instance)
# 如果用了后端存储,只需要验证新flavor和image的numa和pci的有效性,看看是否存在冲突
if volume_backed:
# 主要验证内存加密(hw:mem_encryption)、PMU特性(hw:pmu)、serial_port_count(hw:serial_port_count)、 cpu实时性(hw:cpu_realtime)
self._validate_flavor_image_numa_pci(
image, new_instance_type, validate_pci=True)
else:
# 验证disk相关配置,然后再进行上面内存加密等的验证
self._validate_flavor_image_nostatus(
context, image, new_instance_type, root_bdm=None,
validate_pci=True)
filter_properties = {'ignore_hosts': []}
#如果不允许resize虚拟机到当前宿主机,则将当前宿主机存入不被调入的主机列表中
if not CONF.allow_resize_to_same_host:
filter_properties['ignore_hosts'].append(instance.host)
request_spec = objects.RequestSpec.get_by_instance_uuid(
context, instance.uuid)
request_spec.ignore_hosts = filter_properties['ignore_hosts']
if not same_instance_type:
request_spec.numa_topology = hardware.numa_get_constraints(
new_instance_type, instance.image_meta)
#修改虚拟机状态
instance.task_state = task_states.RESIZE_PREP
instance.progress = 0
instance.update(extra_instance_updates)
instance.save(expected_task_state=[None])
#如果没有提供flavor_id,则更新虚拟机状态为MIGRATE,否则为RESIZE
if not flavor_id:
self._record_action_start(context, instance,
instance_actions.MIGRATE)
else:
self._record_action_start(context, instance,
instance_actions.RESIZE)
scheduler_hint = {'filter_properties': filter_properties}
if host_name is None:
request_spec.requested_destination = None
else:
request_spec.requested_destination = objects.Destination(
host=node.host, node=node.hypervisor_hostname)
self.compute_task_api.resize_instance(context, instance,
scheduler_hint=scheduler_hint,
flavor=new_instance_type,
clean_shutdown=clean_shutdown,
request_spec=request_spec)
# nova/conductor/api.py
class ComputeTaskAPI(object):
"""ComputeTask API that queues up compute tasks for nova-conductor."""
def __init__(self):
self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()
#冷迁移以及resize都走该入口
def resize_instance(self, context, instance, scheduler_hint, flavor,
reservations=None, clean_shutdown=True,
request_spec=None, host_list=None):
#注意这里传的参数:live=False, rebuild=False,flavor=flavor
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, host_list=host_list)
#nova/conductor/rpcapi.py
class ComputeTaskAPI(object):
#发送rpc到conductor请求进行resize虚拟机操作
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit,
reservations=None, clean_shutdown=True, request_spec=None,
host_list=None):
kw = {'instance': instance, 'scheduler_hint': scheduler_hint,
'live': live, 'rebuild': rebuild, 'flavor': flavor,
'block_migration': block_migration,
'disk_over_commit': disk_over_commit,
'reservations': reservations,
'clean_shutdown': clean_shutdown,
'request_spec': request_spec,
'host_list': host_list,
}
cctxt = self.client.prepare(version=version)
return cctxt.call(context, 'migrate_server', **kw)
2. nova-conductor服务入口
python
#nova/conductor/manager.py
class ComputeTaskManager(base.Base):
#支持热迁移、冷迁移和resize
def migrate_server(self, context, instance, scheduler_hint, live, rebuild,
flavor, block_migration, disk_over_commit, reservations=None,
clean_shutdown=True, request_spec=None, host_list=None):
#没有提供flavor时,且live and not rebuild时,进行热迁移
if live and not rebuild and not flavor:
self._live_migrate(context, instance, scheduler_hint,
block_migration, disk_over_commit, request_spec)
#提供flavor时,且not live and not rebuild时,进行冷迁移
#在resize接口调用该接口时,传如下参数:live=False, rebuild=False,flavor=flavor
elif not live and not rebuild and flavor:
instance_uuid = instance.uuid
with compute_utils.EventReporter(context, 'cold_migrate',
self.host, instance_uuid):
self._cold_migrate(context, instance, flavor,
scheduler_hint['filter_properties'],
clean_shutdown, request_spec,
host_list)
else:
raise NotImplementedError()
def _cold_migrate(self, context, instance, flavor, filter_properties,
clean_shutdown, request_spec, host_list):
request_spec = self._get_request_spec_for_cold_migrate(
context, instance, flavor, filter_properties, request_spec)
task = self._build_cold_migrate_task(context, instance, flavor,
request_spec, clean_shutdown, host_list)
try:
#创建迁移任务执行迁移操作
task.execute()
except exception.NoValidHost as ex:
vm_state = instance.vm_state
3. task.execute()对应入口
python
#nova/conductor/tasks/migrate.py
class MigrationTask(base.TaskBase):
def _execute(self):
#根据请求的参数生成过滤条件,
#比如:legacy_props={"ignore_hosts":xxx, "force_hosts":, "retry":xxx,"scheduler_hints":xx}
legacy_props = self.request_spec.to_legacy_filter_properties_dict()
#设置服务组信息
scheduler_utils.setup_instance_group(self.context, self.request_spec)
#如果没有设置目的主机,则设置retry配置
if not ('requested_destination' in self.request_spec and
self.request_spec.requested_destination and
'host' in self.request_spec.requested_destination):
scheduler_utils.populate_retry(legacy_props,
self.instance.uuid)
#清空请求体request_spec中的fore_hosts和fore_nodes信息
self.request_spec.reset_forced_destinations()
#获取虚拟机网络信息
port_res_req = self.network_api.get_requested_resource_for_instance(
self.context, self.instance.uuid)
self.request_spec.requested_resources = port_res_req
#根据虚拟机cell信息设置requested_destination
self._restrict_request_spec_to_cell(legacy_props)
#创建migration记录
migration = self._preallocate_migration()
#确保project、user和network
self.request_spec.ensure_project_and_user_id(self.instance)
self.request_spec.ensure_network_metadata(self.instance)
#保存"root disk是否是卷"bool值到请求体中
compute_utils.heal_reqspec_is_bfv(
self.context, self.request_spec, self.instance)
#调用nova schedule进行主机调度
if self.host_list is None:
selection = self._schedule()
else:
# This is a reschedule that will use the supplied alternate hosts
# in the host_list as destinations.
selection = self._reschedule()
#把选定的主机加入到retry host列表中,如果重新调度就会忽略当前的主机
scheduler_utils.populate_filter_properties(legacy_props, selection)
legacy_props.pop('context', None)
(host, node) = (selection.service_host, selection.nodename)
self.instance.availability_zone = (
availability_zones.get_host_availability_zone(
self.context, host))
#发送rpc请求到目的节点进行resize操作
self.compute_rpcapi.prep_resize(
self.context, self.instance, self.request_spec.image,
self.flavor, host, migration,
request_spec=self.request_spec, filter_properties=legacy_props,
node=node, clean_shutdown=self.clean_shutdown,
host_list=self.host_list)
后面再就nova-schedule调度以及nova-compute resize分别进行分析。