openstack Rocky系列之keystone:(二)keystone中API注册

主要说一下initialize_application中的application_factory

1     def loadapp():
2         app = application.application_factory(name)
3         return app

源码如下

 1 @fail_gracefully
 2 def application_factory(name='public'):
 3     if name not in ('admin', 'public'):
 4         raise RuntimeError('Application name (for base_url lookup) must be '
 5                            'either `admin` or `public`.')
 6 
 7     # NOTE(morgan): The Flask App actually dispatches nothing until we migrate
 8     # some routers to Flask-Blueprints, it is simply a placeholder.
 9     app = flask.Flask(name)
10     app.after_request(_add_vary_x_auth_token_header)
11 
12     # NOTE(morgan): Configure the Flask Environment for our needs.
13     app.config.update(
14         # We want to bubble up Flask Exceptions (for now)
15         PROPAGATE_EXCEPTIONS=True)
16 
17     # TODO(morgan): Convert Subsystems over to Flask-Native, for now, we simply
18     # dispatch to another "application" [e.g "keystone"]
19     # NOTE(morgan): as each router is converted to flask-native blueprint,
20     # remove from this list. WARNING ORDER MATTERS; ordered dict used to
21     # ensure sane ordering of the routers in the legacy-dispatch model.
22     dispatch_map = collections.OrderedDict()
23 
24     # Load in Healthcheck and map it to /healthcheck
25     hc_app = healthcheck.Healthcheck.app_factory(
26         {}, oslo_config_project='keystone')
27     dispatch_map['/healthcheck'] = hc_app
28 
29     # More legacy code to instantiate all the magic for the dispatchers.
30     # The move to blueprints (FLASK) will allow this to be eliminated.
31     _routers = []
32     sub_routers = []
33     mapper = routes.Mapper()
34     for api_routers in ALL_API_ROUTERS:
35         moved_found = [pfx for
36                        pfx in getattr(api_routers, '_path_prefixes', [])
37                        if pfx in _MOVED_API_PREFIXES]
38         if moved_found:
39             raise RuntimeError('An API Router is trying to register path '
40                                'prefix(s) `%(pfx)s` that is handled by the '
41                                'native Flask app. Keystone cannot '
42                                'start.' %
43                                {'pfx': ', '.join([p for p in moved_found])})
44 
45         routers_instance = api_routers.Routers()
46         _routers.append(routers_instance)
47         routers_instance.append_v3_routers(mapper, sub_routers)
48     # TODO(morgan): Remove "API version registration". For now this is kept
49     # for ease of conversion (minimal changes)
50     keystone.api.discovery.register_version('v3')
51 
52     # NOTE(morgan): We add in all the keystone.api blueprints here, this
53     # replaces (as they are implemented) the legacy dispatcher work.
54     for api in keystone.api.__apis__:
55         for api_bp in api.APIs:
56             api_bp.instantiate_and_register_to_app(app)
57 
58     # Build and construct the dispatching for the Legacy dispatching model
59     sub_routers.append(_ComposibleRouterStub(_routers))
60     legacy_dispatcher = keystone_wsgi.ComposingRouter(mapper, sub_routers)
61 
62     for pfx in itertools.chain(*[rtr.Routers._path_prefixes for
63                                  rtr in ALL_API_ROUTERS]):
64         import pdb;pdb.set_trace()
65         dispatch_map['/v3/%s' % pfx] = legacy_dispatcher
66     app.wsgi_app = KeystoneDispatcherMiddleware(
67         app.wsgi_app,
68         dispatch_map)
69     return app
fail_gracefully是个装饰器,主要是为了防止函数报错,抓一下exception,并记录报错信息,其中又使用了一个functools.wraps的装饰器,防止fail_gracefully对被装饰的函数造成影响
主要看27行dispatch_map = collections.OrderedDict(),这里创建了一个dispatch_map的变量,这个变量用于记录所有的路由分发

33行创建了一个router.Mapper()的对象mapper
 1     for api_routers in ALL_API_ROUTERS:
 2         moved_found = [pfx for
 3                        pfx in getattr(api_routers, '_path_prefixes', [])
 4                        if pfx in _MOVED_API_PREFIXES]
 5         if moved_found:
 6             raise RuntimeError('An API Router is trying to register path '
 7                                'prefix(s) `%(pfx)s` that is handled by the '
 8                                'native Flask app. Keystone cannot '
 9                                'start.' %
10                                {'pfx': ', '.join([p for p in moved_found])})
11 
12         routers_instance = api_routers.Routers()
13         _routers.append(routers_instance)
14         routers_instance.append_v3_routers(mapper, sub_routers)

ALL_API_ROUTER是一个包含所有处理路由的类的列表,这个循环中主要是过滤掉所有的被移除的API,api_routes.Routers()继承wsgi.RoutersBase,并通过append_v3_routers将自己的路由信息记录到mapper中,其中sub_routers是记录顶层路由下的路由

sub_routers.append(_ComposibleRouterStub(_routers))这行代码将_router整合到sub_routers中
legacy_dispatcher = keystone_wsgi.ComposingRouter(mapper, sub_routers)这行代码最终调用的源代码如下
 1 class Router(wsgi.ComposableRouter):
 2     def __init__(self, controller, collection_key, key,
 3                  resource_descriptions=None,
 4                  is_entity_implemented=True,
 5                  method_template=None):
 6         self.controller = controller
 7         self.key = key
 8         self.collection_key = collection_key
 9         self._resource_descriptions = resource_descriptions
10         self._is_entity_implemented = is_entity_implemented
11         self.method_template = method_template or '%s'
12 
13     def add_routes(self, mapper):
14         collection_path = '/%(collection_key)s' % {
15             'collection_key': self.collection_key}
16         entity_path = '/%(collection_key)s/{%(key)s_id}' % {
17             'collection_key': self.collection_key,
18             'key': self.key}
19 
20         mapper.connect(
21             collection_path,
22             controller=self.controller,
23             action=self.method_template % 'create_%s' % self.key,
24             conditions=dict(method=['POST']))
25         mapper.connect(
26             collection_path,
27             controller=self.controller,
28             action=self.method_template % 'list_%s' % self.collection_key,
29             conditions=dict(method=['GET', 'HEAD']))
30         mapper.connect(
31             entity_path,
32             controller=self.controller,
33             action=self.method_template % 'get_%s' % self.key,
34             conditions=dict(method=['GET', 'HEAD']))
35         mapper.connect(
36             entity_path,
37             controller=self.controller,
38             action=self.method_template % 'update_%s' % self.key,
39             conditions=dict(method=['PATCH']))
40         mapper.connect(
41             entity_path,
42             controller=self.controller,
43             action=self.method_template % 'delete_%s' % self.key,
44             conditions=dict(method=['DELETE']))
45 
46         # Add the collection resource and entity resource to the resource
47         # descriptions.
48 
49         collection_rel = json_home.build_v3_resource_relation(
50             self.collection_key)
51         rel_data = {'href': collection_path, }
52         self._resource_descriptions.append((collection_rel, rel_data))
53         json_home.JsonHomeResources.append_resource(collection_rel, rel_data)
54 
55         if self._is_entity_implemented:
56             entity_rel = json_home.build_v3_resource_relation(self.key)
57             id_str = '%s_id' % self.key
58             id_param_rel = json_home.build_v3_parameter_relation(id_str)
59             entity_rel_data = {
60                 'href-template': entity_path,
61                 'href-vars': {
62                     id_str: id_param_rel,
63                 },
64             }
65             self._resource_descriptions.append((entity_rel, entity_rel_data))
66             json_home.JsonHomeResources.append_resource(
67                 entity_rel, entity_rel_data)

通过add_router为route添加mapper

1     for pfx in itertools.chain(*[rtr.Routers._path_prefixes for
2                                  rtr in ALL_API_ROUTERS]):
3         dispatch_map['/v3/%s' % pfx] = legacy_dispatcher

这里主要添加dispatch_map中的前缀和处理代码的映射关系,涉及到itertools.chain()用法的小技巧

最后一行的KeystoneDispatcherMiddleware类负责API的分发,同样是通过__call__()这个魔法函数

posted @ 2019-07-22 11:08  苏陌宁  阅读(456)  评论(0编辑  收藏  举报