Oslo/Config
一个通用的配置选项处理模块
目标是在各个项目之间尽可能地重用通用基础设施。
此蓝图专门涉及处理的代码
- 命令行选项解析
- 通用的命令行选项
- 配置文件解析
- 选项值查找
并首先集中精力统一 Nova 和 Glance。
命令行选项解析
Nova 使用 gflags 库进行选项解析。命令行选项的定义分散在整个项目代码库中,例如
from nova import flags
FLAGS = flags.FLAGS
flags.DEFINE_bool('allow_admin_api',
False,
'When True, this API service will accept admin operations.')
if FLAGS.allow_admin_api:
...
该标志只有在定义它的模块加载后才被识别和解析。如果一个模块需要引用另一个模块中定义的标志,它会这样做:
FLAGS = flags.FLAGS
flags.DECLARE('num_iscsi_scan_tries', 'nova.volume.driver')
if tries >= FLAGS.num_iscsi_scan_tries:
...
Glance 对命令行选项的使用要少得多,更倾向于仅使用其配置文件来设置大多数选项。 也许,命令行选项主要用于在加载配置文件之前可能需要设置的选项?
Glance 使用 optparse 库来定义和解析这些选项,例如:
oparser = optparse.OptionParser(version='%%prog %s'
% version.version_string())
...
group = optparse.OptionGroup(parser, "Common Options", help_text)
group.add_option('-v', '--verbose', default=False, dest="verbose",
action="store_true",
help="Print more verbose output")
...
parser.add_option_group(group)
...
(options, args) = parser.parse_args(cli_args)
return (vars(options), args)
最后一步将选项值对象上的属性转换为字典,因此可以通过例如以下方式访问每个选项:
if options.get('verbose'):
...
通用的命令行选项
Glance 的整个选项集是
- --verbose, --debug:将日志级别设置为 INFO 或 DEBUG,否则设置为 WARNING
- --config-file:一个 .ini 样式的配置文件
- --log-config:python logging 配置
- --log-date-format, --log-file, --log-dir, --use-syslog:其他日志配置,如果提供了日志配置文件则覆盖它
- --use-syslog:记录到 syslog
Nova 有一个更大的选项集。与 Glance 共同的选项(大致)是
- --verbose:将日志级别设置为 DEBUG,否则设置为 INFO
- --flagfile:gflags 格式的配置文件
- --use-syslog:记录到 syslog
- --default_log_levels:各个模块的日志级别
- --logging_context_format_string, --logging_debug_format_suffix, --logging_default_format_string, --logging_exception_prefix:各种格式化选项
很明显,一个通用的日志模块,具有与 Glance 当前选项类似的选项集,应该足以满足 Nova 的需求。对某些选项的支持将会丢失,但类似的功能仍然可以通过使用单独的日志配置文件来实现。
配置文件解析
Nova 使用 gflags 格式的配置文件,每行一个命令行选项。 大多数选项可能仅使用此配置文件设置,而不是直接在命令行上设置。
Glance 的配置文件是 PasteDeploy 配置文件,每个 WSGI 应用一个。 但是,对于 glance-scrubber 和 glance-cache-{cleaner,prefetcher,pruner},这些实际上并不是严格意义上的 WSGI 应用,而是由 PasteDeploy 从工厂加载的任意对象。
在直接使用 Glance 的直接方法之前,有一些事情值得考虑
- 对于非 WSGI 应用及其选项使用 PasteDeploy 似乎不太合适。 此外,PasteDeploy 使用 ConfigParser 而不是 SafeConfigParser 似乎导致了一些 问题。 因此,将 WSGI 应用配置放在单独的文件中(例如 glance-paste.conf)中,从其余的配置选项中分离出来,这些选项将使用 SafeConfigParser 进行解析,可能是一个更好的方法。
- 虽然 Glance 服务之间共享的配置值相对较少,但 Nova 服务之间共享的配置值相当多。 尽管,弄清楚 Nova 的哪些服务实际使用给定的选项并不容易。 这表明支持多个配置文件可能是有用的,例如
--config-file /etc/nova/nova-common.conf --config-file /etc/nova/nova-api.conf - 最好在 /etc 目录中的配置文件中尽可能地保留默认值。 例如,使用 RPM 时,如果用户安装 Glance,设置
verbose = True,然后更新到 Glance 的新版本,旧配置文件将保留在原处,并且新版本将以 .rpmnew 后缀安装。 如果我们需要配置文件中存在任何给定值的合理默认值,那么在这种情况下可能会出现问题。 最佳实践是在代码中保留默认值,但也在配置文件中作为注释包含。
选项值查找
Glance 当前的方法涉及在各个地方传递 options 字典
class ImageCache(object):
def __init__(self, options):
self.options = options
...
def prune(self):
max_size = int(self.options.get('image_cache_max_size',
DEFAULT_MAX_CACHE_SIZE))
...
如果需要,默认选项是在选项查找时使用 dict.get() 指定的。
Nova 使用全局标志值对象
from nova import flags
FLAGS = flags.FLAGS
flags.DECLARE('num_iscsi_scan_tries', 'nova.volume.driver')
if tries >= FLAGS.num_iscsi_scan_tries:
...
默认值在定义选项时指定。
全局变量不是理想的,应该避免,所以我们应该采用 Glance 的方法,即传递选项。 但是,Nova 可能会保留一个全局值集,直到代码库可以完全适应为止。
但是,Nova 将选项及其默认值一起以结构化方式定义的方法似乎值得。
需求
需求
- 一种为每个配置选项定义模式的方法 - 它的名称、类型、可选组、默认值和描述
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')
]
- 配置选项类型 - 字符串、整数、浮点数、布尔值、列表、多字符串
enabled_apis_opt = \
cfg.ListOpt('enabled_apis',
default=['ec2', 'osapi'],
help='List of APIs to enable by default')
DEFAULT_EXTENSIONS = [
'nova.api.openstack.contrib.standard_extensions'
]
osapi_extension_opt = \
cfg.MultiStrOpt('osapi_extension',
default=DEFAULT_EXTENSIONS)
- 配置选项模式在运行时注册到配置管理器,但在引用选项之前
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_extension:
....
- 每个配置选项模式都应定义在使用的模块或类中
opts = ...
def add_common_opts(conf):
conf.register_opts(opts)
def get_bind_host(conf):
return conf.bind_host
def get_bind_port(conf):
return conf.bind_port
- 配置选项可以选择作为命令行选项提供;这些必须在解析命令行之前注册到配置管理器(用于验证和 --help)
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)
- 配置管理器默认定义一个 CLI 选项,--config-file
class ConfigOpts(object):
config_file_opt = \
MultiStrOpt('config-file',
...
def __init__(self, ...):
...
self.register_cli_opt(self.config_file_opt)
- 选项值从任何提供的配置文件中解析,使用 SafeConfigParser。 如果没有指定,则使用默认集,例如 ['glance-api.conf', 'glance.conf']
glance-api.conf: [DEFAULT] bind_port = 9292
glance.conf: [DEFAULT] bind_host = 0.0.0.0
- 通过调用配置管理器来启动 CLI 参数和配置文件的解析,例如:
conf = ConfigOpts()
conf(sys.argv[1:])
if conf.verbose:
...
- 选项可以注册到组中
rabbit_group = cfg.OptionGroup(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')
rabbit_ssl_opt = \
cfg.BoolOpt('use_ssl',
default=False,
help='Whether to support SSL connections')
def register_rabbit_opts(conf):
conf.register_group(rabbit_group)
# options can be registered under a group in any of these ways:
conf.register_opt(rabbit_host_opt)
conf.register_opt(rabbit_port_opt, group='rabbit')
conf.register_opt(rabbit_ssl_opt, group=rabbit_group)
- 如果未指定组,则选项属于配置文件的 'DEFAULT' 部分
glance-api.conf: [DEFAULT] bind_port = 9292 ... [rabbit] host = localhost port = 5672 use_ssl = False userid = guest password = guest virtual_host = /
- 组中的命令行选项会自动以组名称为前缀,例如:
--rabbit-host localhost --rabbit-use-ssl False
- Option values in the default group are referenced as attributes/properties on the config manager object; groups are also attributes on the config manager, with attributes for each of the options associated with the group
server.start(app, conf.bind_port, conf.bind_host, conf)
self.connection = kombu.connection.BrokerConnection(
hostname=conf.rabbit.host,
port=conf.rabbit.port,
...)
- 字符串选项值可以使用模板
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'),
]
- CommonConfigOpts 配置管理器类允许自动注册一组通用的配置选项
common_opts = [
cfg.BoolOpt('verbose', ...),
cfg.BoolOpt('debug', ...),
]
logging_opts = [
cfg.StrOpt('log-config', ...),
cfg.StrOpt('log-date-format', ...),
...
]
def CommonConfigOpts(object):
def __init__(self):
...
self.register_cli_opts(common_opts)
self.register_cli_opts(logging_opts)
- PasteDeploy 配置存储在单独的文件中