resize的入口代码在nova/api/openstack/compute/servers.py,方法是_resize(),如下
def _resize(self, req, instance_id, flavor_id, **kwargs):
"""Begin the resize process with given instance/flavor."""
context = req.environ["nova.context"]
instance = self._get_server(context, req, instance_id)
context.can(server_policies.SERVERS % 'resize',
target={'user_id': instance.user_id,
'project_id': instance.project_id})
try:
self.compute_api.resize(context, instance, flavor_id, **kwargs)
except exception.InstanceUnknownCell as e:
raise exc.HTTPNotFound(explanation=e.format_message())
这里很明显看到是调用nova/compute/api.py 中的resize函数
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.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)
这里的compute_task_api 是定义在conductor中的ComputeTaskAPI 类。因此这里的resize_instance是定义在ComputeTaskAPI中
class ComputeTaskAPI(object):
"""ComputeTask API that queues up compute tasks for nova-conductor."""
def __init__(self):
self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()
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)
这里通过conductor_compute_rpcapi调用到conductor/rpcapi.py.按照之前的分析,rpcapi中的函数都会在对应的manager.py 中处理
@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 live and not rebuild and not flavor:
self._live_migrate(context, instance, scheduler_hint,
block_migration, disk_over_commit, request_spec)
elif not live and not rebuild and flavor:
instance_uuid = instance.uuid
with compute_utils.EventReporter(context, 'cold_migrate',
instance_uuid):
self._cold_migrate(context, instance, flavor,
scheduler_hint['filter_properties'],
reservations, clean_shutdown, request_spec)
else:
raise NotImplementedError()
这里看到resize其实调用的是migrate来完成的,其中migrate又可以分为_live_migrate和_cold_migrate。由于_live_migrate可以失败,这里以_cold_migrate为例
同样在manager.py 中处理
def _cold_migrate(self, context, instance, flavor, filter_properties,
reservations, clean_shutdown, request_spec):
image = utils.get_image_from_system_metadata(
instance.system_metadata)
# NOTE(sbauza): If a reschedule occurs when prep_resize(), then
# it only provides filter_properties legacy dict back to the
# conductor with no RequestSpec part of the payload.
if not request_spec:
# Make sure we hydrate a new RequestSpec object with the new flavor
# and not the nested one from the instance
request_spec = objects.RequestSpec.from_components(
context, instance.uuid, image,
flavor, instance.numa_topology, instance.pci_requests,
filter_properties, None, instance.availability_zone)
else:
# NOTE(sbauza): Resizes means new flavor, so we need to update the
# original RequestSpec object for make sure the scheduler verifies
# the right one and not the original flavor
request_spec.flavor = flavor
//得到一个task,然后通过task.execute()来执行.
task = self._build_cold_migrate_task(context, instance, flavor,
request_spec,
reservations, clean_shutdown)
# TODO(sbauza): Provide directly the RequestSpec object once
# _set_vm_state_and_notify() accepts it
legacy_spec = request_spec.to_legacy_request_spec_dict()
try:
task.execute()
except exception.NoValidHost as ex:
def _build_cold_migrate_task(self, context, instance, flavor,
request_spec, reservations,
clean_shutdown):
return migrate.MigrationTask(context, instance, flavor,
request_spec,
reservations, clean_shutdown,
self.compute_rpcapi,
self.scheduler_client)
这个要执行的task其实就是migrate.MigrationTask,而这里的from nova.conductor.tasks import migrate。
所以到nova/conductor/tasks/migrate.py中看看。前面的task.execute()其实就是执行下面这个函数
def _execute(self):
image = self.request_spec.image
self.quotas = objects.Quotas.from_reservations(self.context,
self.reservations,
instance=self.instance)
# TODO(sbauza): Remove that once prep_resize() accepts a RequestSpec
# object in the signature and all the scheduler.utils methods too
legacy_spec = self.request_spec.to_legacy_request_spec_dict()
legacy_props = self.request_spec.to_legacy_filter_properties_dict()
scheduler_utils.setup_instance_group(self.context, legacy_spec,
legacy_props)
scheduler_utils.populate_retry(legacy_props,
self.instance.uuid)
# TODO(sbauza): Remove that RequestSpec rehydratation once
# scheduler.utils methods use directly the NovaObject.
self.request_spec = objects.RequestSpec.from_components(
self.context, self.instance.uuid, image,
self.flavor, self.instance.numa_topology,
self.instance.pci_requests, legacy_props, None,
self.instance.availability_zone)
# NOTE(sbauza): Force_hosts/nodes needs to be reset
# if we want to make sure that the next destination
# is not forced to be the original host
self.request_spec.reset_forced_destinations()
//由于flavor可能会变,所以这里调用scheduler重新选择host
hosts = self.scheduler_client.select_destinations(
self.context, self.request_spec)
host_state = hosts[0]
scheduler_utils.populate_filter_properties(legacy_props,
host_state)
# context is not serializable
legacy_props.pop('context', None)
(host, node) = (host_state['host'], host_state['nodename'])
# FIXME(sbauza): Serialize/Unserialize the legacy dict because of
# oslo.messaging #1529084 to transform datetime values into strings.
# tl;dr: datetimes in dicts are not accepted as correct values by the
# rpc fake driver.
legacy_spec = jsonutils.loads(jsonutils.dumps(legacy_spec))
self.compute_rpcapi.prep_resize(
self.context, self.instance, legacy_spec['image'],
self.flavor, host, self.reservations,
request_spec=legacy_spec, filter_properties=legacy_props,
node=node, clean_shutdown=self.clean_shutdown)
这里有调回compute中。我们这里就直接看computer/manager.py 中的
def _prep_resize(self, context, image, instance, instance_type,
quotas, request_spec, filter_properties, node,
clean_shutdown=True):
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)
这里先调用compute/api.py 中的resize_instance。然后调用manager.py 中的resize_instance
def resize_instance(self, context, instance, image,
reservations, migration, instance_type,
clean_shutdown):
"""Starts the migration of a running instance to another host."""
self.compute_rpcapi.finish_resize(context, instance,
migration, image, disk_info,
migration.dest_compute, reservations=quotas.reservations)
继续调用finish_resize
def _finish_resize(self, context, instance, migration, disk_info,
image_meta):
try:
self.driver.finish_migration(context, migration, instance,
disk_info,
network_info,
image_meta, 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)
可见最终还是调用driver的finish_migration。我们用的是libvirt的话,就是调用libvirt的finish_migration。
nova resize
最新推荐文章于 2024-09-13 09:04:31 发布