首页 > 网络 > 云计算 >

世界杯外围投注官网

2016-11-05

OpenstackNova-MV2API启动与extension加载。在M版中,nova的v2 API默认是使用通过兼容性包装的v21 API。本文只是世界杯外围投注网站随笔,结合世界杯外围投注网站阅读时效果更佳。nova-api 入口 nova cmd api main()。

Openstack Nova-M V2API启动与extension加载

标签(空格分隔): Openstack

在M版中,nova的v2 API默认是使用通过兼容性包装的v21 API。本文只是世界杯外围投注网站随笔,结合世界杯外围投注网站阅读时效果更佳。
nova-api 入口 nova.cmd.api.main()

def main():
***
launcher = service.process_launcher()
started = 0
for api in CONF.enabled_apis:
    should_use_ssl = api in CONF.enabled_ssl_apis
    try:
        server = service.WSGIService(api, use_ssl=should_use_ssl)
        launcher.launch_service(server, workers=server.workers or 1)
        started += 1
    except exception.PasteAppNotFound as ex:
        log.warning(
            _LW("%s. ``enabled_apis`` includes bad values. "
                "Fix to remove this warning."), six.text_type(ex))
if started == 0:
    log.error(_LE(&世界杯外围投注官网39;No APIs were started. &世界杯外围投注官网39;
                  &世界杯外围投注官网39;Check the enabled_apis config option.&世界杯外围投注官网39;))
    sys.exit(1)
launcher.wait()

来看server = service.WSGIService(api, use_ssl=should_use_ssl),其中以 api = osapi_compute 为例,来看service.WSGIService类:

class WSGIService(service.Service):
"""Provides ability to launch API from a &世界杯外围投注官网39;paste&世界杯外围投注官网39; configuration."""
def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
    """Initialize, but do not start the WSGI server.
    :param name: The name of the WSGI server given to the loader.
    :param loader: Loads the WSGI application using the given name.
    :returns: None
    """
    self.name = name
    世界杯外围投注官网 NOTE(danms): Name can be metadata, os_compute, or ec2, per
    世界杯外围投注官网 nova.service&世界杯外围投注官网39;s enabled_apis
    世界杯外围投注官网 nova-osapi-compute
    self.binary = &世界杯外围投注官网39;nova-%s&世界杯外围投注官网39; % name
    self.topic = None
    self.manager = self._get_manager()
    self.loader = loader or wsgi.Loader()
    self.app = self.loader.load_app(name)
    世界杯外围投注官网 inherit all compute_api worker counts from osapi_compute
    if name.startswith(&世界杯外围投注官网39;openstack_compute_api&世界杯外围投注官网39;):
        wname = &世界杯外围投注官网39;osapi_compute&世界杯外围投注官网39;
    else:
        wname = name
    self.host = getattr(CONF, &世界杯外围投注官网39;%s_listen&世界杯外围投注官网39; % name, "0.0.0.0")
    self.port = getattr(CONF, &世界杯外围投注官网39;%s_listen_port&世界杯外围投注官网39; % name, 0)
    self.workers = (getattr(CONF, &世界杯外围投注官网39;%s_workers&世界杯外围投注官网39; % wname, None) or
                    processutils.get_worker_count())
    if self.workers and self.workers < 1:
        worker_name = &世界杯外围投注官网39;%s_workers&世界杯外围投注官网39; % name
        msg = (_("%(worker_name)s value of %(workers)s is invalid, "
                 "must be greater than 0") %
               {&世界杯外围投注官网39;worker_name&世界杯外围投注官网39;: worker_name,
                &世界杯外围投注官网39;workers&世界杯外围投注官网39;: str(self.workers)})
        raise exception.InvalidInput(msg)
    self.use_ssl = use_ssl
    self.server = wsgi.Server(name,
                              self.app,
                              host=self.host,
                              port=self.port,
                              use_ssl=self.use_ssl,
                              max_url_len=max_url_len)
    世界杯外围投注官网 Pull back actual port used
    self.port = self.server.port
    self.backdoor_port = None

来看self.app = self.loader.load_app(name),此处loader为wsgi模块中Loader的实例,初始化时提供了self.config_path,为api-paster.ini的路径,来看load_app方法:

    def load_app(self, name):
    """Return the paste URLMap wrapped WSGI application.
    :param name: Name of the application to load.
    :returns: Paste URLMap object wrapping the requested application.
    :raises: `nova.exception.PasteAppNotFound`
    """
    try:
        LOG.debug("Loading app %(name)s from %(path)s",
                  {&世界杯外围投注官网39;name&世界杯外围投注官网39;: name, &世界杯外围投注官网39;path&世界杯外围投注官网39;: self.config_path})
        return deploy.loadapp("config:%s" % self.config_path, name=name)
    except LookupError:
        LOG.exception(_LE("Couldn&世界杯外围投注官网39;t lookup app: %s"), name)
        raise exception.PasteAppNotFound(name=name, path=self.config_path)

这个方法调用了pastedeploy模块的deploy.loadapp方法,并提供了两个参数: 加了config:前缀的api-paste.ini文件的路径以及
name = osapi_compute
下面给出M版自带api-paste.ini模板中相关部分:

[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v2: openstack_compute_api_v21_legacy_v2_compatible
/v2.1: openstack_compute_api_v21
[composite:openstack_compute_api_v21_legacy_v2_compatible]
use = call:nova.api.auth:pipeline_factory_v21
noauth2 = cors compute_req_id faultwrap sizelimit noauth2 legacy_v2_compatible osapi_compute_app_v21
keystone = cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21
[filter:legacy_v2_compatible]
paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory
[app:osapi_compute_app_v21]
paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory

具体paste.deploy原理大家有兴趣可以去官方文档查看详细手册,这里只简单介绍一些简单具体的实现,先看用于处理osapi_compute的nova.api.openstack.urlmap.urlmap_factory方法:

def urlmap_factory(loader, global_conf, **local_conf):
if &世界杯外围投注官网39;not_found_app&世界杯外围投注官网39; in local_conf:
    not_found_app = local_conf.pop(&世界杯外围投注官网39;not_found_app&世界杯外围投注官网39;)
else:
    not_found_app = global_conf.get(&世界杯外围投注官网39;not_found_app&世界杯外围投注官网39;)
if not_found_app:
    not_found_app = loader.get_app(not_found_app, global_conf=global_conf)
urlmap = URLMap(not_found_app=not_found_app)
for path, app_name in local_conf.items():
    path = paste.urlmap.parse_path_expression(path)
    app = loader.get_app(app_name, global_conf=global_conf)
    urlmap[path] = app
return urlmap

其中抓取日志,可以看到如下参数:

global_conf = {&世界杯外围投注官网39;__file__&世界杯外围投注官网39;: &世界杯外围投注官网39;/etc/nova/api-paste.ini&世界杯外围投注官网39;, &世界杯外围投注官网39;here&世界杯外围投注官网39;: &世界杯外围投注官网39;/etc/nova&世界杯外围投注官网39;}
local_conf = {&世界杯外围投注官网39;/v2&世界杯外围投注官网39;: &世界杯外围投注官网39;openstack_compute_api_v21_legacy_v2_compatible&世界杯外围投注官网39;, &世界杯外围投注官网39;/&世界杯外围投注官网39;: &世界杯外围投注官网39;oscomputeversions&世界杯外围投注官网39;, &世界杯外围投注官网39;/v2.1&世界杯外围投注官网39;: &世界杯外围投注官网39;openstack_compute_api_v21&世界杯外围投注官网39;}

此处loader仍为之前创建的loader实例,我们在此只关注v2的api相关信息,看如下世界杯外围投注网站:

for path, app_name in local_conf.items():
    path = paste.urlmap.parse_path_expression(path)
    app = loader.get_app(app_name, global_conf=global_conf)
    urlmap[path] = app

当local_conf.items()循环到key=&lsquo;/v2&rsquo;时,path经过paste.urlmap.parse_path_expression方法处理后仍然为
&lsquo;/v2&rsquo;,app_name = &lsquo;openstack_compute_api_v21_legacy_v2_compatible&rsquo;
此时注意运行至app = loader.get_app(app_name, global_conf=global_conf)这行世界杯外围投注网站时,deploy内部世界杯外围投注网站逻辑会使用nova.api.auth.pipeline_factory_v21方法:

def pipeline_factory_v21(loader, global_conf, **local_conf):
"""A paste pipeline replica that keys off of auth_strategy."""
return _load_pipeline(loader, local_conf[CONF.auth_strategy].split())

再来看此时的参数:

global_conf = {&世界杯外围投注官网39;__file__&世界杯外围投注官网39;: &世界杯外围投注官网39;/etc/nova/api-paste.ini&世界杯外围投注官网39;, &世界杯外围投注官网39;here&世界杯外围投注官网39;: &世界杯外围投注官网39;/etc/nova&世界杯外围投注官网39;}
local_conf = {&世界杯外围投注官网39;keystone&世界杯外围投注官网39;: &世界杯外围投注官网39;cors compute_req_id faultwrap sizelimit authtoken keystonecontext legacy_v2_compatible osapi_compute_app_v21&世界杯外围投注官网39;, &世界杯外围投注官网39;noauth2&世界杯外围投注官网39;: &世界杯外围投注官网39;cors compute_req_id faultwrap sizelimit noauth2 legacy_v2_compatible osapi_compute_app_v21&世界杯外围投注官网39;}

这时,通过CONF.auth_strategy可以指定所使用的auth策略。在此我们选择简单的noauth2策略来向下进行,进入_load_pipeline方法:

def _load_pipeline(loader, pipeline):
filters = [loader.get_filter(n) for n in pipeline[:-1]]
app = loader.get_app(pipeline[-1])
filters.reverse()
for filter in filters:
    app = filter(app)
return app

filters = [loader.get_filter(n) for n in pipeline[:-1]]会根据noauth2的选项获取
cors compute_req_id faultwrap sizelimit noauth2 legacy_v2_compatible这些filter的类
app = loader.get_app(pipeline[-1])会取出osapi_compute_app_v21对应的类

filters.reverse()将filter次序倒置
for filter in filters:app = filter(app)使用各filter对app进行处理
下面看legacy_v2_compatible这个filter:

[filter:legacy_v2_compatible]
paste.filter_factory = nova.api.openstack:LegacyV2CompatibleWrapper.factory

对应为nova.api.openstack.LegacyV2CompatibleWrapper.factory方法:

@classmethod
def factory(cls, global_config, **local_config):
    """
    """
    def _factory(app):
        return cls(app, **local_config)
    return _factory

世界杯外围投注网站获取了LegacyV2CompatibleWrapper这个类,并将osapi_compute_app_v21类作为app参数传入:

class LegacyV2CompatibleWrapper(base_wsgi.Middleware):
def _filter_request_headers(self, req):
    """For keeping same behavior with v2 API, ignores microversions
    HTTP header X-OpenStack-Nova-API-Version in the request.
    """
    if wsgi.API_VERSION_REQUEST_HEADER in req.headers:
        del req.headers[wsgi.API_VERSION_REQUEST_HEADER]
    return req
def _filter_response_headers(self, response):
    """For keeping same behavior with v2 API, filter out microversions
    HTTP header and microversions field in header &世界杯外围投注官网39;Vary&世界杯外围投注官网39;.
    """
    if wsgi.API_VERSION_REQUEST_HEADER in response.headers:
        del response.headers[wsgi.API_VERSION_REQUEST_HEADER]
    if &世界杯外围投注官网39;Vary&世界杯外围投注官网39; in response.headers:
        vary_headers = response.headers[&世界杯外围投注官网39;Vary&世界杯外围投注官网39;].split(&世界杯外围投注官网39;,&世界杯外围投注官网39;)
        filtered_vary = []
        for vary in vary_headers:
            vary = vary.strip()
            if vary == wsgi.API_VERSION_REQUEST_HEADER:
                continue
            filtered_vary.append(vary)
        if filtered_vary:
            response.headers[&世界杯外围投注官网39;Vary&世界杯外围投注官网39;] = &世界杯外围投注官网39;,&世界杯外围投注官网39;.join(filtered_vary)
        else:
            del response.headers[&世界杯外围投注官网39;Vary&世界杯外围投注官网39;]
    return response
@webob.dec.wsgify(RequestClass=wsgi.Request)
def __call__(self, req):
    req.set_legacy_v2()
    req = self._filter_request_headers(req)
    response = req.get_response(self.application)
    return self._filter_response_headers(response)

其中装饰器@webob.dec.wsgify(RequestClass=wsgi.Request)用来将osapi_compute_app_v21包装,使其拥有WSGI application特性
在后续世界杯外围投注网站中,使用类中定义的方法对environ、req header进行处理,使其同v2的API具有兼容性。

最后return app,返回了经过层层包装后的osapi_compute_app_v21这个app:

[app:osapi_compute_app_v21]
paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory

这个app指向了nova.api.openstack.compute.APIRouterV21.factory方法:

@classmethod
def factory(cls, global_config, **local_config):
    """Simple paste factory, :class:`nova.wsgi.Router` doesn&世界杯外围投注官网39;t have one."""
    return cls()

返回了nova.api.openstack.compute.APIRouterV21这个类本身:

class APIRouterV21(nova.api.openstack.APIRouterV21):
"""Routes requests on the OpenStack API to the appropriate controller
and method.
"""
def __init__(self, init_only=None):
    self._loaded_extension_info = extension_info.LoadedExtensionInfo()
    super(APIRouterV21, self).__init__(init_only)
def _register_extension(self, ext):
    return self.loaded_extension_info.register_extension(ext.obj)
@property
def loaded_extension_info(self):
    return self._loaded_extension_info

首先初始化了一个self.extensions = {},之后调用了父类的__init__:

    def __init__(self, init_only=None, v3mode=False):
    世界杯外围投注官网 TODO(cyeoh): bp v3-api-extension-framework. Currently load
    世界杯外围投注官网 all extensions but eventually should be able to exclude
    世界杯外围投注官网 based on a config file
    世界杯外围投注官网 TODO(oomichi): We can remove v3mode argument after moving all v3 APIs
    世界杯外围投注官网 to v2.1.
    def _check_load_extension(ext):
        if (self.init_only is None or ext.obj.alias in
            self.init_only) and isinstance(ext.obj,
                                           extensions.V21APIExtensionBase):
            世界杯外围投注官网 Check whitelist is either empty or if not then the extension
            世界杯外围投注官网 is in the whitelist
            if (not CONF.osapi_v21.extensions_whitelist or
                        ext.obj.alias in CONF.osapi_v21.extensions_whitelist):
                世界杯外围投注官网 Check the extension is not in the blacklist
                blacklist = CONF.osapi_v21.extensions_blacklist
                if ext.obj.alias not in blacklist:
                    return self._register_extension(ext)
        return False
    if not CONF.osapi_v21.enabled:
        LOG.info(_LI("V2.1 API has been disabled by configuration"))
        LOG.warning(_LW("In the M release you must run the v2.1 API."))
        return
    if (CONF.osapi_v21.extensions_blacklist or
            CONF.osapi_v21.extensions_whitelist):
        LOG.warning(
            _LW(&世界杯外围投注官网39;In the M release you must run all of the API. &世界杯外围投注官网39;
                &世界杯外围投注官网39;The concept of API extensions will be removed from &世界杯外围投注官网39;
                &世界杯外围投注官网39;the codebase to ensure there is a single Compute API.&世界杯外围投注官网39;))
    self.init_only = init_only
    LOG.debug("v21 API Extension Blacklist: %s",
              CONF.osapi_v21.extensions_blacklist)
    LOG.debug("v21 API Extension Whitelist: %s",
              CONF.osapi_v21.extensions_whitelist)
    in_blacklist_and_whitelist = set(
        CONF.osapi_v21.extensions_whitelist).intersection(
        CONF.osapi_v21.extensions_blacklist)
    if len(in_blacklist_and_whitelist) != 0:
        LOG.warning(_LW("Extensions in both blacklist and whitelist: %s"),
                    list(in_blacklist_and_whitelist))
    self.api_extension_manager = stevedore.enabled.EnabledExtensionManager(
        namespace=self.api_extension_namespace(),
        check_func=_check_load_extension,
        invoke_on_load=True,
        invoke_kwds={"extension_info": self.loaded_extension_info})
    if v3mode:
        mapper = PlainMapper()
    else:
        mapper = ProjectMapper()
    self.resources = {}
    世界杯外围投注官网 NOTE(cyeoh) Core API support is rewritten as extensions
    世界杯外围投注官网 but conceptually still have core
    if list(self.api_extension_manager):
        世界杯外围投注官网 NOTE(cyeoh): Stevedore raises an exception if there are
        世界杯外围投注官网 no plugins detected. I wonder if this is a bug.
        self._register_resources_check_inherits(mapper)
        self.api_extension_manager.map(self._register_controllers)
    missing_core_extensions = self.get_missing_core_extensions(
        self.loaded_extension_info.get_extensions().keys())
    if not self.init_only and missing_core_extensions:
        LOG.critical(_LC("Missing core API extensions: %s"),
                     missing_core_extensions)
        raise exception.CoreAPIMissing(
            missing_apis=missing_core_extensions)
    LOG.info(_LI("Loaded extensions: %s"),
             sorted(self.loaded_extension_info.get_extensions().keys()))
    super(APIRouterV21, self).__init__(mapper)

其中,我们先注意这一段:

self.api_extension_manager = stevedore.enabled.EnabledExtensionManager(
            namespace=self.api_extension_namespace(),
            check_func=_check_load_extension,
            invoke_on_load=True,
            invoke_kwds={"extension_info": self.loaded_extension_info})

其中,stevedore.enabled.EnabledExtensionManager类:

class EnabledExtensionManager(ExtensionManager):
"""Loads only plugins that pass a check function.
"""
def __init__(self, namespace, check_func, invoke_on_load=False,
             invoke_args=(), invoke_kwds={},
             propagate_map_exceptions=False,
             on_load_failure_callback=None,
             verify_requirements=False,):
    self.check_func = check_func
    super(EnabledExtensionManager, self).__init__(
        namespace,
        invoke_on_load=invoke_on_load,
        invoke_args=invoke_args,
        invoke_kwds=invoke_kwds,
        propagate_map_exceptions=propagate_map_exceptions,
        on_load_failure_callback=on_load_failure_callback,
        verify_requirements=verify_requirements,
    )

定义了self.check_func为_check_load_extension,根据世界杯外围投注网站可知对于其接受的ext这个参数,如果ext.obj是extensions.V21APIExtensionBase的实例、当白名单开启时ext.obj.alias在白名单中或者当黑名单开启时ext.obj.alias不在黑名单这三个条件均满足时,对其调用_register_extension()方法。具体深入我们后续看到时继续解读。下面继续看调用的父类ExtensionManager的__init__:

class ExtensionManager(object):
"""Base class for all of the other managers.
"""
def __init__(self, namespace,
             invoke_on_load=False,
             invoke_args=(),
             invoke_kwds={},
             propagate_map_exceptions=False,
             on_load_failure_callback=None,
             verify_requirements=False):
    self._init_attributes(
        namespace,
        propagate_map_exceptions=propagate_map_exceptions,
        on_load_failure_callback=on_load_failure_callback)
    extensions = self._load_plugins(invoke_on_load,
                                    invoke_args,
                                    invoke_kwds,
                                    verify_requirements)
    self._init_plugins(extensions)

进来后self._init_attributes方法简单的把传入的三个参数映射为类参数,即self.xxx。然后看
self._load_plugins这个方法:

    def _load_plugins(self, invoke_on_load, invoke_args, invoke_kwds,
                  verify_requirements):
    extensions = []
    for ep in self._find_entry_points(self.namespace):
        LOG.debug(&世界杯外围投注官网39;found extension %r&世界杯外围投注官网39;, ep)
        try:
            ext = self._load_one_plugin(ep,
                                        invoke_on_load,
                                        invoke_args,
                                        invoke_kwds,
                                        verify_requirements,
                                        )
            if ext:
                extensions.append(ext)
        except (KeyboardInterrupt, AssertionError):
            raise
        except Exception as err:
            if self._on_load_failure_callback is not None:
                self._on_load_failure_callback(self, ep, err)
            else:
                世界杯外围投注官网 Log the reason we couldn&世界杯外围投注官网39;t import the module,
                世界杯外围投注官网 usually without a traceback. The most common
                世界杯外围投注官网 reason is an ImportError due to a missing
                世界杯外围投注官网 dependency, and the error message should be
                世界杯外围投注官网 enough to debug that.  If debug logging is
                世界杯外围投注官网 enabled for our logger, provide the full
                世界杯外围投注官网 traceback.
                LOG.error(&世界杯外围投注官网39;Could not load %r: %s&世界杯外围投注官网39;, ep.name, err,
                          exc_info=LOG.isEnabledFor(logging.DEBUG))
    return extensions

self._find_entry_points方法中:

    def _find_entry_points(self, namespace):
    if namespace not in self.ENTRY_POINT_CACHE:
        eps = list(pkg_resources.iter_entry_points(namespace))
        self.ENTRY_POINT_CACHE[namespace] = eps
    return self.ENTRY_POINT_CACHE[namespace]

其中namespace = &lsquo;nova.api.v21.extensions&rsquo;,使用了pkg_resources模块中iter_entry_points方法。这个方法
返回了一个生成器,每次返回一个EntryPoint实例,所以得到eps的值为nova.api.v21.extensions这个entry point section所生成的entrypoint实例列表。entrypoint组可在egg中entry_points.txt查到相关:

[nova.api.v21.extensions]
access_ips = nova.api.openstack.compute.access_ips:AccessIPs
admin_actions = nova.api.openstack.compute.admin_actions:AdminActions
admin_password = nova.api.openstack.compute.admin_password:AdminPassword
agents = nova.api.openstack.compute.agents:Agents
...不一一列出

其中,section中定义的各列以name=module:attr [extras]格式,传入到EntryPoint类的__init__中,生成实例。
继续看self._load_one_plugin:

    def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds,
                     verify_requirements):
    世界杯外围投注官网 NOTE(dhellmann): Using require=False is deprecated in
    世界杯外围投注官网 setuptools 11.3.
    if hasattr(ep, &世界杯外围投注官网39;resolve&世界杯外围投注官网39;) and hasattr(ep, &世界杯外围投注官网39;require&世界杯外围投注官网39;):
        if verify_requirements:
            ep.require()
        plugin = ep.resolve()
    else:
        plugin = ep.load(require=verify_requirements)
    if invoke_on_load:
        obj = plugin(*invoke_args, **invoke_kwds)
    else:
        obj = None
    return Extension(ep.name, ep, plugin, obj)

这段世界杯外围投注网站里面使用的方法很简单,最后获得的各变量为:

plugin = 扩展模块中定义为attr的类
obj = attr类的实例,其中传入了参数extension_info = self.loaded_extension_info,即nova.api.openstack.compute.extension_info中LoadedExtensionInfo类实例

方法最后返回了一个stevedore.extension.Extension的实例,举例说明,对于

access_ips = nova.api.openstack.compute.access_ips:AccessIPs

会生成

stevedore.Extension(&世界杯外围投注官网39;access_ips&世界杯外围投注官网39;, pkg_resource.EntryPoint(&世界杯外围投注官网39;access_ips&世界杯外围投注官网39;, &世界杯外围投注官网39;nova.api.openstack.compute.access_ips&世界杯外围投注官网39;, attrs=[&世界杯外围投注官网39;AccessIPs&世界杯外围投注官网39;]), nova.api.openstack.compute.access_ips.AccessIPs, nova.api.openstack.compute.access_ips.AccessIPs(extension_info=extension_info.LoadedExtensionInfo()))

所以extensions = _load_plugins()这个方法,得到了Extension实例的列表。
继续往下self._init_plugins(extensions)很简单的将实例的self.extensions赋值为上面得到的列表。

到此stevedore.enabled.EnabledExtensionManager类实例的初始化完成,并将实例赋值给了APIRouterV21的self.api_extension_manager。

继续回到APIRouterV21,mapper = ProjectMapper()得到的实例后续使用时分析。下面世界杯外围投注网站运行至
if list(self.api_extension_manager)时,启用了世界杯外围投注网站定义的__iter__,检查了self.extensions列表是否为空。当不为空时,看self._register_resources_check_inherits(mapper):

def _register_resources_check_inherits(self, mapper):
    ext_has_inherits = []
    ext_no_inherits = []
    for ext in self.api_extension_manager:
        for resource in ext.obj.get_resources():
            if resource.inherits:
                ext_has_inherits.append(ext)
                break
        else:
            ext_no_inherits.append(ext)
    self._register_resources_list(ext_no_inherits, mapper)
    self._register_resources_list(ext_has_inherits, mapper)

ext.obj以上已经提及,是扩展类的实例。在扩展类中,都各自复写了get_resource()方法。当此方法返回nova.api.openstack.extensions.ResourceExtension实例,且实例参数中inherits的bool为True时,该扩展类会被添加进ext_has_inherits列表中,而不符合条件的会被添加进ext_no_inherits列表。
self._register_resources_list()中的self._register_resources(ext, mapper):

    def _register_resources(self, ext, mapper):
    """Register resources defined by the extensions
    Extensions define what resources they want to add through a
    get_resources function
    """
    handler = ext.obj
    LOG.debug("Running _register_resources on %s", ext.obj)
    for resource in handler.get_resources():
        LOG.debug(&世界杯外围投注官网39;Extended resource: %s&世界杯外围投注官网39;, resource.collection)
        inherits = None
        if resource.inherits:
            inherits = self.resources.get(resource.inherits)
            if not resource.controller:
                resource.controller = inherits.controller
        wsgi_resource = wsgi.ResourceV21(resource.controller,
                                         inherits=inherits)
        self.resources[resource.collection] = wsgi_resource
        kargs = dict(
            controller=wsgi_resource,
            collection=resource.collection_actions,
            member=resource.member_actions)
        if resource.parent:
            kargs[&世界杯外围投注官网39;parent_resource&世界杯外围投注官网39;] = resource.parent
        世界杯外围投注官网 non core-API plugins use the collection name as the
        世界杯外围投注官网 member name, but the core-API plugins use the
        世界杯外围投注官网 singular/plural convention for member/collection names
        if resource.member_name:
            member_name = resource.member_name
        else:
            member_name = resource.collection
        mapper.resource(member_name, resource.collection,
                        **kargs)
        if resource.custom_routes_fn:
            resource.custom_routes_fn(mapper, wsgi_resource)

可以看到在世界杯外围投注网站中循环控制中,如果ext.obj.get_resource()返回为空,则在此不进行任何处理。
下面我们以cells.py扩展为例,看其中定义的get_resource():

    def get_resources(self):
    coll_actions = {
            &世界杯外围投注官网39;detail&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            &世界杯外围投注官网39;info&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            &世界杯外围投注官网39;sync_instances&世界杯外围投注官网39;: &世界杯外围投注官网39;POST&世界杯外围投注官网39;,
            &世界杯外围投注官网39;capacities&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            }
    memb_actions = {
            &世界杯外围投注官网39;capacities&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            }
    **res = extensions.ResourceExtension(ALIAS, CellsController(),
                                       collection_actions=coll_actions,
                                       member_actions=memb_actions)**
    return [res]

注意**标世界杯外围投注网站,属于没有inherit的情况。回到_register_resources看世界杯外围投注网站对这个扩展的处理逻辑:
其中:

wsgi_resource = wsgi.ResourceV21(resource.controller,
                                         inherits=inherits)

查看wsgi.ResourceV21这个类的初始化方法,继承了父类的__init__:

    def __init__(self, controller, inherits=None):
    """:param controller: object that implement methods created by routes
                          lib
       :param inherits: another resource object that this resource should
                        inherit extensions from. Any action extensions that
                        are applied to the parent resource will also apply
                        to this resource.
    """
    self.controller = controller
    self.default_serializers = dict(json=JSONDictSerializer)
    世界杯外围投注官网 Copy over the actions dictionary
    self.wsgi_actions = {}
    if controller:
        self.register_actions(controller)
    世界杯外围投注官网 Save a mapping of extensions
    self.wsgi_extensions = {}
    self.wsgi_action_extensions = {}
    self.inherits = inherits

看其中的self.register_actions(controller):

    def register_actions(self, controller):
    """Registers controller actions with this resource."""
    actions = getattr(controller, &世界杯外围投注官网39;wsgi_actions&世界杯外围投注官网39;, {})
    for key, method_name in actions.items():
        self.wsgi_actions[key] = getattr(controller, method_name)

看到actions = getattr(controller, &世界杯外围投注官网39;wsgi_actions&世界杯外围投注官网39;, {})这行世界杯外围投注网站,发现controller里有一个wsgi_action的字典,在此其值为空。此字典如何生成在此先不详解,所以·register_actions这个方法实际没有做操作。
那么我们就可以知道这个ResourceV21类的初始化只是简单的对类实例的参数进行了赋值,提供一些方法。最后我们知道wsgi_resource的值为这个类的实例。

简单从世界杯外围投注网站中可知,目前`self.resources[&lsquo;os-cells&rsquo;] = wsgi_resource(以上得到的实例)

下面在看mapper.resource()这个方法之前,我们知道mapper变量的值是在外部世界杯外围投注网站中获得的nova.api.openstack.ProjectMapper这个类的实例。并且,在类中已经对resource()这个方法进行了重写。由于在此需要了解routes库中mapper模块,世界杯外围投注网站过于冗长在此不在一一贴出,只举例说明模块的一些用途。

在重写的resource方法中,要点是对传给父类参数中的path_prefix进行了生成,然后传入routes.Mapper.resource方法。在此方法中,为controller创建了路由。针对于两个不同的模块解析:

1.对于扩展中没有提供parent_resource参数的,如cells.py,生成了如下:

kwargs[&世界杯外围投注官网39;path_prefix&世界杯外围投注官网39;] = &世界杯外围投注官网39;%s/&世界杯外围投注官网39; % project_id_token 
世界杯外围投注官网即为  project_id/

然后,可以看到在cells.py的CellsController类中定义一些的方法都在而在创建extensions.ResourceExtension实例时传入的collection_actions和member_actions参数(dict类型中)中映射了restful关系:

coll_actions = {
            &世界杯外围投注官网39;detail&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            &世界杯外围投注官网39;info&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            &世界杯外围投注官网39;sync_instances&世界杯外围投注官网39;: &世界杯外围投注官网39;POST&世界杯外围投注官网39;,
            &世界杯外围投注官网39;capacities&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            }
memb_actions = {
            &世界杯外围投注官网39;capacities&世界杯外围投注官网39;: &世界杯外围投注官网39;GET&世界杯外围投注官网39;,
            }

这些参数在resource()方法世界杯外围投注网站逻辑中,对API请求收到的restful提供了映射关系,如调用sync_instances方法,属于collection级别,对应的restful为:

世界杯外围投注官网 URL /:project_id/:controller(collection_name)/:collection_action
POST /project_id/os-cells/sync_instances

调用capacities方法,属于member级别,对应为:

世界杯外围投注官网 URL /:project_id/:controller(collection_name)/:member_id/:member_action
GET /project_id/os-cells/member_id/capacities

2.对于扩展中提供了parent_resource参数的,如consoles.py,生成如下:

kwargs[&世界杯外围投注官网39;path_prefix&世界杯外围投注官网39;] = &世界杯外围投注官网39;%s/%s/:%s_id&世界杯外围投注官网39; % (
            project_id_token,
            p_collection,
            p_member)
世界杯外围投注官网即为 project_id/servers/:server_id

而在此模块中,在创建extensions.ResourceExtension实例时没有传入collection_actions和member_actions,说明没有附加的方法提供给URL,仅使用基本定义的restful API,对应为

世界杯外围投注官网 URL /:project_id/:parent_controller(parent_collection_name)/:parent_member_id/:controller(collection_name)
世界杯外围投注官网 如果传入collection_actions和member_actions,则
URL /:project_id/:parent_controller(parent_collection_name)/:parent_member_id/:controller(collection_name)/:collection_action  或
URL /:project_id/:parent_controller(parent_collection_name)/:parent_member_id/:controller(collection_name)/:member_id/:member_action
GET/POST/PUT/DEL /project_id/consoles/member_id

所以,在mapper.resource()中,对各个extension模块定义的方法按照如上类似规则映射了路由。

回到上层,世界杯外围投注网站走到resource.custom_routes_fn(mapper, wsgi_resource),作用在此先不说明,接触相关概念时再进行回顾。

回到APIRouterV21层,self.api_extension_manager.map(self._register_controllers)这段,世界杯外围投注网站不列出了,主要逻辑为将self.extensions中元素iter传入self._register_controllers方法。所以我们看self._register_controllers方法:

    def _register_controllers(self, ext):
    """Register controllers defined by the extensions
    Extensions define what resources they want to add through
    a get_controller_extensions function
    """
    handler = ext.obj
    LOG.debug("Running _register_controllers on %s", ext.obj)
    for extension in handler.get_controller_extensions():
        ext_name = extension.extension.name
        collection = extension.collection
        controller = extension.controller
        if collection not in self.resources:
            LOG.warning(_LW(&世界杯外围投注官网39;Extension %(ext_name)s: Cannot extend &世界杯外围投注官网39;
                            &世界杯外围投注官网39;resource %(collection)s: No such resource&世界杯外围投注官网39;),
                        {&世界杯外围投注官网39;ext_name&世界杯外围投注官网39;: ext_name, &世界杯外围投注官网39;collection&世界杯外围投注官网39;: collection})
            continue
        LOG.debug(&世界杯外围投注官网39;Extension %(ext_name)s extending resource: &世界杯外围投注官网39;
                  &世界杯外围投注官网39;%(collection)s&世界杯外围投注官网39;,
                  {&世界杯外围投注官网39;ext_name&世界杯外围投注官网39;: ext_name, &世界杯外围投注官网39;collection&世界杯外围投注官网39;: collection})
        resource = self.resources[collection]
        resource.register_actions(controller)
        resource.register_extensions(controller)

以admin_actions.py扩展模块为例,resource = self.resource["servers"], 那么controller = AdminActionsController()。又回到了之前提到的问题:register_actions()方法中actions = getattr(controller, &世界杯外围投注官网39;wsgi_actions&世界杯外围投注官网39;, {}),这个&rsquo;wsgi_actions&rsquo;标签是哪里来的?我们看AdminActionsController的父类api.openstack.wsgi.Controller,这个类有一个装饰器:

@six.add_metaclass(ControllerMetaclass)
class Controller(object):
    """Default controller."""

用six库的add_metaclass方法给这个类增加了一个metaclassapi.openstack.wsgi.ControllerMetaclass,有关metaclass的概念不在此写出了,可以注意到这个metaclass中有如下世界杯外围投注网站:

for key, value in cls_dict.items():
        if not callable(value):
            continue
        if getattr(value, &世界杯外围投注官网39;wsgi_action&世界杯外围投注官网39;, None):
            actions[value.wsgi_action] = key
        elif getattr(value, &世界杯外围投注官网39;wsgi_extends&世界杯外围投注官网39;, None):
            extensions.append(value.wsgi_extends)
世界杯外围投注官网 Add the actions and extensions to the class dict
cls_dict[&世界杯外围投注官网39;wsgi_actions&世界杯外围投注官网39;] = actions
cls_dict[&世界杯外围投注官网39;wsgi_extensions&世界杯外围投注官网39;] = extensions

对于由这个metaclass控制生成的class,会对controller类中定义的所有方法中尝试以&rsquo;wsgi_action&rsquo;及&rsquo;wsgi_extends&rsquo;作为key来获取value,并且以dict=(value, 方法名称str)的形式存入到controller类的wsgi_actions及wsgi_extensions属性中。
再回到admin_actions.py中,可看到类似装饰器“,查看解释器世界杯外围投注网站:

def action(name):
    """
    def decorator(func):
        func.wsgi_action = name
        return func
    return decorator

这样就比较明了,此装饰器可以给类中的方法做上wsgi_action属性,value为装饰器接收的参数。如:

dict=(wsgi.action(name)装饰器接收的name, controller中对应的func.__name__)

再看一下@wsgi.extends装饰器:

def extends(*args, **kwargs):
    """
    def decorator(func):
        世界杯外围投注官网 Store enough information to find what we&世界杯外围投注官网39;re extending
        func.wsgi_extends = (func.__name__, kwargs.get(&世界杯外围投注官网39;action&世界杯外围投注官网39;))
        return func

可知 wsgi_extends赋值为(方法名, 装饰器接收的action参数的值)的tuple,则controller类中wsgi_extensions属性为各func生成的tuple的列表。如:
[(controller中对应的func.__name__, wsgi.extends(action=name)中的name),...]
知道了参数的来源与格式,再看register_actions和register_extensions这两个方法:

第一个:

def register_actions(self, controller):
    """Registers controller actions with this resource."""
    actions = getattr(controller, &世界杯外围投注官网39;wsgi_actions&世界杯外围投注官网39;, {})
    for key, method_name in actions.items():
        self.wsgi_actions[key] = getattr(controller, method_name)

为resource增加了wsgi_actions属性, 内容为如下dict映射:

wsgi_actions[wsgi.action(name)中的name] = controller中的func

第二个:

def register_extensions(self, controller):
    """Registers controller extensions with this resource."""
    extensions = getattr(controller, &世界杯外围投注官网39;wsgi_extensions&世界杯外围投注官网39;, [])
    for method_name, action_name in extensions:
        世界杯外围投注官网 Look up the extending method
        extension = getattr(controller, method_name)
        if action_name:
            世界杯外围投注官网 Extending an action...
            if action_name not in self.wsgi_action_extensions:
                self.wsgi_action_extensions[action_name] = []
            self.wsgi_action_extensions[action_name].append(extension)
        else:
            世界杯外围投注官网 Extending a regular method
            if method_name not in self.wsgi_extensions:
                self.wsgi_extensions[method_name] = []
            self.wsgi_extensions[method_name].append(extension)

为resource增加了wsgi_action_extensions和wsgi_extensions两个属性,内容为如下dict映射:

wsgi_action_extensions[wsgi.extends(action=name)中的name] = [controller中的func]
wsgi_extensions[controller中的func.__name__] = [controller中的func]

注意,extends映射中的value是list类型,说明在不同的扩展模块中可能对同一个方法进行扩展。但actions映射中是一一对应的关系。

回到APIRouterV21层,missing_core_extensions是核心API检测预警,如果发现某些核心API扩展没有加载启动会将其raise。
至此APIRouterV21层初始化完毕。将mapper作为参数传入父类nova.wsgi.Router的初始化方法及附属方法:

def __init__(self, mapper):
    """Create a router for the given routes.Mapper.
    Each route in `mapper` must specify a &世界杯外围投注官网39;controller&世界杯外围投注官网39;, which is a
    WSGI app to call.  You&世界杯外围投注官网39;ll probably want to specify an &世界杯外围投注官网39;action&世界杯外围投注官网39; as
    well and have your controller be an object that can route
    the request to the action-specific method.
    Examples:
      mapper = routes.Mapper()
      sc = ServerController()
      世界杯外围投注官网 Explicit mapping of one route to a controller+action
      mapper.connect(None, &世界杯外围投注官网39;/svrlist&世界杯外围投注官网39;, controller=sc, action=&世界杯外围投注官网39;list&世界杯外围投注官网39;)
      世界杯外围投注官网 Actions are all implicitly defined
      mapper.resource(&世界杯外围投注官网39;server&世界杯外围投注官网39;, &世界杯外围投注官网39;servers&世界杯外围投注官网39;, controller=sc)
      世界杯外围投注官网 Pointing to an arbitrary WSGI app.  You can specify the
      世界杯外围投注官网 {path_info:.*} parameter so the target app can be handed just that
      世界杯外围投注官网 section of the URL.
      mapper.connect(None, &世界杯外围投注官网39;/v1.0/{path_info:.*}&世界杯外围投注官网39;, controller=BlogApp())
    """
    self.map = mapper
    self._router = routes.middleware.RoutesMiddleware(self._dispatch,
                                                      self.map)
@webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
    """Route the incoming request to a controller based on self.map.
    If no match, return a 404.
    """
    return self._router
@staticmethod
@webob.dec.wsgify(RequestClass=Request)
def _dispatch(req):
    """Dispatch the request to the appropriate controller.
    Called by self._router after matching the incoming request to a route
    and putting the information into req.environ.  Either returns 404
    or the routed WSGI app&世界杯外围投注官网39;s response.
    """
    match = req.environ[&世界杯外围投注官网39;wsgiorg.routing_args&世界杯外围投注官网39;][1]
    if not match:
        return webob.exc.HTTPNotFound()
    app = match[&世界杯外围投注官网39;controller&世界杯外围投注官网39;]
    return app

此处不在赘述,贴出相关资料:

关于routes.middleware: http://routes.readthedocs.io/en/latest/porting.html
关于webob.desc.wsgify: http://docs.webob.org/en/latest/api/dec.html?highlight=wsgify

理解相关概念后,可清楚如何根据restful请求生成对应的wsgi的app(controller),生成wsgi server,再通过nova-api的launch_service启动多worker server,即nova的API服务。

相关文章
最新文章
热点推荐