[openstack-dev] [oslo] Option registration convention?
Mark McLoughlin
markmc at redhat.com
Thu Jun 13 06:09:53 UTC 2013
On Thu, 2013-06-13 at 13:35 +0800, Zhongyue Luo wrote:
> Hi, all
>
>
> I'm facing problems with sample config file generation on some
> projects.
>
>
> The doc web team is working on finding a way to get all options for
> all projects (or as needed) to create more up-to-date and accurate doc
> config tables. They are planning to make the process as generic as
> possible to work with the source for any project so as to generate all
> config options, so that we can then have the options list converted to
> docbook format and update the docs site.
>
>
> Currently, listing of all register options can be done in Nova and
> Cinder using the tools below
> https://github.com/openstack/nova/blob/master/tools/conf/generate_sample.sh
>
> https://github.com/openstack/cinder/blob/master/tools/conf/generate_sample.sh
>
>
>
> So our plan is to move the "generate_sample.sh" to oslo with an
> additional docbook format ouput module which will enable:
> ./generate_sample.sh /opt/stack/nova
> ./generate_sample.sh /opt/stack/quantum
>
> ./generate_sample.sh /opt/stack/keystone
>
>
>
> The module which traverses the target file handed by
> "generate_sample.sh" is
> https://github.com/openstack/oslo-incubator/blob/master/openstack/common/config/generator.py
>
>
>
> The "generate_sample.sh" looks for files containing "Opt(" which means
> that file has a high chance of defining and registering an option
> inside. For instance,
>
>
> resource_tracker_opts = [
>
>
> cfg.IntOpt('reserved_host_disk_mb', default=0,
>
>
> help='Amount of disk in MB to reserve for the host'),
>
>
> cfg.IntOpt('reserved_host_memory_mb', default=512,
>
>
> help='Amount of memory in MB to reserve for the host'),
>
>
> cfg.StrOpt('compute_stats_class',
>
>
> default='nova.compute.stats.Stats',
>
>
> help='Class that will manage stats for the local
> compute host')
>
>
> ]
>
>
> CONF = cfg.CONF
>
>
> CONF.register_opts(resource_tracker_opts)
>
>
>
>
> So if a file is passed to "generator.py", a module object is created
> by importing the target file and all attributes in that module
> object which are oslo.config.cfg.Opt sub-classes or a list of
> oslo.config.cfg.Opt sub-classes are printed out in the sample config
> file.
>
>
> Therefore the assumption was that an option will be defined and
> registered in the same module.
>
>
> However, some projects have options defined and registered on
> different modules. For instance,
> https://github.com/openstack/keystone/blob/master/keystone/common/config.py
>
>
> Here, the options are defined but instead of directly registering
> them, it provides a function which registers the options. And the
> actual registration happens in,
> https://github.com/openstack/keystone/blob/master/keystone/config.py#L24
>
>
>
> Another pattern which is hard to detect the registered options is,
> https://github.com/openstack/glance/blob/master/glance/cmd/scrubber.py#L54
>
>
>
> This line is not only registering a option inside a function but the
> option is also declared inside cfg.register_opt() which is makes it
> even harder to detect the option.
>
>
> How can we solve this problem of not being able to detect options?
Well, first thing I'd say is that I wouldn't worry about handling every
possible way of declaring cfg config options.
We can have a convention for how to register config options and a tool
that supports projects using that convention.
I think we have that now, but keystone chooses not to follow that
convention ... e.g.
https://review.openstack.org/4547
I'm pretty sure part of my argument for those keystone changes was that
the sample config file generator wouldn't work with keystone as it
stands now.
So, IMHO - any project which doesn't follow the convention should just
come up with its own custom approach to automating config documentation.
> I have two suggestions.
>
>
> First method is to change how generate.py works.
> Rake all modules in project which imports oslo.config.cfg and create a
> module object of it to inspect the cfg.CONF object inside.
> We can cache all the options which were already seen and print out
> newly discovered options.
Ok, so we'd actually look in cfg.CONF for registered options rather than
just looking at *declared* options.
I don't think we should rely on everything being visible through
cfg.CONF. There can be good reasons not to use the global object. Also,
the cfgfilter thing in oslo-incubator would mean that not all options
are visible through cfg.CONF.
> The downside of this solution is that we have to call a private
> function in ConfigOpts called _all_opt_infos() to look at all options
> registered.
> https://github.com/openstack/oslo.config/blob/master/oslo/config/cfg.py#L1822
>
> We would have to create a public api version of it.
I think it would be useful to have an API which would return the Opt
object associated with a given config option name:
def lookup(self, name, group=None):
return self._get_opt_info(opt_name, group)['opt']
and then we can just do:
def print_help(conf, group=None):
d = conf if group is None else conf[group]
for key in d.keys():
if isinstance(conf.GroupAttr, d[key]):
print_help(conf, group=key)
else:
print key, conf.lookup(key, group).help
> The second option is to make it a convention that all options should
> be declared in global view and also registered in the same module
> where they were defined.
Hmm, why would that help?
Doesn't the current tool just require that you declare options at
top-level module scope i.e.
my_opt = cfg.StrOpt(...)
my_opts = [cfg.StrOpt(...), cfg.IntOpt(...)
but the problem with keystone is it is dynamically creating the opt
objects at runtime.
> However it would be difficult to write a hacking.py routine to check
> this and there will be significant amount of code we have to change.
>
>
> So some questions,
> 1) Is it worth documenting all the options in a project?
Yes.
> 2) Should there be a convention of declaring and registering options?
There is a convention, but keystone doesn't follow it.
Also, I think the important thing for the sample generator should be the
convention for declaring options, not registering options.
> Or just have generate.py handle things on its own.
> 3) Any objections of adding a public version of _all_opt_infos()?
No objection to something like the lookup() above, but we already have a
way of iterating over registered options and groups.
Cheers,
Mark.
More information about the OpenStack-dev
mailing list