oslo.config
OpenStack的oslo项目旨在独立出系统中可重用的基础功能,oslo.config就是其中一个被广泛使用的库,该项工作的主要目的就是解析OpenStack中命令行(CLI)或配置文件(.conf)中的配置信息。
argparse 标准库
在阅读 oslo.config 源码之前,首先要对 argparse 标准库有一定的了解,我们需要对 argparse 定义的概念对象以及 argparse 的使用流程有所了解,因为 oslo.config 仍然沿用了这些概念对象。
argparse模块是 Python 标准库中推荐的命令行解析模块。
脚本描述信息
import argparse
# 通过description属性添加脚本描述信息
parser = argparse.ArgumentParser(description="this is script description")
args = parser.parse_args()
如下:通过–help可以查看脚本的功能描述信息。
–help
import argparse
parser = argparse.ArgumentParser()
parser.parse_args()
如果下图,–help可以简写为-h,是唯一内置的可以直接使用的选项(即不需要指定该选项的内容)。
自定义位置参数
import argparse
parser = argparse.ArgumentParser()
# add_argument() 方法用于指定程序能够接受哪些命令行选项,
# 如果调用脚本的时候不传入则会直接报错!!!
parser.add_argument("test")
parser.add_argument("test1")
# parser.parse_args()方法会返回一个NameSpace对象,
# test和test1是这个对象的属性,可以通过.test获取值
args = parser.parse_args()
print(f"args:{args}, args.test:{args.test}, args.test1:{args.test1}")
自定义帮助信息,指定输入参数类型
import argparse
parser = argparse.ArgumentParser()
# argparse会把传递给它的选项默认视作为字符串,除非指定type字段
parser.add_argument("square", type=int, help="display a square of a given number")
args = parser.parse_args()
print(f"args: {args}, args.square**2: {args.square**2}")
可选参数
import argparse
parser = argparse.ArgumentParser()
# 定义可选参数,可以参数要以--开头
parser.add_argument("--verbosity", help="increase output verbosity")
args = parser.parse_args()
print(f"args: {args}, args.verbosity: {args.verbosity}")
如下:
- 没有传入参数执行脚本时也没有报错,说明是可选参数,没传入的情况下args.verbosity是None。
- 如果要传入参数需要使用 “–参数名 参数值 ”的方式。
action和短选项
import argparse
parser = argparse.ArgumentParser()
# action参数指定了这个命令行参数应当如何处理,
# 如果指定了 --verbose那么 args.verbose=True,反之 args.verbose=False
parser.add_argument("-v", "--verbose", action="store_true", help="increase output verbosity")
args = parser.parse_args()
print(f"args: {args}, args.verbosity: {args.verbose}")
如下图:
- action参数可以有几种选项,例如:‘store_true’ 、‘store_false’、 ‘store_const’ 、‘append’ 等。
- 例如:store_true就是指定 --verbose时,args.verbose就是True,没有传入时就是False,并且不能指定其他值。
- 其他action参考官方文档: link。
互斥选项
import argparse
parser = argparse.ArgumentParser()
# 通过parser.add_mutually_exclusive_group()获取group
group = parser.add_mutually_exclusive_group()
# 通过group添加的选项都是互斥的
group.add_argument("-v", "--verbose", action="store_true")
group.add_argument("-q", "--quiet", action="store_true")
args = parser.parse_args()
print(f"args:{args}, args.verbose:{args.verbose}, args.quiet:{args.quiet}")
如下图:-v 和-q不能同时指定。
基本用法
示例引自官方文档:
test.py:
# Load up the cfg module, which contains all the classes and methods
# you'll need.
from oslo_config import cfg
# Define an option group
myGroup = cfg.OptGroup('myGroup')
authGroup = cfg.OptGroup('authGroup')
# Define a couple of options
my_opts = [cfg.StrOpt('option1'),
cfg.IntOpt('option2', default=42)]
auth_opts = [cfg.StrOpt('ssl'),
cfg.StrOpt('auth_url')]
# Register your config group
cfg.CONF.register_group(myGroup)
cfg.CONF.register_group(authGroup)
# Register your options within the config group
cfg.CONF.register_opts(my_opts, group=myGroup)
cfg.CONF.register_opts(auth_opts, group=authGroup)
# Process command line arguments. The arguments tell CONF where to
# find your config file, which it loads and parses to populate itself.
cfg.CONF(default_config_files=["test.conf"])
# Now you can access the values from the config file as
# CONF.<group>.<opt>
print("The value of option1 is %s" % cfg.CONF.myGroup.option1)
print("The value of option2 is %d" % cfg.CONF.myGroup.option2)
print("The value of ssl is %s" % cfg.CONF.authGroup.ssl)
print("The value of auth_url is %s" % cfg.CONF.authGroup.auth_url)
test.conf:
[myGroup]
option1 = foo
# Comment out option2 to test the default value
# option2 = 123
[authGroup]
ssl = "ssl_test"
auth_url = "https://identity.az0.dc0.com:443/"
核心模块 cfg.py
cfg.py最后定义了CONF = ConfigOpts(),from oslo_config import cfg 时候就导入了CONF全局变量。
一般解析配置文件都是在服务的启动脚本中做的,即启动的同时解析了配置文件,然后再其他地方的代码中from oslo_config import cfg,导入全局变量cfg.CONF拿到的是已经解析过配置文件的全局对象,可以从中获取配置文件数据。
从这里可以看出来,最终使用的都是ConfigOpts类的对象。
逐步分析上面的简单示例中的代码:
# 实例化一个group对象,这里的group类似于解析ini配置文件中的section
myGroup = cfg.OptGroup('myGroup')
# 实例化两个options对象,两个对象类型不同,并且option2设置了默认值
my_opts = [cfg.StrOpt('option1'), cfg.IntOpt('option2', default=42)]
# 这是属于ConfigOpts类的方法,其实就是全局变量cfg.CONF有一个_groups字典属性,这里就是将{mygroup.name: mygroup}更新到_groups属性中。
cfg.CONF.register_group(myGroup)
# 这一步实际上就是将option和group绑定起来,一个group对应多个option
cfg.CONF.register_opts(my_opts, group=myGroup)
如下图:group有_opts字典属性,调用_register_opt方法会将传入的option对象 {opt.dest: {‘opt’: opt, ‘cli’:cli}}更新在_opts属性上
最终的数据结构就是: cfg.CONF._groups—>group._opts—>opt。
# 根据定义的conf文件刷新option数据
cfg.CONF(default_config_files=["test.conf"])