Nova创建虚拟机的底层代码分析

作为个人学习笔记分享。有不论什么问题欢迎交流!

在openstack中创建虚拟机的底层实现是nova使用了libvirt,代码在nova/virt/libvirt/driver.py。

#image_meta:镜像的相关内容,#injected_files:要注入到VM的文件
#network_info:网络相关信息。block_device_info:磁盘相关信息
def spawn(self, context, instance, image_meta, injected_files,
              admin_password, network_info=None, block_device_info=None):
		#确定客户机的磁盘映射关系
        disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
                                            instance,
                                            block_device_info,
                                            image_meta)
		#创建VM的磁盘文件
        self._create_image(context, instance,
                           disk_info['mapping'],
                           network_info=network_info,
                           block_device_info=block_device_info,
                           files=injected_files,
                           admin_pass=admin_password)
		#综合各方面的信息,拼装一个define VM的XML文件
        xml = self.to_xml(context, instance, network_info,
                          disk_info, image_meta,
                          block_device_info=block_device_info,
                          write_to_disk=True)
		#向neutron请求IP,然后使用xml创建domain
        self._create_domain_and_network(context, xml, instance, network_info,
                                        block_device_info)

以下将具体分析上述4个函数,比源码略有删减

#virt_type: cpu_mode,一般为kvm,具体可參见该选项的凝视
def get_disk_info(virt_type, instance, block_device_info=None,
                  image_meta=None, rescue=False):
	#依据device_type和virt_type返回总线类型。如kvm和disk。则会返回virtio
	#cdrom和kvm,则会返回ide
    disk_bus = get_disk_bus_for_device_type(virt_type, image_meta, "disk")
cdrom_bus = get_disk_bus_for_device_type(virt_type, image_meta, "cdrom")
#确定如何映射默认的disks到VM中,如root挂到/dev/vda等。还有swap, local等
    mapping = get_disk_mapping(virt_type, instance,
                               disk_bus, cdrom_bus,
                               block_device_info,
                               image_meta, rescue)
#准备磁盘文件
def _create_image(self, context, instance,
                      disk_mapping, suffix='',
                      disk_images=None, network_info=None,
                      block_device_info=None, files=None,
                      admin_pass=None, inject_files=True):
		#推断是否从volume启动VM
        booted_from_volume = self._is_booted_from_volume(
            instance, disk_mapping)
		#依据images_type。如qcow2, RBD等,创建各种格式的镜像相应的类
        def image(fname, image_type=CONF.libvirt.images_type):
            return self.image_backend.image(instance,
                                            fname + suffix, image_type)
		#创建raw格式的镜像相应的类
        def raw(fname):
            return image(fname, image_type='raw')

        LOG.info(_('Creating image'), instance=instance)

        if not disk_images:
            disk_images = {'image_id': instance['image_ref'],
                           'kernel_id': instance['kernel_id'],
                           'ramdisk_id': instance['ramdisk_id']}

        if disk_images['kernel_id']:
            fname = imagecache.get_cache_fname(disk_images, 'kernel_id')
#这里用到了上面定义的def raw()。其返回一个raw类型的类。cache()使用#fetch_image()完毕镜像文件的kernel下载,关于镜像的下载具体情况能够參#考:http://blog.csdn.net/epugv/article/details/27970625
            raw('kernel').cache(fetch_func=libvirt_utils.fetch_image,
                                context=context,
                                filename=fname,
                                image_id=disk_images['kernel_id'],
                                user_id=instance['user_id'],
                                project_id=instance['project_id'])
            if disk_images['ramdisk_id']:
                fname = imagecache.get_cache_fname(disk_images, 'ramdisk_id')
				#完毕ramdisk文件下载
                raw('ramdisk').cache(fetch_func=libvirt_utils.fetch_image,
                                     context=context,
                                     filename=fname,
                                     image_id=disk_images['ramdisk_id'],
                                     user_id=instance['user_id'],
                                     project_id=instance['project_id'])

# 创建暂时磁盘,ephemeral disk指的是除了root disk和swap disk之外的ephemeral
#空间
        ephemeral_gb = instance['ephemeral_gb']
        if 'disk.local' in disk_mapping:
            disk_image = image('disk.local')
            fn = functools.partial(self._create_ephemeral,
                                   fs_label='ephemeral0',
                                   os_type=instance["os_type"],
                                   is_block_dev=disk_image.is_block_dev)
            fname = "ephemeral_%s_%s" % (ephemeral_gb, os_type_with_default)
            size = ephemeral_gb * units.Gi
            disk_image.cache(fetch_func=fn,
                             filename=fname,
                             size=size,
                             ephemeral_size=ephemeral_gb)
		#假设还有ephemeral disk,继续创建。label为'ephemeral%d' % idx
        for idx, eph in enumerate(driver.block_device_info_get_ephemerals(
                block_device_info)):
            disk_image = image(blockinfo.get_eph_disk(idx))
            fn = functools.partial(self._create_ephemeral,
                                   fs_label='ephemeral%d' % idx,
                                   os_type=instance["os_type"],
                                   is_block_dev=disk_image.is_block_dev)
            size = eph['size'] * units.Gi
            fname = "ephemeral_%s_%s" % (eph['size'], os_type_with_default)
            disk_image.cache(
                             fetch_func=fn,
                             filename=fname,
                             size=size,
                             ephemeral_size=eph['size'])
			#创建swap disk
            if swap_mb > 0:
                size = swap_mb * units.Mi
                image('disk.swap').cache(fetch_func=self._create_swap,
                                         filename="swap_%s" % swap_mb,
                                         size=size,
                                         swap_mb=swap_mb)
# OpenStack还能够使用Config Drive实现元数据的注入,可是在制作image时一
#定要安装cloud-init软件,否则无法实现元数据注入
        # Config drive
        if configdrive.required_by(instance):
            LOG.info(_('Using config drive'), instance=instance)
            extra_md = {}
            if admin_pass:
                extra_md['admin_pass'] = admin_pass
			#提取instance matedata
            inst_md = instance_metadata.InstanceMetadata(instance,
                content=files, extra_md=extra_md, network_info=network_info)
            with configdrive.ConfigDriveBuilder(instance_md=inst_md) as cdb:
                configdrive_path = self._get_disk_config_path(instance)
                LOG.info(_('Creating config drive at %(path)s'),
                         {'path': configdrive_path}, instance=instance)
				#将metadata写入实例所在的目录下的一个disk.config文件里
                try:
                    cdb.make_drive(configdrive_path)
                except processutils.ProcessExecutionError as e:
                    with excutils.save_and_reraise_exception():
                        LOG.error(_('Creating config drive failed '
                                  'with error: %s'),
                                  e, instance=instance)

#依据官方文档 -2 => disable, -1 => inspect (libguestfs only), 0 =>
#not partitioned, >0 => partition number
        # File injection only if needed
        elif inject_files and CONF.libvirt.inject_partition != -2:
            if booted_from_volume:
                LOG.warn(_('File injection into a boot from volume '
                           'instance is not supported'), instance=instance)
            self._inject_data(
                instance, network_info, admin_pass, files, suffix)
def to_xml(self, context, instance, network_info, disk_info,
               image_meta=None, rescue=None,
               block_device_info=None, write_to_disk=False):
        # We should get image metadata every time for generating xml
        if image_meta is None:
            image_ref = instance['image_ref']
            image_meta = compute_utils.get_image_metadata(
                                context, self._image_api, image_ref, instance)

        msg = ('Start to_xml '
               'network_info=%(network_info)s '
               'disk_info=%(disk_info)s '
               'image_meta=%(image_meta)s rescue=%(rescue)s '
               'block_device_info=%(block_device_info)s' %
               {'network_info': network_info_str, 'disk_info': disk_info,
                'image_meta': image_meta, 'rescue': rescue,
                'block_device_info': block_device_info})
		#返回guest的具体參数
        conf = self.get_guest_config(instance, network_info, image_meta,
                                     disk_info, rescue, block_device_info)
        xml = conf.to_xml()#将具体參数使用etree.tostring()生产一个xml dom
  def _create_domain_and_network(self, context, xml, instance, network_info,
                                   block_device_info=None, power_on=True,
                                   reboot=False, vifs_already_plugged=False):

        for vol in block_device_mapping:
            connection_info = vol['connection_info']
            disk_info = blockinfo.get_info_from_bdm(
                CONF.libvirt.virt_type, vol)
			#挂载volume前与volume建立连接,如iscsi中的discover
            conf = self.volume_driver_method('connect_volume',
                                             connection_info,
                                             disk_info)
		#。。。

。。。
        launch_flags = events and libvirt.VIR_DOMAIN_START_PAUSED or 0
        try:
            with self.virtapi.wait_for_instance_event(
                    instance, events, deadline=timeout,
                    error_callback=self._neutron_failed_callback):
                self.plug_vifs(instance, network_info)#向network注入VIF
                self.firewall_driver.setup_basic_filtering(instance,
                                                           network_info)
                self.firewall_driver.prepare_instance_filter(instance,
                                                             network_info)
domain = self._create_domain(#调用libvirt的#defineXML () and #createWithFlags()
                    xml, instance=instance,
                    launch_flags=launch_flags,
                    power_on=power_on)
				#应用防火墙
                self.firewall_driver.apply_instance_filter(instance,
                                                           network_info)
      #以下是错误处理。不表
时间: 2024-10-07 22:46:38

Nova创建虚拟机的底层代码分析的相关文章

在Ceph中创建虚拟机流程改进之分析

作为个人学习笔记分享,有任何问题欢迎交流! 最近在Gerrit中看到一个change:https://review.openstack.org/#/c/94295/ , 它主要是对当前在Ceph中创建虚拟机的流程的改进.如果glance的backend是ceph, 则nova创建虚拟机到RBD的流程是这样的: 通过glance从ceph中下载image --> 本地 --> 复制image到rbd 这个change的目的就是:不需要下载到本地,直接在rbd中复制image,以提高虚拟机创建的速

nova创建虚拟机源码分析系列之七 传入参数转换成内部id

上一篇博文将nova创建虚机的流程推进到了/compute/api.py中的create()函数,接下来就继续分析. 在分析之前简单介绍nova组件源码的架构.以conductor组件为例: 每个组件都会有这三个文件:api.py rpcapi.py manager.py. api.py              conductor实现自身功能的文件 rpcapi.py         conductor提供给其他组件通过消息队列调用的接口函数 manager.py    其他组件通过消息队列调

nova创建虚拟机源码分析系列之八 compute创建虚机

/conductor/api.py _build_instance()  /conductor/rpcapi.py  _build_instance() 1 构造一些数据类型2 修改一些api版本信息3 投入消息队列  /conductor/manager.py  _build_instance()1 过滤信息2 调度模块工作(选出最合适的主机,并返回)3 调用/nova/compute/rpcapi.py::build_and_run_instance() 此时已经拿到最合适主机 /compu

java.lang.Object底层代码分析-jdk1.8

首先先来说明两件事情 1.后面的一些博客都会对jdk底层的方法进行一些分析,因为我个人开发了2年多,发现有很多事情还不是清楚,当我今天再好好底层源码的时候还是有不少的收获,所以和大家分    享一下,我只要参考一下相关文档 jdk1.8api:https://docs.oracle.com/javase/8/docs/api/index.html 代码下载地址:https://gitee.com/luanmihun/java-jdk-study 2.为什么要分析jdk1.8的版本,只是一些个人的

HashMap底层代码分析

public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; //this.loadFactor为加载因子,其值为默认的加载因子常量:DEFAULT_LOAD_FACTOR的值,即0.75 threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR); //阈值,当容量达到默认容量的75%时,自动扩容 table = new Entry[DEFAULT_INITIAL_CA

HashSet——add remove contains方法底层代码分析(hashCode equals 方法的重写)

引言:我们都知道HashSet这个类有add   remove   contains方法,但是我们要深刻理解到底是怎么判断它是否重复加入了,什么时候才移除,什么时候才算是包括????????? add()方法 首先我们看下这个代码 1 package com.xt.set; 2 3 import java.util.HashSet; 4 import java.util.Iterator; 5 import java.util.Set; 6 7 public class AddTest { 8

Openstack之Nova创建虚机流程分析

前言  Openstack作为一个虚拟机管理平台,核心功能自然是虚拟机的生命周期的管理,而负责虚机管理的模块就是Nova. 本文就是openstack中Nova模块的分析,所以本文重点是以下三点: 先了解Openstack的整体架构,搞清楚为什么要用这样的架构: 然后再了解架构中的各个组件,组件提供的主要功能与各个组件之间的交互: 了解虚机的启动过程,能在遇到问题时发现问题出在哪个模块中的哪个组件. Nova组件介绍 接下来进行详细介绍,如有错误,欢迎拍砖! 下图为创建虚拟机的一个大概流程图.

nova boot from volume代码分析

OpenStack Liberty版本,这里简单记录下nova boot from volume的代码调用过程. nova boot from volume命令行 nova client novaclient/v2/shell.py   # novaclient端发起请求 def do_boot(cs, args):     """Boot a new server."""     boot_args, boot_kwargs = _boot(c

linux c 文件打开并创建代码分析

[[email protected] 03]# cat ex03-open-03.c/*文件ex03-open-03.c,O_CREAT和O_EXCL的使用*/#include #include #include #include int main(void){ int fd = -1; char filename[] = "test.txt"; /*打开文件,如果文件不存在,则报错*/ fd = open(filename,O_RDWR|O_CREAT|O_EXCL,S_IRWXU)