nova-api代码分析(1)
2014-07-29 14:42
302 查看
1.首先查看/usr/local/bin/nova-api命令内容:
2.下面查看nova模块中nova.cmd.api模块里的main()方法
parse_args:
初始化process_launcher
def process_launcher():
return service.ProcessLauncher()
handle_signal方法分析:
def handle_signal(self):
_set_signals_handler(self._handle_signal)
初始化WSGIService方法分析:
初始化loader:
加载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父类,先看看父类
ExtensionManger类,此类继承自父类nova.api.openstack.ExtensionManger
ExtensionManger父类中方法:
nova.api.openstack.compute.contrib目录下的standard_extensions函数
调用了在nova.api.openstack.extensions下的load_standard_extensions函数
分析一下ext_mgr.load_extension(classpath)加载类文件的核心方法:
import_class方法用来加载类文件:
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' % n 4000 ame, 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())))
相关文章推荐
- nova-api代码分析(2)
- nova-api代码分析(3)
- API Demo Snake代码分析一 FrameLayout新的认识
- API Demo SearchableDictionary代码分析二
- nova-api源码分析(APP中用到的开源库)
- Tripleo之nova-compute 和Ironic的代码深入分析(四)
- Tripleo之nova-compute 和Ironic的代码深入分析(一)
- Radmin的抓屏和显屏API代码部分分析
- novaclient代码解析之---通过nova show <server-id>命令解析Nova client与Nova API之间的调用关系
- JQuery data API实现代码分析
- JQuery html API支持解析执行Javascript脚本功能实现-代码分析
- openstack之nova-api服务流程分析
- openstack nova 源码分析2之nova-api,nova-compute
- openstack nova 源码分析2之nova-api,nova-compute
- nova-api源码分析(WSGI server的创建及启动)
- OpenStack 之 nova-api 的代码结构图
- API Demo Snake代码分析三 程序架构的分析
- DIOCP开源项目 API代码的封装和流程分析
- nova boot from volume代码分析
- Tripleo之nova-compute 和Ironic的代码深入分析(三)