nova resize虚拟机接口分析1

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分别进行分析。

相关推荐
mqiqe3 天前
云计算Openstack Neutron
云计算·openstack·perl
mqiqe3 天前
云计算Openstack Keystone
数据库·云计算·openstack
mqiqe5 天前
云计算Openstack Cinder
云计算·php·openstack
mqiqe6 天前
云计算Openstack Glance
云计算·openstack
mqiqe7 天前
云计算Openstack Nova
microsoft·云计算·openstack
mqiqe9 天前
云计算Openstack
云计算·openstack
mqiqe9 天前
云计算Openstack Swift
云计算·openstack·swift
苦逼IT运维10 天前
OpenStack 部署实践与原理解析 - Ubuntu 22.04 部署 (DevStack)
linux·运维·ubuntu·openstack·运维开发·devops
kuuuugua11 天前
2024广东省职业技能大赛云计算——OpenStack镜像、脚本详解
云计算·bash·openstack
qlau200718 天前
基于kolla-ansible在AnolisOS8.6上部署all-in-one模式OpenStack-Train
ansible·openstack