原文:https://blog.csdn.net/happyanger6/article/details/54586463

首先,先分析WSGI应用的实现。

由前面的文章http://blog.csdn.net/happyanger6/article/details/54518491可知,WSGI应用的构建过程主要就是通过paste库的loadapp加载,因此核心就是分析这个过程。我们从neutron-server的起始代码开始逐步分析。

neutron-server的入口是:

neutron/cmd/eventlet/server/__init__.py:main

  1. def main():
  2. server.boot_server(_main_neutron_server)

boot_server在neutron/server/__init__.py中,它主要的功能就是解析命令行指定的配置文件,一般是"--config-file=/etc/neutron/neutron.conf",然后就执行_main_neutron_server。

neutron/cmd/eventlet/server/__init__.py::_main_neutron_serve

  1. def _main_neutron_server():
  2. if cfg.CONF.web_framework == 'legacy':
  3. wsgi_eventlet.eventlet_wsgi_server()
  4. else:
  5. wsgi_pecan.pecan_wsgi_server()

可以看到,接下来根据配置文件中配置的web框架方式,决定如何启动wsgi_server,传统的方式是通过eventlet,现在又新加入了pecan方式。默认情况下,还是使用的eventlet方式,因此接着分析eventlet_wsig_server。这并不响应我们分析WSGI应用的代码,因为这属于WSGI服务器的部分。

neutron/server/wsgi_eventlet.py:

  1. def eventlet_wsgi_server():
  2. neutron_api = service.serve_wsgi(service.NeutronApiService)
  3. start_api_and_rpc_workers(neutron_api)

这里也能看到,核心功能一部分是WSGI,另一部分就是rpc部分。这里将Netron提供的API功能封装成了NeutronApiService类。我们来看看serve_wsgi:

neutron/service.py:

  1. def serve_wsgi(cls):
  2.  
  3. try:
  4. service = cls.create()
  5. service.start()
  6. except Exception:
  7. with excutils.save_and_reraise_exception():
  8. LOG.exception(_LE('Unrecoverable error: please check log '
  9. 'for details.'))
  10.  
  11. return service

很明显,这是用NeutronApiService的类方法"create"来创建实例,然后"start"启动服务。接着分析下NeutronApiService的代码:

neutron/service.py:

  1. class NeutronApiService(WsgiService):
  2. """Class for neutron-api service."""
  3.  
  4. @classmethod
  5. def create(cls, app_name='neutron'):
  6.  
  7. # Setup logging early, supplying both the CLI options and the
  8. # configuration mapping from the config file
  9. # We only update the conf dict for the verbose and debug
  10. # flags. Everything else must be set up in the conf file...
  11. # Log the options used when starting if we're in debug mode...
  12.  
  13. config.setup_logging()
  14. service = cls(app_name)
  15. return service

可以看到NeutronApiService继承自"WsgiService",表明其是一个WSGI服务。然后类方法"create"构造了其实例并返回。

  1. class WsgiService(object):
  2. """Base class for WSGI based services.
  3.  
  4. For each api you define, you must also define these flags:
  5. :<api>_listen: The address on which to listen
  6. :<api>_listen_port: The port on which to listen
  7.  
  8. """
  9.  
  10. def __init__(self, app_name):
  11. self.app_name = app_name
  12. self.wsgi_app = None
  13.  
  14. def start(self):
  15. self.wsgi_app = _run_wsgi(self.app_name)
  16.  
  17. def wait(self):
  18. self.wsgi_app.wait()

构造过程很简单,只是简单的记录app_name,这里是"neutron",然后在start函数里真正加载WSGI APP,并运行服务,因此这才是我们分析的开始。

  1. def _run_wsgi(app_name):
  2. app = config.load_paste_app(app_name)
  3. if not app:
  4. LOG.error(_LE('No known API applications configured.'))
  5. return
  6. return run_wsgi_app(app)

load_paste_app从函数名,也可以明白它的作用就是加载paste定义的WSGI应用。

neutron/commom/config.py:

  1. def load_paste_app(app_name):
  2. """Builds and returns a WSGI app from a paste config file.
  3.  
  4. :param app_name: Name of the application to load
  5. """
  6. loader = wsgi.Loader(cfg.CONF)
  7. app = loader.load_app(app_name)
  8. return app

wsgi.Loader是从neutron.conf中读取deploy配置文件的路径,然后根据指定的配置文件来加载app,默认是"/etc/neutron/api-paste.ini"。然后通过deploy.loadapp来加载app,这个deploy就是PasteDeploy。

  1.  
  1. oslo_service/wsgi.py:
  2. def load_app(self, name):
  3. """Return the paste URLMap wrapped WSGI application.
  4.  
  5. :param name: Name of the application to load.
  6. :returns: Paste URLMap object wrapping the requested application.
  7. :raises: PasteAppNotFound
  8.  
  9. """
  10. try:
  11. LOG.debug("Loading app %(name)s from %(path)s",
  12. {'name': name, 'path': self.config_path})
  13. return deploy.loadapp("config:%s" % self.config_path, name=name)
  14. except LookupError:
  15. LOG.exception(_LE("Couldn't lookup app: %s"), name)
  16. raise PasteAppNotFound(name=name, path=self.config_path)

分析到这里可知,后面app的加载过程就是PasteDeploy的加载过程,有了上篇http://blog.csdn.net/happyanger6/article/details/54518491文章中的基础,我们对着源码来理解:

先来看下配置文件"/etc/neutron/api-paste.ini":

[composite:neutron]
use = egg:Paste#urlmap
/: neutronversions
/v2.0: neutronapi_v2_0

[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory
noauth = cors request_id catch_errors extensions neutronapiapp_v2_0
keystone = cors request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0

[filter:request_id]
paste.filter_factory = oslo_middleware:RequestId.factory

[filter:catch_errors]
paste.filter_factory = oslo_middleware:CatchErrors.factory

[filter:cors]
paste.filter_factory = oslo_middleware.cors:filter_factory
oslo_config_project = neutron

[filter:keystonecontext]
paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory

[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory

[filter:extensions]
paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory

[app:neutronversions]
paste.app_factory = neutron.api.versions:Versions.factory

[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory

首先是一个组合类型的section,这个section表明用Paste.urlmap来构造应用,因此会将对"/"的访问交给另外一个app[app:nuetronversion],而将对"/v2.0"的访问交给另外一个组合[composite:neutronapi_v2_0]生成的app。

通过这2个就构造了所有的WSGI应用,其中对"/"的访问,而通过neutron.api,version:Versions.factory类方法来构造一个对象,然后将请求交于这个对象处理,

具体而言就是交于对象的__call__方法。我们来看下是如何构造的:

neutron/api/versinos.py:

  1. class Versions(object):
  2.  
  3. @classmethod
  4. def factory(cls, global_config, **local_config):
  5. return cls(app=None)

通过factory方法构造一个对象,这个对象就是一个WSGI应用。它就处理对"/"的方法,而根据WSGI规范,会调用这个对象的__call__方法:

  1. @webob.dec.wsgify(RequestClass=wsgi.Request)
  2. def __call__(self, req):
  3. """Respond to a request for all Neutron API versions."""
  4. version_objs = [
  5. {
  6. "id": "v2.0",
  7. "status": "CURRENT",
  8. },
  9. ]
  10.  
  11. if req.path != '/':
  12. if self.app:
  13. return req.get_response(self.app)
  14. language = req.best_match_language()
  15. msg = _('Unknown API version specified')
  16. msg = oslo_i18n.translate(msg, language)
  17. return webob.exc.HTTPNotFound(explanation=msg)

..............

可以看到,通过@webob.dec.wsgify装饰器将__call__封装成符合WSGI规范的函数,这样"/"请求最终就是由"__call__"处理的。

这个"/"还比较简单,复杂的是对"/v2.0"的访问,这是大部分API的接口,我们看到这个组合段的app是用一个函数来构造的:

[composite:neutronapi_v2_0]
use = call:neutron.auth:pipeline_factory

use = call:...表示后面的是一个可调用对象,用它来构造最终的app.剩余的参数noauth,keystone等会作为参数传给pipeline_factory。

neutron/auth.py:

  1. def pipeline_factory(loader, global_conf, **local_conf):
  2. """Create a paste pipeline based on the 'auth_strategy' config option."""
  3. pipeline = local_conf[cfg.CONF.auth_strategy]
  4. pipeline = pipeline.split()
  5. filters = [loader.get_filter(n) for n in pipeline[:-1]]
  6. app = loader.get_app(pipeline[-1])
  7. filters.reverse()
  8. for filter in filters:
  9. app = filter(app)
  10. return app

先从配置文件neutron.conf中读取auth策略,默认是"auth_strategy = keystone",因此从api-paste.ini中取到的pipeline为"cors request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0"它们都定义在其它的"filter"或"app" section段中。

首先,从pipeline中获取最后一个app,即为"neutronapiapp_v2_0",从中加载app,然后依次用各个filter处理构造的app,并最终返回最后构造出的WSGI APP.

因此,我们按下面的顺序分析即可:

通过app_factory工厂方法来构造app,然后通过不同的filter_factory方法构造不同的filter对象,并将app依次通过filter对象处理。
[app:neutronapiapp_v2_0]
paste.app_factory = neutron.api.v2.router:APIRouter.factory

neutron/api/v2/router.py:

  1. class APIRouter(base_wsgi.Router):
  2.  
  3. @classmethod
  4. def factory(cls, global_config, **local_config):
  5. return cls(**local_config)

工厂方法构造了一个APIRouter对象作为app返回,因此分析其__init__方法:

  1. def __init__(self, **local_config):
  2. mapper = routes_mapper.Mapper()
  3. plugin = manager.NeutronManager.get_plugin()
  4. ext_mgr = extensions.PluginAwareExtensionManager.get_instance()
  5. ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP)
  6.  
  7. col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
  8. member_actions=MEMBER_ACTIONS)
  9. def _map_resource(collection, resource, params, parent=None):
  10. allow_bulk = cfg.CONF.allow_bulk
  11. allow_pagination = cfg.CONF.allow_pagination
  12. allow_sorting = cfg.CONF.allow_sorting
  13. controller = base.create_resource(
  14. collection, resource, plugin, params, allow_bulk=allow_bulk,
  15. parent=parent, allow_pagination=allow_pagination,
  16. allow_sorting=allow_sorting)
  17. path_prefix = None
  18. if parent:
  19. path_prefix = "/%s/{%s_id}/%s" % (parent['collection_name'],
  20. parent['member_name'],
  21. collection)
  22. mapper_kwargs = dict(controller=controller,
  23. requirements=REQUIREMENTS,
  24. path_prefix=path_prefix,
  25. **col_kwargs)
  26. return mapper.collection(collection, resource,
  27. **mapper_kwargs)
  28.  
  29. mapper.connect('index', '/', controller=Index(RESOURCES))
  30. for resource in RESOURCES:
  31. _map_resource(RESOURCES[resource], resource,
  32. attributes.RESOURCE_ATTRIBUTE_MAP.get(
  33. RESOURCES[resource], dict()))
  34. resource_registry.register_resource_by_name(resource)
  35.  
  36. for resource in SUB_RESOURCES:
  37. _map_resource(SUB_RESOURCES[resource]['collection_name'], resource,
  38. attributes.RESOURCE_ATTRIBUTE_MAP.get(
  39. SUB_RESOURCES[resource]['collection_name'],
  40. dict()),
  41. SUB_RESOURCES[resource]['parent'])
  42.  
  43. # Certain policy checks require that the extensions are loaded
  44. # and the RESOURCE_ATTRIBUTE_MAP populated before they can be
  45. # properly initialized. This can only be claimed with certainty
  46. # once this point in the code has been reached. In the event
  47. # that the policies have been initialized before this point,
  48. # calling reset will cause the next policy check to
  49. # re-initialize with all of the required data in place.
  50. policy.reset()
  51. super(APIRouter, self).__init__(mapper)

这个属于核心API的构造,因此详细分析一下。

  1. mapper = routes_mapper.Mapper()

首先,是声明一个routes.Mapper,这个上篇routes分析时讲过,用来构造URL和对应controller的映射,方便根据不同的URL路由给不同的controller处理。

  1. plugin = manager.NeutronManager.get_plugin()

然后,先构造了一个NeutronManger的单例,这个对象构造的过程中会根据配置加载核心插件,一般就是"Ml2Plugin",然后会加载以下几个默认的服务插件:

  1. neutron/plugings/common/constants.py:
  2.  
  3. DEFAULT_SERVICE_PLUGINS = {
  4. 'auto_allocate': 'auto-allocated-topology',
  5. 'tag': 'tag',
  6. 'timestamp_core': 'timestamp_core',
  7. 'network_ip_availability': 'network-ip-availability'
  8. }

然后是加载扩展插件:

  1. extensions.PluginAwareExtensionManager.get_instance()

扩展插件的加载会从neutron/extensions目录下加载所有插件。

通过上面2步就加载完了核心插件,服务插件和扩展插件,然后就是构造不同URL的controller。

  1. for resource in RESOURCES:
  2. _map_resource(RESOURCES[resource], resource,
  3. attributes.RESOURCE_ATTRIBUTE_MAP.get(
  4. RESOURCES[resource], dict()))

依次构造以下几个URL的controller."/networks","/subnets","/subnetpools","/ports"。

  1. RESOURCES = {'network': 'networks',
  2. 'subnet': 'subnets',
  3. 'subnetpool': 'subnetpools',
  4. 'port': 'ports'}
  5. 这个构造过程是通过_map_resource函数完成的,构造时会从配置文件中获取一些允许进行的操作,如"allow_bulk"
  6. 等。
  7.  
  8. 在构造具体的mapper时,会传递以下参数:
  1. col_kwargs = dict(collection_actions=COLLECTION_ACTIONS,
  2. member_actions=MEMBER_ACTIONS)
  1. COLLECTION_ACTIONS = ['index', 'create']
  2. MEMBER_ACTIONS = ['show', 'update', 'delete']

这些就是可以对URL发起的操作类型,这些操作最终会根据访问的URL(/networks,ports)转换为create_network,update_port这些函数交给对应的controller处理。这些后面还会分析。

具体的controller是通过base.create_resource生成的,来看下代码:

neutron/api/v2/base.py:

  1. class Controller(object):
  2. LIST = 'list'
  3. SHOW = 'show'
  4. CREATE = 'create'
  5. UPDATE = 'update'
  6. DELETE = 'delete'

..........

..........

  1. def create_resource(collection, resource, plugin, params, allow_bulk=False,
  2. member_actions=None, parent=None, allow_pagination=False,
  3. allow_sorting=False):
  4. controller = Controller(plugin, collection, resource, params, allow_bulk,
  5. member_actions=member_actions, parent=parent,
  6. allow_pagination=allow_pagination,
  7. allow_sorting=allow_sorting)
  8. return wsgi_resource.Resource(controller, FAULT_MAP)

可以看到,所有的Controller都是这个文件中定义的Controller类的实例对象,然后还会再将其调用wsgi_resouce.Resource.

neutron/api/v2/resouce.py:

  1. def Resource(controller, faults=None, deserializers=None, serializers=None,
  2. action_status=None):
  3. """Represents an API entity resource and the associated serialization and
  4. deserialization logic
  5. """
  6. default_deserializers = {'application/json': wsgi.JSONDeserializer()}
  7. default_serializers = {'application/json': wsgi.JSONDictSerializer()}
  8. format_types = {'json': 'application/json'}
  9. action_status = action_status or dict(create=201, delete=204)
  10.  
  11. default_deserializers.update(deserializers or {})
  12. default_serializers.update(serializers or {})
  13.  
  14. deserializers = default_deserializers
  15. serializers = default_serializers
  16. faults = faults or {}
  17.  
  18. @webob.dec.wsgify(RequestClass=Request)
  19. def resource(request):
  20. route_args = request.environ.get('wsgiorg.routing_args')
  21. if route_args:
  22. args = route_args[1].copy()
  23. else:
  24. args = {}
  25.  
  26. # NOTE(jkoelker) by now the controller is already found, remove
  27. # it from the args if it is in the matchdict
  28. args.pop('controller', None)
  29. fmt = args.pop('format', None)
  30. action = args.pop('action', None)
  31. content_type = format_types.get(fmt,
  32. request.best_match_content_type())
  33. language = request.best_match_language()
  34. deserializer = deserializers.get(content_type)
  35. serializer = serializers.get(content_type)
  36.  
  37. try:
  38. if request.body:
  39. args['body'] = deserializer.deserialize(request.body)['body']
  40.  
  41. method = getattr(controller, action)
  42.  
  43. result = method(request=request, **args)
  44. except (exceptions.NeutronException,
  45. netaddr.AddrFormatError,
  46. oslo_policy.PolicyNotAuthorized) as e:
  47. for fault in faults:
  48. if isinstance(e, fault):
  49. mapped_exc = faults[fault]
  50. break
  51. else:
  52. mapped_exc = webob.exc.HTTPInternalServerError
  53. if 400 <= mapped_exc.code < 500:
  54. LOG.info(_LI('%(action)s failed (client error): %(exc)s'),
  55. {'action': action, 'exc': e})
  56. else:
  57. LOG.exception(_LE('%s failed'), action)
  58. e = translate(e, language)
  59. body = serializer.serialize(
  60. {'NeutronError': get_exception_data(e)})
  61. kwargs = {'body': body, 'content_type': content_type}
  62. raise mapped_exc(**kwargs)
  63. except webob.exc.HTTPException as e:
  64. type_, value, tb = sys.exc_info()
  65. if hasattr(e, 'code') and 400 <= e.code < 500:
  66. LOG.info(_LI('%(action)s failed (client error): %(exc)s'),
  67. {'action': action, 'exc': e})
  68. else:
  69. LOG.exception(_LE('%s failed'), action)
  70. translate(e, language)
  71. value.body = serializer.serialize(
  72. {'NeutronError': get_exception_data(e)})
  73. value.content_type = content_type
  74. six.reraise(type_, value, tb)
  75. except NotImplementedError as e:
  76. e = translate(e, language)
  77. # NOTE(armando-migliaccio): from a client standpoint
  78. # it makes sense to receive these errors, because
  79. # extensions may or may not be implemented by
  80. # the underlying plugin. So if something goes south,
  81. # because a plugin does not implement a feature,
  82. # returning 500 is definitely confusing.
  83. body = serializer.serialize(
  84. {'NotImplementedError': get_exception_data(e)})
  85. kwargs = {'body': body, 'content_type': content_type}
  86. raise webob.exc.HTTPNotImplemented(**kwargs)
  87. except Exception:
  88. # NOTE(jkoelker) Everything else is 500
  89. LOG.exception(_LE('%s failed'), action)
  90. # Do not expose details of 500 error to clients.
  91. msg = _('Request Failed: internal server error while '
  92. 'processing your request.')
  93. msg = translate(msg, language)
  94. body = serializer.serialize(
  95. {'NeutronError': get_exception_data(
  96. webob.exc.HTTPInternalServerError(msg))})
  97. kwargs = {'body': body, 'content_type': content_type}
  98. raise webob.exc.HTTPInternalServerError(**kwargs)
  99.  
  100. status = action_status.get(action, 200)
  101. body = serializer.serialize(result)
  102. # NOTE(jkoelker) Comply with RFC2616 section 9.7
  103. if status == 204:
  104. content_type = ''
  105. body = None
  106.  
  107. return webob.Response(request=request, status=status,
  108. content_type=content_type,
  109. body=body)
  110. # NOTE(blogan): this is something that is needed for the transition to
  111. # pecan. This will allow the pecan code to have a handle on the controller
  112. # for an extension so it can reuse the code instead of forcing every
  113. # extension to rewrite the code for use with pecan.
  114. setattr(resource, 'controller', controller)
  115. return resource

可以看到,所有的请求都会先交于resouce函数处理,进行反序列化和请求参数的获取,最终再交给controller处理。

  1. action = args.pop('action', None)
  1. method = getattr(controller, action)
  2.  
  3. result = method(request=request, **args)

这样对于"/networks","/subnets","/subnetpools","/ports"都会最终交于controller对应的action函数,以create_network为例:

  1. def create(self, request, body=None, **kwargs):
  2. self._notifier.info(request.context,
  3. self._resource + '.create.start',
  4. body)
  5. return self._create(request, body, **kwargs)
  1. @db_api.retry_db_errors
  2. def _create(self, request, body, **kwargs):
  1. action = self._plugin_handlers[self.CREATE]
  2. _create中会从selc._plugin_handlers里取对应操作映射的action,这个映射是在controller的构造函数里创建的:
  1. self._plugin_handlers = {
  2. self.LIST: 'get%s_%s' % (parent_part, self._collection),
  3. self.SHOW: 'get%s_%s' % (parent_part, self._resource)
  4. }
  5. for action in [self.CREATE, self.UPDATE, self.DELETE]:
  6. self._plugin_handlers[action] = '%s%s_%s' % (action, parent_part,
  7. self._resource)

self._resource为"network","port"这些RESOUCES,因此create对应的为create_network,create_port。
在_create中最终调用do_create:

  1. obj_creator = getattr(self._plugin, action)
  2. try:
  3. if emulated:
  4. return self._emulate_bulk_create(obj_creator, request,
  5. body, parent_id)
  6. else:
  7. if self._collection in body:
  8. # This is weird but fixing it requires changes to the
  9. # plugin interface
  10. kwargs.update({self._collection: body})
  11. else:
  12. kwargs.update({self._resource: body})
  13. return obj_creator(request.context, **kwargs)

可以看到会从self._plugin里获取对应的action,这个_plugin就是核心插件Ml2Plugin,因此所有的核心操作最终都
会交给Ml2Plugin的对应create_network,create_port等方法执行。这样就明白了所有核心资源的创建删除等
操作最终都会将给Ml2Plugin的对应方法处理。

那么Ml2Plugin插件的处理过程又是如何呢?我们先来看下其构造函数:

  1. def __init__(self):
  2. # First load drivers, then initialize DB, then initialize drivers
  3. self.type_manager = managers.TypeManager()
  4. self.extension_manager = managers.ExtensionManager()
  5. self.mechanism_manager = managers.MechanismManager()
  6. super(Ml2Plugin, self).__init__()

可以看到它初始化了type_manager,mechanism_manager这2个管理器分别用来管理type和mechanism.其中不同的网络拓扑类型对应着Type Driver,而网络实现机制对应着Mechanism Driver。这两个管理器都是通过stevedor来管理的,这样就可以向查找标准库一样管理Type,Mechanism Driver了。

其中Type插件的加载会以'neutron.ml2.type_drivers'作为命名空间,Mechanism插件的加载会以'neutron.ml2.mechanism_drivers"作为命名空间。

这样实际上Ml2Plugin的不同操作会交给不同的type,mechanism插件处理,这样的架构十分灵活,比如:

  1. def create_network(self, context, network):
  2. result, mech_context = self._create_network_db(context, network)
  3. try:
  4. self.mechanism_manager.create_network_postcommit(mech_context)

创建网络会交由mechanism_manager处理。

这样就是APIRouter构造出的app的全部内容了,对于核心URL会交由resource->Controller-->Ml2Plugin--->Type,Mechanism层层处理。也很方便我们根据需要自己实现不同的Type,Mechanism Driver.

然后就是将这个app交由不同的filter处理,我们继续看这些filter干了些什么。第一个filter是:

[filter:extensions]
paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory

neutron/api/extensions.py:

  1. def plugin_aware_extension_middleware_factory(global_config, **local_config):
  2. """Paste factory."""
  3. def _factory(app):
  4. ext_mgr = PluginAwareExtensionManager.get_instance()
  5. return ExtensionMiddleware(app, ext_mgr=ext_mgr)
  6. return _factory

可以看到会用ExtensionMiddleware对象对app进行处理,这个处理和APIRouter的__init__函数处理类似,只不过这次是为扩展插件构造URL和Controller.这些扩展插件的Controller是ExtensionController。由于过程类似,就不再详细展开了,可以自行分析下。这样通过第一个filter就构造出了扩展插件的WSGI应用。

第二个filter:

[filter:authtoken]
paste.filter_factory = keystonemiddleware.auth_token:filter_factory

keystonemiddleware/auth_token/__init__.py:

  1. def filter_factory(global_conf, **local_conf):
  2. """Returns a WSGI filter app for use with paste.deploy."""
  3. conf = global_conf.copy()
  4. conf.update(local_conf)
  5.  
  6. def auth_filter(app):
  7. return AuthProtocol(app, conf)
  8. return auth_filter

可以看到对app封装了一个AuthProtocol对象。分析其代码不难看出其作用是对请求是否通过了认证进行检查,即是否携带合法token。这样后面的filter的作用也类似,就是对请求进行一些预处理,所有预处理都完成后再交由实际的Controller处理。

这样我们就分析完了整个WSGI应用的构造和处理过程,不难得出下面的处理流程:

OpenStack源码分析 Neutron源码分析(一)-----------Restful API篇的更多相关文章

  1. neutron源码分析(一)OpenStack环境搭建

    一.OpenStack安装 安装一个初始化的Mitaka版本的OpenStack环境用于分析,neutron源码 序号 角色 IP地址 版本 1 controller 172.16.15.161 mi ...

  2. nova创建虚拟机源码分析系列之一 restful api

    开始学习openstack源码,源码文件多,分支不少.按照学习的方法走通一条线是最好的,而网上推荐的最多的就是nova创建虚机的过程.从这一条线入手,能够贯穿openstack核心服务.写博文仅做学习 ...

  3. kafka源码分析之一server启动分析

    0. 关键概念 关键概念 Concepts Function Topic 用于划分Message的逻辑概念,一个Topic可以分布在多个Broker上. Partition 是Kafka中横向扩展和一 ...

  4. [ASP.NET]分析MVC5源码,并实现一个ASP.MVC

    本节内容不是MVC入门教程,主要讲MVC原理,实现一个和ASP.NET MVC类似基本原理的项目. MVC原理是依赖于ASP.NET管道事件基础之上的.对于这块,可阅读上节内容 [ASP.NET]谈谈 ...

  5. Android源码分析--CircleImageView 源码详解

    源码地址为 https://github.com/hdodenhof/CircleImageView 实际上就是一个圆形的imageview 的自定义控件.代码写的很优雅,实现效果也很好, 特此分析. ...

  6. 老李推荐:第6章8节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-小结

    老李推荐:第6章8节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-小结   本章我们重点围绕处理网络过来的命令的MonkeySourceNetwork这个事 ...

  7. 老李推荐:第6章7节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-注入按键事件实例

    老李推荐:第6章7节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-注入按键事件实例   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜 ...

  8. 老李推荐:第6章6节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-命令队列

    老李推荐:第6章6节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-命令队列   事件源在获得字串命令并把它翻译成对应的MonkeyEvent事件后,会把这些 ...

  9. 老李推荐:第6章4节《MonkeyRunner源码剖析》Monkey原理分析-事件源-事件源概览-翻译命令字串

    老李推荐:第6章4节<MonkeyRunner源码剖析>Monkey原理分析-事件源-事件源概览-翻译命令字串   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自 ...

随机推荐

  1. SQL添加事务处理

    --modi by lmt declare @errorSum int --记录错误数 begin Create table #CheckreqAccState(CheckReqID varchar( ...

  2. unity项目build成webgl时选择生成目录(解决方法)

    在unity里点击File>>Build Settings...>>勾选你要生成的Scenes>>选择webgl>>后面Development Buil ...

  3. 自己封装的一个类似axios的请求

    下载:https://pan.baidu.com/s/1k0-CpGGtfDTsCm85NMfuHA 使用: axios({ method:'post', baseURL:baseAddress, u ...

  4. Matlab -- Portfolio

    1.创建空 p = Portfolio; 2.需要了解 均值,方差,协方差实现 X为矩阵 均值 = mean(X): 中位数 = median(X): 方差 = var(X): 标准差 = std(X ...

  5. c++ 查缺补漏

    c++句柄 win句柄保存对象的实时地址(对象消失,句柄消失).指针保存固定地址(对象消失,内存泄漏) 超简单句柄类 指针型句柄 管理图书类句柄 c++ 枚举 enum Suit { Diamonds ...

  6. SQL合并

     SELECT   idName,SUM(Money),SUM(Revenue)FROM ( SELECT agentID AS idName,SUM(totalMoney) AS Money,0 A ...

  7. 多条件分类统计group by 显示数目为0的类别

    CREATE TABLE #authorTable(author VARCHAR(50)) INSERT #authorTable SELECT 'peter' UNION SELECT '捌妮' U ...

  8. WPF中定时器与进度条的配合使用

    WPF中定时器使用的注意事项: WPF需要使用System.Windows.Threading.DispatcherTimer定时器,而不能使用System.Timers.Timer定时器.因为Sys ...

  9. element-ui <el-input> 注册keyup事件,即键盘enter.

    <template> <!-- 需求:keyup事件一般用在input中,在input框中输入内容,用户点击键盘的enter,执行搜索 --> <div class=&q ...

  10. dede后台登陆不了、出现index.htm Not Found!、无更新模板,解析不了

    以下2个选项内设为空.