OpenStack的项目貌似愈来愈多了,在Grizzly版以前,每一个项目都得实现一套处理配置文件的代码。在每一个项目的源码中基本上均可以找到openstack/common/cfg.py,iniparser.py
文件,固然,这些不一样项目之间的cfg.py等文件很大多是copy-and-paste分分钟来搞定。这种状况确定没法被大神忍受,最终,社区决定改变这一切,提出了Oslo项目。Oslo项目的宗旨是提供一系列OpenStack Projects共享的基础库,能够从wiki的原话中了解到。python
To produce a set of Python libraries containing code shared by OpenStack projects. The APIs provided by these libraries should be high quality, stable, consistent, documented and generally applicable.git
社区显然已经没法忍受在不一样项目中大量重复的代码了。Oslo 项目提供了一系列的库,咱们接触的最多的为oslo.config这个库,要来处理程序命令行参数和配置文件。固然还有其余的,例如,pbr(Python Build Reasonableness)与setuptools相关的库,hacking,用来处理编码风格的库,还有oslo.messageing等等。这些库有的是在oslo这个namespace下,有的是彻底独立的。Oslo开发者是这样考虑的,若是这个库存在被普遍使用的潜质的话,则不将其放在oslo命名空间下。github
Oslo项目包含的库较多,咱们将目光汇集到接触最多的oslo.config这个库上,这个库应该是全部OpenStack项目中重用最多的。包括测试框架Tempest等sql
在了解oslo.confg的使用和实现以前,咱们须要知道,这个库是用来解决什么样的问题。在有了此问题答案的基础上,而后沿着怎样来使用这个库和这个库究竟是如何实现的路线来分析。api
前面咱们介绍了,OpenStack在G版以前的几乎每一个项目都得拷贝一份cfg.py,iniparser.py两个文件放到openstack/common/
目录下,这两个文件主要致力于解决读取配置文件和解析命令行参数的问题。在实际使用OpenStack的过程当中,咱们启动一个服务,例如nova-api或者glance-api,每每都是这样的形式:app
/usr/bin/nova-api –config-file=/etc/nova/nova.conf –log-file=/var/log/nova/api.log
从这个启动命令来看,咱们须要可以正确的处理命令行参数,还有配置文件。细心观察会发现,不一样的服务,nova-api,glance-api等等,都会有一些共同的命令行参数,如上面的–config-file,–log-file等等,而后每一个服务还有本身专属的命令行参数。对于配置文件,可能存在多个,例如,nova项目存在多个服务,nova-api,nova-compute等等。那么这些nova services之间会存在大量共同的配置,对此,Oslo建议若是支持多个配置文件的话,那么就很给力了,像这个形式:--config-file=/etc/nova/nova-commmon.conf --config-file=/etc/nova/nova-api.conf
。对配置文件格式的支持,目前主要是ini风格的文件。除了解析配置选项以外,另外一个问题是,快速访问到这些配置选项的值。框架
所以,olso.config wiki上贴出了oslo.config须要解决的几点问题:ide
1.command line option parsing 2.common command line options 3.configuration file parsing 4.option value lookup
在wiki中还提到一种场景,建议最好将一些options的默认值写在code里面,同时也在config file中做为注释代表。这应该就是在config中看到的不少被注释掉的配置,在代码中一样能够看到这些默认值。函数
oslo.config库只有两个文件,cfg.py和iniparser.py,oslo.config的使用方法在cfg.py文件中已经给出不是通常详细的注释。测试
options即所谓的配置选项,能够经过命令行和配置文件设置,它通常的形式以下:
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') ]
上面的通常形式,指定了options的名字,默认值和帮助信息,还能够看出,不一样的配置选项属于不一样的类型。StrOpt,IntOpt。除此以外,Options还支持floats,booleans,list,dict,multi strings。
这些options在被引用以前,必须先在运行期经过config manager注册该options,即便用前得先注册。例以下的状况:
class ExtensionManager(object): enabled_apis_opt = cfg.ListOpt(...) def __init__(self, conf): self.conf = conf self.conf.register_opt(enabled_apis_opt) ... def _load_extensions(self): for ext_factory in self.conf.osapi_compute_extension: ....
咱们若要使用osapi_compute_extension选项,则须要先经过self.conf.register_opt(enabled_apis_opt)完成option的注册。
前面咱们提到options能够在启动服务的命令行中启动,这些选项在被程序解析以前,必须先经过config manager注册。这样的好处,咱们能够实现经常使用的help参数,而且确认命令行参数的正确性。命令行的注册略微不一样前面提到的注册方式,调用的是特定的函数,conf.register_cli_opts(cli_opts)。
cli_opts = [ cfg.BoolOpt('verbose', short='v', default=False, help='Print more verbose output'), cfg.BoolOpt('debug', short='d', default=False, help='Print debugging output'), ] def add_common_opts(conf): conf.register_cli_opts(cli_opts)
前面咱们提到oslo.config支持的是ini风格的配置文件,该文件将全部的配置选项进行了分组,即所谓的section或者group,这两个单词是同一个概念,没有指定section的,则会分到default组。下面给出了一个ini风格的配置文件例子:
glance-api.conf: [DEFAULT] bind_port = 9292 glance-common.conf: [DEFAULT] bind_host = 0.0.0.0
在config manager中,会默认的指定两个值,即--config-file --config-dir
,config manager会在没有显示指定这两个参数的状况下去默认的文件夹中查找默认的文件。例如~/.${project}, ~/, /etc/${project},/etc/
这几个目录下查找配置文件,若是程序是nova,则会查找默认路径下的nova.conf文件。
在代码中的注释指出,Option values in config files override those on the command line.
即config files中的选项值会覆盖命令行中的选项值。这貌似与潜意识中的相反呀,英文是原话。补充:2013-11-28,通过本身的测试和对源码的阅读,应该是Option values specified on command lines override those in config files,具体参考下一篇的分析。
多个配置文件会按顺序来解析,后面文件中的选项会覆盖前面出现过的选项。
在配置文件中,咱们已经看到不少配置选项已经被咱们主动的进行了一个分组的划分,没有归属的选项则扔到了default组。一样,在代码中options能够显示的注册某个组中。注册的方式有两种,直接指定group,或者指定group的name,参考下面代码:
rabbit_group = cfg.OptGroup(name='rabbit', title='RabbitMQ options')) rabbit_host_opt = cfg.StrOpt('host', default='localhost', help='IP/hostname to listen on') rabbit_port_opt = cfg.IntOpt('port', default=5672, help='Port number to listen on') def register_rabbit_opts(conf): conf.register_group(rabbit_group) # options can be registered under a group in either of these ways: conf.register_opt(rabbit_host_opt, group=rabbit_group) conf.register_opt(rabbit_port_opt, group='rabbit')
咱们须要先定义一个group,指定group的name和title属性,也得将group注册,最后可经过两种方式将options注册到该组中。
若一个group仅只有name属性,那么咱们能够不用显示的注册group,例以下面的代码:
def register_rabbit_opts(conf): # The group will automatically be created, equivalent calling:: # conf.register_group(OptGroup(name='rabbit')) conf.register_opt(rabbit_port_opt, group='rabbit')
若要引用某个option的值,则直接经过访问config manager属性的方式便可。例如,访问default组或者其余的组,能够经过以下的方式:
conf.bind_port conf.rabbit.port
同时,option值还能够经过PEP 292 string substitution(pep 292描述了字符串替换的方式)再引用其余的option的值,具体看下面的例子,在sql_connection值中,咱们引用了其余的option的值。
opts = [ cfg.StrOpt('state_path', default=os.path.join(os.path.dirname(__file__), '../'), help='Top-level directory for maintaining nova state'), cfg.StrOpt('sqlite_db', default='nova.sqlite', help='file name for sqlite'), cfg.StrOpt('sql_connection', default='sqlite:///$state_path/$sqlite_db', help='connection string for sql database'), ]
还有在某些状况下,咱们须要在日志文件中隐藏关键option的值,能够在建立该option时,添加secret参数,设置为True。
咱们已经屡次提到config manager了,要使用options,得先将options注册到config manager中,访问option的值,直接访问config manager的属性,config manager对options进行了统一的管理,其实config manager是一个全局的对象,重载了__call__方法,还有__getattr__方法。最为关键的,全局就只有这么一个实例,在cfg.py的注释尾,再给出了一个完整的例子,首先获取全局的这个实例,而后注册options,最后使用options。
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) def start(server, app): server.start(app, CONF.bind_port, CONF.bind_host)
这一部分只是按照源码中的注释来介绍了下oslo.config的使用,下一篇,将分析cfg.py的代码结构,由于也只有这一个关键的文件,代码在两千行左右,任务不是很重,因此争取将其看仔细,写明白。
在阅读oslo wiki的时候,发现了一个颇有趣的问题,Why does oslo.config have a CONF object? Global object SUCK!
,看来社区对这个Global Object有很大的争论,致使做者还特地在wiki上作个专门的介绍!咱们在使用cfg时,通常都是经过 CONF = cfg.CONF方式来获取这个全局的实例。做者提到在Folsom Design Summit上,有人想remove our dependence on a global object like this
,显然,不少争论,结果的结果是,你们达成了一个初步共识,仍是坚持使用这种global object的方式。做者还提到了一句话:The idea is that having all projects use the same apporach is more important than the objections to the approach.
不明觉历的模样!这仍是强调了OpenStack的projects使用一样的方法更好!具体的回答你们能够点击后面的wiki连接,本身细读。