<keystone name="keystone-all"> <possible_topdir path="/keystone/keystone/cmd/all.py" function="初始化服务器"> possible_dir = os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir, os.pardir, os.pardir))) if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py'))) sys.path.insert(0, possible_topdir)): from keystone.server import eventlet as eventlet_server def main(): eventlet_server.run(possible_dir) <eventlet_server.run path="/keystone/keystone/server/eventlet.py" function="启动服务器"> def run(possible_dir): join = os.path.join(possible_topdir, 'etc', 'keystone.conf') # join = '/etc/keystone/keystone.conf' dev_conf = join config_files = None if os.path.exists(dev_conf): # 如果存在 join这一配置文件 config_files = [dev_conf] # 将配置文件装入列表,赋值给config_files common.configure( version=pbr.version.VersionInfo('keystone').version_string(), config_files=config_files, pre_setup_logging_fn=configure_threading) <common.configure path="/keystone/keystone/server/common.py" function="配置与sql初始化"> from oslo_config import cfg from keystone.common import dependency from keystone.common import sql from keystone import config from keystone.server import backends CONF = cfg.CONF # keystone代码中几乎都import cfg库,但是只实例化一次,因为CONF=ConfigOpts import一次 def configure(version=None, conf_files=None, pre_setup_logging_fn=lambda: None): config.configure() <config.configure name="config.configure()" path="/keystone/keystone/config.py" alias="配置选项"> from oslo_config import cfg configure = config.configure <configre name="configure=config.configure" path="/keystone/keystone/common/config.py"> from keystone.common import config def configure(conf=None): if conf is None: conf = CONF conf.register_cli_opt( cfg.BoolOpt('standard-threads', default=False, help='Do not monkey-patch threading system modules.')) conf.register_cli_opt( cfg.StrOPt('pydev-debug-host', help='Host to connect to for remote debugger.')) conf.register_cli_opt( cfg.IntOpt('pydev-debug-port', help='Port to connect to for remote debugger.')) <conf.register_cli_opt> CONF = ConfigOpts() conf=CONF class ConfigOpts(collections.Mapping): # 需要了解collections.Mapping def __init__(self): self._opts = {} self._groups = {} self._args = None self._oparser = None self._namespace = None self.__cache = {} self._config_opts = [] self._cli_opts = collections.deque() self._validate_default_values = False def __clear_cache(f) # 只是将self.__cache.clear() @functools.wraps(f) # 被装饰的f可以保留自身属性 def __inner(self, *args, **kwargs): if kwargs.pop('clear_cache', True): # 只要加了这个装饰器,除非传入参数clear_cache=False, # 否则都需要clear result = f(self, *args, **kwargs) self.__cache.clear() return result esle: return f(self, *args, **kwargs) return __inner @__clear_cache def register_cli_opts(self, opts, group=None): for opt in opts: self.register_cli_opt(opt, group, clear_cache=False) # 注意 clear_cache=False @__clear_cache def register_cli_opt(self, opt, group=None): # self._args = None # CLI 选项必须在命令行和配置文件被解析前注册好,这样可以确保--help等的正常使用 if self._args is not None: raise ArgsAlreadyParsedError("cannot register CLI option.") return self.register_opt(opt, group, cli=True, clear_cache=False) @__clear_cache def register_opt(self, opt, group=None, cli=False): if group is not None: group = self._get_group(group, autocreate=True) <group name="self._get_group(group, autocreate=True)" alias="取得OptGroup"> def _get_group(self, group_or_name, autocreate=False): # autocreate=False 如果为True则会创建GroupOpt实例 group = group_or_name if isinstance(group_or_name, OptGroup) else None # 如果group_or_name 是实例则返回group_or_name 否则返回None group_name = group.name if group else group_or_name # 如果group不为空,则返回group.name否则返回group_or_name if group_name not in self._groups: # 如果group_name不在self._groups中 if not autocreate: # 如果autocreate=False raise NoSuchError(group_name) self.register_group(group or OptGroup(name=group_name)) <register_group name="self.register_group(group or OptGroup(name=group_name))"> def register_group(self, group): # group是OptGroup实例 # group befor options register if group.name in self._groups: # self._groups = {} return self._groups[group.name] = copy.copy(group) </register_group> <OptGroup name="OptGroup(name=group_name)"> class OptGroup(object): def __init__(self, name, title=None, help=None): self.name = name self.title = '%s options' % name if title is None else title self.help = help self._opts = {} self._argparse_group = None </OptGroup> return self._groups[group_name] # 返回的是OptGroup实例 </group> if cli: self._add_cli_opt(opt, group) <self._add_cli_opt name="self._add_cli_opt(opt, group)" path="cfg.ConfigOpts"> def _add_cli_opt(self, opt, group): if {'opt': opt, 'group': group} in self._cli_opts: return if opt.positional: # self._cli_opts collections.deque() # 有放置要求时候 self._cli_opts.append({'opt': opt, 'group': group}) else: self._cli_opts.appendleft({'opt': opt, 'group': group}) </self._add_cli_opt> return group._register_opt(opt, cli) <group._register_opt name="group._register_opt(opt, cli)" path="cfg.OptGroup"> def _register_opt(self, opt, cli=False): # cli:是否为cli option if _is_opt_registered(self._opts, opt): <_register_opt name="_is_opt_registered(self._opts, opt)" path="cfg"> def _is_opt_registered(opts, opt): if opt.dest in opts: if opts[opt.dest]['opt'] !=opt raise DuplicateOptError(opt.name) return True else: return False </_register_opt> return False self._opts[opt.dest] = {'opt': opt, 'cli': cli} return True </group._register_opt> if cli: self._add_cli_opt(opt, None) if _is_opt_registered(self._opts, opt): return False self._opts[opt.dest] = {'opt': opt, 'cli': cli} return True </conf.register_cli_opt> for section in FILE_OPTIONS: for option in FILE_OPTIONS[section]: # FILE_OPTIONS 在keystone.common.config 是字典 # 此操作是将options都注册进ConfigOpts实例中 if section: conf.register_opt(option, group=section) else: conf.register_opt(option) setup_authentication(conf) <setup_authentication values="conf" path="keystone.common.config" function=""> def setup_authentication(conf=None): conf=CONF if conf is None: for method_name in conf.auth.methods: # conf.auth.methods 默认就是下面的 if method_name not in _DEFAULT_AUTH_METHODS: # _DEFAULT_AUTH_METHODS = ['external', 'password', 'token', 'oauth1'] option = cfg.StrOpt(method_name) _register_auth_plugin_opt(conf, option) <_register_auth_plugin_opt vlues="(conf, option)" path=""> def _register_auth_plugin_opt(conf, option): conf.register_opt(option, group='auth') </_register_auth_plugin_opt> </setup_authentication> </configre> </config.configure> sql.initialize() <sql.initializa name="sql.initializa()" path="keyston.common.sql.core" function="sql初始化工作"> from oslo_config import cfg from oslo_db import options as db_options CONF = cfg.CONF LOG = log.getLogger(__name__) def initialize(): db_options.set_defaults( CONF, connections="sqlite:///keystone.db") <db_options.set_defaults path="oslo_db.options" name="db_options.set_defaults(CONF, connections='sqlite:///keystone.db')"> def set_defaults(conf, connection=None, sqlite_db=None, max_pool_size=None, max_overflow=None, pool_timeout=None): # 设置默认配置,覆盖以前默认值 conf.register_opts(database_opts, group='database') if connection is not None: conf.set_default('connection', connection, group='database') if sqlite_db is not None: conf.set_default('sqlite_db', sqlite_db, group='database') if max_pool_size is not None: conf.set_default('max_pool_size', max_pool_size, group='database') if max_overflow is not None: conf.set_default('max_overflow', max_overflow, group='database') if pool_timeout is not None: conf.set_default('pool_timeout', pool_timeout, group='database') <conf.setdefault path='oslo_config.cfg.ConfigOpts' name="conf.setdefault('pool_timeout', pool_timeout, group='database')"> @__clear_cache def set_default(self, name, default, group=None): # name: dest/name # group: GroupOpt/ group name opt_info = self._get_opt_info(name, group) opt_info['default'] = default <self._get_opt_info name="self._get_opt_info(name, group)"> # 返回 (opt, override, default) if group is None: opts = self._opts else: group = self._get_group(group) opts = group._opts if opt_name not in opts: raise NoSuchOptError(opt_name, group) return opts[opt_name] </self._get_opt_info> </conf.setdefault> </db_options.set_defaults> </sql.initializa> CONF(project='keystone', version=version, default_config_files=config_files) <CONF path='oslo_config.cfg.ConfigOpts' name="CONF(project='keystone', version=version, default_config_files=config_files)"> def __call__(self, args=None, project=None, # 'keystone' prog=None, version=None, # pbr.version... usage=None, default_config_files=None, # keystone.conf validate_default_values=False): # 解析命令行参数与配置文件 # 将参数实例成属性 # args: 命令行参数 argv[1:] # project: 顶级的项目名称,这里是keystone,用于定位配置文件 # prog: 程序名 默认为 sys.argv[0] basename # version: 程序版本 # usage: 用法 # default_config_files: 默认使用的配置文件 # validate_default_values: 默认值是否合法 # return: 解析后剩余选项列表 self.clear() <self.clear name="self.clear()"> @__clear_cache def clear(self): # 任何使用add_cli_subparsers()添加的subparsers都会被remove,这是副作用 self._args = None self._oparser = None self._namespace = None self._validate_default_values = False self.unregister_opts(self._config_opts) <self.unregister_opts name="self.unregister_opts(self._config_opts)"> @__clear_cache def unregister_opts(self, opts, group=None): for opt in opts: self.unregister_opt(opt, group, clear_cache=False) <self.unregister_opt name="self.unregister_opt(opt, group, clear_cache=False)"> @__clear_cache def unregister_opt(self, opt, group=None): if self._args is not None: # self._args 是命令行参数 raise ArgsALreadyParsedError("reset before unregistering options") remitem = None for item in self._cli_opts: # self._cli_opts.append({'opt':opt, 'group': group}) if (item['opt'].dest == opt.dest and (group is None or self._get_group(group).name == item['group'].name)): remitem = item break if remitem is not None: self._cli_opts.remove(remitem) </self.unregister_opt> </self.unregister_opts> for group in self._group.values(): group._clear() <group._clear path="oslo_config.cfg.OptGroup"> def _clear(self): # 清楚group 选项的解析状态 self._argparse_group = None <self._argparse_group name="self._argparse_group = None"> def _get_argparse_group(self, parser): if self._argparse_group is None: self._argparse_group = parser.add_argument_group( self.title, self.help) return self._argparse_group </self._argparse_group> </group._clear> </self.clear> self._validate_default_values = validate_default_values prog, default_config_files = self._pre_setup(project, prog, version, usage, default_config_files) <self._pre_setup name="prog, default_config_files = self._pre_setup()"> def _pre_setup(self, project, prog, version, usage, default_config_files): # project=keystone, version=pbr.version... # default_config_files=['/etc/keystone/keystone.conf',] # 为解析选项初始化一个ConfigCliParser if prog is None: prog = os.path.basename(sys.argv[0]) # 第一个参数(即程序)赋值给prog if default_config_files is None: default_config_files = find_config_files(project, prog) <find_config_files path='oslo_config.cfg' name="default_config_files = find_config_files(project, prog)"> def find_config_files(project=None, prog=None, extension='.conf'): # 在 ~/.${project}/ 、 ~/、/etc/${project}/、/etc/中查找配置文件 # 返回最高目录下的配置文件的绝对路径 # 如果没有项目名,则我们找寻${prog.conf} if prog is None: prog = os.path.basename(sys.argv[0]) cfg_dirs = _get_config_dirs(project) <cfg_dirs name="cfg_dirs = _get_config_dirs(project)"> def _get_config_dirs(projects=None): # 返回~/.${project}/ 、 ~/、/etc/${project}/、/etc/ # 如果没有指定project,则返回~/、/etc/ cfg_dirs = [ _fixpath(os.path.join('~', '.'+)) if project else None, _fixpath('~'), <_fixpath name="_fixpath(os.path.join('~', '.'+)) if project else None"> def _fixpath(p): # p 应当为~user或者为~,其他无用 #返回p的绝对路径 return os.path.abspath(os.path.expanduser(p)) </_fixpath> os.path.join('/etc', project) if project else None '/etc' ] return list(moves.filter(bool, cfg_dirs)) # from six import moves </cfg_dirs> </find_config_files> self._oparser = _CachedArgumentParser(prog=prog, usage=usage) <self.oparser path="oslo_config.cfg" name="self._oparser=_CachedArgumentParser(prog=prog, usage=usage)"> class _CachedAgurumentParser(argument.ArgumentParser): # caching/collectiong 命令行参数 # 在初始化ArgumentParser之前,给参数排序 </self.oparser> self._oparser.add_parser_argument(self._oparser, '--version', action='version', version=version) <add_parser-argument path="oslo_config.cfg._CachedArgumentParser" name="self._oparser.add_parser_argument"> def add_parser_argument(self, container, *args, **kwargs): values = [] if container in self._args_cache: values = self._args_cache[container] values.append({'args': args, 'kwargs': kwargs}) self._args_cache[container] = values # self._args_cache['--version'] = values.append({'arg': args, 'kwargs': kwargs}) </add_parser-argument> return prog, default_config_files # prog if prog else os.path.basename(sys.argv[0]) # default_config_files if default_config_files else find_config_files(project, prog) </self._pre_setup> self._setup(project, prog, version, usage, default_config_files) <self._setup name="self._setup(project, prog, version, usage, default_config_files)"> def _setup(self, project, prog, version, usage, default_config_files): # 为选项的解析而进程初始化ConfigOpts实例 self._config_opts = [ _ConfigFileOpts('config-file', default_config_files, metavar='PATH', help=('Path to a config file to use. Multiple ' 'config files can be specified, with values ' 'in later files taking precedence. The ' 'default files used are: %(default)s.')), _ConfigDirOpt('config-dir', metavar='DIR', help='Path to a config directory to pull *.conf ' 'files from. This file set is sorted, so as to ' 'provide a predictable parse order if ' 'is parsed after the file(s) specified via ' 'previous --config-file, arguments hence ' 'over-ridden options in the directory take ' 'precedence.'), self.register_cli_opts(self._config_opts) self.project = project self.prog = prog self.version = version self.usage = usage self.default_config_files = default_config_files </self._setup> self._namespace = self._parse_cli_opts(args if args is not None else sys.argv[1:]) # 将解析的配置内容放进_namespace中 <self._namespace function="解析配置文件与命令行参数"> def _parse_cli_opts(self, args): self._args = args for opt,group in self._all_cli_opts(): <self._all_cli_opts path="" function="产生器 输出opt, group"> def _all_cli_opts(self): for ite in self._cli_opts: yield item['opt'], item['group'] </self._all_cli_opts> opt._add_to_cli(self._oparser, group) <_add_to_cli path="oslo_config.cfg.Opt" name="opt._add_to_cli(self._oparser, group)" function="使选项在命令行中可用"> def _add_to_cli(self, parser, group=None): container = self._get_argparse_container(parser, group) <self._get_argparse_container args="(parser, group)" function=""> def _get_argparse_container(self, parser, group): # 返回argparse._ArgumentGroup 如果group已经给出,否则返回parser # parser: argparse.ArgumentParser # opt: OptGroup 对象 if group is not None: return group._get_argparse_group(parser) <group._get_argparse_group values="parser" path="oslo_config.cfg.OptGroup" function="为OptGroup对象构建argparse._ArgumentGroup" > def _get_argparse_group(self, parser): if self._argparse_group is None: self._argparse_group = parse.add_argument_group(self.title, self.help) <parse.add_argument_group> # 这个是argument.ArgumentParser(...).add_argument_group </parse.add_argument_group> return self._argparse_group </group._get_argparse_group> </self._get_argparse_container> kwargs = self._get_argparse_kwargs(group) <self._get_argparse_kwargs path="oslo_config.cfg.Opt" values="group" function="为add_argument()准备好参数"> def _get_argparse_kwargs(self, group, **kwargs): # 大多Opt继承并覆写该方法 if not self.positional: dest = self.dest if group is not None: dest = group.name + '_' + dest kwargs['dest'] = dest else: kwargs['nargs'] = '?' kwargs.update({'default': None, 'metavar': self.metavar, 'help': self.help}) return kwargs </self._get_argparse_kwargs> prefix = self._get_argparse_prefix('', group.name if group else None) <self._get_argparse_prefix values="('', group.name if group else None)" function=""> def _get_argparse_prefix(self, prefix, group_name): # return: prefix if not group_name else group_name + '-' + prefix if group_name is not None: return group_name + '-' + prefix else: return prefix </self._get_argparse_prefix> deprecated_names = [] for opt in self.deprecated_opts: # self.deprecated_opts = copy.deepcopy(deprecated_opts) or [] deprecated_name = self._get_deprecated_cli_name(opt.name, opt.group) <self._get_deprecated_cli_name function="为淘汰的选项构建CLI参数名"> def _get_deprecated_cli_name(self, dname, dgroup, prefix=''): # return self._get_argparse_prefix(prefix, dgroup) + (dname if dname is None else dname) # if dgroup != 'DEFAULT' ... if dgroup == 'DEFAULT': dgroup =None if dname is None and dgroup is None: return None if dname is None: dname = self.name return self._get_argparse_prefix(prefix, dgroup) + dname </self._get_deprecated_cli_name> if deprecated_name is not None: deprecated_names.append(deprecated_name) self._add_to_argparse(parser,container, self.name, self.short, kwargs, prefix, self.positional, deprecated_names) <self.add_to_argparse values="(parser, container, self.name, self.short, kwargs, prefix)"> def _add_to_argparse(self, parser, container, name, short, kwargs, prefix='', positional=False, deprecated_names=None): # 将选项加入argprese parser 或者group # container: argument._ArgumentGroup object # kwargs: key argu for add_argument() # positional: 是否这一选项是positional CLI argument 初始化的时候赋值 def hyphen(arg): return arg if not positional else '' args = [hypen('--') + prefix + name] if short: args.append(hyphen('-') + short) for deprecated_name in deprecated_names: args.append(hypen('--') + deprecated_name) parser.add_parser_argument(container, * args, **kwargs) </self.add_to_argparse> </_add_to_cli> return self._parse_config_files() <self._parse_config_files values="" path="oslo_config.cfg.ConfigOpts"> def _parse_config_files(self): namespace = _Namespace(self): for arg in self._args: if arg == '--config-file' or arg.startswith('--config-file=') break else: # for 遍历未遇到break则进行else操作 for config_file in self.default_config_files: ConfigParser._parse_file(config_file, namespace) self._oparser.parse_args(self._args, namespace) self._validate_cli_options(namespace) return namespace </self._parse_config_files> </self._namespace> if self._namespace._files_not_found: # namespace 类中初始化 self._files_not_found = [] raise ConfigFilesNotFOundError(self._namespace._files_not_found): if self._namespace._files_premission_denied: # 同样初始化self._files_premission_denied = [] raise ConfigFilesPermissionDeniedError( self._namespace._files_premission_denied) self._check_required_opts() <self._check_required_opts values="()" path="oslo_config.cfg.ConfigOpts" function=""> def _check_required_opts(self, namespace=None): # 检测是否所以标记为required的opts是否用值指定 for info, group in self._all_opt_infos(): # self._all_opt_info() 产生器,无group 与有group <self._all_opt_info> def _all_opt_info(self): for info in self._opts.values(): yield info, None for group in self._groups.values(): for info in group._opts.values(): yield info, group </self._all_opt_info> opt = info['opt'] if opt.required: if 'default' in info or 'override' in info: continue if self._get(opt.dest, group, namespace) is None: <self._get path="oslo_config.cfg.ConfigOpts" name="self._get(opt.dest, group, namespace) is None" function="取值"> def _get(self, name, group=None, namespace=None): if isinstance(group, OptGroup): # 如果是OptGroup类 key = (group.name, name) else: key = (group, name) try: if namespace is not None: raise KeyError: return self._cache[key] except KeyError: value = self._do_get(name, group, namespace) <self._do_get name="self._do_get(name, group, namespace)" function="取值"> # 取opt对象的值 # return: 选项值或者 GroupAtt对象 def _do_get(self, name, group=None, namespace=None): if group is None and name in self._groups: return self.GroupAttr(self, self._get_group(name)) info = self._get_opt_info(name, group) opt = info('opt') if isinstance(opt, SubCOmmandOpt): return self.SubCOmmandAttr(self, group, opt.dest) if 'override' in info: namespace = self._substitute(info['override']) <self._substitute path="oslo_config.cfg.ConfigOpts" name="self._substitute(info['override'])"> def _substitute(self, value, group=None, namespace=None): #用值替换template变量 if isinstance(value, list): return [self._substitute(i, group=group, namespace=namespace) for i in value] elif isinstance(value, str): # 将 '\$' 替换成'$$' if '\$' in value: value = value.replace('\$', '$$') tmpl = self.Template(value) <self.Template name="self.Template(value)" path="oslo_config.cfg.ConfigOpts"> 设置模板类,规则参数开头 class Template(string.Template): idpattern = r'[_a-z][\.a-z0-9]*' </self.Template> ret = tmpl.safe_substitute( self.StrSubWrapper(self, group=group, namespace=namespace)) <tmpl.safe_substitue path='/usr/lib/../../string.Template' value="self.StrWrapper(self, group=group, namespace=namespace)"> from string import Template def safe_substitute(*args, **kws): # 不传self,后面其实也是有了 if not args: raise TypeError("descriptor 'safe_substitute' of 'Tempalte' object" "needs an argument") self, args = args[0], args[1:] if len(args) > 1: raise TypeError('Too many positional arguments') if not args: mappings = kws elif kws: mapping = _multimap(kws, args[0]) <_multimap values="(kws, args[0])" functions="将参数装到这个类里面,复写get"> class _multimap: def __init__(self, primary, secondary): self._primary = primary self._secondary = secondary def __getitem__(self, key): try: return self._primary[key] except KeyError: return self._secondary[key] </_multimap> else: mapping = args[0] def convert(mo): # 辅助.sub() named = mo.group('named') or mo.group('braced') if named is not None: try: return '%s' % (mapping[named],) except KeyError: return mo.group() if mo.group('escaped') is not None: return self.delimiter if mo.group('invalid') is not None: return mo.group() raise ValueError('Unrecognized named group in pattern', self.pattern) # self.pattern 是re... metaclass继承 return self.pattern.sub(convert, self.template) <self.StrSubWrapper path='oslo_config.cfg.ConfigOpts' values="(self, group, namespace=namespace)" path=""> clas StrSubWrapper(object): # 将opt值暴露成字典 def __init__(self, conf, group=None, namespace=None): # conf: ConfigOpts object self.conf = conf self.namespace = namespace self.group = group def __getitem__(self, key): # key: opt name # return: opt value try: group_name, option = key.split('.', 1) except ValueError: group = self.group option = key else: group = OptGroup(name=group_name) try: value = self.conf._get(option, group=group, namespace=self.namespace) except NoSuchOptError: value = self.conf._get(key, namespace=self.namespace) if isinstance(value, self.conf.GroupAttr): raise TemplateSubstituteError( 'substituting group %s not supported' % key) return value </self.StrSubWrapper> </tmpl.safe_substitue> return ret else: return value </self._substitute> </self._do_get> self.__cache[key] = value </self._get> raise RequiredOptError(opt.name, group) </self._check_required_opts> </CONF> pre_setup_logging_fn() <pre_setup_logging_fn name="()" path="keystone.server.eventlet" function="eventlet thread"> pre_setup_logging_fn=configure_threading def configure_threading(): monkeypatch_thread = not CONF.standard_threads # CONF.standard_threads 在path: keystone.common.config.configure中有 default=False pydev_debug_url = utils.setup_remote_pydev_debug() <utils.setup_remote_pydev_debug value="()" path="keystone.common.utils" function="使用pydev调试的才有用"> # 使用pydev调试的才有用 def setup_remote_pydev_debug(): if CONF.pydev_debug_host and CONF.pydev_debug_port: # 没有默认值 try: try: from pydev import pydevd except ImportError: import pydevd pydevd.settrace(CONF.pydev_debug_host, port=CONF.pydev_debug_port, stdoutToServer=True, stderrToServer=True) return True except Exception: LOG.exception(_LE('Error...')) raise </utils.setup_remote_pydev_debug> if pydev_debug_url: # CONF.standard_threads = False monkeypatch_thread = False from keystone.common import environment environment.use_eventlet(monkeypatch_thread) <environment.use_eventlet values="(monkeypatch_thread)" path="keystone.common.environment.__init__"> monkeypathc_thread = True @configure_once(eventlet') <configure_once values="eventlet" function="" path="."> def configure_once(name): # 确保环境配置只执行一次 _configured = False from oslo_config import log LOG = log.getLogger(__name__) <log.getLogger values="(__name__)" path="oslo_log.log"> def getLogger(name=None, project='unknown', version='unknown'): if name not in _loggers: # _loggers = {} _loggers[name] = KeywordArgumentAdapter(logging.getLogger(name), {'project': project, 'version': version}) <KeywordArgumentAdapter values="(logging.getLogger(name), {'project': project, 'version': version})" path="'.'"> </KeywordArgumentAdapter> return _loggers[name] </log.getLogger> def decorator(func): @functools.wrap(func) def wrapper(*args, **kwargs): global _configured if _configured: if _configured == name: return else: raise SystemError("Environment has already been" "configured as %s" % _configured) LOG.debug("ENvironment configured as: %s", name) _configured = name return func(*args, **kwargs) return wrapper return decorator </configure_once> Server = None httplib = None subprocess = None global httplib, subprocess, Server os.environ['EVENTLET_NO_GREENDNS'] = 'yes' # 必须在初始import eventlet时设置一下,以防存在dnspython # 那么socket.getaddinfo()将在IPV6下无法工作 import eventlet from eventlet.green import httplib as _httplib from eventlet.green import subprocess as _subprocess from keystone.common.environment import eventlet_server if monkeypatch_thread is None: monkeypatch_thread = not os.getenv('STANDARD_THREADS') eventlet.wsgi.MAX_HEADER_LINE = 16384 # 默认为8192 eventlet.patcher.monkey_patch(os=False, select=True, socket=True, thread=monkeypatch_thread, time=True, psycopg=False, MySQLdb=False) <eventlet.patcher.monkey_patch values="on=False..."> def monkey_patch(**on): acceptd_args = set(('os','select', 'socket', 'thread', 'time', 'psycopg', 'MySQLdb', 'buildins' )) assert not ('__builein__' in on and 'buildins' in on) try: b = on.pop('__buildtn__') except KeyError: pass else: on['builtins'] = b default_on = on.pop('all', None) for k in six.iterkeys(on): if k not in accepted_args: raise TypeError("monkey_patch() got an unexpected" "keyword argument %r" % k) if default_on is None: default_on = not (True in on.values()) # default_on = True for modname in accepted_args: if modname == 'MySQLdb': on.setdefault(modname, False) if modname == 'builtins': on.setdefault(modname, False) modules_to_patch = [] # 下面就是import eventlet的各个库了,先判断后import if on['os'] and not already_patched.get('os'): # already_patched = {} modules_to_patch += _green_os_modules() already_patched['os'] = True if on['select'] and not already_patched.get('select'): modules_to_patch += _green_select_modules() already_patched['select'] = True if on['socket'] and not already_patched.get('socket'): modules_to_patch += _green_socket_modules() <_green_socket_modules path="."> from eventlet.green import socket try: from eventlet.green import ssl return [('socket', socket), ('ssl', ssl)] except ImportError: return [(''socket), socket] </_green_socket_modules> already_patched['socket'] = True if on['thread'] and not already_patched.get('thread'): modules_to_patch += _green_thread_modules() already_patched['thread'] = True if on['time'] and not already_patched.get('time'): modules_to_patch += _green_time_modules() already_patched['time'] = True if on.get('MySQLdb') and not already_patched.get('MySQLdb'): modules_to_patch += _green_MySQLdb() already_patched['MySQLdb'] = True if on.get('builtins') and not already_patched.get('builtins'): modules_to_patch += _green_builtins() already_patched['builtins'] = True if on['psycopg'] and not already_patched.get('psycopg'): try: from eventlet.support import psycopg2_patcher psycopg2_patcher.make_psycopg_green() already_patched['psycopg'] = True except ImportError: pass imp.acquire() # import lock try: for name, mod in modules_to_patch: orig_mod = sys.modules.get(name) # 获取name模块, 因为在modules_to_patch 中已经该import的import了 if orig_mod is None: orig_mod = __import__(name) for attr_name in mod.__patched__: # 查看是否已经patched attr_name 覆写的方法 patched_attr = getattr(mod, attr_name, None) #获取覆写的方法 if patched_attr is not None: setattr(orig_mod, attr_name, patched_attr) # orig_mod.attr_name = patched_attr finally: imp.release_lock() if sys.version_info >= (3, 3) import importlib._bootstrap thread = original('_thread') importlib._bootstrap._thread = thread import threading threading.RLock = threading.PyRLock </eventlet.patcher.monkey_patch> Server = eventlet_server.Server # keystone.common.environment.eventlet_server.Server <Server name="eventlet_server.Server" path="keystone.common.environment.eventlet_server.Server"> class Server(service.ServiceBase): <service.ServiceBase path="oslo_service.ServiceBase"> @six.add_metaclass(abc.ABCMeta) class ServiceBase(object): # 任何服务的基础类 @abc.abstractmethod def start(self): # 启动服务 @abc.abstractmethod def stop(self): # 停止服务 @abc.abstractmethod def wait(self): # 等待服务完结 @abc.abstractmethod def reset(self): # 重置服务 # 接受SIGHUP时候 </service.ServiceBase> # Server class 管理多个WSGI sockets以及applications def __init__(self, application, host=None, port=None, keepalive=False, keepidle=None): self.application = application self.host = host or '0.0.0.0' self.port = port or 0 # pool for a green thread wsgi服务将执行此pool self.pool = eventlet.GreenPool(POOL_SIZE) self.socket_info = {} self.greenthread = None self.do_ssl = False self.cert_required = False self.keepalive = keepalive self.socket = None self.keepidle = keepidle def listen(self, key=None, backlog=128): # backlog 为排队上限 # 创建并启动socket监听 # 在fork worker进程前调用 # getadd_info不支持ip6 info = socket.getaddinfo(self.host, self.port, socket.AF_UNSPEC, socket.SOCK_STREAM)[0] try: self.socket = eventlet.listen(info[-1], family=info[0], backlog=backlog) except EnvironmentError: LOG.error(_LE("Could not bind to %(host)s: %(port)s"), {'host': self.host, 'port': self.port}) raise LOG.info(_LI('Starting %(arg0)s on %(host)s:%(port)s'), {'arg0': sys.argv[0], 'host': self.host, 'port': self.port}) def start(self, key=None, backlog=128): # run a wsgi server with 给定的application if self.socket is None: self.listen(key=key, backlog=backlog) dup_socket = self.socket.dup() <self.socket.dup path="eventlet.greenio.base.GreenSocket"> #就是将socket重新初始化成_socketobject(), 设置非阻塞 </self.socket.dup> if key: self.socket_info[key] = self.socket.getsocketname() # SSL使能 if self.do_ssl: # 初始化赋值 if self.cert_required: # 初始化赋值 cert_reqs = ssl.CERT_REQUIRED # ssl.CERT_REQUIRED = 2 path='ssl' else: cert_reqs = ssl.CERT_NONE # ssl.CERT_NONE = 0 path='ssl' dup_socket = socket.wrap_ssl(dup_socket, certfile=self.certfile, keyfile=self.keyfile, server_side=True, cert_reqs=cert_reqs, ca_certs=self.ca_certs) # 包装成ssl socket if self.keepalive: # wsgi socket要保活 dup_socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) # 设置配置 if self.keepidle is not None: dup_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, self.keepidle) self.greenthread = self.pool.spwan(self._run, self.application, dup_socket) def set_ssl(self, certfile, keyfile=None, ca_certs=None, cert_required=True): self.certfile = certfile self.keyfile = keyfile self.ca_certs = ca_certs self.cert_required = cert_required self.do_ssl = True def stop(self): if slef.greenthread is not None: self.greenthread.kill() def wait(self): try: self.pool.waitall() except KeyboardInterrupt: pass except greenlet.GreenletExit: pass def reset(self): pass def _run(self, application, socket): logger = log.getLogger('eventlet.wsgi.server') # [eventlet_server]client_socket_timeout 在keystone.conf中 # 应该设置为整数,但是为了让eventlet.wsgi.server()永远服务,设置为了0 socket_timeout = CONF.eventlet_server.client_socket_timeout or None try: eventlet.wsgi.server( socket, application, log=EventletFilteringLogger(logger), debug=False, keepalive=CONF.eventlet_server.wsgi_keep_alive, socket_timeout=socket_timeout) except greenlet.GreenExit: # 直至所以的servers 完成运行 except Exception: LOG.exception(_LE('Server error')) raise </Server> httplib = _httplib # eventlet.green.httplib subprocess = _subprocess # eventlet.green.subprocess </environment.use_eventlet> </pre_setup_logging_fn> config.setup_logging() <config.setup_logging path="keystone.config"> from oslo_log import log import logging def setup_logging(): log.setup(CONF, 'keystone') <log.setup values="(CONF, 'keystone')" path="oslo_log.log"> def setup(conf, product_name, version='unknown'): if conf.log_config_append: # None _load_log_config(conf.log_config_append) else: _setup_logging_from_conf(conf, product_name, version) <---------------------------------这个以后在分析> sys.excepthook = _create_logging_excepthook(product_name) </log.setup> logging.captureWarnings(True) # 如果设为True,重定向所以的warning到 logging package </config.setup_logging> </common.configure> paste_config = config.find_paste_config() <config.find_paste_config name="paste_config = config.find_paste_config()" path="keystone.config"> # 寻找keystone paste.deploy 配置文件 # 配置文件路径已经在keystone.conf 中的[paste_desploy]设置出来了 if CONF.paste_deploy.config_file: paste_config = CONF.paste_deploy.config_file paste_config_value = paste_config if not os.path.isabs(paste_config): paste_config = CONF.find_file(paste_config) <CONF.find_file values="paste_config" path="oslo_config.cfg.ConfigOpts" function="定位配置文件的绝对路径"> def find_file(self, name): dirs = [] if self.config_dir: dirs.append(_fixpath(self.config_dir)) for cfg in reversed(self.config_file): dirs.append(os.path.dirname(_fixpath(cf))) dirs.extend(_get_config_dirs(self.project)) return _search_dirs(dirs, name) <_search_dirs values="(dir, name)" path="."> def _search_dirs(dirs, basename, extension=""): for d in dirs: path = os.path.join(d, '%s%s' % (baename, extension)) if os.path.exists(path): return path </_search_dirs> </CONF.find_file> elif CONF.config_file: paste_config = CONF.config_file[0] paste_config_value = paste_config else: paste_config = CONF.find_file('keystone.conf') paste_config_value = 'keystone.conf' if not paste_config or not os.path.exists(paste_config): raise exception.ConfigFileNotFound(config_file=paste_config_value) return paste_config </config.find_paste_config> _unused, servers = common.setup_backends( startup_application_fn=create_servers) <common.setup_backends values="startup_application_fn=creste_servers" path="keystone.server.common"> def setup_backs(load_extra_backends_fn=lambda: {}, startup_application_fn=lambda: None): # start_application_fn = create_servers drivers = backends.load_backends() <backends.load_backends values="" path="keystone.common.backends"> from keystone import assignment from keystone import auth from keystone import catalog from keystone.common import cache from keystone.contrib import endpoint_filter from keystone.contrib import fedration from keystone.contrib import oauth1 from keystone.contrib import revoke from keystone import credential from keystone import endpoint_policy from keystone import identity from keystone import policy from keystone import resource from keystone import token from keystone import trust def load_backends(): # 配置并且创建cache cache.configure_cache_region(cache.REGION) <cache.configure_cache_region values="cache.REGION" path="keystone.common.cache.core"> cache.REGION <cache.REGION path="keyston.common.cache.core"> REGION = dogpile.cache.make_region( function_key_generator=function_key_generator) # 返回的REGION是cache的前端,可以通过其进行存取 # 但还需要配置一番,具体的可以再研究研究 </cache.REGION> def configure_cache_region(region): # 配置 cache region # return: CacheRegion if not isinstance(region, dogpile.cache.CacheRegion): raise exception.ValidationError( _('region not type dogpile.cache.CacheRegion')) if not region.is_configured: <is_configured path="dogpile.cache.region.CacheRegion"> @property def is_configured(self): return 'backend' in self.__dict__ </is_configured> config_dict = build_cache_config() <build_cache_config path="."> # 创建配置文件字典 # return: conf_dict def build_cache_conf(): prefix = CONF.cache.config_prefix # 在keystone.common.config 还是keystone.conf都有设置 # CONF.cache.config_prefix = cache.keystone conf_dict = {} conf_dict['%s.backend' % prefix] = CONF.cache.backend conf_dict['%s.expiration_time' % prefix] = CONF.cache.expiration_time for argument in CONF.cache.backend_argument: try: argument, argvalue = argument.split(':', 1) except ValueError: msg = _LE('Unable to build cache config-key. Expected format' '"argname: value" . Skipping unknown format: %s' ) LOG.error(msg, argument) continue arg_key = '.'.join([prefix, 'arguments', argname]) conf_dict[arg_key] = argvalue conf_dict.setdefault('%s.argument.url' % prefix, CONF.cache.memcache_servers) for arg in ('dead_retry', 'socket_timeout', 'pool_maxsize', 'pool_unused_timeout', 'pool_connection_get_timeout'): value = getattr(CONF.cache, 'memcache_', arg) conf_dict['%s.argument.%s' % (prefix, arg)] = value return conf_dict </build_cache_config> region.configure_from_config(config_dict, '%s.' % CONF.cache.config_prefix) # 从配置字典中写入配置 if CONF.cache.debug_backend: region.wrap(DebugProxy) # 下面不是太懂,先这样吧,无关大雅 if region.key_mangler is None: region.key_manager = util.sha1_mangle_key for class_path in CONF.cache.proxies: cls = importutils.import_class(class_path) LOG.debug("Adding cache-proxy '%s' to backend," class_path) region.wrap(cls) return region </cache.configure_cache_region> # 上述不返回值,但是cache.REGION已经设置好了,可以使用cache # 还有这个参数 on_arguments = REGION.cache_on_arguments _IDENTITY_API = identity.Manager() <identity.Manager path="keystone.identity.core"> @notification.listener <notification.listener path="keystone.notification"> def listener(cls): # 申明一个类为notification listener # 一个notification listener必须指定event, 定义event_callbacks def init_wrapper(init): @functools.wraps(init) def __new_init__(self, *args, **kwargs): init(self, * args, **kwargs) _register_event_callbacks(self) return __new_init__ def _register_event_callbacks(self): for event, resource_types in self.event_callbacks.items(): for resource_type, callbacks in resource_type.items(): register_event_callback(event, resource_type, callbacks) # 用identity.manager实例 # register_event_callback('delete', 'domain', self._domain_deleted) <register_event_callback values="(event, resource_type, callbacks)"> def register_event_callback(event, resource_type, callbacks): # register each callback with event # event: 处理事件 类型: keystone.notifications.ACTIONS # resource: 将要被操作的资源类型 参数类型: str # callbacks: 将要与事件进行关联的处理函数 if event not in ACTIONS: raise ValueError(_('%(event)s is not a valid notification event, must ' 'be one of: %(actions)s') % {'event': event, 'actions': ', '.join(ACTIONS)}) if not hasattr(callbacks, '__iter__'): callbacks = [callbacks] for callback in callbacks: if not callable(callback): # 其实调用就是可以加上() 函数加上可以,类加上也可以 # 所以 你懂的 ,只要实现__call__就可以 msg = _('Method not callable: %s' % callable) LOG.error(msg) # _SUBSCRIBERS = {} _SUBSCRIBERS.setdefault(event, {}).setdefault(resource_type, set()) # 实现结果类似于 {event: {resource_type: set()}} _SUBSCRIBERS[event][resource_type].add(callback) if LOG.logger.getEffectiveLevel() <= logging.DEBUG: msg = 'Callback: `%(callback)s` subscribed to event `%(event)s`.' callback_info = _get_callback_info(callback) callback_str = '.'.join(for i in callback_info if i is not None) event_str = '.'.join(['identity', resource_type, event]) LOG.debug(msg, {'callback': callback_str, 'event': event_str}) # oslo_log 这一块有时间可以看看 </register_event_callback> cls.__init__ = init_wrapper(cls.__init__) return cls </notification.listener> @dependency.provider('identity_api') <dependency.provider values="(identity_api)" path="keystone.common.dependency"> def provider(name): # used to register providers def wrapper(cls): def wrapped(init): def __wrapped_init__(self, *args, **kwargs): # 初始化包裹对象并将其添加到register init(self, *args, **kwargs) _set_provider(name, self) # _REGISTRY[name] = (provider, traceback.format_stack()) <_set_provider values="(name, self)" path="."> # 将self注册进_REGISTRY def _set_provider(name, provider): _original_provider, where_registered = _REGISTRY.get(name, (None, None)) if where_registered: raise Exception('%s already has a registered provider, at\n%s' % (name, ''.join(where_registered)) _REGISTRY[name] = (provider, traceback.format_stack()) # _REGISTRY = {} 初始值 # traceback.format_stack()追踪异常的,这个有空再了解 </_set_provider> resolve_future_dependencies(__provider_name=name) <resolve_future_dependencies values="(__provider_name=name)" path="."> def resolve_future_dependencies(__provider_name=None): # 注入所有依赖 # _future_dependencies = {} new_providers = dict() if __provider_name: targets = _future_dependencies.pop(__provider_name, []) for target in targets: setattr(target, __provider_name, get_provider(__provider_name)) <get_provider values="(__provider_name)" path="."> def get_provider(name, optional=GET_REQUIRED): # GET_REQUIRED = object() if optional is GET_REQUIRED: return _REQUIRED: return _REGISTRY.get(name, (None, None))[0] </get_provider> return # self.keystone = ... </resolve_future_dependencies> return __wrap_init__ cls.__init__ = wrapped(cls.__init__) _factories[name] = cls # 初始为{} return cls return wrapper </dependency.provider> @dependency.requires('assignment_api', 'credential_api', 'id_mapping_api', 'resource_api', 'revoke_api') <dependency.requires values="(assignment,..)" path="." function=""> def requires(*dependencies): def wrapper(self, *args, **kwargs): self.__wrapped_init__(*args, **kwargs) _process_dependencies(self) <_process_dependencies values="self"> </_process_dependencies> def wrapped(cls): existing_dependencies = getattr(cls, '_dependencies', set()) cls._dependencies = existing_dependencies.union(dependencies) if not hasattr(cls, '__wrapped_init__'): cls.__wrapped_init__ = cls.__init__ cls.__init__ = wrapper return cls return wrapped </dependency.requires> class Manager(manager.Manager): # Identity backend # 等下我们分析 keystone.common.manager.Manager driver_namespace = 'keystone.identity' # 其实这也是entry_point ,manager.Manger初始化时候会用到 _USER = 'user' _GROUP = 'group' def __init__(self): super(Manager, self).__init__(CONF.identity.driver) <__init__ values="CONF.identity.driver" path="keystone.common.manager.Manager"> class Manager(object): driver_namespace = None def __init__(self, driver_name): self.driver = load_driver(self.driver_namespace, driver_name) # 具体看书签里面的python/entry_point # 通过查找egg.info 中的entry_point.txt中的namespace对应选项,获取加载模块 <load_driver values="(self.driver_namespace, driver_name)" path="keystone.identity.backend.sql.Identity"> class Identity(identity.Driver): # 覆写__init__方法,使其能够传递config属性,使得sql能够当做domain-specific驱动来使用 def __init__(self, conf=None): super(Identity, self).__init__() def default_assginment_driver(self): return 'sql' @property # 将其变成属性调用 def is_sql(self): return True def _check_password(self, password, user_ref): return utils.check_password(password, user_ref.password) # 认证接口 def authenticate(self, user_id, password): session = sql.get_session() <sql.get_session values="" path=""> 这个最后再分析 </sql.get_session> user_ref = None try: user_ref = self._get_user(session, user_id) <self._get_user values="(session, user_id)" path="."> 见下面分析 </self._get_user> except exception.UserNotFound: raise AssertionError(_('Invalid user / password')) if not self._check_password(password, user_ref): # 上面已分析 raise AssertionError(_('Invalid user / password')) return identity.filter_user(user_ref.to_dict()) <user_ref.to_dict values="()" path="keystone.identity.backend.sql.User"> def to_dict(self, include_extra_dict=False): # 将self的属性都放入参数中,对于default_project_id 存在并为None则删除 d = super(User, self).to_dict(include_extra_dict=include_extra_dict) if 'default_project_id' in d and d['default_project_id'] is None: del d['default_project_id'] return d </user_ref.to_dict> <identity.filter_user values="(user_ref.to_dict)" path="keystone.identity.core"> def filter_user(user_ref): # 从user字典中过滤出私密项 # 'password' 'tenants' 'group'不会返回 # 返回 user_ref if user_ref: user_ref = user_ref.copy() # 为何要如此做个copy --》 # 这个可以限定在此函数中使用与修改,而不会对整体发生改变 user_ref.pop('password', None) user_ref.pop('tenants', None) user_ref.pop('groups', None) user_ref.pop('domains', None) try: user_ref['extra'].pop('password', None) user_ref['extra'].pop('tenants', None) except KeyError: pass return user_ref </identity.filter_user> @sql.handle_conflicts(conflict_type='user') # 在操作数据库遇到问题时候会报错,资源访问不可用报409 <sql.handler_conflicts values="(conflict_type='user')" path="keystone.common.sql.core"> def handle_conflicts(conflict_type='object'): _conflict_msg = 'Conflict %(conflict_type)s: %(details)s' def decorator(method): @functools.wraps(method) def wrapper(*args, **kwargs): try: return method(*args, **kwargs) except db_exception.DEDuplicateEntry as e: LOG.debug(_conflict_msg, {'conflict_type': conflict_type, 'details': six.text_type(e)}) raise exception.Conflict(type=conflict_type, details=_('Duplicate Entry')) except db_exception.DBError as e: if isinstance(e.inner_exception, InterityError): raise exception.UnexceptedError( _('An unexpected error occurred when trying to ' 'store %s' ) % conflict_type) raise </sql.handler_conflicts> def create_user(self, user_id, user) user = utils.hash_user_password(user) # 只将password 哈希化 返回 dict(user, password=hash_password(password)) <utils.hash_user_password values="user" path="keystone.common.utils"> def hash_user_password(user): password = user.get('password') if password is None: return return dict(user, password=hash_password(password)) </utils.hash_user_password> session = sql.get_session() # 取得sql连接 with session.begin(): user_ref = User.from_dict(user) # 创建User实例 session.add(user_ref) return identity.filter(user_ref.to_dict()) </load_driver> def __getattr__(self, name): # self.keystone--->return self.driver 用名字返回驱动实例 f = getattr(self.driver, name) setattr(self, name, f) return f </__init__> self.domain_configs = DomainConfigs() <DomainConfigs values="" path="."> @dependncy.requires('domain_config_api') class DomainConfigs(dict): # 这边先分析到此处 ‘domain_config_api以后应该会看到’ </DomainConfigs> self.event_callbacks = { notifications.ACTIONS.deleted: { 'domain': [self._domain_detleted], }, } </identity.Manager> _ASSIGNMENT_API = assignment.Manager() <assignment.Manager values="" path="keystone.assignment.core"> @dependency.provider('assignment_api') @dependency.requires('credential_api', 'identity_api', 'resource_api', 'revoke_api', 'role_api') class Manager(manager.Manager): driver_namespace = 'keystone.assignment' _PROJECT = 'project' _ROLE_REMOVE_FROM_USER = 'role_remove_from_user' _INVALIDATE_USER_PROJECT_TOKENS = 'invalidate_user_project_tokens' def __init__(self): # 如果没有指定驱动的化,我们就让identity告诉我们用什么,为以后identity涵盖 # identity、resource、assignment作准备 assignment_driver = CONF.assignment.driver if assignment is None: identity_driver = dependency.get_provider('identity_api').driver assignment_driver = identity_driver.default_assignment_driver() <identity_driver.default_assignment_driver path=""> 返回的是一个字符串 'sql' 或者 'ldap' 现在返回的是'sq' </identity_driver.default_assignment_driver> super(Manager, self).__init__(assignment_driver) <__init__ values="sql"> </__init__> </assignment.Manager> # 确保identity 驱动在assignment manager之前创建 # assignment 驱动在 resource manager之前创建 # 默认resource 驱动依赖assignment,这也导致其依赖于identity,所以需要确保这条链可用 # require 是从_REGISTRY 取出注册的,provider是向_REGISTRY中写入 DRIVERS = dict( assignment_api=_ASSIGNMENT_API, catalog_api=catalog.Manager(), # 'catalog_api' credential_api=credential.Manager(), # 'credential_api' domain_config_api=resource.DomainConfigManager(), # 'domain_config_api' endpoint_filter_api=endpoint_filter.Manager(), # endpoint_filter_api endpoint_policy_api=endpoint_policy.Manager(), # endpoint_policy federation_api=federation.Manager(), # federation_api id_generator_api=identity.generator.Manager(), # id_generator_api id_mapping_api=identity.MappingManager(), identity_api=_IDENTITY_API, oauth_api=oauth1.Manager(), policy_api=policy.Manager(), resource_api=resource.Manager(), revoke_api=revoke.Manager(), # 添加listener <revoke.Manager values="" path="keystone.contrib.revoke.core"> @dependency.provider('revoke_api') class Manager(manager.Manager): driver_namespace = 'keystone.revoke' def __init__(self): super(Manager, slef).__init__(CONF.revoke.driver) self._register_listeners() <_register_listeners values="" path="keystone.contrib.revoke.core"> # 安装listener def _register_listeners(self): </_register_listeners> self.model = model </revoke.Manager> role_api=assignment.RoleManager(), token_api=token.persistence.Manager(), trust_api=trust.Manager(), token_provider_api=token.provider.Manager()) # 添加listener auth.controllers.load_auth_methods() <auth.controllers.load_auth_methods values="" path="keystone.auth.controllers"> def load_auth_methods(): global AUTH_PLUGIN_LOADED # AUTH_PLUGIN_LOADED = False if AUTH_PLUGIN_LOADED: return config.setup_authentication() # 将配置那些认证方法注册一遍 for plugin in set(CONF.auth.methods): AUTH_METHODS[plugin] = load_auth_method(plugin) # 将其装入AUTH_METHODS字典 <load_auth_method values="plugin" path=""> def load_auth_method(method): plugin_name = CONF.auth.get(method) or 'default' try: namespace = 'keystone.auth.%s' % method driver_manager = stevedore.DriverManager(namespace, plugin_name, invoke_on_load=True) return driver_manager.driver except RuntimeError: LOG.debug('Failed to load the %s driver (%s) using stevedore, will ' 'attempt to load using import_object instead.', method, plugin_name) @versionutilt.deprecated(as_of=versionutils.deprecated.LIBERTY, in_favor_of='entrypoints', what='direct import of driver') def _load_using_import(plugin_name): return importutils.import_object(plugin_name) </load_auth_method> AUTH_PLUGINS_LOADED = True </auth.controllers.load_auth_methods> return DRIVERS </backends.load_backends> # Router URL的匹配是按照注册顺序进行的 # environ['wsgiorg.routing_args'] = ((url, match)) # environ['routes.route'] = route # environ['routes.url'] = url drivers.update(load_extra_backends_fn()) res = startup_application_fn() <create_servers values="" path="keystone.server.eventlet"> </create_servers> dirvers.update(dependency.resolve_future_dependencies()) return drivers, res </common.setup_backends> serve(*servers) </eventlet_server.run> </possible_topdir> </keystone>
查看原文:http://www.zoues.com/index.php/2015/09/13/keystone-all/