Switch to oslo.config

The config module in oslo-incubator has been replaced by oslo.config.
Projects need to switch completely to oslo.config for kilo.

This patch does:
* Register options with oslo.config (new file manila/opts.py)
* Generate the config file using oslo-config-generator
* Remove obsoleted entries for this from openstack-common.conf
* Remove oslo-incubator config files

Change-Id: I30b594423c08782e13a1f615099ae65f997a8551
Closes-Bug: #1382183
This commit is contained in:
Andreas Jaeger 2014-10-16 21:03:03 +02:00
parent 06a8dc7ae5
commit 883e02f219
11 changed files with 130 additions and 524 deletions

View File

@ -0,0 +1,6 @@
[DEFAULT]
output_file = etc/manila/manila.conf.sample
namespace = manila
namespace = oslo.messaging
namespace = oslo.db
namespace = oslo.db.concurrency

View File

@ -1,313 +0,0 @@
# Copyright 2012 SINA Corporation
# Copyright 2014 Cisco Systems, Inc.
# All Rights Reserved.
#
# 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)."""
from __future__ import print_function
import argparse
import imp
import os
import re
import socket
import sys
import textwrap
from oslo.config import cfg
import six
import stevedore.named
from manila.openstack.common import gettextutils
from manila.openstack.common import importutils
gettextutils.install('manila')
STROPT = "StrOpt"
BOOLOPT = "BoolOpt"
INTOPT = "IntOpt"
FLOATOPT = "FloatOpt"
LISTOPT = "ListOpt"
DICTOPT = "DictOpt"
MULTISTROPT = "MultiStrOpt"
OPT_TYPES = {
STROPT: 'string value',
BOOLOPT: 'boolean value',
INTOPT: 'integer value',
FLOATOPT: 'floating point value',
LISTOPT: 'list value',
DICTOPT: 'dict value',
MULTISTROPT: 'multi valued',
}
OPTION_REGEX = re.compile(r"(%s)" % "|".join([STROPT, BOOLOPT, INTOPT,
FLOATOPT, LISTOPT, DICTOPT,
MULTISTROPT]))
PY_EXT = ".py"
BASEDIR = os.path.abspath(os.path.join(os.path.dirname(__file__),
"../../../../"))
WORDWRAP_WIDTH = 60
def raise_extension_exception(extmanager, ep, err):
raise
def generate(argv):
parser = argparse.ArgumentParser(
description='generate sample configuration file',
)
parser.add_argument('-m', dest='modules', action='append')
parser.add_argument('-l', dest='libraries', action='append')
parser.add_argument('srcfiles', nargs='*')
parsed_args = parser.parse_args(argv)
mods_by_pkg = dict()
for filepath in parsed_args.srcfiles:
pkg_name = filepath.split(os.sep)[1]
mod_str = '.'.join(['.'.join(filepath.split(os.sep)[:-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 = sorted(pkg for pkg in mods_by_pkg if pkg.endswith(PY_EXT))
ext_names = sorted(pkg for pkg in mods_by_pkg if pkg not in pkg_names)
pkg_names.extend(ext_names)
# opts_by_group is a mapping of group name to an options list
# The options list is a list of (module, options) tuples
opts_by_group = {'DEFAULT': []}
if parsed_args.modules:
for module_name in parsed_args.modules:
module = _import_module(module_name)
if module:
for group, opts in _list_opts(module):
opts_by_group.setdefault(group, []).append((module_name,
opts))
# Look for entry points defined in libraries (or applications) for
# option discovery, and include their return values in the output.
#
# Each entry point should be a function returning an iterable
# of pairs with the group name (or None for the default group)
# and the list of Opt instances for that group.
if parsed_args.libraries:
loader = stevedore.named.NamedExtensionManager(
'oslo.config.opts',
names=list(set(parsed_args.libraries)),
invoke_on_load=False,
on_load_failure_callback=raise_extension_exception
)
for ext in loader:
for group, opts in ext.plugin():
opt_list = opts_by_group.setdefault(group or 'DEFAULT', [])
opt_list.append((ext.name, opts))
for pkg_name in pkg_names:
mods = mods_by_pkg.get(pkg_name)
mods.sort()
for mod_str in mods:
if mod_str.endswith('.__init__'):
mod_str = mod_str[:mod_str.rfind(".")]
mod_obj = _import_module(mod_str)
if not mod_obj:
raise RuntimeError("Unable to import module %s" % mod_str)
for group, opts in _list_opts(mod_obj):
opts_by_group.setdefault(group, []).append((mod_str, opts))
print_group_opts('DEFAULT', opts_by_group.pop('DEFAULT', []))
for group in sorted(opts_by_group.keys()):
print_group_opts(group, opts_by_group[group])
def _import_module(mod_str):
try:
if mod_str.startswith('bin.'):
imp.load_source(mod_str[4:], os.path.join('bin', mod_str[4:]))
return sys.modules[mod_str[4:]]
else:
return importutils.import_module(mod_str)
except Exception as e:
sys.stderr.write("Error importing module %s: %s\n" % (mod_str, str(e)))
return None
def _is_in_group(opt, group):
"""Check if opt is in group."""
for value in group._opts.values():
# NOTE(llu): Temporary workaround for bug #1262148, wait until
# newly released oslo.config support '==' operator.
if not(value['opt'] != opt):
return True
return False
def _guess_groups(opt):
# is it in the DEFAULT group?
if _is_in_group(opt, cfg.CONF):
return 'DEFAULT'
# what other groups is it in?
for value in cfg.CONF.values():
if isinstance(value, cfg.CONF.GroupAttr):
if _is_in_group(opt, value._group):
return value._group.name
raise RuntimeError(
"Unable to find group for option %s, "
"maybe it's defined twice in the same group?"
% opt.name
)
def _list_opts(obj):
def is_opt(o):
return (isinstance(o, cfg.Opt) and
not isinstance(o, cfg.SubCommandOpt))
opts = list()
for attr_str in dir(obj):
attr_obj = getattr(obj, attr_str)
if is_opt(attr_obj):
opts.append(attr_obj)
elif (isinstance(attr_obj, list) and
all(map(lambda x: is_opt(x), attr_obj))):
opts.extend(attr_obj)
ret = {}
for opt in opts:
ret.setdefault(_guess_groups(opt), []).append(opt)
return ret.items()
def print_group_opts(group, opts_by_module):
print("[%s]" % group)
print('')
for mod, opts in opts_by_module:
print('#')
print('# Options defined in %s' % mod)
print('#')
print('')
for opt in opts:
_print_opt(opt)
print('')
def _get_my_ip():
try:
csock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
csock.connect(('8.8.8.8', 80))
(addr, port) = csock.getsockname()
csock.close()
return addr
except socket.error:
return None
def _sanitize_default(name, value):
"""Set up a reasonably sensible default for pybasedir, my_ip and host."""
hostname = socket.gethostname()
fqdn = socket.getfqdn()
if value.startswith(sys.prefix):
# NOTE(jd) Don't use os.path.join, because it is likely to think the
# second part is an absolute pathname and therefore drop the first
# part.
value = os.path.normpath("/usr/" + value[len(sys.prefix):])
elif value.startswith(BASEDIR):
return value.replace(BASEDIR, '/usr/lib/python/site-packages')
elif BASEDIR in value:
return value.replace(BASEDIR, '')
elif value == _get_my_ip():
return '10.0.0.1'
elif value in (hostname, fqdn):
if 'host' in name:
return 'manila'
elif value.endswith(hostname):
return value.replace(hostname, 'manila')
elif value.endswith(fqdn):
return value.replace(fqdn, 'manila')
elif value.strip() != value:
return '"%s"' % value
return value
def _print_opt(opt):
opt_name, opt_default, opt_help = opt.dest, opt.default, opt.help
if not opt_help:
sys.stderr.write('WARNING: "%s" is missing help string.\n' % opt_name)
opt_help = ""
try:
opt_type = OPTION_REGEX.search(str(type(opt))).group(0)
except (ValueError, AttributeError) as err:
sys.stderr.write("%s\n" % str(err))
sys.exit(1)
opt_help = u'%s (%s)' % (opt_help,
OPT_TYPES[opt_type])
print('#', "\n# ".join(textwrap.wrap(opt_help, WORDWRAP_WIDTH)))
if opt.deprecated_opts:
for deprecated_opt in opt.deprecated_opts:
if deprecated_opt.name:
deprecated_group = (deprecated_opt.group if
deprecated_opt.group else "DEFAULT")
print('# Deprecated group/name - [%s]/%s' %
(deprecated_group,
deprecated_opt.name))
try:
if opt_default is None:
print('#%s=<None>' % opt_name)
elif opt_type == STROPT:
assert(isinstance(opt_default, six.string_types))
print('#%s=%s' % (opt_name, _sanitize_default(opt_name,
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 == DICTOPT:
assert(isinstance(opt_default, dict))
opt_default_strlist = [str(key) + ':' + str(value)
for (key, value) in opt_default.items()]
print('#%s=%s' % (opt_name, ','.join(opt_default_strlist)))
elif opt_type == MULTISTROPT:
assert(isinstance(opt_default, list))
if not opt_default:
opt_default = ['']
for default in opt_default:
print('#%s=%s' % (opt_name, default))
print('')
except Exception:
sys.stderr.write('Error in option "%s"\n' % opt_name)
sys.exit(1)
def main():
generate(sys.argv[1:])
if __name__ == '__main__':
main()

121
manila/opts.py Normal file
View File

@ -0,0 +1,121 @@
# Copyright (c) 2014 SUSE Linux Products GmbH.
#
# 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.
__all__ = [
'list_opts'
]
import copy
import itertools
import manila.api.middleware.auth
import manila.api.middleware.sizelimit
import manila.common.config
import manila.compute
import manila.compute.nova
import manila.db.api
import manila.db.base
import manila.exception
import manila.network
import manila.network.linux.interface
import manila.network.neutron.api
import manila.openstack.common.eventlet_backdoor
import manila.openstack.common.lockutils
import manila.openstack.common.log
import manila.openstack.common.policy
import manila.openstack.common.sslutils
import manila.quota
import manila.scheduler.driver
import manila.scheduler.host_manager
import manila.scheduler.manager
import manila.scheduler.scheduler_options
import manila.scheduler.simple
import manila.scheduler.weights
import manila.scheduler.weights.capacity
import manila.service
import manila.share.api
import manila.share.driver
import manila.share.drivers.emc.driver
import manila.share.drivers.generic
import manila.share.drivers.glusterfs
import manila.share.drivers.glusterfs_native
import manila.share.drivers.netapp.cluster_mode
import manila.share.drivers.service_instance
import manila.share.manager
import manila.volume
import manila.volume.cinder
import manila.wsgi
# List of *all* options in [DEFAULT] namespace of manila.
# Any new option list or option needs to be registered here.
_global_opt_lists = [
# Keep list alphabetically sorted
[manila.api.middleware.auth.use_forwarded_for_opt],
[manila.api.middleware.sizelimit.max_request_body_size_opt],
manila.common.config.core_opts,
manila.common.config.debug_opts,
manila.common.config.global_opts,
manila.compute._compute_opts,
manila.compute.nova.nova_opts,
manila.db.api.db_opts,
[manila.db.base.db_driver_opt],
manila.exception.exc_log_opts,
manila.network.linux.interface.OPTS,
manila.network.network_opts,
manila.network.neutron.api.neutron_opts,
manila.openstack.common.eventlet_backdoor.eventlet_backdoor_opts,
manila.openstack.common.lockutils.util_opts,
manila.openstack.common.log.common_cli_opts,
manila.openstack.common.log.generic_log_opts,
manila.openstack.common.log.log_opts,
manila.openstack.common.log.logging_cli_opts,
manila.openstack.common.policy.policy_opts,
manila.quota.quota_opts,
manila.scheduler.driver.scheduler_driver_opts,
manila.scheduler.host_manager.host_manager_opts,
manila.scheduler.host_manager.host_manager_opts,
[manila.scheduler.manager.scheduler_driver_opt],
[manila.scheduler.scheduler_options.scheduler_json_config_location_opt],
manila.scheduler.simple.simple_scheduler_opts,
manila.scheduler.simple.simple_scheduler_opts,
manila.scheduler.weights.capacity.capacity_weight_opts,
manila.scheduler.weights.capacity.capacity_weight_opts,
manila.service.service_opts,
manila.share.api.share_api_opts,
manila.share.driver.share_opts,
manila.share.drivers.emc.driver.EMC_NAS_OPTS,
manila.share.drivers.generic.share_opts,
manila.share.drivers.glusterfs.GlusterfsManilaShare_opts,
manila.share.drivers.glusterfs_native.glusterfs_native_manila_share_opts,
manila.share.drivers.netapp.cluster_mode.NETAPP_NAS_OPTS,
manila.share.drivers.service_instance.server_opts,
manila.share.manager.share_manager_opts,
manila.volume._volume_opts,
manila.volume.cinder.cinder_opts,
manila.wsgi.eventlet_opts,
manila.wsgi.socket_opts,
]
_opts = [
(None, list(itertools.chain(*_global_opt_lists))),
# ssl namespace:
("ssl", manila.openstack.common.sslutils.ssl_opts)
]
def list_opts():
"""Return a list of oslo.config options available in Manila."""
return [(m, copy.deepcopy(o)) for m, o in _opts]

View File

@ -1,7 +1,6 @@
[DEFAULT]
# The list of modules to copy from openstack-common
module=config
module=context
module=eventlet_backdoor
# TODO(jaegerandi) remove excutils with next sync of oslo-incubator
@ -35,9 +34,6 @@ module=versionutils
# The list of scripts to copy from oslo common code
script = tools/colorizer.py
script = tools/config/README
script = tools/config/check_uptodate.sh
script = tools/config/generate_sample.sh
script = tools/install_venv.py
script = tools/install_venv_common.py
script = tools/with_venv.sh

View File

@ -50,6 +50,8 @@ oslo.messaging.notify.drivers =
manila.openstack.common.notifier.rpc_notifier2 = oslo.messaging.notify._impl_messaging:MessagingV2Driver
manila.openstack.common.notifier.rpc_notifier = oslo.messaging.notify._impl_messaging:MessagingDriver
manila.openstack.common.notifier.test_notifier = oslo.messaging.notify._impl_test:TestDriver
oslo.config.opts =
manila = manila.opts:list_opts
[build_sphinx]
all_files = 1

View File

@ -1,38 +0,0 @@
This generate_sample.sh tool is used to generate sample config files
from OpenStack project source trees.
Run it by passing the base directory and package name i.e.
$> generate_sample.sh --base-dir /opt/stack/nova --package-name nova \
--output-dir /opt/stack/nova/etc
$> generate_sample.sh -b /opt/stack/neutron -p nova -o /opt/stack/neutron/etc
Optionally, include libraries that register entry points for option
discovery, such as oslo.messaging:
$> generate_sample.sh -b /opt/stack/ceilometer -p ceilometer \
-o /opt/stack/ceilometer/etc -l oslo.messaging
Watch out for warnings about modules like libvirt, qpid and zmq not
being found - these warnings are significant because they result
in options not appearing in the generated config file.
This check_uptodate.sh tool is used to ensure that the generated sample
config file in the OpenStack project source tree is continually kept up
to date with the code itself.
This can be done by adding a hook to tox.ini. For example, if a project
already had flake8 enabled in a section like this:
[testenv.pep8]
commands =
flake8 {posargs}
This section would be changed to:
[testenv.pep8]
commands =
flake8 {posargs}
{toxinidir}/tools/config/check_uptodate.sh

View File

@ -1,29 +0,0 @@
#!/usr/bin/env bash
PROJECT_NAME=${PROJECT_NAME:-manila}
CFGFILE_NAME=${PROJECT_NAME}.conf.sample
if [ -e etc/${PROJECT_NAME}/${CFGFILE_NAME} ]; then
CFGFILE=etc/${PROJECT_NAME}/${CFGFILE_NAME}
elif [ -e etc/${CFGFILE_NAME} ]; then
CFGFILE=etc/${CFGFILE_NAME}
else
echo "${0##*/}: can not find config file"
exit 1
fi
TEMPDIR=`mktemp -d /tmp/${PROJECT_NAME}.XXXXXX`
trap "rm -rf $TEMPDIR" EXIT
tools/config/generate_sample.sh -b ./ -p ${PROJECT_NAME} -o ${TEMPDIR}
if [ $? != 0 ]
then
exit 1
fi
if ! diff -u ${TEMPDIR}/${CFGFILE_NAME} ${CFGFILE}
then
echo "${0##*/}: ${PROJECT_NAME}.conf.sample is not up to date."
echo "${0##*/}: Please run ${0%%${0##*/}}generate_sample.sh."
exit 1
fi

View File

@ -1,138 +0,0 @@
#!/usr/bin/env bash
# Generate sample configuration for your project.
#
# Aside from the command line flags, it also respects a config file which
# should be named oslo.config.generator.rc and be placed in the same directory.
#
# You can then export the following variables:
# MANILA_CONFIG_GENERATOR_EXTRA_MODULES: list of modules to interrogate for options.
# MANILA_CONFIG_GENERATOR_EXTRA_LIBRARIES: list of libraries to discover.
# MANILA_CONFIG_GENERATOR_EXCLUDED_FILES: list of files to remove from automatic listing.
print_hint() {
echo "Try \`${0##*/} --help' for more information." >&2
}
PARSED_OPTIONS=$(getopt -n "${0##*/}" -o hb:p:m:l:o: \
--long help,base-dir:,package-name:,output-dir:,module:,library: -- "$@")
if [ $? != 0 ] ; then print_hint ; exit 1 ; fi
eval set -- "$PARSED_OPTIONS"
while true; do
case "$1" in
-h|--help)
echo "${0##*/} [options]"
echo ""
echo "options:"
echo "-h, --help show brief help"
echo "-b, --base-dir=DIR project base directory"
echo "-p, --package-name=NAME project package name"
echo "-o, --output-dir=DIR file output directory"
echo "-m, --module=MOD extra python module to interrogate for options"
echo "-l, --library=LIB extra library that registers options for discovery"
exit 0
;;
-b|--base-dir)
shift
BASEDIR=`echo $1 | sed -e 's/\/*$//g'`
shift
;;
-p|--package-name)
shift
PACKAGENAME=`echo $1`
shift
;;
-o|--output-dir)
shift
OUTPUTDIR=`echo $1 | sed -e 's/\/*$//g'`
shift
;;
-m|--module)
shift
MODULES="$MODULES -m $1"
shift
;;
-l|--library)
shift
LIBRARIES="$LIBRARIES -l $1"
shift
;;
--)
break
;;
esac
done
BASEDIR=${BASEDIR:-`pwd`}
if ! [ -d $BASEDIR ]
then
echo "${0##*/}: missing project base directory" >&2 ; print_hint ; exit 1
elif [[ $BASEDIR != /* ]]
then
BASEDIR=$(cd "$BASEDIR" && pwd)
fi
PACKAGENAME=${PACKAGENAME:-$(python setup.py --name)}
TARGETDIR=$BASEDIR/$PACKAGENAME
if ! [ -d $TARGETDIR ]
then
echo "${0##*/}: invalid project package name" >&2 ; print_hint ; exit 1
fi
OUTPUTDIR=${OUTPUTDIR:-$BASEDIR/etc}
# NOTE(bnemec): Some projects put their sample config in etc/,
# some in etc/$PACKAGENAME/
if [ -d $OUTPUTDIR/$PACKAGENAME ]
then
OUTPUTDIR=$OUTPUTDIR/$PACKAGENAME
elif ! [ -d $OUTPUTDIR ]
then
echo "${0##*/}: cannot access \`$OUTPUTDIR': No such file or directory" >&2
exit 1
fi
BASEDIRESC=`echo $BASEDIR | sed -e 's/\//\\\\\//g'`
find $TARGETDIR -type f -name "*.pyc" -delete
FILES=$(find $TARGETDIR -type f -name "*.py" ! -path "*/tests/*" \
-exec grep -l "Opt(" {} + | sed -e "s/^$BASEDIRESC\///g" | sort -u)
RC_FILE="`dirname $0`/oslo.config.generator.rc"
if test -r "$RC_FILE"
then
source "$RC_FILE"
fi
for filename in ${MANILA_CONFIG_GENERATOR_EXCLUDED_FILES}; do
FILES="${FILES[@]/$filename/}"
done
for mod in ${MANILA_CONFIG_GENERATOR_EXTRA_MODULES}; do
MODULES="$MODULES -m $mod"
done
for lib in ${MANILA_CONFIG_GENERATOR_EXTRA_LIBRARIES}; do
LIBRARIES="$LIBRARIES -l $lib"
done
export EVENTLET_NO_GREENDNS=yes
OS_VARS=$(set | sed -n '/^OS_/s/=[^=]*$//gp' | xargs)
[ "$OS_VARS" ] && eval "unset \$OS_VARS"
DEFAULT_MODULEPATH=manila.openstack.common.config.generator
MODULEPATH=${MODULEPATH:-$DEFAULT_MODULEPATH}
OUTPUTFILE=$OUTPUTDIR/$PACKAGENAME.conf.sample
python -m $MODULEPATH $MODULES $LIBRARIES $FILES > $OUTPUTFILE
if [ $? != 0 ]
then
echo "Can not generate $OUTPUTFILE"
exit 1
fi
# Hook to allow projects to append custom config file snippets
CONCAT_FILES=$(ls $BASEDIR/tools/config/*.conf.sample 2>/dev/null)
for CONCAT_FILE in $CONCAT_FILES; do
cat $CONCAT_FILE >> $OUTPUTFILE
done

View File

@ -1 +0,0 @@
export MANILA_CONFIG_GENERATOR_EXTRA_LIBRARIES="oslo.messaging oslo.db oslo.db.concurrency"

View File

@ -21,7 +21,7 @@ commands =
[testenv:genconfig]
commands =
bash tools/config/generate_sample.sh -o etc/manila
oslo-config-generator --config-file etc/oslo-config-generator/manila.conf
[testenv:venv]
commands = {posargs}