分享

Openstack源代码分析之keystone服务(keystone-all)

tntzbzc 发表于 2014-11-19 23:14:48 [显示全部楼层] 回帖奖励 阅读模式 关闭右栏 0 62837
问题导读
1.keystone-all.py的作用是什么?
2.Python应用程序使用WSGI(Web Server Gateway Interface)协议来做什么?







!在调试keystone遇到问题,evenlet线程出错,解决办法参考: http://adam.younglogic.com/2012/12/keystone-and-eclipse-pydev/,主要是在调试keystone-all时增加启动参数,–standard-threads解决,其实里面也有说明,在用pydev调试是需要将monkeypatch_thread设置为False。
!原因:cannot switch to a different thread
keystone-all.py启动keystone服务,所以从keystone-all来入手分析。
首先将keystone源码目录下的模块路径进行设置,将目录配置在系统目录下。
  1. possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__),
  2.                                    os.pardir,
  3.                                    os.pardir))
  4. if os.path.exists(os.path.join(possible_topdir,
  5.                                'keystone',
  6.                                '__init__.py')):
  7.     sys.path.insert(0, possible_topdir)
复制代码


再引入目录下的其他模块
  1. from paste import deploy
  2. import pbr.version
  3. from keystone.openstack.common import gettextutils
  4. gettextutils.install('keystone', lazy=True)
复制代码


引入paste.deploy模块,这个模块官方的解释是,Paste Deployment is a system for finding and configuring WSGI applications and servers.意思是这个模块是一个用于发现和配置WSGI应用或者服务的系统。官方网址是:paste.deploy模块官方网址
WSGI(Web Server Gateway Interface)是一个协议,Python应用程序或框架和Web服务器之间的一种接口,我总结的通俗说法就是将Python应用程序的某些接口转变成Web上的接口可以通过http服务来进行调用。官方网址是:PEP 333,python2.7 wsgi官方网址
pbr模块是用于安装时配置的一个模块,官方解释是:PBR is a library that injects some useful and sensible default behaviors into your setuptools run.意思是PBR是一个库,用于注入一些有用的和敏感的基本配置信息到setuptools.setuptools是一个工具用于下载,编译,安装,升级,卸载python的包文件。
pbr的官方网址是:pbr官方网址
setuptools的官方网址是:setuptools官方网址
gexttextutil是一个运行后加载的模块,用于识别用户的提交的语言,帮助返回错误信息时使用用户语言。
  1. from keystone.common import environment
  2. from keystone.common import utils
  3. from keystone import config
  4. from keystone.openstack.common import importutils
  5. CONF = config.CONF
复制代码


environment模块用于配置环境变量,用于配置keystone的web服务是使用eventlet库里的htttp模块还是python库下http模块。
utils模块提供一些Openstack工具类。
config模块,是一个包装模块,用于包装keystone.common下的config模块。包装的概念就是对一个已经存在的对象增加,删除,修改其内部属性和功能。顾名思义,就是为服务提供配置信息的模块。
importutils模块,帮助导入其他类,对象,模块的一个模块。
下面就是启动keystone服务的主体程序,首先读取配置文件keystone.conf和其他配置文件,再配置log打印信息。
配置paste_config对象和上文提到的paste.deploy模块对应为其配置文件,后面就是开启服务的主体。
  1. if __name__ == '__main__':
  2.     dev_conf = os.path.join(possible_topdir,
  3.                             'etc',
  4.                             'keystone.conf')
  5.     config_files = None
  6.     if os.path.exists(dev_conf):
  7.         config_files = [dev_conf]
  8.     CONF(project='keystone',
  9.          version=pbr.version.VersionInfo('keystone').version_string(),
  10.          default_config_files=config_files)
  11.     config.setup_logging(CONF, product_name='keystone')
  12.     # Log the options used when starting if we're in debug mode...
  13.     if CONF.debug:
  14.         CONF.log_opt_values(logging.getLogger(CONF.prog), logging.DEBUG)
  15.     paste_config = config.find_paste_config()
  16.     monkeypatch_thread = not CONF.standard_threads
  17.     pydev_debug_url = utils.setup_remote_pydev_debug()
  18.     if pydev_debug_url:
  19.         # in order to work around errors caused by monkey patching we have to
  20.         # set the thread to False.  An explanation is here:
  21.         # http://lists.openstack.org/pipermail/openstack-dev/2012-August/
  22.         # 000794.html
  23.         monkeypatch_thread = False
  24.     environment.use_eventlet(monkeypatch_thread)
  25.     servers = []
  26.     servers.append(create_server(paste_config,
  27.                                  'admin',
  28.                                  CONF.bind_host,
  29.                                  int(CONF.admin_port)))
  30.     servers.append(create_server(paste_config,
  31.                                  'main',
  32.                                  CONF.bind_host,
  33.                                  int(CONF.public_port)))
  34.     serve(*servers)
复制代码


通过eventlet模块启动配置服务器,完成eventlet环境的配置,并启动服务器。eventlet是一个第三方库,官方说明提供并发网络服务器的功能,官方网址:eventlet官方网址
  1.   environment.use_eventlet(monkeypatch_thread)
复制代码


在environment模块下代码
  1. @configure_once('eventlet')
  2. def use_eventlet(monkeypatch_thread=None):
  3.     global httplib, subprocess, Server
  4.     # This must be set before the initial import of eventlet because if
  5.     # dnspython is present in your environment then eventlet monkeypatches
  6.     # socket.getaddrinfo() with an implementation which doesn't work for IPv6.
  7.     os.environ['EVENTLET_NO_GREENDNS'] = 'yes'
  8.     import eventlet
  9.     from eventlet.green import httplib as _httplib
  10.     from eventlet.green import subprocess as _subprocess
  11.     from keystone.common.environment import eventlet_server
  12.     if monkeypatch_thread is None:
  13.         monkeypatch_thread = not os.getenv('STANDARD_THREADS')
  14.     # Raise the default from 8192 to accommodate large tokens
  15.     eventlet.wsgi.MAX_HEADER_LINE = 16384
  16.     # NOTE(ldbragst): Explicitly declare what should be monkey patched and
  17.     # what shouldn't. Doing this allows for more readable code when
  18.     # understanding Eventlet in Keystone. The following is a complete list
  19.     # of what is monkey patched instead of passing all=False and then passing
  20.     # module=True to monkey patch a specific module.
  21.     eventlet.patcher.monkey_patch(os=False, select=True, socket=True,
  22.                                   thread=monkeypatch_thread, time=True,
  23.                                   psycopg=False, MySQLdb=False)
  24.     Server = eventlet_server.Server
  25.     httplib = _httplib
  26.     subprocess = _subprocess
复制代码


完成环境配置后创建服务器,启动服务。
create_server函数完成deploy模块配置信息的读取,通过loadapp加载WSGI服务器配置选项,再通过environment启动配置过的文件Server,并设置ssl协议,配置证书,保证服务器的安全传输。
serve函数启动信号监听机制=(SIGINT中断),并start服务器,导入notifier模块,给keystone提供notify的服务(事件通知服务,回调函数?),最后使服务wait(),等待全部操作完成后,提供服务。
   
  1. servers = []
  2.     servers.append(create_server(paste_config,
  3.                                  'admin',
  4.                                  CONF.bind_host,
  5.                                  int(CONF.admin_port)))
  6.     servers.append(create_server(paste_config,
  7.                                  'main',
  8.                                  CONF.bind_host,
  9.                                  int(CONF.public_port)))
  10.     serve(*servers)
复制代码

  1. def create_server(conf, name, host, port):
  2.     app = deploy.loadapp('config:%s' % conf, name=name)
  3.     server = environment.Server(app, host=host, port=port)
  4.     if CONF.ssl.enable:
  5.         server.set_ssl(CONF.ssl.certfile, CONF.ssl.keyfile,
  6.                        CONF.ssl.ca_certs, CONF.ssl.cert_required)
  7.     return server
  8. def sigint_handler(signal, frame):
  9.     """Exits at SIGINT signal."""
  10.     logging.debug('SIGINT received, stopping servers.')
  11.     sys.exit(0)
  12. def serve(*servers):
  13.     signal.signal(signal.SIGINT, sigint_handler)
  14.     for server in servers:
  15.         server.start()
  16.     # notify calling process we are ready to serve
  17.     if CONF.onready:
  18.         try:
  19.             notifier = importutils.import_module(CONF.onready)
  20.             notifier.notify()
  21.         except ImportError:
  22.             try:
  23.                 utils.check_output(CONF.onready.split())
  24.             except Exception:
  25.                 logging.exception('Failed to execute onready command')
  26.     for server in servers:
  27.         server.wait()
复制代码


Server类实现,通过eventlet模块开启两个并发的网络服务器。
  1. class Server(object):
  2.     """Server class to manage multiple WSGI sockets and applications."""
  3.     def __init__(self, application, host=None, port=None, threads=1000):
  4.         self.application = application
  5.         self.host = host or '0.0.0.0'
  6.         self.port = port or 0
  7.         self.pool = eventlet.GreenPool(threads)
  8.         self.socket_info = {}
  9.         self.greenthread = None
  10.         self.do_ssl = False
  11.         self.cert_required = False
  12.     def start(self, key=None, backlog=128):
  13.         """Run a WSGI server with the given application."""
  14.         LOG.info(_('Starting %(arg0)s on %(host)s:%(port)s') %
  15.                  {'arg0': sys.argv[0],
  16.                   'host': self.host,
  17.                   'port': self.port})
  18.         # TODO(dims): eventlet's green dns/socket module does not actually
  19.         # support IPv6 in getaddrinfo(). We need to get around this in the
  20.         # future or monitor upstream for a fix
  21.         info = socket.getaddrinfo(self.host,
  22.                                   self.port,
  23.                                   socket.AF_UNSPEC,
  24.                                   socket.SOCK_STREAM)[0]
  25.         _socket = eventlet.listen(info[-1],
  26.                                   family=info[0],
  27.                                   backlog=backlog)
  28.         if key:
  29.             self.socket_info[key] = _socket.getsockname()
  30.         # SSL is enabled
  31.         if self.do_ssl:
  32.             if self.cert_required:
  33.                 cert_reqs = ssl.CERT_REQUIRED
  34.             else:
  35.                 cert_reqs = ssl.CERT_NONE
  36.             sslsocket = eventlet.wrap_ssl(_socket, certfile=self.certfile,
  37.                                           keyfile=self.keyfile,
  38.                                           server_side=True,
  39.                                           cert_reqs=cert_reqs,
  40.                                           ca_certs=self.ca_certs)
  41.             _socket = sslsocket
  42.         self.greenthread = self.pool.spawn(self._run,
  43.                                            self.application,
  44.                                            _socket)
  45.     def set_ssl(self, certfile, keyfile=None, ca_certs=None,
  46.                 cert_required=True):
  47.         self.certfile = certfile
  48.         self.keyfile = keyfile
  49.         self.ca_certs = ca_certs
  50.         self.cert_required = cert_required
  51.         self.do_ssl = True
  52.     def kill(self):
  53.         if self.greenthread:
  54.             self.greenthread.kill()
  55.     def wait(self):
  56.         """Wait until all servers have completed running."""
  57.         try:
  58.             self.pool.waitall()
  59.         except KeyboardInterrupt:
  60.             pass
  61.         except greenlet.GreenletExit:
  62.             pass
  63.     def _run(self, application, socket):
  64.         """Start a WSGI server in a new green thread."""
  65.         log = logging.getLogger('eventlet.wsgi.server')
  66.         try:
  67.             eventlet.wsgi.server(socket, application, custom_pool=self.pool,
  68.                                  log=logging.WritableLogger(log))
  69.         except Exception:
  70.             LOG.exception(_('Server error'))
  71.             raise
复制代码


存在不明确的地方:
1.eventlet网络服务器的实现
2.paste.deploy机制的实现
3.notify机制的实现


相关文章

Openstack之keystone源代码分析1--WSGI接口流程分析
http://www.aboutyun.com/thread-10137-1-1.html

Openstack之keystone源代码分析2--Controller->Manager->Driver
http://www.aboutyun.com/thread-10138-1-1.html


[openstack][G版]keystone源码学习
http://www.aboutyun.com/thread-10136-1-1.html










bluefire1991


没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条