zoukankan      html  css  js  c++  java
  • 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) #以下是错误处理。不表




  • 相关阅读:
    C语言-错误处理
    C语言-排序和查找
    PCB设计要点
    C语言-调试
    c++ 概述
    C语言-指针
    C语言-数组与指针 字符与字符串
    C语言-(void*)类型指针
    C语言-字符操作函数
    C语言-链表
  • 原文地址:https://www.cnblogs.com/mengfanrong/p/5090960.html
Copyright © 2011-2022 走看看