keystone在httpd的入口执行文件为/usr/bin/keystone-wsgi-public
查看文件/usr/bin/keystone-wsgi-public,根据代码,看到主要是这几行代码对keystone的服务进行了初始化,keystone服务通过wsgiref.simple_server与httpd进行交互,初始化函数为initialize_public_application
1 from keystone.server.wsgi import initialize_public_application 2 3 if __name__ == "__main__": 4 ...... 5 import wsgiref.simple_server as wss 6 7 ...... 8 9 server = wss.make_server(args.host, args.port, initialize_public_application()) 10 11 ....... 12 server.serve_forever()
查看initialize_public_application
1 from keystone.server.flask import core as flask_core 2 def initialize_public_application(): 3 return flask_core.initialize_application(name='public', config_files=flask_core._get_config_files()) 4 initialize_admin_application = initialize_public_application
从这里可以看到在Rocky版本的keystone中,admin和public已经合并在了一起,不再区分admin和public,flask_core.initialize_application接受三个参数,原型如下
1 def initialize_application(name, post_log_configured_function=lambda: None, 2 config_files=None): 3 possible_topdir = os.path.normpath(os.path.join( 4 os.path.abspath(__file__), 5 os.pardir, 6 os.pardir, 7 os.pardir, 8 os.pardir)) 9 10 dev_conf = os.path.join(possible_topdir, 11 'etc', 12 'keystone.conf') 13 if not config_files: 14 config_files = None 15 if os.path.exists(dev_conf): 16 config_files = [dev_conf] 17 18 keystone.server.configure(config_files=config_files) 19 20 if CONF.debug: 21 CONF.log_opt_values(log.getLogger(CONF.prog), log.DEBUG) 22 23 post_log_configured_function() 24 25 def loadapp(): 26 app = application.application_factory(name) 27 return app 28 29 _unused, app = keystone.server.setup_backends( 30 startup_application_fn=loadapp) 31 32 profiler.setup(name) 33 34 return setup_app_middleware(app)
name 这个不需要讲,但是name必须是public或者admin,在后面的初始化过程中有限制
post_log_configured_function 接收一个函数,会在initialize_application这个函数中调用,但是具体是什么作用,还没有领悟出来,但是这个函数应该是可以做一些类似于初始化等等作用的
config_files 在开发模式下可以自定义keystone.conf,如果自己定义了keyston.conf这个文件,那么就会完全的替代/etc/keystone/keystone.conf中定义的一些配置参数,关于配置参数,后续会介绍
接着往下看这个函数
keystone.server.configure这个函数对keystone进行了一些配置
1 def configure(version=None, config_files=None, 2 pre_setup_logging_fn=lambda: None): 3 keystone.conf.configure() 4 sql.initialize() 5 keystone.conf.set_config_defaults() 6 7 CONF(project='keystone', version=version, 8 default_config_files=config_files) 9 10 pre_setup_logging_fn() 11 keystone.conf.setup_logging() 12 13 if CONF.insecure_debug: 14 LOG.warning( 15 'insecure_debug is enabled so responses may include sensitive ' 16 'information.')
keystone.conf.configure()这个函数添加了几个参数,
standard-threads 可以自定义多少个线程
pydev-debug-host以及pydev-debug-port是对远程debug的机器和port进行了定义,并在conf中注册了它们三个参数
1 def configure(conf=None): 2 if conf is None: 3 conf = CONF 4 5 conf.register_cli_opt( 6 cfg.BoolOpt('standard-threads', default=False, 7 help='Do not monkey-patch threading system modules.')) 8 conf.register_cli_opt( 9 cfg.StrOpt('pydev-debug-host', 10 help='Host to connect to for remote debugger.')) 11 conf.register_cli_opt( 12 cfg.PortOpt('pydev-debug-port', 13 help='Port to connect to for remote debugger.')) 14 15 for module in conf_modules: 16 module.register_opts(conf) 17 18 # register any non-default auth methods here (used by extensions, etc) 19 auth.setup_authentication() 20 21 # add oslo.cache related config options 22 cache.configure(conf)
auth.setup_authentication(),配置了几种认证方式,password,token等等,跟上述参数一样,先定义,后注册到conf中
cache.configure(conf),对cache进行了定义和注册,主要涉及到各种cache的定义等等
回到 keystone.server.configure
sql.initialize() 这个函数主要是对数据库进行了初始化,导入配置文件中的参数
keystone.conf.set_config_defaults() 采用默认值对keystone的设置进行初始化
CONF(project='keystone', version=version, default_config_files=config_files) 这块比较难理解,先说第三个参数,第三个参数采用指定的config_files对默认参数进行复写
CONF的原型是 keystone.conf.CONF, 它采用了从文件中直接导入的方式,构造了一个单例的configure配置类 CONF = ConfigOpts()
CONF()调用了ConfigOpts.__call__()这个方法
pre_setup_logging_fn()这个和前文的post_log_configured_function是相似的,都是完成一些自定义的动作
keystone.conf.setup_logging()这个参数是对log进行配置
回到initialize_application
keystone.server.setup_backend(startup_application_fn=loadapp)
1 def setup_backends(load_extra_backends_fn=lambda: {}, 2 startup_application_fn=lambda: None): 3 drivers = backends.load_backends() 4 drivers.update(load_extra_backends_fn()) 5 res = startup_application_fn() 6 return drivers, res
1 def loadapp(): 2 app = application.application_factory(name) 3 return app
这两段代码中,setup_backends首先导入一些driver,导入方法主要是用stevedore的DriverManager类对一些方法进行导入,并将这些driver注册到provider_api中,供后续调用
load_app采用了flask.application的工厂模式方法构造
1 def application_factory(name='public'): 2 if name not in ('admin', 'public'): 3 raise RuntimeError('Application name (for base_url lookup) must be ' 4 'either `admin` or `public`.') 5 6 app = flask.Flask(name) 7 app.after_request(_add_vary_x_auth_token_header) 8 9 app.config.update(PROPAGATE_EXCEPTIONS=True) 10 11 # TODO(morgan): Convert Subsystems over to Flask-Native, for now, we simply 12 dispatch_map = collections.OrderedDict() 13 14 hc_app = healthcheck.Healthcheck.app_factory( 15 {}, oslo_config_project='keystone') 16 dispatch_map['/healthcheck'] = hc_app 17 _routers = [] 18 sub_routers = [] 19 mapper = routes.Mapper() 20 for api_routers in ALL_API_ROUTERS: 21 moved_found = [pfx for 22 pfx in getattr(api_routers, '_path_prefixes', []) 23 if pfx in _MOVED_API_PREFIXES] 24 if moved_found: 25 raise RuntimeError('An API Router is trying to register path ' 26 'prefix(s) `%(pfx)s` that is handled by the ' 27 'native Flask app. Keystone cannot ' 28 'start.' % 29 {'pfx': ', '.join([p for p in moved_found])}) 30 31 routers_instance = api_routers.Routers() 32 _routers.append(routers_instance) 33 routers_instance.append_v3_routers(mapper, sub_routers) 34 35 keystone.api.discovery.register_version('v3') 36 for api in keystone.api.__apis__: 37 for api_bp in api.APIs: 38 api_bp.instantiate_and_register_to_app(app) 39 sub_routers.append(_ComposibleRouterStub(_routers)) 40 legacy_dispatcher = keystone_wsgi.ComposingRouter(mapper, sub_routers) 41 42 for pfx in itertools.chain(*[rtr.Routers._path_prefixes for 43 rtr in ALL_API_ROUTERS]): 44 dispatch_map['/v3/%s' % pfx] = legacy_dispatcher 45 46 app.wsgi_app = KeystoneDispatcherMiddleware( 47 app.wsgi_app, 48 dispatch_map) 49 return app
这块限定了name只能是public or admin,这个函数主要是为keystone的请求添加路由的mapper以及中间件的调用,先由这个函数对请求的API的路径进行解析, 最后KeystoneDispatcherMiddleware完成对路径API的访问,同样是调用了__call__()方法
profiler.setup(name)是对信息的采集,对性能统计有作用
setup_app_middleware()同样是通过stevedore.DriverManager进行中间件的导入
到此基本上整个keystone便启动了~