前段时间, 阅读了openstack E版本源码中关于配置文件读取下面对配置文件的读取做一个总结(注现在openstack关于配置文件不再是common中的cfg.py 文件, 而是独立实现了一个包oslo-config):
首先对openstack配置项做一个介绍,
我们以compute.manager 为例:
- compute_opts = [
- cfg.StrOpt('instances_path',
- default='$state_path/instances',
- help='where instances are stored on disk'),
- ……………………
- 上面申明了配置项
- FLAGS = flags.FLAGS
- FLAGS.register_opts(compute_opts) #会把值注册到cfg.ConfigOpts 中的opts属性中
现在在compute.manager.py中就可以通过FLAGS.instances_path 读取到配置项instance_path的值
通过FLAGS.instances_path 读取的逻辑如下
1: FLAGS = cfg.CONF
2: CONF = CommonConfigOpts()
这样可知这次调用会调用CommonCOnfigOpts.__getattr__()
这里会读取到config.opts。
上面是读取代码default 配置的值, 那么是怎样读取配置文件的呢?
首先我们应该知道是在哪里注入配置文件的:
下面以nova.conf 为例:
在启动nova-compute(不仅仅是compute服务所有的nova服务是一样的), 会调用bin/ 下面的文件 nova-compute 文件, 下面是执行的主要逻辑
- if __name__ == '__main__':
- flags.parse_args(sys.argv)
- logging.setup('nova')
- utils.monkey_patch()
- server = service.Service.create(binary='nova-compute')
- service.serve(server)
- service.wait()
- def parse_args(argv, default_config_files=None):
- FLAGS.disable_interspersed_args()
- return argv[:1] + FLAGS(argv[1:],
- project='nova',
- default_config_files=default_config_files)
flags.parse_args(sys.argv)中会把配置文件转化到内存 , 这里我们可以通过--config-file 指定配置文件的路径, 但是在实际转化中
他会到当前用户的工作目录, 以及/etc/nova 或者/etc/ 中需找nova.conf, nova-compute.conf 文件如果找到这个文件那么他会把值转化到
ConfigParser.sections 中
- def assignment(self, key, value):
- if not self.section:
- raise self.error_no_section()
- self.sections[self.section].setdefault(key, [])
- self.sections[self.section][key].append('\n'.join(value))
在上面读取配置文件中先读取ConfigOpts 中的opts 如果没有这个key那么会报错, 如果存在那么会然后检索ConfigParser.sections 如果存在那么会覆盖掉opts中
的值。这样就读取到配置文件的value了。
下面是我的一个测试代码,这里调用了oslo-config(就是对e,f 版本中cfg文件的封装)包,
- from oslo.config import cfg
- import sys
- test_opts = [
- cfg.StrOpt('topic',
- default='this is a test value',
- help='this is a test config'),
- cfg.StrOpt('say',
- default='world',
- help=' jkjk ')
- ]
- def parse_argv(argv, default_config_files=None):
- cfg.CONF(argv[1:], project='test',
- default_config_files=default_config_files)
- def test(argv):
- CONF=cfg.CONF
- CONF.register_opts(test_opts)
- parse_argv(argv)
- print CONF.topic+“ ”+CONF.say
- if __name__ == "__main__":
- test(sys.argv)
那么你可以创建一个配置文件test.conf 内容如下
- [default]
- topic="test-topic"
运行结果如下:
test-topic world
在Oslo的cfg模块载入的时候(from Oslo.config import cfg),会自动运行模块中的载入代码CONF = ConfigOpts(),创建一个全局的配置项管理类。
和许多Conf配置模块一样,Oslo.conf在使用时,需要先声明配置项的名称、定义类型、帮助文字、缺省值等,然后再按照事先声明的配置项,对CLI或conf中的内容进行解析。
配置项声明结构示例如下:
- common_opts = [
- cfg.StrOpt('bind_host',
- default='0.0.0.0',
- help='IP address to listen on'),
- cfg.IntOpt('bind_port',
- default=9292,
- help='Port number to listen on')
- ]
Oslo使用register_opt方法,将配置项定义向配置项管理类configOpts的注册是在程序的运行时刻,但是必须在配置项的引用前完成。
- CONF = cfg.CONF
- CONF.register_opts(common_opts)
- port = CONF.bind_port
使用conf.register_cli_opts()方法,配置项还可以在管理类ConfigOpts中可选注册为CLI配置项,通过程序运行的CLI参数中获得配置项取值,并在错误打印时,自动输出给CLI配置项参数的帮助文档。
conf配置文件采用的是ini风格的格式
- glance-api.conf:
- [DEFAULT]
- bind_port = 9292
- ...
- [rabbit]
- host = localhost
- port = 5672
- use_ssl = False
- userid = guest
- password = guest
- virtual_host = /
- def __call__(self,
- args=None,
- project=None,
- prog=None,
- version=None,
- usage=None,
- default_config_files=None):
- """Parse command line arguments and config files.
- from oslo.config import cfg
- opts = [
- cfg.StrOpt('bind_host', default='0.0.0.0'),
- cfg.IntOpt('bind_port', default=9292),
- ]
- CONF = cfg.CONF
- CONF.register_opts(opts)
- CONF(default_config_files='glance.conf')
- def start(server, app):
- server.start(app, CONF.bind_port, CONF.bind_host)
OpenStack项目的配置项声明和许多其他开源Python项目一样,配置项声明是放在各个调用的模块里面的。也就是说哪里用到才到哪里声明。我觉得这种方式是完全体现了Pthonic的一种声明方式,有别于其他方式,程序员在阅读程序的时候可以非常方便的在文件开头就能找到配置项的声明定义,而不用到某个指定的文件去查找,实现了KISS的原则。