nova-api代码分析(1)

1.首先查看/usr/local/bin/nova-api命令内容:

import sys
#导入nova.cmd.api模块的main方法
from nova.cmd.api import main

if __name__ == "__main__":
    sys.exit(main())


2.下面查看nova模块中nova.cmd.api模块里的main()方法

def main():
    #命令行参数解析
    config.parse_args(sys.argv)
    logging.setup("nova")
    #导入monkeypatch插件
    utils.monkey_patch()
    #注册nova的object对象
    objects.register_all()

    gmr.TextGuruMeditation.setup_autorun(version)
    #初始化service.ProcessLauncher类,process的构造函数
    launcher = service.process_launcher()
    #加载的使能的api接口ec2,osapi_compute,metadata等
    for api in CONF.enabled_apis:
        #如果enabled_ssl_apis配置为True,WSGI接口将支持ssl
        should_use_ssl = api in CONF.enabled_ssl_apis
        if api == 'ec2':
            #初始化WSGIserver,加载paste配置文件
            server = service.WSGIService(api, use_ssl=should_use_ssl,
                                         max_url_len=16384)
        else:
            #初始化WSGIserver,加载paste配置文件</span>
            server = service.WSGIService(api, use_ssl=should_use_ssl)
        #launch启动WSGI服务进程
        launcher.launch_service(server, workers=server.workers or 1)
    #Loop waiting on children to die and respawning as necessary
    #循环等待子进程die和重新生成子进程
    launcher.wait()

parse_args:

def parse_args(argv, default_config_files=None):
    options.set_defaults(sql_connection=_DEFAULT_SQL_CONNECTION,
                         sqlite_db='nova.sqlite')
    rpc.set_defaults(control_exchange='nova')
    debugger.register_cli_opts()
    cfg.CONF(argv[1:],
             project='nova',
             version=version.version_string(),
             default_config_files=default_config_files)
    rpc.init(cfg.CONF)


初始化process_launcher

def process_launcher():
    return service.ProcessLauncher()

class ProcessLauncher(object):
    def __init__(self, wait_interval=0.01):
        """Constructor.

        :param wait_interval: The interval to sleep for between checks
                              of child process exit.
        """
        #子进程字典
        self.children = {}
        #
        self.sigcaught = None
        #running状态True
        self.running = True
        #
        self.wait_interval = wait_interval
        #生成管道读描述符和谐描述符
        rfd, self.writepipe = os.pipe()
        #读管道为绿色线程
        self.readpipe = eventlet.greenio.GreenPipe(rfd, 'r')
        #
        self.handle_signal()

handle_signal方法分析:

def handle_signal(self):
    _set_signals_handler(self._handle_signal)

def _set_signals_handler(handler):
    signal.signal(signal.SIGTERM, handler)
    signal.signal(signal.SIGINT, handler)
    if _sighup_supported():
        signal.signal(signal.SIGHUP, handler)

def _sighup_supported():
    return hasattr(signal, 'SIGHUP')




初始化WSGIService方法分析:

server = service.WSGIService(api, use_ssl=should_use_ssl)

class WSGIService(object):
    """Provides ability to launch API from a 'paste' configuration."""
    #WSGIService的初始化方法
    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
        self.manager = self._get_manager()
        #初始化WSGI loader实例
        self.loader = loader or wsgi.Loader()
        #调用load_app方法加载paste配置文件
        self.app = self.loader.load_app(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' % name, 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

初始化loader:

self.loader = loader or wsgi.Loader()

class Loader(object):
    """Used to load WSGI applications from paste configurations."""

    def __init__(self, config_path=None):
        """Initialize the loader, and attempt to find the config.

        :param config_path: Full or relative path to the paste config.
        :returns: None

        """
        self.config_path = None
        #设置paste配置文件路径
        config_path = config_path or CONF.api_paste_config
        if not os.path.isabs(config_path):
            self.config_path = CONF.find_file(config_path)
        elif os.path.exists(config_path):
            self.config_path = config_path

        if not self.config_path:
            raise exception.ConfigNotFound(path=config_path)
<pre name="code" class="python">    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",
                      {'name': name, 'path': self.config_path})
            #调用loadapp加载配置文件
            return deploy.loadapp("config:%s" % self.config_path, name=name)
        except LookupError as err:
            LOG.error(err)
            raise exception.PasteAppNotFound(name=name, path=self.config_path)

 

加载paste配置文件:

self.app = self.loader.load_app(name)

paste配置文件:

[app:osapi_compute_app_v2]paste.app_factory = nova.api.openstack.compute:APIRouter.factory通过此配置项,paste把应用路由到APIRouter的方法中,下面重点分析一下APIRouter方法

由于class APIRouter(nova.api.openstack.APIRouter)此类继承自nova.api.openstack.APIRouter父类,先看看父类

class APIRouter(base_wsgi.Router):
    """Routes requests on the OpenStack API to the appropriate controller
    and method.
    """
    ExtensionManager = None  # override in subclasses
    
    #paste配置文件加载app配置工厂函数,加载配置路由的入口
    @classmethod
    def factory(cls, global_config, **local_config):
        """Simple paste factory, :class:`nova.wsgi.Router` doesn't have one."""
        #调用类init方法
        return cls()

    def __init__(self, ext_mgr=None, init_only=None):
        if ext_mgr is None:
            #在子类中定义了ExtensionManager = extensions.ExtensionManager
            if self.ExtensionManager:
                #初始化ExtensionManager实例
                ext_mgr = self.ExtensionManager()
            else:
                raise Exception(_("Must specify an ExtensionManager class"))
        #生成mapper实例,后面调用mapper.<span><span><span><span><span><span><span>resource</span></span></span></span></span></span></span>来加载路由信息
        mapper = ProjectMapper()
        #定义resources资源
        self.resources = {}
        #加载核心路由,nova.api.openstack.compute下面的控制文件
        self._setup_routes(mapper, ext_mgr, init_only)
        #加载contrib目录下的文件路由,所有文件已经在ext_mgr实例中保存
        self._setup_ext_routes(mapper, ext_mgr, init_only)
        self._setup_extensions(ext_mgr)
        super(APIRouter, self).__init__(mapper)

    def _setup_ext_routes(self, mapper, ext_mgr, init_only):
        for resource in ext_mgr.get_resources():
            LOG.debug('Extending resource: %s',
                      resource.collection)

            if init_only is not None and resource.collection not in init_only:
                continue

            inherits = None
            if resource.inherits:
                inherits = self.resources.get(resource.inherits)
                if not resource.controller:
                    resource.controller = inherits.controller
            wsgi_resource = wsgi.Resource(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['parent_resource'] = resource.parent

            mapper.resource(resource.collection, resource.collection, **kargs)

            if resource.custom_routes_fn:
                resource.custom_routes_fn(mapper, wsgi_resource)

    def _setup_extensions(self, ext_mgr):
        for extension in ext_mgr.get_controller_extensions():
            collection = extension.collection
            controller = extension.controller

            msg_format_dict = {'collection': collection,
                               'ext_name': extension.extension.name}
            if collection not in self.resources:
                LOG.warn(_LW('Extension %(ext_name)s: Cannot extend '
                             'resource %(collection)s: No such resource'),
                         msg_format_dict)
                continue

            LOG.debug('Extension %(ext_name)s extended resource: '
                      '%(collection)s',
                      msg_format_dict)

            resource = self.resources[collection]
            resource.register_actions(controller)
            resource.register_extensions(controller)

    def _setup_routes(self, mapper, ext_mgr, init_only):
        raise NotImplementedError()

ExtensionManger类,此类继承自父类nova.api.openstack.ExtensionManger

class ExtensionManager(base_extensions.ExtensionManager):
    def __init__(self):
        LOG.audit(_('Initializing extension manager.'))
        #定义cls_list为nova.api.openstack.compute.contrib.standard_extensions ,指定contrib目录加载类
        self.cls_list = CONF.osapi_compute_extension
        self.extensions = {}
        self.sorted_ext_list = []
        #调用父类方法_load_extensions()
        self._load_extensions()

ExtensionManger父类中方法:

    def load_extension(self, ext_factory):
        """Execute an extension factory.

        Loads an extension.  The 'ext_factory' is the name of a
        callable that will be imported and called with one
        argument--the extension manager.  The factory callable is
        expected to call the register() method at least once.
        """

        LOG.debug("Loading extension %s", ext_factory)

        if isinstance(ext_factory, six.string_types):
            # Load the factory
            #根据函数路径,import加载文件
            factory = importutils.import_class(ext_factory)
        else:
            factory = ext_factory

        # Call it
        LOG.debug("Calling extension factory %s", ext_factory)
        #调用函数nova.api.openstack.compute.contrib.standard_extensions
        factory(self)

    def _load_extensions(self):
        """Load extensions specified on the command line."""

        extensions = list(self.cls_list)

        for ext_factory in extensions:
            try:
                #加载扩展路由方法,通过指定contrib目录加载类
                self.load_extension(ext_factory)
            except Exception as exc:
                LOG.warn(_LW('Failed to load extension %(ext_factory)s: '
                             '%(exc)s'),
                         {'ext_factory': ext_factory, 'exc': exc})

nova.api.openstack.compute.contrib目录下的standard_extensions函数

def standard_extensions(ext_mgr):
    extensions.load_standard_extensions(ext_mgr, LOG, __path__, __package__)

调用了在nova.api.openstack.extensions下的load_standard_extensions函数

def load_standard_extensions(ext_mgr, logger, path, package, ext_list=None):
    """Registers all standard API extensions."""

    # Walk through all the modules in our directory...
    our_dir = path[0]
    for dirpath, dirnames, filenames in os.walk(our_dir):
        # Compute the relative package name from the dirpath
        relpath = os.path.relpath(dirpath, our_dir)
        if relpath == '.':
            relpkg = ''
        else:
            relpkg = '.%s' % '.'.join(relpath.split(os.sep))

        # Now, consider each file in turn, only considering .py files
        for fname in filenames:
            root, ext = os.path.splitext(fname)

            # Skip __init__ and anything that's not .py
            if ext != '.py' or root == '__init__':
                continue

            # Try loading it
            #生成路由文件名中类名,首字母大写
            classname = "%s%s" % (root[0].upper(), root[1:])
            classpath = ("%s%s.%s.%s" %
                         (package, relpkg, root, classname))

            if ext_list is not None and classname not in ext_list:
                logger.debug("Skipping extension: %s" % classpath)
                continue

            try:
                #加载contrib目录下路由类
                ext_mgr.load_extension(classpath)
            except Exception as exc:
                logger.warn(_('Failed to load extension %(classpath)s: '
                              '%(exc)s'),
                            {'classpath': classpath, 'exc': exc})

        # Now, let's consider any subdirectories we may have...
        #如果包含子目录,处理子目录中的文件
        subdirs = []
        for dname in dirnames:
            # Skip it if it does not have __init__.py
            if not os.path.exists(os.path.join(dirpath, dname, '__init__.py')):
                continue

            # If it has extension(), delegate...
            ext_name = "%s%s.%s.extension" % (package, relpkg, dname)
            try:
                ext = importutils.import_class(ext_name)
            except ImportError:
                # extension() doesn't exist on it, so we'll explore
                # the directory for ourselves
                subdirs.append(dname)
            else:
                try:
                    ext(ext_mgr)
                except Exception as exc:
                    logger.warn(_('Failed to load extension %(ext_name)s:'
                                  '%(exc)s'),
                                {'ext_name': ext_name, 'exc': exc})

        # Update the list of directories we'll explore...
        dirnames[:] = subdirs

分析一下ext_mgr.load_extension(classpath)加载类文件的核心方法:

    def load_extension(self, ext_factory):
        """Execute an extension factory.

        Loads an extension.  The 'ext_factory' is the name of a
        callable that will be imported and called with one
        argument--the extension manager.  The factory callable is
        expected to call the register() method at least once.
        """

        LOG.debug("Loading extension %s", ext_factory)
        #ext_factory为字符类型加载此class
        if isinstance(ext_factory, six.string_types):
            # Load the factory
            #加载ext_factory类文件,方法import_class
            factory = importutils.import_class(ext_factory)
        else:
            factory = ext_factory

        # Call it
        LOG.debug("Calling extension factory %s", ext_factory)
        #调用类文件,初始化类
        factory(self)

import_class方法用来加载类文件:

def import_class(import_str):
    """Returns a class from a string including module and class."""
    #分拆类文件字符串
    mod_str, _sep, class_str = import_str.rpartition('.')
    #调用内部方法import模块文件mod_str
    __import__(mod_str)
    try:
        #返回模块mod_str下类class_str
        return getattr(sys.modules[mod_str], class_str)
    except AttributeError:
        raise ImportError('Class %s cannot be found (%s)' %
                          (class_str,
                           traceback.format_exception(*sys.exc_info())))


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值