zoukankan      html  css  js  c++  java
  • Openstack创建虚拟机 Restful api和RPC调用

    Horizon前台界面用于接受用户的输入或动作(action),然后将这些参数构造成RESTful API(https://developer.openstack.org/api-ref/compute/#create-server),并通过novaclient发出。Nova-API负责接受该RESTFul API请求,通过RPC调用将消息发送到RabbitMQ,Nova-Compute订阅了该消息,接受消息后随机调用Libvirt API,指挥KVM-QEMU去真正创建虚拟机。

    对虚拟机的增删改查(前台)操作,发出的url请求都会被以下模块正则匹配到:

    urlpatterns = patterns(
        VIEW_MOD,
        url(r'^$', views.IndexView.as_view(), name='index'),
        url(r'^filter/(?P<vm_state>[^/]+)/$', views.IndexView.as_view(), name='filter'),
        url(r'^create$', views.CreateInstanceView.as_view(), name='create'),
        url(INSTANCES % 'update', views.UpdateView.as_view(), name='update'),
        url(INSTANCES % 'rebuild', views.RebuildView.as_view(), name='rebuild'),

    拿创建虚拟机的操作为例,有一系列的步骤(workflow)需要用户选择和操作,目的是拼接足够多的(创建虚拟机所必须的)参数:

    class CreateInstanceView(workflows.WorkflowView):
        workflow_class = project_workflows.CreateInstance
        ajax_template_name = constants.INSTANCE_CREATE_TEMPLATE
        success_url = reverse_lazy(constants.INSTANCES_INDEX_URL)

    默认的workflow为:

    class CreateInstance(workflows.Workflow):
        slug = "create_instance"
        name = _("Create Instance")
        finalize_button_name = _("Create")
        success_message = _('Launched %(count)s named "%(name)s".')
        failure_message = _('Unable to launch %(count)s named "%(name)s".')
        success_url = constants.INSTANCES_INDEX_URL
        wizard = True
        default_steps = (
            SelectImages,
            CustomFlavors,
            SelectHost,
            SelectNetworks,
            SetInstanceDetails
        )
    @sensitive_variables('context')
    def handle(self, request, context):
        custom_script = context.get('script_data', '')
        vcpus = int(context.get('vcpus', 0))
        ram = int(context.get('ram', 0)) * 1024
        disk = int(float(context.get('disk', 0)))
        count = int(context.get('count', 0))
        pool = context.get('pool', None)
        try:
            rets = api.nova.server_create(request,
                                          context['name'],
                                          image_id,
                                          flavor_id,
                                          None,
                                          normalize_newlines(custom_script),
                                          context['groups'],
                                          meta=meta,
                                          fips=fips)

    workflow走完之后,就执行handle方法了,Horizon中有一个api python包,里面有nova,keystone,glance等的api,这些api都是对novaclient、keystoneclient、glanceclient等的封装。

    @write_log("Create Server")
    def server_create(request, name, image, flavor, key_name, user_data,
                      security_groups, block_device_mapping=None,
                      block_device_mapping_v2=None, nics=None,
                      availability_zone=None, instance_count=1, admin_pass=None,
                      disk_config=None, config_drive=None, meta=None,
                      host=None, fips=None):
    
        return Server(novaclient(request).servers.create(
            name, image, flavor, userdata=user_data,
            security_groups=security_groups,
            key_name=key_name, block_device_mapping=block_device_mapping,
            block_device_mapping_v2=block_device_mapping_v2,
            nics=nics, availability_zone=availability_zone,
            min_count=instance_count, admin_pass=admin_pass,
            disk_config=disk_config, config_drive=config_drive,
            meta=meta, fips=fips), request)

    novaclient的create方法获得需要的参数后,构造Restful api,并发送到Nova服务中,由nova-api负责接收。

    在Nova组件中,nova/api/openstack/compute/servers.py(nova-api)的Controller负责接受请求并进行处理。

    class Controller(wsgi.Controller):
        def create(self, req, body):
            try:
                _get_inst_type = flavors.get_flavor_by_flavor_id
                inst_type = _get_inst_type(flavor_id, ctxt=context,
                                           read_deleted="no")
    
                (instances, resv_id) = self.compute_api.create(context,
                            inst_type,
                            image_uuid,
                            display_name=name,
                            display_description=name,
                            key_name=key_name,
                            metadata=server_dict.get('metadata', {}),
                            config_drive=config_drive,
                            block_device_mapping=block_device_mapping,
                            auto_disk_config=auto_disk_config,
                            scheduler_hints=scheduler_hints,
                            legacy_bdm=legacy_bdm,
                            check_server_group_quota=check_server_group_quota,
                            floating_ips=floating_ips)
    
            return self._add_location(robj)
    
        def _delete(self, context, req, instance_uuid):
            instance = self._get_server(context, req, instance_uuid)
            if CONF.reclaim_instance_interval:
                try:
                    self.compute_api.soft_delete(context, instance)
                except exception.InstanceInvalidState:
                    self.compute_api.delete(context, instance)
            else:
                self.compute_api.delete(context, instance)

    之后,会调用Nova Compute的api模块的相应方法:

    def _create_instance(self, context, instance_type,
               image_href, kernel_id, ramdisk_id,
               min_count, max_count,
               display_name, display_description,
               key_name, key_data, security_groups,
               availability_zone, user_data, metadata,
               injected_files, admin_password,
               access_ip_v4, access_ip_v6,
               requested_networks, config_drive,
               block_device_mapping, auto_disk_config,
               reservation_id=None, scheduler_hints=None,
               legacy_bdm=True, shutdown_terminate=False,
               check_server_group_quota=False,
               floating_ips=None):
        
        self.compute_task_api.build_instances(context,
                instances=instances, image=boot_meta,
                filter_properties=filter_properties,
                admin_password=admin_password,
                injected_files=injected_files,
                requested_networks=requested_networks,
                security_groups=security_groups,
                block_device_mapping=block_device_mapping,
                legacy_bdm=False,
                floating_ips=floating_ips)
    
        return (instances, reservation_id)

    然后,会调用Nova-Conductor API中的ComputeTaskAPI相应方法:

    def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,
            security_groups, block_device_mapping, legacy_bdm=True,
            floating_ips=None):
        self.conductor_compute_rpcapi.build_instances(context,
                instances=instances, image=image,
                filter_properties=filter_properties,
                admin_password=admin_password, injected_files=injected_files,
                requested_networks=requested_networks,
                security_groups=security_groups,
                block_device_mapping=block_device_mapping,
                legacy_bdm=legacy_bdm,
                floating_ips=floating_ips)

    之后,又会调用Nova-Conductor模块RPCAPI中的相应方法:

    def build_instances(self, context, instances, image, filter_properties,
            admin_password, injected_files, requested_networks,
            security_groups, block_device_mapping, legacy_bdm=True, 
            floating_ips=None):
        image_p = jsonutils.to_primitive(image)
        kw = {'instances': instances, 'image': image_p,
               'filter_properties': filter_properties,
               'admin_password': admin_password,
               'injected_files': injected_files,
               'requested_networks': requested_networks,
               'security_groups': security_groups,
               'floating_ips': floating_ips}
    
        version = '1.9'
        if not self.client.can_send_version('1.9'):
            version = '1.8'
            kw['requested_networks'] = kw['requested_networks'].as_tuples()
        if not self.client.can_send_version('1.7'):
            version = '1.5'
            kw.update({'block_device_mapping': block_device_mapping,
                       'legacy_bdm': legacy_bdm})
    
        cctxt = self.client.prepare(version=version)
        cctxt.cast(context, 'build_instances', **kw)

    该方法会获取messaging模块中的RPCClient,然后通过cast的方式将消息发送到RabbitMQ中。

    • RPC.call:发送请求到消息队列,等待返回最终结果。例如nova-compute调用nova-network服务分配网络,需要获得结果。
    • RPC.cast:发送请求到消息队列,不需要等待最终返回的结果。例如nova-conductor服务调用nova-compute的build_and_run_instance。

    最终,nova-compute订阅了RabbitMQ的消息,接受到创建虚拟机的消息。创建虚拟机的操作,由/nova/compute/manager.py ComputeManager中的_do_build_and_run_instance方法完成。

    def _do_build_and_run_instance(self, context, instance, image,
            request_spec, filter_properties, admin_password, injected_files,
            requested_networks, security_groups, block_device_mapping,
            node=None, limits=None,
            floating_ip=None):
        try:
            self._build_and_run_instance(context, instance, image,
                    decoded_files, admin_password, requested_networks,
                    security_groups, block_device_mapping, node, limits,
                    filter_properties,
                    floating_ip=floating_ip)
    def _build_and_run_instance(self, context, instance, image, injected_files,
            admin_password, requested_networks, security_groups,
            block_device_mapping, node, limits, filter_properties,
            floating_ip=None):
        try:
            self.driver.spawn(context, instance, image,
                              injected_files, admin_password,
                              network_info=network_info,
                              block_device_info=block_device_info)

    最后,调用Libvirt(Driver)的api接口,完成创建虚拟机的操作。/nova/virt/libvirt/driver spawn方法为创建本地镜像,获取虚机xml文件,创建domain:

    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)
        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, disk_info=disk_info)
        LOG.debug("Instance is running", instance=instance)
  • 相关阅读:
    [树状数组]JZOJ 4658 小Z调顺序
    [差分数组]JZOJ 3187 的士
    [BFS]JZOJ 4671 World Tour
    [BFS]JZOJ 4672 Graph Coloring
    [数学]JZOJ 4673 LCS again
    shell:正则表达式和文本处理器
    开发脚本自动部署及监控
    Linux中的重启命令
    nginx和nfs
    网络相关配置
  • 原文地址:https://www.cnblogs.com/james111/p/8399539.html
Copyright © 2011-2022 走看看