當前位置:網站首頁>OpenStack基於Libvirt的虛擬化平臺調度實現----Nova虛擬機啟動源碼實現(4)

OpenStack基於Libvirt的虛擬化平臺調度實現----Nova虛擬機啟動源碼實現(4)

2022-05-13 13:11:45qq_42533216

完成對方法_create_image的解析,我們回到方法spawn中,繼續對Nova虛擬機啟動源碼實現進行解析。

再來看方法spawn的源碼:

def spawn(self, context, instance, image_meta, injected_files,
              admin_password, network_info=None, block_device_info=None):
        """
        在虛擬化平臺上建立新的實例/虛擬機/域;
        # 調用之一傳進來的參數:
        # context:上下文信息;
        # instance:實例信息;
        # image_meta:從glance獲取的鏡像文件image的元數據;
        # injected_files:編碼後的注入文件;
        # admin_password:admin密碼;
        # network_info=self._legacy_nw_info(network_info):轉換為傳統格式的網絡資源信息;
        # block_device_info:實例錯誤記錄的塊設備;
        """
        
        # get_disk_info:確定來賓系統的磁盤映射信息;
        # 返回disk_bus、cdrom_bus和mapping的值這些磁盤映射信息給disk_info;
        # CONF.libvirt_type:這個參數定義了libvirt的域名類型,參數的默認值為'kvm';
        # instance:實例信息;
        # block_device_info:實例錯誤記錄的塊設備;
        # image_meta:從glance獲取的鏡像文件image的元數據;
        
        # get_disk_info返回值:
        # return {'disk_bus': disk_bus,    # 獲取disk類型的磁盤總線;
        #         'cdrom_bus': cdrom_bus,  # 獲取cdrom類型的磁盤總線;
        #         'mapping': mapping}      # 確定怎樣映射從默認的磁盤到虛擬機,返回客戶對於設備的磁盤映射;
        disk_info = blockinfo.get_disk_info(CONF.libvirt_type,
                                            instance,
                                            block_device_info,
                                            image_meta)
        
        # to_xml:為新建立的實例參數獲取配置數據conf,並把獲取的數據conf轉換為xml格式;
        # instance:實例信息;
        # network_info:轉換為傳統格式的網絡資源信息;
        # disk_info:來賓系統的磁盤映射信息;
        # image_meta:從glance獲取的鏡像文件image的元數據;
        # block_device_info:實例錯誤記錄的塊設備;
        xml = self.to_xml(instance, network_info,
                          disk_info, image_meta,
                          block_device_info=block_device_info)
 
        # _create_image:建立虛擬機實例鏡像;
        # context:上下文信息;
        # instance:實例信息;
        # xml:為新建立的實例參數獲取配置數據conf,並把獲取的數據conf轉換為xml格式;
        # disk_info['mapping']:來賓系統磁盤的映射信息;
        # network_info=network_info:轉換為傳統格式的網絡資源信息;
        # block_device_info=block_device_info:實例錯誤記錄的塊設備;
        # files=injected_files:編碼後的注入文件;
        # admin_pass=admin_password:admin密碼;
        self._create_image(context, instance, xml,
                           disk_info['mapping'],
                           network_info=network_info,
                           block_device_info=block_device_info,
                           files=injected_files,
                           admin_pass=admin_password)


 
 

       # 執行所需網絡的安裝以及建立域;
        self._create_domain_and_network(xml, instance, network_info,
                                        block_device_info)
        LOG.debug(_("Instance is running"), instance=instance)
 
        def _wait_for_boot():
            """
            在固定時間間隔調用,檢測虛擬機啟動狀態,直到虛擬機成功運行;
            """
            state = self.get_info(instance)['state']
 
            if state == power_state.RUNNING:
                LOG.info(_("Instance spawned successfully."),
                         instance=instance)
                raise utils.LoopingCallDone()
 
        # _wait_for_boot:在固定時間間隔調用,檢測虛擬機啟動狀態,直到虛擬機成功運行;
        # 以一定的時間間隔(0.5)循環調用_wait_for_boot方法;
        timer = utils.FixedIntervalLoopingCall(_wait_for_boot)
        timer.start(interval=0.5).wait()
在完成image鏡像的建立之後,這裏將會調用方法_create_domain_and_network來實現執行所需網絡的安裝和domain的建立以及虛擬機的啟動。具體實現我們來看方法_create_domain_and_network的源代碼:

def _create_domain_and_network(self, xml, instance, network_info,
                                   block_device_info=None):
 
        """
        執行所需網絡的安裝以及建立域;
        """
        block_device_mapping = driver.block_device_info_get_mapping(block_device_info)
 
        # 獲取卷驅動方法,並進行卷的連接驅動;
        for vol in block_device_mapping:
            connection_info = vol['connection_info']
            disk_dev = vol['mount_device'].rpartition("/")[2]
            
            # 磁盤信息disk_info:
            # 設備、磁盤總線、磁盤類型;
            disk_info = {
                'dev': disk_dev,
                'bus': blockinfo.get_disk_bus_for_disk_dev(CONF.libvirt_type,disk_dev),
                'type': 'disk',
                }
            # 匹配指定的方法connect_volume,實現鏈接卷,返回xml文件;
            self.volume_driver_method('connect_volume',connection_info,disk_info)
 
        # plug_vifs:為network_info中的每一個網絡,增加VIF到網絡中;
        self.plug_vifs(instance, network_info)
        # setup_basic_filtering:為network_info中的每一個網絡設置基本的網絡防火牆過濾器;
        self.firewall_driver.setup_basic_filtering(instance, network_info)
        # prepare_instance_filter:為實例准備過濾器,此時實例還沒有運行;
        self.firewall_driver.prepare_instance_filter(instance, network_info)
        # _create_domain:建立一個domain,並運行實例;
        domain = self._create_domain(xml, instance=instance)
        self.firewall_driver.apply_instance_filter(instance, network_info)
        
        return domain


這個方法主要完成了兩個任務,一個是為實例的運行安裝配置所需的網絡信息,再有就是為實例建立一個domain,並運行實例。
其中方法_create_domain就是實現了建立domain並運行實例這個任務。具體的實現我們來看方法_create_domain的源代碼:

def _create_domain(self, xml=None, domain=None,
                       instance=None, launch_flags=0):
        """        
        建立一個domain,並運行實例;
        """
        inst_path = None
        if instance:
            # 確定實例存儲的正確的路徑;
            inst_path = libvirt_utils.get_instance_path(instance)
 
        if CONF.libvirt_type == 'lxc':
            if not inst_path:
                inst_path = None
 
            container_dir = os.path.join(inst_path, 'rootfs')
            # 按照path的路徑信息,建立container_dir完整的路徑,包括所有需要的上級路徑;            
            fileutils.ensure_tree(container_dir)
            # 為選擇的後端構造鏡像image;
            image = self.image_backend.image(instance, 'disk')
            
            disk.setup_container(image.path,
                                 container_dir=container_dir,
                                 use_cow=CONF.use_cow_images)
 
        # 定義一個域domain,但是不啟動它;
        # 其中調用方法virDomainDefineXML,實現為一個持久性的域建立並存儲配置文件;
        if xml:
            domain = self._conn.defineXML(xml)
        # createWithFlags:啟動一個已定義的域,如果調用成功,域將從定義的集合轉移到運行域的集合;
        # 返回啟動虛擬機的結果;
        domain.createWithFlags(launch_flags)
        # XMLDesc:調用方法virDomainGetXMLDesc,來實現獲取一個虛擬機(也就是一個Domain)的XML文件;
        # 這個XML文件可能會被重複使用,調用方法virDomainCreateXML()用來重新啟動這個域;
        self._enable_hairpin(domain.XMLDesc(0))
 
        if CONF.libvirt_type == 'lxc':
            state = self.get_info(instance)['state']
            container_dir = os.path.join(inst_path, 'rootfs')
            if state == power_state.RUNNING:
                disk.clean_lxc_namespace(container_dir=container_dir)
            else:
                disk.teardown_container(container_dir=container_dir)
 
        return domain
我們逐條對這個方法的代碼進行解析:
1.inst_path = libvirt_utils.get_instance_path(instance)

這條語句實現了確定實例存儲的正確的路徑;

2.if CONF.libvirt_type == 'lxc':

       ......

這部分語句判斷CONF.libvirt_type的值是否是“lxc”,如果是的話,則執行其下面的語句。但是經閱讀代碼知道,系統默認的CONF.libvirt_type參數值為“kvm”,所以,這裏我們暫且不對CONF.libvirt_type值為“lxc”的情况作解析。

3.domain = self._conn.defineXML(xml)

這條語句實現了根據前面配置生成的xml文件定義建立一個域domain,但是不啟動它,其中調用了方法virDomainDefineXML,實現為一個持久性的域建立並存儲配置文件。具體我們來看方法defineXML的源代碼實現:

def defineXML(self, xml):
        """
        根據配置好的xml文件定義一個域,但是不啟動它;
        """
        # virDomainDefineXML將會為一個持久性的域建立並存儲配置文件;
        ret = libvirtmod.virDomainDefineXML(self._o, xml)
        if ret is None:raise libvirtError('virDomainDefineXML() failed', conn=self)
        __tmp = virDomain(self,_obj=ret)
        return __tmp


這個方法中調用了libvirt的python接口方法virDomainDefineXML,這個方法所實現的功能就是將會建立一個持久性的域並存儲相關的配置文件。
4.domain.createWithFlags(launch_flags)

這條語句調用了方法createWithFlags實現了啟動一個已定義的域domain,如果調用成功,域將從定義的集合轉移到運行域的集合,並返回啟動虛擬機的結果。具體我們來看方法createWithFlags的源代碼實現:

def createWithFlags(self, flags):
        """       
        啟動一個已定義的域,如果調用成功,域將從定義的集合轉移到運行域的集合;
        返回啟動虛擬機的結果;
        """
        # virDomainCreateWithFlags:這個libvirt API是實現虛擬機啟動的;
        ret = libvirtmod.virDomainCreateWithFlags(self._o, flags)
        if ret == -1: raise libvirtError ('virDomainCreateWithFlags() failed', dom=self)
        return ret


這個方法中調用了libvirt的python接口方法virDomainCreateWithFlags,這個方法所實現的功能就是實現虛擬機的啟動的。
方法_create_domain的最後,返回已經建立執行的domain。

我們回到方法spawn,可以看到,在方法的最後,有這樣的兩條語句:


timer = utils.FixedIntervalLoopingCall(_wait_for_boot)
timer.start(interval=0.5).wait()
這兩條語句所實現的功能是以一定的時間間隔(0.5)循環調用_wait_for_boot方法,來檢測虛擬機啟動狀態,直到虛擬機成功運行。
至此,方法spawn全部解析完成,也就是說Nova通過libvirt庫來實現虛擬機啟動的實現源碼解析完成。
————————————————
版權聲明:本文為CSDN博主「溜溜小哥」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/gaoxingnengjisuan/article/details/10233043

版權聲明
本文為[qq_42533216]所創,轉載請帶上原文鏈接,感謝
https://cht.chowdera.com/2022/133/202205131301438848.html

隨機推薦