这里简单对nova resize的代码调用简单记录,写得不好的地方还请见谅!
测试环境:OpenStack Liberty(rdo)
# compute-1计算节点下一台test-cirros-1的虚拟机
[root@controller1 ~(keystone_admin)]# nova show test-cirros-1
+--------------------------------------+------------------------------------------------------------+
| Property | Value |
+--------------------------------------+------------------------------------------------------------+
| NetworkA network | 192.168.0.26 |
| OS-DCF:diskConfig | MANUAL |
| OS-EXT-AZ:availability_zone | nova |
| OS-EXT-SRV-ATTR:host | compute-1 |
| OS-EXT-SRV-ATTR:hostname | test-cirros-1 |
| OS-EXT-SRV-ATTR:hypervisor_hostname | compute-1 |
| OS-EXT-SRV-ATTR:instance_name | instance-00000018 |
| OS-EXT-SRV-ATTR:kernel_id | |
| OS-EXT-SRV-ATTR:launch_index | 0 |
| OS-EXT-SRV-ATTR:ramdisk_id | |
| OS-EXT-SRV-ATTR:reservation_id | r-a30b0e7o |
| OS-EXT-SRV-ATTR:root_device_name | /dev/vda |
| OS-EXT-SRV-ATTR:user_data | - |
| OS-EXT-STS:power_state | 1 |
| OS-EXT-STS:task_state | - |
| OS-EXT-STS:vm_state | active |
| OS-SRV-USG:launched_at | 2016-04-17T13:21:54.000000 |
| OS-SRV-USG:terminated_at | - |
| accessIPv4 | |
| accessIPv6 | |
| config_drive | |
| created | 2016-04-17T12:36:38Z |
| flavor | m1.tiny (1) |
| hostId | f6f5e4c5991491de79426865084507060287154e125bc2657a21fd23 |
| id | 21015376-8c4f-4fad-a834-245b57f23dcc |
| p_w_picpath | cirros-0.3.4-x86_64 (1acb04d0-a910-45e4-aad3-5e6686fd1a91) |
| key_name | - |
| locked | False |
| metadata | {} |
| name | test-cirros-1 |
| os-extended-volumes:volumes_attached | [] |
| progress | 0 |
| security_groups | default |
| status | ACTIVE |
| tenant_id | 52b09594cf604d45ae3a12e44da8eac9 |
| updated | 2016-04-17T13:21:55Z |
| user_id | 082ef596bad842d180cc5f9326b1fd35 |
+--------------------------------------+------------------------------------------------------------+
/usr/lib/python2.7/site-packages/nova/compute/api.py(2621)resize() # 设置断点
@wrap_check_policy
@check_instance_lock
@check_instance_cell
@check_instance_state(vm_state=[vm_states.ACTIVE, vm_states.STOPPED])
def resize(self, context, instance, flavor_id=None, clean_shutdown=True,
**extra_instance_updates):
。。。
self._check_auto_disk_config(instance, **extra_instance_updates)
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:
new_instance_type = flavors.get_flavor_by_flavor_id(
flavor_id, read_deleted="no")
if (new_instance_type.get('root_gb') == 0 and
current_instance_type.get('root_gb') != 0 and
not self.is_volume_backed_instance(context, instance)):
reason = _('Resize to zero disk flavor is not allowed.')
raise exception.CannotResizeDisk(reason=reason)
if (new_instance_type.get('root_gb') < current_instance_type.get('root_gb')): # 这个是我加的,防止resize到disk小的flavor。
reason = _('Resize to smaller disk flavor is not allowed.')
raise exception.CannotResizeDisk(reason=reason)
。。。。。。
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': []}
if not CONF.allow_resize_to_same_host: # 是否enable allow_resize_to_same_host,还是走调度
filter_properties['ignore_hosts'].append(instance.host)
。。。
scheduler_hint = {'filter_properties': filter_properties}
self.compute_task_api.resize_instance(context, instance, # self.compute_task_api=conductor.ComputeTaskAPI()
extra_instance_updates, scheduler_hint=scheduler_hint,
flavor=new_instance_type,
reservations=quotas.reservations or [],
clean_shutdown=clean_shutdown)
虚拟机状态发生改变
[root@controller1 ~(keystone_admin)]# nova list
+--------------------------------------+---------------+--------+-------------+-------------+-----------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------------+--------+-------------+-------------+-----------------------+
| 21015376-8c4f-4fad-a834-245b57f23dcc | test-cirros-1 | RESIZE | resize_prep | Running | NetworkA=192.168.0.26 |
+--------------------------------------+---------------+--------+-------------+-------------+-----------------------+
进入conductor的resize_instance function
/usr/lib/python2.7/site-packages/nova/conductor/api.py
189 class ComputeTaskAPI(object):
190 """ComputeTask API that queues up compute tasks for nova-conductor."""
191
192 def __init__(self):
193 self.conductor_compute_rpcapi = rpcapi.ComputeTaskAPI()
194
195 def resize_instance(self, context, instance, extra_instance_updates,
196 scheduler_hint, flavor, reservations,
197 clean_shutdown=True):
198 # NOTE(comstud): 'extra_instance_updates' is not used here but is
199 # needed for compatibility with the cells_rpcapi version of this
200 # method.
201 self.conductor_compute_rpcapi.migrate_server( # rpc操作,走消息队列
202 context, instance, scheduler_hint, live=False, rebuild=False, # live是False,冷迁移
203 flavor=flavor, block_migration=None, disk_over_commit=None,
204 reservations=reservations, clean_shutdown=clean_shutdown)
/usr/lib/python2.7/site-packages/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):
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,
}
version = '1.13'
if not self.client.can_send_version(version):
del kw['request_spec']
version = '1.11'
if not self.client.can_send_version(version):
del kw['clean_shutdown']
version = '1.10'
if not self.client.can_send_version(version):
kw['flavor'] = objects_base.obj_to_primitive(flavor)
version = '1.6'
if not self.client.can_send_version(version):
kw['instance'] = jsonutils.to_primitive(
objects_base.obj_to_primitive(instance))
version = '1.4'
cctxt = self.client.prepare(version=version)
return cctxt.call(context, 'migrate_server', **kw)
# nova resize实际上是冷迁移的一种
/usr/lib/python2.7/site-packages/nova/conductor/manager.py(551)migrate_server()
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 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)
else:
raise NotImplementedError()
# 冷迁移function
def _cold_migrate(self, context, instance, flavor, filter_properties,
reservations, clean_shutdown):
p_w_picpath = utils.get_p_w_picpath_from_system_metadata(
instance.system_metadata)
request_spec = scheduler_utils.build_request_spec(
context, p_w_picpath, [instance], instance_type=flavor)
task = self._build_cold_migrate_task(context, instance, flavor,
filter_properties, request_spec,
reservations, clean_shutdown)
try:
task.execute()
def _build_cold_migrate_task(self, context, instance, flavor,
filter_properties, request_spec, reservations,
clean_shutdown):
return migrate.MigrationTask(context, instance, flavor,
filter_properties, request_spec,
reservations, clean_shutdown,
self.compute_rpcapi,
self.scheduler_client)
resize的prepare
/usr/lib/python2.7/site-packages/nova/conductor/tasks/migrate.py
class MigrationTask(base.TaskBase):
def __init__(self, context, instance, flavor, filter_properties,
request_spec, reservations, clean_shutdown, compute_rpcapi,
scheduler_client):
super(MigrationTask, self).__init__(context, instance)
self.clean_shutdown = clean_shutdown
self.request_spec = request_spec
self.reservations = reservations
self.filter_properties = filter_properties
self.flavor = flavor
self.quotas = None
self.compute_rpcapi = compute_rpcapi
self.scheduler_client = scheduler_client
def _execute(self):
p_w_picpath = self.request_spec.get('p_w_picpath')
self.quotas = objects.Quotas.from_reservations(self.context,
self.reservations,
instance=self.instance)
scheduler_utils.setup_instance_group(self.context, self.request_spec,
self.filter_properties)
scheduler_utils.populate_retry(self.filter_properties,
self.instance.uuid)
# TODO(sbauza): Hydrate here the object until we modify the
# scheduler.utils methods to directly use the RequestSpec object
spec_obj = objects.RequestSpec.from_primitives(
self.context, self.request_spec, self.filter_properties)
hosts = self.scheduler_client.select_destinations(
self.context, spec_obj)
host_state = hosts[0]
scheduler_utils.populate_filter_properties(self.filter_properties,
host_state)
# context is not serializable
self.filter_properties.pop('context', None)
(host, node) = (host_state['host'], host_state['nodename'])
self.compute_rpcapi.prep_resize(
self.context, p_w_picpath, self.instance, self.flavor, host,
self.reservations, request_spec=self.request_spec,
filter_properties=self.filter_properties, node=node,
clean_shutdown=self.clean_shutdown)
# 又是rpc
/usr/lib/python2.7/site-packages/nova/compute/rpcapi.py
def prep_resize(self, ctxt, p_w_picpath, instance, instance_type, host,
reservations=None, request_spec=None,
filter_properties=None, node=None,
clean_shutdown=True):
p_w_picpath_p = jsonutils.to_primitive(p_w_picpath)
msg_args = {'instance': instance,
'instance_type': instance_type,
'p_w_picpath': p_w_picpath_p,
'reservations': reservations,
'request_spec': request_spec,
'filter_properties': filter_properties,
'node': node,
'clean_shutdown': clean_shutdown}
version = '4.1'
if not self.client.can_send_version(version):
version = '4.0'
msg_args['instance_type'] = objects_base.obj_to_primitive(
instance_type)
cctxt = self.client.prepare(server=host, version=version)
cctxt.cast(ctxt, 'prep_resize', **msg_args)
# prep_resize function
/usr/lib/python2.7/site-packages/nova/compute/manager.py
def prep_resize(self, context, p_w_picpath, instance, instance_type,
reservations, request_spec, filter_properties, node,
clean_shutdown):
"""Initiates the process of moving a running instance to another host.
Possibly changes the RAM and disk size in the process.
"""
if node is None:
node = self.driver.get_available_nodes(refresh=True)[0]
LOG.debug("No node specified, defaulting to %s", node,
instance=instance)
# NOTE(melwitt): Remove this in version 5.0 of the RPC API
# Code downstream may expect extra_specs to be populated since it
# is receiving an object, so lookup the flavor to ensure this.
if not isinstance(instance_type, objects.Flavor):
instance_type = objects.Flavor.get_by_id(context,
instance_type['id'])
quotas = objects.Quotas.from_reservations(context,
reservations,
instance=instance)
with self._error_out_instance_on_exception(context, instance,
quotas=quotas):
compute_utils.notify_usage_exists(self.notifier, context, instance,
current_period=True)
self._notify_about_instance_usage(
context, instance, "resize.prep.start")
try:
self._prep_resize(context, p_w_picpath, instance,
instance_type, quotas,
request_spec, filter_properties,
node, clean_shutdown)
# _prep_resize function
def _prep_resize(self, context, p_w_picpath, instance, instance_type,
quotas, request_spec, filter_properties, node,
clean_shutdown=True):
if not filter_properties:
filter_properties = {}
if not instance.host:
self._set_instance_obj_error_state(context, instance)
msg = _('Instance has no source host')
raise exception.MigrationError(reason=msg)
same_host = instance.host == self.host
# if the flavor IDs match, it's migrate; otherwise resize
if same_host and instance_type.id == instance['instance_type_id']:
# check driver whether support migrate to same host
if not self.driver.capabilities['supports_migrate_to_same_host']:
raise exception.UnableToMigrateToSelf(
instance_id=instance.uuid, host=self.host)
# NOTE(danms): Stash the new instance_type to avoid having to
# look it up in the database later
instance.new_flavor = instance_type
# NOTE(mriedem): Stash the old vm_state so we can set the
# resized/reverted instance back to the same state later.
vm_state = instance.vm_state
LOG.debug('Stashing vm_state: %s', vm_state, instance=instance)
instance.system_metadata['old_vm_state'] = vm_state
instance.save()
limits = filter_properties.get('limits', {})
rt = self._get_resource_tracker(node)
with rt.resize_claim(context, instance, instance_type,
p_w_picpath_meta=p_w_picpath, limits=limits) as claim:
LOG.info(_LI('Migrating'), context=context, instance=instance)
self.compute_rpcapi.resize_instance( #
context, instance, claim.migration, p_w_picpath,
instance_type, quotas.reservations,
clean_shutdown)
又是rpc
/usr/lib/python2.7/site-packages/nova/compute/rpcapi.py
def resize_instance(self, ctxt, instance, migration, p_w_picpath, instance_type,
reservations=None, clean_shutdown=True):
msg_args = {'instance': instance, 'migration': migration,
'p_w_picpath': p_w_picpath, 'reservations': reservations,
'instance_type': instance_type,
'clean_shutdown': clean_shutdown,
}
version = '4.1'
if not self.client.can_send_version(version):
msg_args['instance_type'] = objects_base.obj_to_primitive(
instance_type)
version = '4.0'
cctxt = self.client.prepare(server=_compute_host(None, instance),
version=version)
cctxt.cast(ctxt, 'resize_instance', **msg_args)
/usr/lib/python2.7/site-packages/nova/compute/manager.py(3765)resize_instance()
def resize_instance(self, context, instance, p_w_picpath,
reservations, migration, instance_type,
clean_shutdown):
"""Starts the migration of a running instance to another host."""
quotas = objects.Quotas.from_reservations(context,
reservations,
instance=instance)
with self._error_out_instance_on_exception(context, instance,
quotas=quotas):
# TODO(chaochin) Remove this until v5 RPC API
# Code downstream may expect extra_specs to be populated since it
# is receiving an object, so lookup the flavor to ensure this.
if (not instance_type or
not isinstance(instance_type, objects.Flavor)):
instance_type = objects.Flavor.get_by_id(
context, migration['new_instance_type_id'])
network_info = self.network_api.get_instance_nw_info(context,
instance)
migration.status = 'migrating'
with migration.obj_as_admin():
migration.save()
instance.task_state = task_states.RESIZE_MIGRATING
instance.save(expected_task_state=task_states.RESIZE_PREP)
self._notify_about_instance_usage(
context, instance, "resize.start", network_info=network_info)
bdms = objects.BlockDeviceMappingList.get_by_instance_uuid(
context, instance.uuid)
block_device_info = self._get_instance_block_device_info(
context, instance, bdms=bdms)
timeout, retry_interval = self._get_power_off_values(context,
instance, clean_shutdown)
disk_info = self.driver.migrate_disk_and_power_off( # virt/driver.py
context, instance, migration.dest_host,
instance_type, network_info,
block_device_info,
timeout, retry_interval)
self._terminate_volume_connections(context, instance, bdms) # 断开volume的连接
migration_p = obj_base.obj_to_primitive(migration)
self.network_api.migrate_instance_start(context,
instance,
migration_p)
migration.status = 'post-migrating'
with migration.obj_as_admin():
migration.save()
instance.host = migration.dest_compute
instance.node = migration.dest_node
instance.task_state = task_states.RESIZE_MIGRATED
instance.save(expected_task_state=task_states.RESIZE_MIGRATING)
self.compute_rpcapi.finish_resize(context, instance, # rpc操作
migration, p_w_picpath, disk_info,
migration.dest_compute, reservations=quotas.reservations)
self._notify_about_instance_usage(context, instance, "resize.end",
network_info=network_info)
self.instance_events.clear_events_for_instance(instance)
虚拟机状态也发生了改变
[root@controller1 ~(keystone_admin)]# nova list
+--------------------------------------+---------------+--------+------------------+-------------+-----------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------------+--------+------------------+-------------+-----------------------+
| 21015376-8c4f-4fad-a834-245b57f23dcc | test-cirros-1 | RESIZE | resize_migrating | Running | NetworkA=192.168.0.26 |
+--------------------------------------+---------------+--------+------------------+-------------+-----------------------+
[root@controller1 ~(keystone_admin)]# nova list
+--------------------------------------+---------------+--------+-----------------+-------------+-----------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------------+--------+-----------------+-------------+-----------------------+
| 21015376-8c4f-4fad-a834-245b57f23dcc | test-cirros-1 | RESIZE | resize_migrated | Running | NetworkA=192.168.0.26 |
+--------------------------------------+---------------+--------+-----------------+-------------+-----------------------+
instance.save(expected_task_state=task_states.RESIZE_MIGRATING) # save写库操作
rpc操作
/usr/lib/python2.7/site-packages/nova/compute/rpcapi.py
def finish_resize(self, ctxt, instance, migration, p_w_picpath, disk_info,
host, reservations=None):
version = '4.0'
cctxt = self.client.prepare(server=host, version=version)
cctxt.cast(ctxt, 'finish_resize',
instance=instance, migration=migration,
p_w_picpath=p_w_picpath, disk_info=disk_info, reservations=reservations)
# resize的finish过程
/usr/lib/python2.7/site-packages/nova/compute/manager.py
def finish_resize(self, context, disk_info, p_w_picpath, instance,
reservations, migration):
"""Completes the migration process.
Sets up the newly transferred disk and turns on the instance at its
new host machine.
"""
quotas = objects.Quotas.from_reservations(context,
reservations,
instance=instance)
try:
p_w_picpath_meta = objects.ImageMeta.from_dict(p_w_picpath)
self._finish_resize(context, instance, migration,
disk_info, p_w_picpath_meta)
quotas.commit()
except Exception:
LOG.exception(_LE('Setting instance vm_state to ERROR'),
instance=instance)
with excutils.save_and_reraise_exception():
try:
quotas.rollback()
except Exception:
LOG.exception(_LE("Failed to rollback quota for failed "
"finish_resize"),
instance=instance)
self._set_instance_obj_error_state(context, instance)
# _finish_resize function
def _finish_resize(self, context, instance, migration, disk_info,
p_w_picpath_meta):
resize_instance = False
old_instance_type_id = migration['old_instance_type_id']
new_instance_type_id = migration['new_instance_type_id']
old_instance_type = instance.get_flavor()
# NOTE(mriedem): Get the old_vm_state so we know if we should
# power on the instance. If old_vm_state is not set we need to default
# to ACTIVE for backwards compatibility
old_vm_state = instance.system_metadata.get('old_vm_state',
vm_states.ACTIVE)
instance.old_flavor = old_instance_type
if old_instance_type_id != new_instance_type_id:
instance_type = instance.get_flavor('new')
self._set_instance_info(instance, instance_type)
for key in ('root_gb', 'swap', 'ephemeral_gb'):
if old_instance_type[key] != instance_type[key]:
resize_instance = True
break
instance.apply_migration_context()
# NOTE(tr3buchet): setup networks on destination host
self.network_api.setup_networks_on_host(context, instance,
migration['dest_compute'])
migration_p = obj_base.obj_to_primitive(migration)
self.network_api.migrate_instance_finish(context,
instance,
migration_p)
network_info = self.network_api.get_instance_nw_info(context, instance)
instance.task_state = task_states.RESIZE_FINISH
instance.save(expected_task_state=task_states.RESIZE_MIGRATED)
self._notify_about_instance_usage(
context, instance, "finish_resize.start",
network_info=network_info)
block_device_info = self._get_instance_block_device_info(
context, instance, refresh_conn_info=True)
# NOTE(mriedem): If the original vm_state was STOPPED, we don't
# automatically power on the instance after it's migrated
power_on = old_vm_state != vm_states.STOPPED
try:
self.driver.finish_migration(context, migration, instance,
disk_info,
network_info,
p_w_picpath_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)
migration.status = 'finished'
with migration.obj_as_admin():
migration.save()
instance.vm_state = vm_states.RESIZED
instance.task_state = None
instance.launched_at = timeutils.utcnow()
instance.save(expected_task_state=task_states.RESIZE_FINISH)
self._update_scheduler_instance_info(context, instance)
self._notify_about_instance_usage(
context, instance, "finish_resize.end",
network_info=network_info)
虚拟机状态又发生改变
[root@controller1 ~(keystone_admin)]# nova list
+--------------------------------------+---------------+--------+---------------+-------------+-----------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------------+--------+---------------+-------------+-----------------------+
| 21015376-8c4f-4fad-a834-245b57f23dcc | test-cirros-1 | RESIZE | resize_finish | Running | NetworkA=192.168.0.26 |
+--------------------------------------+---------------+--------+---------------+-------------+-----------------------+
[root@controller1 ~(keystone_admin)]# nova list
+--------------------------------------+---------------+---------------+------------+-------------+-----------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------------+---------------+------------+-------------+-----------------------+
| 21015376-8c4f-4fad-a834-245b57f23dcc | test-cirros-1 | VERIFY_RESIZE | - | Running | NetworkA=192.168.0.26 |
+--------------------------------------+---------------+---------------+------------+-------------+-----------------------+
revert_resize、confirm_resize调用后续补充。。。
转载于:https://blog.51cto.com/iceyao/1764873