openstack-nova-API解析流程分析

来源:互联网 发布:mac os x 10.11正式版 编辑:程序博客网 时间:2024/05/17 07:07

接下来就是分析nova模块的代码

1. 目前的Nova主要由API,Compute,Conductor,Scheduler组成
    API:其它模块的通过HTTP协议进入Nova模块的接口;
    Compute:用来交互并管理虚拟机的生命周期;
    Scheduler:从可用池中根据各种策略选择最合适的计算节点来创建新的虚拟机;
    Conductor:为数据库的访问提供统一的接口层。

2. Nova代码结构:

[root@Alljun-Lee nova]# lsapi-guide    contrib           doc          LICENSE      openstack-common.conf  releasenotes      setup.cfg  test-requirements.txt  tox.inibabel.cfg    CONTRIBUTING.rst  etc          MAINTAINERS  plugins                requirements.txt  setup.py   tests-py3.txtbandit.yaml  devstack          HACKING.rst  nova         README.rst             run_tests.sh      tags       tools
3. Nova体系结构图:

4. 注意:我们每关注一个新的项目都可以从setup.cfg文件开始
  进入文件并找到'[entry_points]',里面有'console_scripts',
  它对应的是每一个脚本文件,用来初始化nova模块内的各个子服务;

console_scripts =    nova-all = nova.cmd.all:main  #用来启动所有Nova服务的辅助脚本    nova-api = nova.cmd.api:main    nova-api-metadata = nova.cmd.api_metadata:main    nova-api-os-compute = nova.cmd.api_os_compute:main    nova-cells = nova.cmd.cells:main    nova-cert = nova.cmd.cert:main    nova-compute = nova.cmd.compute:main    nova-conductor = nova.cmd.conductor:main    nova-console = nova.cmd.console:main    nova-consoleauth = nova.cmd.consoleauth:main    nova-dhcpbridge = nova.cmd.dhcpbridge:main    nova-idmapshift = nova.cmd.idmapshift:main    nova-manage = nova.cmd.manage:main    nova-network = nova.cmd.network:main    nova-novncproxy = nova.cmd.novncproxy:main    nova-rootwrap = oslo_rootwrap.cmd:main    nova-rootwrap-daemon = oslo_rootwrap.cmd:daemon    nova-scheduler = nova.cmd.scheduler:main    nova-serialproxy = nova.cmd.serialproxy:main    nova-spicehtml5proxy = nova.cmd.spicehtml5proxy:main    nova-xvpvncproxy = nova.cmd.xvpvncproxy:main
其中每个服务都会被创建成一个或者多个WSGI Server,例如:
for api in CONF.enabled_apis:
enabled_apis----default=['osapi_compute', 'metadata']
下面会将这两个初始化为WSGI的service,这个名字对应文件'etc/nova/api-paste.ini'内的'[composite:osapi_compute/metadata]'
server = service.WSGIService(api, use_ssl=should_use_ssl)
launcher.launch_service(server, workers=server.workers or 1)

5. 现在进入文件'nova/service.py'

class WSGIService(service.Service):    """Provides ability to launch API from a 'paste' 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's enabled_apis        self.binary = 'nova-%s' % name        self.topic = None        self.manager = self._get_manager()        self.loader = loader or wsgi.Loader()        # 从配置文件'etc/nova/api-paste.ini'加载wsgi的application,这个很重要        self.app = self.loader.load_app(name)        # inherit all compute_api worker counts from osapi_compute        if name.startswith('openstack_compute_api'):            wname = 'osapi_compute'        else:            wname = name        self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")        self.port = getattr(CONF, '%s_listen_port' % name, 0)        self.workers = (getattr(CONF, '%s_workers' % wname, None) or                        processutils.get_worker_count())        if self.workers and self.workers < 1:            worker_name = '%s_workers' % name            msg = (_("%(worker_name)s value of %(workers)s is invalid, "                     "must be greater than 0") %                   {'worker_name': worker_name,                    'workers': 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
6. 进入配置文件'etc/nova/api-paste.ini'

6.1 找到对应的wsgi Service

[composite:osapi_compute]use = call:nova.api.openstack.urlmap:urlmap_factory/: oscomputeversions# starting in Liberty the v21 implementation replaces the v2# implementation and is suggested that you use it as the default. If# this causes issues with your clients you can rollback to the # *frozen* v2 api by commenting out the above stanza and using the # following instead::# /v2: openstack_compute_api_legacy_v2# if rolling back to v2 fixes your issue please file a critical bug # at - https://bugs.launchpad.net/nova/+bugs## 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

6.1.1 其中使用'nova.api.openstack.urlmap模块的urlmap_factory函数来进行API的分发'
  例如创建云主机,http://192.168.1.9:8774/v2/14fd316568bc4f6992ba161fd4e23001/servers,
  用的是v2版本的api,根据路由解析可以很明显的看到最后调用的是‘paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory’
6.1.2 到了具体的执行函数

# vim nova/api/openstack/compute/__init__.py  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

6.1.3 进入nova.api.openstack.APIRouterV21

# vim nova/api/openstack/__init__.py  class APIRouterV21(base_wsgi.Router):    """Routes requests on the OpenStack v2.1 API to the appropriate controller    and method.    """    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        # 配置文件内的黑白名单,主要是用来加载扩展api的,但是M版以后将会废除这两个配置项        if (CONF.osapi_v21.extensions_blacklist or                CONF.osapi_v21.extensions_whitelist):            LOG.warning(                _LW('In the M release you must run all of the API. '                'The concept of API extensions will be removed from '                'the codebase to ensure there is a single Compute API.'))        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))        # 使用stevedore的enabled的EnabledExtensionManager类来加载位于setup.cfg中        # 命名空间为'nova.api.v21.extensions'的所有的资源        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.            # 我们查看目录'nova/api/openstack/compute/'下的任一个类都有方法'get_resources()'            # 这个函数的作用就是循环调用‘get_resources()’方法内的action            # 最后调用方法self._register_resources(ext, mapper)来注册为WSGI的服务            self._register_resources_check_inherits(mapper)            # 注册各个api对应的controllers,然后将它们使用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()))        # 调用父类的'__init__()'方法        super(APIRouterV21, self).__init__(mapper)

6.1.4 进入父类

# vim nova/wsgi.py  class Router(object):    """WSGI middleware that maps incoming requests to WSGI apps."""    def __init__(self, mapper):        """Create a router for the given routes.Mapper.        Each route in `mapper` must specify a 'controller', which is a        WSGI app to call.  You'll probably want to specify an 'action' 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, '/svrlist', controller=sc, action='list')          # Actions are all implicitly defined          mapper.resource('server', 'servers', 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, '/v1.0/{path_info:.*}', controller=BlogApp())        """        self.map = mapper        # 使用routes模块将mapper和self._dispatch关联起来        # 'routes.middleware.RoutesMiddleware'会调用mapper.routematch()函数        # 来获取 url(会对它进行解析并拿到各个参数和api) 的controller(这些前面就已经被解析出来了)等参数,        # 保存在match中,并设置environ变量供_dispatch()使用        self._router = routes.middleware.RoutesMiddleware(self._dispatch,                                                          self.map)

6.1.5 到这里一个HTTP请求就被分发给了对应的资源来进行对应的操作了


0 0
原创粉丝点击