在分析Neutron源码的过程中,会遇到很多阻碍,oslo.config就是其中之一。启动Neutron的标准方式是systemctl start neutron-server.service。那么该命令具体做了哪些事情,neutron-server的配置参数又是怎么被加载的呢?
下面让我们具体分析。
-
neutron-server.service的具体内容
文件路径在/usr/lib/systemd/system/neutron-server.service。如果不清楚怎么确定systemd配置文件,可以查看我的另外一篇专门讲解systemd的博文。
[Unit] Description=OpenStack Neutron Server After=syslog.target network.target [Service] Type=notify User=neutron ExecStart=/usr/bin/neutron-server --config-file /usr/share/neutron/neutron-dist.conf --config-dir /usr/share/neutron/server --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugin.ini --config-dir /etc/neutron/conf.d/common --config-dir /etc/neutron/conf.d/neutron-server --log-file /var/log/neutron/server.log PrivateTmp=true NotifyAccess=all KillMode=process Restart=on-failure TimeoutStartSec=0 [Install] WantedBy=multi-user.target
从上可知,neutron-server的配置文件有neutron-dist.conf,neutron.conf, plugin.ini和server.log四个文件。
下面根据文件之间的引用关系,按顺序依次找到并展示如下:
-
/usr/bin/neutron-server
#!/usr/bin/python2 # PBR Generated from u'console_scripts' import sys from neutron.cmd.eventlet.server import main if __name__ == "__main__": sys.exit(main())
-
neutron.cmd.eventlet.server.main
from neutron import server from neutron.server import rpc_eventlet from neutron.server import wsgi_eventlet def main(): server.boot_server(wsgi_eventlet.eventlet_wsgi_server)
-
neutron.server.boot_server
def boot_server(server_func): _init_configuration() try: server_func() except KeyboardInterrupt: pass except RuntimeError as e: sys.exit(_("ERROR: %s") % e)
-
boot_server回调了_init_configuration函数
def _init_configuration(): # the configuration will be read into the cfg.CONF global data structure config.init(sys.argv[1:]) config.setup_logging() config.set_config_defaults() if not cfg.CONF.config_file: sys.exit(_("ERROR: Unable to find configuration file via the default" " search paths (~/.neutron/, ~/, /etc/neutron/, /etc/) and" " the '--config-file' option!"))
至此,我们终于找到neutron-server服务加载配置文件的函数。它又调用了neutron.common.config的init方法。
-
neutron.common.config.init
def init(args, **kwargs): cfg.CONF(args=args, project='neutron', version='%%(prog)s %s' % version.version_info.release_string(), **kwargs) # FIXME(ihrachys): if import is put in global, circular import # failure occurs from neutron.common import rpc as n_rpc n_rpc.init(cfg.CONF) # Validate that the base_mac is of the correct format msg = validators.validate_regex(cfg.CONF.base_mac, validators.MAC_PATTERN) if msg: msg = _("Base MAC: %s") % msg raise Exception(msg)
我们知道args的实际值是:
--config-file /usr/share/neutron/neutron-dist.conf --config-dir /usr/share/neutron/server --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/plugin.ini --config-dir /etc/neutron/conf.d/common --config-dir /etc/neutron/conf.d/neutron-server --log-file /var/log/neutron/server.log
让我们在交互命令窗口模拟neutron-server的cfg.CONF对象初始化过程:
>>> from oslo_config import cfg >>> cfg.CONF.register_opt(cfg.StrOpt('transport_url')) True >>> cfg.CONF(args=['--config-file', '/usr/share/neutron/neutron-dist.conf', '--config-dir', '/usr/share/neutron/server', '--config-file', '/etc/neutron/neutron.conf', '--config-file', '/etc/neutron/plugin.ini', '--config-dir', '/etc/neutron/conf.d/common', '--config-dir', '/etc/neutron/conf.d/neutron-server']) >>> cfg.CONF.items() [('transport_url', 'rabbit://guest:guest@192.168.90.26'), ('config_dir', ['/etc/neutron/conf.d/neutron-server']), ('config_file', ['/usr/share/neutron/neutron-dist.conf', '/etc/neutron/neutron.conf', '/etc/neutron/plugin.ini'])] >>> cfg.CONF.transport_url 'rabbit://guest:guest@192.168.90.26'
从上可知,在加载配置文件之前,需要先注册配置项的键。neutron-server键值注册有部分是在neutron.conf.common中处理的,相关方法如下:
register_core_common_config_opts register_nova_opts register_placement_opts
neutron-server配置加载流程就介绍到这里,如果想继续深入,可直接阅读源码。
oslo.config模块使用介绍
上面我们已经对neutron-server通过oslo.config加载配置文件的流程进行了详细分析,接下来对oslo.config模块的用法进行简单介绍,以帮助大家快速上手oslo.config,并且能够应用到生产实践中。
众所周知,oslo.config是一个OpenStack通用库,用于解析并加载来自于CLI或者配置文件中提供的参数项或者配置项。
-
定义配置项
default_opts = [ cfg.StrOpt('host',default='0.0.0.0',help='default ip'), cfg.IntOpt('port',default='443',help='listen port'), ]
-
注册配置项
from oslo_config import cfg cfg.CONF.register_opts(default_opts)
register_opts用于注册option对象数组,如果仅注册单项可使用register_opt方法。
-
定义配置组并注册
group = cfg.OptGroup(name='rabbit',title='group') cfg.CONF.register_group(group) cfg.CONF.register_opts(default_opts,group)
-
从命令行读取配置
cfg.CONF(default_config_files=['/etc/api.ini'])
在交互命令窗口进行验证:
>>> from oslo_config import cfg >>> default_opts = [ ... cfg.StrOpt('host',default='0.0.0.0',help='default ip'), ... cfg.IntOpt('port',default='443',help='listen port'), ... ] >>> cfg.CONF.register_opts(default_opts) >>> cfg.CONF.items() [('host', '0.0.0.0'), ('port', 443)] >>> cfg.CONF.host '0.0.0.0' >>> cfg.CONF.port 443 >>> group = cfg.OptGroup(name='rabbit',title='group') >>> cfg.CONF.register_group(group) >>> cfg.CONF.register_opts(default_opts,group) >>> cfg.CONF.items() [('host', '0.0.0.0'), ('port', 443), ('rabbit', <oslo_config.cfg.GroupAttr object at 0x7fb42693ca50>)] >>> cfg.CONF.rabbit.host '0.0.0.0' >>> cfg.CONF.rabbit.port 443 >>> cfg.CONF(default_config_files=['/etc/api.ini']) >>> cfg.CONF.items() [('config_dir', []), ('host', '0.0.0.0'), ('config_file', ['/etc/api.ini']), ('port', 443), ('rabbit', <oslo_config.cfg.GroupAttr object at 0x7fb419c8cf10>)] >>> cfg.CONF.rabbit.host '192.168.90.26' >>> cfg.CONF.rabbit.port 9527
其中,/etc/api.ini内容如下:
[rabbit] host=192.168.90.26 port=9527
最后,一个需要注意的地方是各种配置方式的优先级:CLI参数项>配置文件项>自定义的配置项默认值。推荐使用配置文件对项目变量进行配置。另外,cfg.CONF对象采用了单例模式,整个neutron-server项目中的cfg.CONF实例仅有一个。
小结
oslo.config模块被广泛应用于OpenStack各个模块,所以学会使用oslo.config,对快速分析OpenStack项目特别重要。其次,在进行Python项目开发时,使用oslo.config模块解析项目配置也是一个不错的选择。
如果对云计算感兴趣,可以关注我的微信公众号: