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

相关推荐
huhy~10 天前
基于Ubuntu2410脚本搭建OpenStack-D版
服务器·网络·openstack
xserver210 天前
Openstack9--安装etcd分布式键-值对存储系统
数据库·分布式·openstack·etcd
mqiqe19 天前
云计算Openstack 虚拟机调度策略
云计算·openstack
小安运维日记23 天前
Linux云计算 |【第五阶段】CLOUD-DAY2
linux·运维·云计算·openstack
南宫乘风1 个月前
OpenStack将运行的系统导出 QCOW2 镜像并导入阿里云
阿里云·云计算·openstack
学习向前冲1 个月前
开源OpenStack
openstack
suum1 个月前
openstack-swift.18421165
openstack
极客先躯1 个月前
开源的云平台有哪些?
kubernetes·开源·openstack·cloudstack·云平台·docker swarm·opennebula
mqiqe1 个月前
云计算Openstack Horizon
云计算·openstack·perl
zkyqss2 个月前
OpenStack Yoga版安装笔记(十六)Openstack网络理解
笔记·openstack