这里简单对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 |
| image | 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): 。。。 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): image = utils.get_image_from_system_metadata( instance.system_metadata) request_spec = scheduler_utils.build_request_spec( context, image, [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): image = self.request_spec.get('image') 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, image, 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, image, instance, instance_type, host, reservations=None, request_spec=None, filter_properties=None, node=None, clean_shutdown=True): image_p = jsonutils.to_primitive(image) msg_args = {'instance': instance, 'instance_type': instance_type, 'image': image_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, image, 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, image, instance, instance_type, quotas, request_spec, filter_properties, node, clean_shutdown) # _prep_resize function def _prep_resize(self, context, image, 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, 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)
又是rpc
/usr/lib/python2.7/site-packages/nova/compute/rpcapi.py def resize_instance(self, ctxt, instance, migration, image, instance_type, reservations=None, clean_shutdown=True): msg_args = {'instance': instance, 'migration': migration, 'image': image, '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, image, 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, image, 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, image, 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, image=image, 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, image, 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: image_meta = objects.ImageMeta.from_dict(image) self._finish_resize(context, instance, migration, disk_info, image_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, image_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, 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) 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调用后续补充。。。