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