咱們接着上週的流程繼續講解,接着看_prep_resize()方法,其代碼和註釋以下:node
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.set_flavor(instance_type, 'new') # 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)
接着調用/nova/compute/rpcapi.py中的resize_instance()方法,其代碼以下:數據庫
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)
接着調用/nova/compute/manage.py中的resize_instance()方法,該節點爲源計算節點,其代碼以下:json
@wrap_exception() @reverts_task_state @wrap_instance_event @errors_out_migration @wrap_instance_fault 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) #設置遷移相關狀態,並保存在數據庫表migrate中 #(包括源主機,目的主機,遷移狀態,此處爲migrating狀態) migration.status = 'migrating' with migration.obj_as_admin(): migration.save() #設置虛擬機狀態爲RESIZE_PREP 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( context, instance, migration.dest_host, instance_type, network_info, block_device_info, timeout, retry_interval) self._terminate_volume_connections(context, instance, bdms) migration_p = obj_base.obj_to_primitive(migration) #開始網絡遷移 self.network_api.migrate_instance_start(context, instance, migration_p) #migration數據庫表狀態設置爲post-migrating、 migration.status = 'post-migrating' with migration.obj_as_admin(): migration.save() instance.host = migration.dest_compute instance.node = migration.dest_node #instance數據庫表instance.task_state爲RESIZE_MIGRATED instance.task_state = task_states.RESIZE_MIGRATED instance.save(expected_task_state=task_states.RESIZE_MIGRATING) self.compute_rpcapi.finish_resize(context, instance, 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)
這裏直接看/nova/compute/manage.py中的finish_resize()方法,該節點爲目的計算節點,其代碼以下:api
@wrap_exception() @reverts_task_state @wrap_instance_event @errors_out_migration @wrap_instance_fault 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: self._finish_resize(context, instance, migration, disk_info, image) 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()方法,其代碼以下:網絡
def _finish_resize(self, context, instance, migration, disk_info, image): 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.set_flavor(old_instance_type, 'old') #修改虛擬機類型(內存和磁盤等信息) 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) #數據庫實例狀態更新爲RESIZE_FINISH 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, 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)
其中driver.finish_migration()方法,因爲compute_driver=libvirt.LibvirtDriver,這個參數也是在nova.conf配置文件獲得的。因此這裏會去調用nova/virt/libvirt/driver.py.finish_migration()方法,其代碼和註釋以下:app
def finish_migration(self, context, migration, instance, disk_info, network_info, image_meta, resize_instance, block_device_info=None, power_on=True): LOG.debug("Starting finish_migration", instance=instance) image_meta = objects.ImageMeta.from_dict(image_meta) # resize disks. only "disk" and "disk.local" are necessary. #遷移磁盤信息(僅僅對於root_gb與ephemeral_gb) disk_info = jsonutils.loads(disk_info) for info in disk_info: #獲取該disk類型下的磁盤size size = self._disk_size_from_instance(instance, info) if resize_instance: image = imgmodel.LocalFileImage(info['path'], info['type']) self._disk_resize(image, size) #若是磁盤格式爲raw,且使用了use_cow_images,則先轉化爲qcow2 if info['type'] == 'raw' and CONF.use_cow_images: self._disk_raw_to_qcow2(info['path']) #獲取磁盤信息(例如virio、disk.local、disk.swap) disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type, instance, image_meta, block_device_info) # assume _create_image do nothing if a target file exists. self._create_image(context, instance, disk_info['mapping'], network_info=network_info, block_device_info=None, inject_files=False, fallback_from_host=migration.source_compute) #生成xml文件 xml = self._get_guest_xml(context, instance, network_info, disk_info, image_meta, block_device_info=block_device_info, write_to_disk=True) # NOTE(mriedem): vifs_already_plugged=True here, regardless of whether # or not we've migrated to another host, because we unplug VIFs locally # and the status change in the port might go undetected by the neutron # L2 agent (or neutron server) so neutron may not know that the VIF was # unplugged in the first place and never send an event. #根據配置和xml文件建立虛擬機 self._create_domain_and_network(context, xml, instance, network_info, disk_info, block_device_info=block_device_info, power_on=power_on, vifs_already_plugged=True) #若是遷移前爲開機狀態,則開機(至此遷移所有完成) if power_on: timer = loopingcall.FixedIntervalLoopingCall( self._wait_for_running, instance) timer.start(interval=0.5).wait() LOG.debug("finish_migration finished successfully.", instance=instance)