nova分析(8)—— nova-compute


nova-compute是管理和配置虛擬機的入口,在所有compute機器上都需要該服務來創建和管理虛擬機。

nova-compute服務的入口在 nova.cmd.compute:main ,其啟動過程與其他nova服務類似。

簡單看下它是如何啟動的, binary='nova-compute', topic='nova.compute.rpcapi' ,  manager=nova.compute.manager.ComputeManager ,manager類在初始化的時候會創建與其他服務交互的api實例:

class ComputeManager(manager.Manager):
    """Manages the running instances from creation to destruction."""

    target = messaging.Target(version='3.32')

    def __init__(self, compute_driver=None, *args, **kwargs):
        """Load configuration options and connect to the hypervisor."""
        self.virtapi = ComputeVirtAPI(self)
        self.network_api = network.API()
        self.volume_api = volume.API()
        self.image_api = image.API()
        self._last_host_check = 0
        self._last_bw_usage_poll = 0
        self._bw_usage_supported = True
        self._last_bw_usage_cell_update = 0
        self.compute_api = compute.API()
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()
        self.conductor_api = conductor.API()
        self.compute_task_api = conductor.ComputeTaskAPI()
        self.is_neutron_security_groups = (
            openstack_driver.is_neutron_security_groups())
        self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI()
        self.cells_rpcapi = cells_rpcapi.CellsAPI()
        self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI()
        self._resource_tracker_dict = {}
        self.instance_events = InstanceEvents()

        super(ComputeManager, self).__init__(service_name="compute",
                                             *args, **kwargs)

        # NOTE(russellb) Load the driver last.  It may call back into the
        # compute manager via the virtapi, so we want it to be fully
        # initialized before that happens.
        self.driver = driver.load_compute_driver(self.virtapi, compute_driver)   #libvirt.LibvirtDriver
        self.use_legacy_block_device_info = \
                            self.driver.need_legacy_block_device_info

在service.start()中會初始化主機上的虛擬機和安全組,並開啟RPC服務

    def start(self):
        verstr = version.version_string_with_package()
        LOG.audit(_('Starting %(topic)s node (version %(version)s)'),
                  {'topic': self.topic, 'version': verstr})
        self.basic_config_check()
        # Initialization for a standalone compute service
        # init instances and iptables
        self.manager.init_host()
        self.model_disconnected = False
        ctxt = context.get_admin_context()
        try: # init conductor api
            self.service_ref = self.conductor_api.service_get_by_args(ctxt,
                    self.host, self.binary)
            self.service_id = self.service_ref['id']
        except exception.NotFound:
            try:
                self.service_ref = self._create_service_ref(ctxt)
            except (exception.ServiceTopicExists,
                    exception.ServiceBinaryExists):
                # NOTE(danms): If we race to create a record with a sibling
                # worker, don't fail here.
                self.service_ref = self.conductor_api.service_get_by_args(ctxt,
                    self.host, self.binary)

        """After the service is initialized, but before we fully bring
        the service up by listening on RPC queues, make sure to update
        our available resources (and indirectly our available nodes).
        """
        self.manager.pre_start_hook()

        if self.backdoor_port is not None:
            self.manager.backdoor_port = self.backdoor_port

        LOG.debug("Creating RPC server for service %s", self.topic)

        # start rpc api
        target = messaging.Target(topic=self.topic, server=self.host)

        endpoints = [
            self.manager,
            baserpc.BaseRPCAPI(self.manager.service_name, self.backdoor_port)
        ]
        endpoints.extend(self.manager.additional_endpoints)

        serializer = objects_base.NovaObjectSerializer()

        self.rpcserver = rpc.get_server(target, endpoints, serializer)
        self.rpcserver.start()

        self.manager.post_start_hook()

        LOG.debug("Join ServiceGroup membership for this service %s",
                  self.topic)
        # Add service to the ServiceGroup membership group.
        self.servicegroup_api.join(self.host, self.topic, self)

        if self.periodic_enable:
            if self.periodic_fuzzy_delay:
                initial_delay = random.randint(0, self.periodic_fuzzy_delay)
            else:
                initial_delay = None

            self.tg.add_dynamic_timer(self.periodic_tasks,
                                     initial_delay=initial_delay,
                                     periodic_interval_max=
                                        self.periodic_interval_max)

這樣,nova-compute服務就起來了,而之后接收到的請求都會到 nova.compute.manager.ComputeManager 類來處理。

最常使用的KVM虛擬化需要配置driver為libvirt.LibvirtDriver(所有支持的driver有 libvirt.LibvirtDriver, xenapi.XenAPIDriver, fake.FakeDriver, baremetal.BareMetalDriver, vmwareapi.VMwareVCDriver, hyperv.HyperVDriver).

下面來看看libvirt是如何創建虛擬機的:

   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)
        # 獲取鏡像信息
        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)
        # 生成libvirt配置文件格式的XML
        xml = self._get_guest_xml(context, instance, network_info,
                                  disk_info, image_meta,
                                  block_device_info=block_device_info,
                                  write_to_disk=True)
        # 創建卷、網絡,並創建虛擬機
        self._create_domain_and_network(context, xml, instance, network_info,
                                        block_device_info)
        LOG.debug("Instance is running", instance=instance)

        def _wait_for_boot():
            """Called at an interval until the VM is running."""
            state = self.get_info(instance)['state']

            if state == power_state.RUNNING:
                LOG.info(_LI("Instance spawned successfully."),
                         instance=instance)
                raise loopingcall.LoopingCallDone()
        # 等待虛擬機狀態變為RUNNING
        timer = loopingcall.FixedIntervalLoopingCall(_wait_for_boot)
        timer.start(interval=0.5).wait()
_create_domain_and_network為實際的創建設備和創建虛擬機函數
   def _create_domain_and_network(self, context, xml, instance, network_info,
                                   block_device_info=None, power_on=True,
                                   reboot=False, vifs_already_plugged=False):

        """Do required network setup and create domain."""
        # 查詢卷的映射關系
        block_device_mapping = driver.block_device_info_get_mapping(
            block_device_info)
        # 掛載卷
        for vol in block_device_mapping:
            connection_info = vol['connection_info']
            disk_info = blockinfo.get_info_from_bdm(
                CONF.libvirt.virt_type, vol)
            # 根據volume類型,調用其connect_volume方法掛載卷
            # 支持的類型包括:
            #     [
            #       'iscsi=nova.virt.libvirt.volume.LibvirtISCSIVolumeDriver',
            #       'iser=nova.virt.libvirt.volume.LibvirtISERVolumeDriver',
            #       'local=nova.virt.libvirt.volume.LibvirtVolumeDriver',
            #       'fake=nova.virt.libvirt.volume.LibvirtFakeVolumeDriver',
            #       'rbd=nova.virt.libvirt.volume.LibvirtNetVolumeDriver',
            #       'sheepdog=nova.virt.libvirt.volume.LibvirtNetVolumeDriver',
            #       'nfs=nova.virt.libvirt.volume.LibvirtNFSVolumeDriver',
            #       'aoe=nova.virt.libvirt.volume.LibvirtAOEVolumeDriver',
            #       'glusterfs='nova.virt.libvirt.volume.LibvirtGlusterfsVolumeDriver',
            #       'fibre_channel=nova.virt.libvirt.volume.LibvirtFibreChannelVolumeDriver',
            #       'scality=nova.virt.libvirt.volume.LibvirtScalityVolumeDriver',
            #     ]
            conf = self._connect_volume(connection_info, disk_info)

            # cache device_path in connection_info -- required by encryptors
            if 'data' in connection_info:
                connection_info['data']['device_path'] = conf.source_path
                vol['connection_info'] = connection_info
                vol.save(context)

            # 如果是加密卷,需要attach到encryptor
            if (not reboot and 'data' in connection_info and
                    'volume_id' in connection_info['data']):
                volume_id = connection_info['data']['volume_id']
                encryption = encryptors.get_encryption_metadata(
                    context, self._volume_api, volume_id, connection_info)

                if encryption:
                    encryptor = self._get_volume_encryptor(connection_info,
                                                           encryption)
                    encryptor.attach_volume(context, **encryption)

        timeout = CONF.vif_plugging_timeout
        if (self._conn_supports_start_paused and
            utils.is_neutron() and not
            vifs_already_plugged and power_on and timeout):
            events = self._get_neutron_events(network_info)
        else:
            events = []

        launch_flags = events and libvirt.VIR_DOMAIN_START_PAUSED or 0
        domain = None
        try:
            with self.virtapi.wait_for_instance_event(
                    instance, events, deadline=timeout,
                    error_callback=self._neutron_failed_callback):
                # Plug VIFs into networks using vif_driver
                self.plug_vifs(instance, network_info)
                # Init iptables using firewall_driver
                self.firewall_driver.setup_basic_filtering(instance,
                                                           network_info)
                self.firewall_driver.prepare_instance_filter(instance,
                                                             network_info)
                # 調用libvirt庫提供的方法創建虛擬機
                # if xml:
                #     err = _LE('Error defining a domain with XML: %s') % xml
                #     domain = self._conn.defineXML(xml)

                # if power_on:
                #     err = _LE('Error launching a defined domain with XML: %s') \
                #               % domain.XMLDesc(0)
                #     domain.createWithFlags(launch_flags)
                domain = self._create_domain(
                    xml, instance=instance,
                    launch_flags=launch_flags,
                    power_on=power_on)

                self.firewall_driver.apply_instance_filter(instance,
                                                           network_info)
        except exception.VirtualInterfaceCreateException:
            # Neutron reported failure and we didn't swallow it, so
            # bail here
            with excutils.save_and_reraise_exception():
                if domain:
                    domain.destroy()
                self.cleanup(context, instance, network_info=network_info,
                             block_device_info=block_device_info)
        except eventlet.timeout.Timeout:
            # We never heard from Neutron
            LOG.warn(_LW('Timeout waiting for vif plugging callback for '
                         'instance %(uuid)s'), {'uuid': instance['uuid']})
            if CONF.vif_plugging_is_fatal:
                if domain:
                    domain.destroy()
                self.cleanup(context, instance, network_info=network_info,
                             block_device_info=block_device_info)
                raise exception.VirtualInterfaceCreateException()

        # Resume only if domain has been paused
        if launch_flags & libvirt.VIR_DOMAIN_START_PAUSED:
            domain.resume()
        return domain
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM