zoukankan      html  css  js  c++  java
  • openstack源码阅读基础:openstack中Nova组件RESTful请求的具体处理函数确定

           由于openstack项目是通过RESTful API向外提供服务,比如当我们通过openstackclient创建虚拟机时,openstackclient不直接调用创建虚拟机的具体python lib,而是发送一个http请求,当nova组件接收到请求后再调用具体函数和其它服务去创建虚拟机。这种机制使openstack具有很好的扩展性和可移植性,但也为我们阅读源码带来了困难,特别对于初学者想寻找函数入口添加远程断点都比较困难(openstack已经有上百万行代码了)。本文主要介绍如何去确定http请求和具体处理函数。

           首先,需要了解一些openstack wsgi框架的知识,可以参见http://blog.csdn.net/happyanger6/article/details/54518491。

           nova组件执行nova-api时根据api-paste.ini来构建web应用,截取的一段api-paste.ini文件如下:

    [composite:osapi_compute]
    use = call:nova.api.openstack.urlmap:urlmap_factory
    /: oscomputeversions
    # v21 is an exactly feature match for v2, except it has more stringent
    # input validation on the wsgi surface (prevents fuzzing early on the
    # API). It also provides new features via API microversions which are
    # opt into for clients. Unaware clients will receive the same frozen
    # v2 API feature set, but with some relaxed validation
    /v2: openstack_compute_api_v21_legacy_v2_compatible
    /v2.1: openstack_compute_api_v21
    ...
    [composite:openstack_compute_api_v21]
    use = call:nova.api.auth:pipeline_factory_v21
    noauth2 = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler noauth2 osapi_compute_app_v21
    keystone = cors http_proxy_to_wsgi compute_req_id faultwrap request_log sizelimit osprofiler authtoken keystonecontext osapi_compute_app_v21
    ...
    [app:osapi_compute_app_v21]
    paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory

            从中可以看到一个/v2.1/***的请求将首先被路由到openstack_compute_api_V21,经过一些filter处理后,最终由/nova/api/openstack/compute下的APIRouterV21类的factory方法处理。所以关于url如何路由到具体函数的确定应该在于APIRouterV21类。

          APIRouterV21位于/nova/api/openstack/compute/routes.py下,部分代码如下:

    from nova.api.openstack.compute import flavors
    # /nova/api/openstack/compute/flavor.py部分代码如下:
    class FlavorsController(wsgi.Controller):
        """Flavor controller for the OpenStack API."""
    
        _view_builder_class = flavors_view.ViewBuilder
    
        @validation.query_schema(schema.index_query)
        @wsgi.expected_errors(400)
        def index(self, req):
            """Return all flavors in brief."""
            limited_flavors = self._get_flavors(req)
            return self._view_builder.index(req, limited_flavors)
    
        @validation.query_schema(schema.index_query)
        @wsgi.expected_errors(400)
        def detail(self, req):
            """Return all flavors in detail."""
            limited_flavors = self._get_flavors(req)
            req.cache_db_flavors(limited_flavors)
            return self._view_builder.detail(req, limited_flavors)
    
    
    def _create_controller(main_controller, controller_list,
                          action_controller_list):
        """This is a helper method to create controller with a
        list of extended controller. This is for backward compatible
        with old extension interface. Finally, the controller for the
        same resource will be merged into single one controller.
        """
    
        controller = wsgi.Resource(main_controller())
        for ctl in controller_list:
            controller.register_extensions(ctl())
        for ctl in action_controller_list:
            controller.register_actions(ctl())
        return controller
    
    flavor_controller = functools.partial(_create_controller,
    # 如下类中包含detail()方法,查看/nova/api/openstack/compute/flavor.py知道 flavors.FlavorsController, [], [ flavor_manage.FlavorManageController, flavor_access.FlavorActionController ] )
    # NOTE(alex_xu): This is structure of this route list as below: # ( # ('Route path': { # 'HTTP method: [ # 'Controller', # 'The method of controller is used to handle this route' # ], # ... # }), # ... # ) # # Also note that this is ordered tuple. For example, the '/servers/detail' # should be in the front of '/servers/{id}', otherwise the request to # '/servers/detail' always matches to '/servers/{id}' as the id is 'detail'. ROUTE_LIST = ( # NOTE: This is a redirection from '' to '/'. The request to the '/v2.1' # or '/2.0' without the ending '/' will get a response with status code # '302' returned. ('/flavors', { 'GET': [flavor_controller, 'index'], 'POST': [flavor_controller, 'create'] }),
    # 我们以openstack flavor list为例,当执行这条语句后openstackclient就会发出一个后缀包含/flavors/detail的请求,并且http方法是GET;此时,这个请求就被路由到flavor_controller这个APP
    中,具体方法是detail()。关于flavor_controller如何确定,看前一段代码。 (
    '/flavors/detail', { 'GET': [flavor_controller, 'detail'] }), ('/flavors/{id}', { 'GET': [flavor_controller, 'show'], 'PUT': [flavor_controller, 'update'], 'DELETE': [flavor_controller, 'delete'] }), ('/flavors/{id}/action', { 'POST': [flavor_controller, 'action'] }), ('/flavors/{flavor_id}/os-extra_specs', { 'GET': [flavor_extraspec_controller, 'index'], 'POST': [flavor_extraspec_controller, 'create'] }), ('/flavors/{flavor_id}/os-extra_specs/{id}', { 'GET': [flavor_extraspec_controller, 'show'], 'PUT': [flavor_extraspec_controller, 'update'], 'DELETE': [flavor_extraspec_controller, 'delete'] }), ('/flavors/{flavor_id}/os-flavor-access', { 'GET': [flavor_access_controller, 'index'] }), ) class APIRouterV21(base_wsgi.Router): """Routes requests on the OpenStack API to the appropriate controller and method. The URL mapping based on the plain list `ROUTE_LIST` is built at here. """ def __init__(self, custom_routes=None): """:param custom_routes: the additional routes can be added by this parameter. This parameter is used to test on some fake routes primarily. """ super(APIRouterV21, self).__init__(nova.api.openstack.ProjectMapper()) if custom_routes is None: custom_routes = tuple()
    # 将ROUTE_LIST表中的路由规则加载到map中,可见ROUTE_LIST表中包含了路由信息
    for path, methods in ROUTE_LIST + custom_routes: # NOTE(alex_xu): The variable 'methods' is a dict in normal, since # the dict includes all the methods supported in the path. But # if the variable 'method' is a string, it means a redirection. # For example, the request to the '' will be redirect to the '/' in # the Nova API. To indicate that, using the target path instead of # a dict. The route entry just writes as "('', '/)". if isinstance(methods, str): self.map.redirect(path, methods) continue for method, controller_info in methods.items(): # TODO(alex_xu): In the end, I want to create single controller # instance instead of create controller instance for each # route. controller = controller_info[0]() action = controller_info[1] self.map.create_route(path, method, controller, action) @classmethod def factory(cls, global_config, **local_config): """Simple paste factory, :class:`nova.wsgi.Router` doesn't have one.""" return cls()

           具体确定方法:1.找到ROUTE_LIST中的url后缀,确定APP方法和ACTION名称;2.根据APP方法和ACTION名称确定具体执行函数。上文中我们就可以知道当openstackclient执行openstack flavor list时,最终会调用到/nova/api/openstack/compute/flavors.py中FlavorsController类的detail()方法。

  • 相关阅读:
    webpack常用插件及作用
    函数柯里化
    防抖和节流
    实现深拷贝
    实现new操作符
    关于js中断ajax请求
    从输入 URL 到页面加载完成,发生了什么?
    vue路由传参params和query的区别
    input输入框限制(座机,手机号码)
    判断当前页面是不是用户正在浏览的页面
  • 原文地址:https://www.cnblogs.com/hurongpu/p/8405886.html
Copyright © 2011-2022 走看看