Sample config file tool updates

Fixes bug #1000133

Current method of extracting config options will not work
once the global config object gets removed.

1. Update tool to read a cfg.Opt subclass instance or list of cfg.Opt subclass
   instances rather than the FLAG object.
2. Word wrap help messages
3. Insert line between consecutive options
4. Print warnings for missing option help string

Change-Id: Idd17048b6e8db6e939946968e011e68da8585b8c
This commit is contained in:
Zhongyue Luo
2012-05-16 13:27:50 +08:00
parent cb7c4377b3
commit 991614add8
4 changed files with 1787 additions and 1124 deletions
+1625 -964
View File
File diff suppressed because it is too large Load Diff
-159
View File
@@ -1,159 +0,0 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 SINA Corporation
# All Rights Reserved.
# Author: Zhongyue Luo <lzyeval@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Generates a nova.conf file."""
import os
import re
import sys
_PY_EXT = ".py"
_FLAGS = "FLAGS"
_STROPT = "StrOpt"
_BOOLOPT = "BoolOpt"
_INTOPT = "IntOpt"
_FLOATOPT = "FloatOpt"
_LISTOPT = "ListOpt"
_MULTISTROPT = "MultiStrOpt"
_OPTION_CACHE = list()
_OPTION_REGEX = re.compile(r"(%s)" % "|".join([_STROPT, _BOOLOPT, _INTOPT,
_FLOATOPT, _LISTOPT,
_MULTISTROPT]))
_BASEDIR = os.path.abspath(os.path.dirname(__file__) + "../../")
def main(srcfiles):
def mod_prefer(mod_str):
prefer = ["flags.py", "log.py", "utils.py", "service.py"]
return prefer.index(mod_str) if mod_str in prefer else ord(mod_str[0])
def pkg_prefer(pkg_str):
prefer = ["auth", "api", "vnc", "ipv6", "network", "compute", "virt",
"console", "consoleauth", "image"]
return prefer.index(pkg_str) if pkg_str in prefer else ord(pkg_str[0])
print '#' * 20 + '\n# nova.conf sample #\n' + '#' * 20
# NOTE(lzyeval): sort top level modules and packages
# to process modules first
print
print '[DEFAULT]'
print
mods_by_pkg = dict()
for filepath in srcfiles:
pkg_name = filepath.split(os.sep)[3]
mod_str = '.'.join(['.'.join(filepath.split(os.sep)[2:-1]),
os.path.basename(filepath).split('.')[0]])
mods = mods_by_pkg.get(pkg_name, list())
if not mods:
mods_by_pkg[pkg_name] = mods
mods.append(mod_str)
# NOTE(lzyeval): place top level modules before packages
pkg_names = filter(lambda x: x.endswith(_PY_EXT), mods_by_pkg.keys())
pkg_names.sort(key=lambda x: mod_prefer(x))
ext_names = filter(lambda x: x not in pkg_names, mods_by_pkg.keys())
ext_names.sort(key=lambda x: pkg_prefer(x))
pkg_names.extend(ext_names)
for pkg_name in pkg_names:
mods = mods_by_pkg.get(pkg_name)
mods.sort()
for mod_str in mods:
print_module(mod_str)
def print_module(mod_str):
opts = list()
flags = None
if mod_str.endswith('.__init__'):
mod_str = mod_str[:mod_str.rfind(".")]
try:
__import__(mod_str)
flags = getattr(sys.modules[mod_str], _FLAGS)
except (ValueError, AttributeError), err:
return
except ImportError, ie:
sys.stderr.write("%s\n" % str(ie))
return
except Exception, e:
return
for opt_name in sorted(flags.keys()):
# check if option was processed
if opt_name in _OPTION_CACHE:
continue
opt_dict = flags._get_opt_info(opt_name)
opts.append(opt_dict['opt'])
_OPTION_CACHE.append(opt_name)
# return if flags has no unique options
if not opts:
return
# print out module info
print '######### defined in %s #########' % mod_str
print
for opt in opts:
print_opt(opt)
print
def convert_abspath(s):
"""Set up a reasonably sensible default for pybasedir."""
if not s.startswith(_BASEDIR):
return s
return s.replace(_BASEDIR, '/usr/lib/python/site-packages')
def print_opt(opt):
opt_type = None
try:
opt_type = _OPTION_REGEX.search(str(type(opt))).group(0)
except (ValueError, AttributeError), err:
sys.stderr.write("%s\n" % str(err))
sys.exit(1)
# print out option info
print "######", "".join(["(", opt_type, ")"]), opt.help
name, default = opt.name, opt.default
if isinstance(default, basestring):
default = convert_abspath(default)
if default is None:
print '# %s=<None>' % name
else:
if opt_type == 'StrOpt':
print '# %s="%s"' % (name, default)
elif opt_type == 'ListOpt':
print '# %s="%s"' % (name, ','.join(default))
elif opt_type == 'MultiStrOpt':
for default in default:
print '# %s="%s"' % (name, default)
elif opt_type == 'BoolOpt':
print '# %s=%s' % (name, str(default).lower())
else:
print '# %s=%s' % (name, default)
if __name__ == '__main__':
if len(sys.argv) < 2:
print "usage: python %s [srcfile]...\n" % sys.argv[0]
sys.exit(0)
main(sys.argv[1:])
print "#", "Total option count: %d" % len(_OPTION_CACHE)
+161
View File
@@ -0,0 +1,161 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2012 SINA Corporation
# All Rights Reserved.
# Author: Zhongyue Luo <lzyeval@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Extracts OpenStack config option info from module(s)."""
import os
import re
import sys
import textwrap
from nova.openstack.common import cfg
from nova.openstack.common import importutils
STROPT = "StrOpt"
BOOLOPT = "BoolOpt"
INTOPT = "IntOpt"
FLOATOPT = "FloatOpt"
LISTOPT = "ListOpt"
MULTISTROPT = "MultiStrOpt"
OPTION_COUNT = 0
OPTION_REGEX = re.compile(r"(%s)" % "|".join([STROPT, BOOLOPT, INTOPT,
FLOATOPT, LISTOPT,
MULTISTROPT]))
OPTION_HELP_INDENT = "####"
PY_EXT = ".py"
BASEDIR = os.path.abspath(os.path.dirname(__file__) + "../../")
WORDWRAP_WIDTH = 60
def main(srcfiles):
print '\n'.join(['#' * 20, '# nova.conf sample #', '#' * 20,
'', '[DEFAULT]', ''])
mods_by_pkg = dict()
for filepath in srcfiles:
pkg_name = filepath.split(os.sep)[3]
mod_str = '.'.join(['.'.join(filepath.split(os.sep)[2:-1]),
os.path.basename(filepath).split('.')[0]])
mods_by_pkg.setdefault(pkg_name, list()).append(mod_str)
# NOTE(lzyeval): place top level modules before packages
pkg_names = filter(lambda x: x.endswith(PY_EXT), mods_by_pkg.keys())
pkg_names.sort()
ext_names = filter(lambda x: x not in pkg_names, mods_by_pkg.keys())
ext_names.sort()
pkg_names.extend(ext_names)
for pkg_name in pkg_names:
mods = mods_by_pkg.get(pkg_name)
mods.sort()
for mod_str in mods:
_print_module(mod_str)
print "# Total option count: %d" % OPTION_COUNT
def _print_module(mod_str):
global OPTION_COUNT
opts = list()
mod_obj = None
if mod_str.endswith('.__init__'):
mod_str = mod_str[:mod_str.rfind(".")]
try:
mod_obj = importutils.import_module(mod_str)
except (ValueError, AttributeError), err:
return
except ImportError, ie:
sys.stderr.write("%s\n" % str(ie))
return
except Exception, e:
return
for attr_str in dir(mod_obj):
attr_obj = getattr(mod_obj, attr_str)
if isinstance(attr_obj, cfg.Opt):
opts.append(attr_obj)
elif (isinstance(attr_obj, list) and
all(map(lambda x: isinstance(x, cfg.Opt), attr_obj))):
opts.extend(attr_obj)
# NOTE(lzyeval): return if module has no options
if not opts:
return
OPTION_COUNT += len(opts)
print '######## defined in %s ########\n' % mod_str
for opt in opts:
_print_opt(opt)
print
def _convert_abspath(s):
"""Set up a reasonably sensible default for pybasedir."""
if not s.startswith(BASEDIR):
return s
return s.replace(BASEDIR, '/usr/lib/python/site-packages')
def _wrap(msg, indent):
padding = ' ' * indent
prefix = "\n%s %s " % (OPTION_HELP_INDENT, padding)
return prefix.join(textwrap.wrap(msg, WORDWRAP_WIDTH))
def _print_opt(opt):
opt_name, opt_default, opt_help = opt.name, opt.default, opt.help
if not opt_help:
sys.stderr.write('WARNING: "%s" is missing help string.\n' % opt_name)
opt_type = None
try:
opt_type = OPTION_REGEX.search(str(type(opt))).group(0)
except (ValueError, AttributeError), err:
sys.stderr.write("%s\n" % str(err))
sys.exit(1)
try:
if opt_default is None:
print '# %s=<None>' % opt_name
elif opt_type == STROPT:
assert(isinstance(opt_default, basestring))
print '# %s="%s"' % (opt_name, _convert_abspath(opt_default))
elif opt_type == BOOLOPT:
assert(isinstance(opt_default, bool))
print '# %s=%s' % (opt_name, str(opt_default).lower())
elif opt_type == INTOPT:
assert(isinstance(opt_default, int) and
not isinstance(opt_default, bool))
print '# %s=%s' % (opt_name, opt_default)
elif opt_type == FLOATOPT:
assert(isinstance(opt_default, float))
print '# %s=%s' % (opt_name, opt_default)
elif opt_type == LISTOPT:
assert(isinstance(opt_default, list))
print '# %s="%s"' % (opt_name, ','.join(opt_default))
elif opt_type == MULTISTROPT:
assert(isinstance(opt_default, list))
for default in opt_default:
print '# %s="%s"' % (opt_name, default)
except Exception:
sys.stderr.write('Error in option "%s"\n' % opt_name)
sys.exit(1)
opt_type_tag = "(%s)" % opt_type
print OPTION_HELP_INDENT, opt_type_tag, _wrap(opt_help, len(opt_type_tag))
print
if __name__ == '__main__':
if len(sys.argv) < 2:
print "usage: python %s [srcfile]...\n" % sys.argv[0]
sys.exit(0)
main(sys.argv[1:])
+1 -1
View File
@@ -19,7 +19,7 @@
pushd $(cd $(dirname "$0") && pwd) >/dev/null
find ../../nova -type f -name "*.py" ! -path "../../nova/tests/*" -exec \
grep -l "Opt(" {} \; | sort -u | xargs python create_conf.py > \
grep -l "Opt(" {} \; | sort -u | xargs python extract_opts.py > \
../../etc/nova/nova.conf.sample
popd >/dev/null