Oslo系列之oslo.config

生命太过短暂,今日放弃了明天不必须能得到。

随着OpenStack项目的不断发展与完善,OpenStack社区将所有组件中的具有共性的组件剥离出来,并统一放在oslo组件下。oslo中的组件不仅可以在OpenStack项目中使用,也可以单独作为第三方工具包供其他项目使用。oslo.config项目是oslo组件中用于OpenStack配置文件的一个项目。本文首先以Nova项目为例,介绍了oslo.config的用法;然后,根据源码详细分析了其实现原理。

oslo.config用法

定义配置项

本小节以nova组件为例介绍oslo.config组件的用法。首先,要使用oslo.config需要导入该模块,一般地,直接导入oslo.config中的cfg即可。

1
from oslo_config import cfg

导入cfg后,需要新建一个表示配置组的OptGroup类,如在Nova中添加api相关配置时可以添加一个api_group类。

1
2
3
4
5
api_group = cfg.OptGroup('api',
title='API options',
help="""
Options under this group are used to define Nova API.
""")

api_group表示了一组关于api的配置项,其中,api表示该配置组的名称;title描述了该配置组的标题;help则详细描述了该配置组的作用。

创建了配置组类之后,则需要创建所有的配置项,如在Nova中添加关于api权限相关的配置参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
auth_opts = [
cfg.StrOpt("auth_strategy",
default="keystone",
choices=("keystone", "noauth2"),
deprecated_group="DEFAULT",
help="""
This determines the strategy to use for authentication: keystone or noauth2.
'noauth2' is designed for testing only, as it does no actual credential
checking. 'noauth2' provides administrative credentials only if 'admin' is
specified as the username.
"""),
cfg.BoolOpt("use_forwarded_for",
default=False,
deprecated_group="DEFAULT",
help="""
When True, the 'X-Forwarded-For' header is treated as the canonical remote
address. When False (the default), the 'remote_address' header is used.
You should only enable this if you have an HTML sanitizing proxy.
"""),
]

auth_opts配置了一组与api权限等相关的配置项,其中有String和Bool类型的配置项,通过分别创建StrOpt对象和BoolOpt对象设置参数的类型。

创建配置项之后,则需要将所有配置项都注册到配置组中,如在Nova中将auth_opts等配置项注册到api_group配置组中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
API_OPTS = (auth_opts +
metadata_opts +
file_opts +
osapi_opts +
allow_instance_snapshots_opts +
osapi_hide_opts +
fping_path_opts +
os_network_opts +
enable_inst_pw_opts)


def register_opts(conf):
conf.register_group(api_group)
conf.register_opts(API_OPTS, group=api_group)
conf.register_opts(deprecated_opts)

其中,调用register_opts(API_OPTS, group=api_group)表示将与api相关的配置项注册到api_group配置组中;register_group(api_group)表示将api_group配置组与具体的配置文件相关联;而register_opts(deprecated_opts)则将一些不再支持的配置项单独配置。

读取配置文件并使用配置参数

一般,OpenStack组件在启动服务时便加载了配置文件,读取了各配置项;因此如果想要修改的配置信息被应用到服务中需要重启服务。如Nova启动nova-api服务时,会调用nova/cmd/api.py中的main()函数,在main()函数中,首先便会进行读取文件的操作。

1
config.parse_args(sys.argv)

而parse_args()函数通过sys.argv中指定的配置项和配置文件中的配置项,读取api相关的配置参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def parse_args(argv, default_config_files=None, configure_db=True,
init_rpc=True):
log.register_options(CONF)
# We use the oslo.log default log levels which includes suds=INFO
# and add only the extra levels that Nova needs
if CONF.glance.debug:
extra_default_log_levels = ['glanceclient=DEBUG']
else:
extra_default_log_levels = ['glanceclient=WARN']
log.set_defaults(default_log_levels=log.get_default_log_levels() +
extra_default_log_levels)
rpc.set_defaults(control_exchange='nova')
if profiler:
profiler.set_defaults(CONF)
config.set_middleware_defaults()

CONF(argv[1:],
project='nova',
version=version.version_string(),
default_config_files=default_config_files)

if init_rpc:
rpc.init(CONF)

if configure_db:
sqlalchemy_api.configure(CONF)

nova通过导入nova.conf模块,此时会调用nova.conf的init.py文件中的各个register_opt()函数注册所有配置项。然后,调用了Config中的CongifOpts类的call()方法通过命令行参数和配置文件将配置项缓存到配置对象CONF中。之后,只需要在需要用到配置参数的文件中导入CONF对象即可使用其中的配置项。如在Nova中可以使用CONF.api.enable_instance_password读取配置文件中是否允许配置实例密码的配置项。

oslo.config实现原理

本节结合oslo.config的用法详细介绍oslo.config的实现原理。oslo.config项目相对比较简单,其中主要设计到oslo_config目录下的cfg.py和types.py等文件。其中,cfg.py定义了配置类的数据结构和方法等;types.py则封装了OpenStack中各配置项的类型。

要使用oslo.config最重要的便是ConfigOpts类,所以本文首先介绍ConfigOpts类。ConfigOpts类是一个用来提供注册配置组和配置项的配置项管理类,其中包含几个重要的属性,_opts表示所有的配置项,_cli_opts表示所有的命令行配置项,_groups表示所有的配置组,_namespace表示各服务的命名空间;OpenStack服务启动后所有的配置参数都会通过这四个参数保存到内存中。

上面提到启动服务时,会调用ConfigOpts类的call()方法将服务的所有配置项缓存到内存中,其定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
def __call__(self,
args=None,
project=None,
prog=None,
version=None,
usage=None,
default_config_files=None,
default_config_dirs=None,
validate_default_values=False,
description=None,
epilog=None):
"""Parse command line arguments and config files.
Calling a ConfigOpts object causes the supplied command line arguments
and config files to be parsed, causing opt values to be made available
as attributes of the object.
The object may be called multiple times, each time causing the previous
set of values to be overwritten.
Automatically registers the --config-file option with either a supplied
list of default config files, or a list from find_config_files().
If the --config-dir option is set, any *.conf files from this
directory are pulled in, after all the file(s) specified by the
--config-file option.
:param args: command line arguments (defaults to sys.argv[1:])
:param project: the toplevel project name, used to locate config files
:param prog: the name of the program (defaults to sys.argv[0]
basename, without extension .py)
:param version: the program version (for --version)
:param usage: a usage string (%prog will be expanded)
:param description: A description of what the program does
:param epilog: Text following the argument descriptions
:param default_config_files: config files to use by default
:param default_config_dirs: config dirs to use by default
:param validate_default_values: whether to validate the default values
:raises: SystemExit, ConfigFilesNotFoundError, ConfigFileParseError,
ConfigFilesPermissionDeniedError,
RequiredOptError, DuplicateOptError
"""
self.clear()

self._validate_default_values = validate_default_values

prog, default_config_files, default_config_dirs = self._pre_setup(
project, prog, version, usage, description, epilog,
default_config_files, default_config_dirs)

self._setup(project, prog, version, usage, default_config_files,
default_config_dirs)

self._namespace = self._parse_cli_opts(args if args is not None
else sys.argv[1:])
if self._namespace._files_not_found:
raise ConfigFilesNotFoundError(self._namespace._files_not_found)
if self._namespace._files_permission_denied:
raise ConfigFilesPermissionDeniedError(
self._namespace._files_permission_denied)

self._check_required_opts()

在该方法中,首先将所有配置项清空,然后根据项目名称和版本等信息获取配置文件路径,然后根据命令行参数和配置文件读取服务所有配置信息,并进行合法性校验。

除此之外,ConfigOpts类还定义了register_opt()方法,register_cli_opt()方法和register_group()方法等分别实现注册配置文件或命令行参数的配置项和注册配置组的操作。另外,也提供了unregister_opt()等方法卸载配置项,添加了clear()方法清空ConfigOpts对象中的配置信息。

在ConfigOpts类定义的属性和方法中,用到了两个重要的类:Opt类和OptGroup类。其中,Opt类定义了一个配置项的模板,主要属性包括配置项名称name,配置项类型type,关联的ConfigOpts对象dest,默认值default和帮助信息help等。为了更好的封装配置项,oslo.config针对具体的配置参数类型为Opt继承了一系列子类,包括StrOpt、BoolOpt、IntOpt、FloatOpt、ListOpt、DictOpt、IPOpt、PortOpt、HostnameOpt、HostAddressOpt、URIOpt、MultiOpt、MultiStrOpt、SubCommandOpt、_ConfigFileOpt、_ConfigDirOpt等。OptGroup类则定义了一个配置组的模板,其主要属性包括配置组名称name,配置组描述title,配置组帮助信息help等。ConfigOpts类利用Opt和OptGroup辅助完成配置项的注册和读取等。

在读取配置文件时,oslo.config还定义了ConfigParser类将配置文件中的所有配置项封装为Json数据包便于处理。

最后,oslo.config将所有配置项的数据类型使用ConfigType类进行了封装,定义了各数据类型的类型名称、格式、最大值、最小值、最大长度等;这些数据类型主要包括String、MultiString、Boolean、Number、Integer、Float、Port、List、Range、Dict、IPAddress、Hostname、HostAddress、URI等。在使用时,可以调用call()方法校验读取的参数是否符合类型要求,也可以调用_formatter()方法对配置参数进行格式化操作。

原文链接:https://blog.csdn.net/Bill_Xiang_/article/details/78392616

-------------本文结束 感谢您的阅读-------------
作者Magiceses
有问题请 留言 或者私信我的 微博
满分是10分的话,这篇文章你给几分