<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>