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

相关推荐
哈里谢顿3 天前
Nova parse_args 函数详解
openstack
哈里谢顿5 天前
OpenStack 中的 nova-conductor 与 ironic-conductor 及其分布式锁机制详解
openstack
哈里谢顿9 天前
OpenStack oslo-config 详解
openstack
感哥14 天前
OpenStack Cinder 创建卷
openstack
感哥14 天前
OpenStack Cinder 架构
openstack
感哥14 天前
OpenStack Nova Scheduler 计算节点选择机制
openstack
感哥17 天前
OpenStack Nova 创建虚拟机
openstack
感哥17 天前
OpenStack Glance(镜像)
openstack
感哥18 天前
OpenStack Keystone详解
openstack
安全菜鸟1 个月前
传统方式部署OpenStack具体教程
openstack