From d050e3399a2af8d0dba210024194954803ef7954 Mon Sep 17 00:00:00 2001 From: Slawek Kaplonski Date: Thu, 18 Jul 2019 14:09:46 +0200 Subject: [PATCH] Autogenerate config options and sample config file This patch adds to "docs" tox job ability to automatically generate sample config file and config reference based on config options defined in code. Change-Id: Id960cc28d1b13ef3ed15e911f59620afb83f45b9 --- .gitignore | 6 +- doc/source/conf.py | 22 +++++ doc/source/configuration/index.rst | 25 ++++++ doc/source/configuration/samples/tobiko.rst | 8 ++ doc/source/configuration/tobiko.rst | 6 ++ doc/source/contents.rst | 7 ++ etc/oslo-config-generator/tobiko.conf | 5 ++ setup.cfg | 2 + tobiko/config.py | 36 ++++++-- tobiko/openstack/glance/config.py | 65 +++++++++----- tobiko/openstack/keystone/config.py | 72 ++++++++------- tobiko/openstack/neutron/config.py | 60 +++++++------ tobiko/openstack/nova/config.py | 22 +++-- tobiko/shell/ping/config.py | 61 +++++++------ tobiko/shell/sh/config.py | 20 +++-- tobiko/shell/ssh/config.py | 99 +++++++++++---------- 16 files changed, 340 insertions(+), 176 deletions(-) create mode 100644 doc/source/configuration/index.rst create mode 100644 doc/source/configuration/samples/tobiko.rst create mode 100644 doc/source/configuration/tobiko.rst create mode 100644 etc/oslo-config-generator/tobiko.conf diff --git a/.gitignore b/.gitignore index 57e19048e..355b37e50 100644 --- a/.gitignore +++ b/.gitignore @@ -22,11 +22,13 @@ ChangeLog cover/ doc/build/* dist/ -etc/ +etc/*.sample zuul/versioninfo # Files created by releasenotes build releasenotes/build +# Docs related files +doc/source/_static/config-samples/*.sample + Pipfile.lock -tobiko.conf diff --git a/doc/source/conf.py b/doc/source/conf.py index 7235c0e16..1c39cc6db 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -59,6 +59,8 @@ extensions = [ 'sphinx.ext.ifconfig', 'sphinx.ext.graphviz', 'sphinx.ext.todo', + 'oslo_config.sphinxext', + 'oslo_config.sphinxconfiggen', ] # Add any paths that contain templates here, relative to this directory. @@ -111,3 +113,23 @@ html_theme_options = { # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] + +# -- Options for oslo_config.sphinxconfiggen --------------------------------- + +_config_generator_config_files = [ + 'tobiko.conf', +] + + +def _get_config_generator_config_definition(conf): + config_file_path = '../../etc/oslo-config-generator/%s' % conf + # oslo_config.sphinxconfiggen appends '.conf.sample' to the filename, + # strip file extentension (.conf or .ini). + output_file_path = '_static/config-samples/%s' % conf.rsplit('.', 1)[0] + return (config_file_path, output_file_path) + + +config_generator_config_file = [ + _get_config_generator_config_definition(conf) + for conf in _config_generator_config_files +] diff --git a/doc/source/configuration/index.rst b/doc/source/configuration/index.rst new file mode 100644 index 000000000..c13693251 --- /dev/null +++ b/doc/source/configuration/index.rst @@ -0,0 +1,25 @@ +.. _configuring: + +============================= +Tobiko Configuration Options +============================= + +This section provides a list of all configuration options for Tobiko. +These are auto-generated from Tobiko code when this documentation is +built. + +Configuration Reference +----------------------- + +.. toctree:: + :maxdepth: 1 + + tobiko.rst + +Sample Configuration Files +-------------------------- + +.. toctree:: + :maxdepth: 1 + + samples/tobiko.rst diff --git a/doc/source/configuration/samples/tobiko.rst b/doc/source/configuration/samples/tobiko.rst new file mode 100644 index 000000000..a0114eb5a --- /dev/null +++ b/doc/source/configuration/samples/tobiko.rst @@ -0,0 +1,8 @@ +=================== +Sample tobiko.conf +=================== + +This sample configuration can also be viewed in `the raw format +<../../_static/config-samples/tobiko.conf.sample>`_. + +.. literalinclude:: ../../_static/config-samples/tobiko.conf.sample diff --git a/doc/source/configuration/tobiko.rst b/doc/source/configuration/tobiko.rst new file mode 100644 index 000000000..bc75c9689 --- /dev/null +++ b/doc/source/configuration/tobiko.rst @@ -0,0 +1,6 @@ +=========== +tobiko.conf +=========== + +.. show-options:: + :config-file: etc/oslo-config-generator/tobiko.conf diff --git a/doc/source/contents.rst b/doc/source/contents.rst index adc1ca018..394915521 100644 --- a/doc/source/contents.rst +++ b/doc/source/contents.rst @@ -11,3 +11,10 @@ Tobiko Documentation Contents :maxdepth: 1 reference/index + +Configuration Reference +----------------------- +.. toctree:: + :maxdepth: 2 + + configuration/index diff --git a/etc/oslo-config-generator/tobiko.conf b/etc/oslo-config-generator/tobiko.conf new file mode 100644 index 000000000..ad03b92ba --- /dev/null +++ b/etc/oslo-config-generator/tobiko.conf @@ -0,0 +1,5 @@ +[DEFAULT] +output_file = etc/tobiko.conf.sample +wrap_width = 79 + +namespace = tobiko diff --git a/setup.cfg b/setup.cfg index 2b4aa7c12..642154b06 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,6 +34,8 @@ console_scripts = tobiko-list = tobiko.cmd.list:main tobiko-fault = tobiko.cmd.fault:main tobiko = tobiko.cmd.run:main +oslo.config.opts = + tobiko = tobiko.config:list_tobiko_options [global] setup-hooks = diff --git a/tobiko/config.py b/tobiko/config.py index 2bc6904b9..72a23b4b2 100644 --- a/tobiko/config.py +++ b/tobiko/config.py @@ -14,6 +14,7 @@ from __future__ import absolute_import import importlib +import itertools import logging import os @@ -36,6 +37,16 @@ CONFIG_DIRS = [os.getcwd(), os.path.expanduser("~/.tobiko"), '/etc/tobiko'] +HTTP_CONF_GROUP_NAME = "http" + +HTTP_OPTIONS = [ + cfg.StrOpt('http_proxy', + help="HTTP proxy URL for Rest APIs"), + cfg.StrOpt('https_proxy', + help="HTTPS proxy URL for Rest APIs"), + cfg.StrOpt('no_proxy', + help="Don't use proxy server to connect to listed hosts")] + class GlobalConfig(object): @@ -93,14 +104,7 @@ def init_tobiko_config(default_config_dirs=None, product_name='tobiko', def register_tobiko_options(conf): conf.register_opts( - group=cfg.OptGroup('http'), - opts=[cfg.StrOpt('http_proxy', - help="HTTP proxy URL for Rest APIs"), - cfg.StrOpt('https_proxy', - help="HTTPS proxy URL for Rest APIs"), - cfg.StrOpt('no_proxy', - help="Don't use proxy server to connect to listed " - "hosts")]) + group=cfg.OptGroup(HTTP_CONF_GROUP_NAME), opts=HTTP_OPTIONS) for module_name in CONFIG_MODULES: module = importlib.import_module(module_name) @@ -108,6 +112,22 @@ def register_tobiko_options(conf): module.register_tobiko_options(conf=conf) +def list_http_options(): + return [ + (HTTP_CONF_GROUP_NAME, itertools.chain(HTTP_OPTIONS)) + ] + + +def list_tobiko_options(): + all_options = list_http_options() + + for module_name in CONFIG_MODULES: + module = importlib.import_module(module_name) + if hasattr(module, 'list_options'): + all_options += module.list_options() + return all_options + + def setup_tobiko_config(conf): # Redirect all warnings to logging library logging.captureWarnings(True) diff --git a/tobiko/openstack/glance/config.py b/tobiko/openstack/glance/config.py index e3607da5b..d90420d48 100644 --- a/tobiko/openstack/glance/config.py +++ b/tobiko/openstack/glance/config.py @@ -13,40 +13,61 @@ # under the License. from __future__ import absolute_import +import itertools + from oslo_config import cfg CIRROS_IMAGE_URL = \ 'http://download.cirros-cloud.net/0.4.0/cirros-0.4.0-x86_64-disk.img' +GROUP_NAME = 'glance' +OPTIONS = [ + cfg.StrOpt('image_dir', + default='~/.tobiko/cache/glance/images', + help=("Default directory where to look for image " + "files")), +] + GLANCE_IMAGE_NAMES = ['cirros', 'ubuntu'] def register_tobiko_options(conf): - conf.register_opts( - group=cfg.OptGroup('glance'), - opts=[cfg.StrOpt('image_dir', - default='~/.tobiko/cache/glance/images', - help=("Default directory where to look for image " - "files")), ]) + conf.register_opts(group=cfg.OptGroup(GROUP_NAME), opts=OPTIONS) + for image_options in get_images_options(): + conf.register_opts(group=image_options[0], opts=image_options[1]) + + +def get_images_options(): + options = [] for name in GLANCE_IMAGE_NAMES: group_name = name.lower() - conf.register_opts( - group=cfg.OptGroup(group_name), - opts=[cfg.StrOpt('image_name', - help="Default " + name + " image name"), - cfg.StrOpt('image_url', - help="Default " + name + " image URL"), - cfg.StrOpt('image_file', - help="Default " + name + " image filename"), - cfg.StrOpt('container_format', - help="Default " + name + " container format"), - cfg.StrOpt('disk_format', - help="Default " + name + " disk format"), - cfg.StrOpt('username', - help="Default " + name + " username"), - cfg.StrOpt('password', - help="Default " + name + " password"), ]) + options += [( + group_name, + [cfg.StrOpt('image_name', + help="Default " + name + " image name"), + cfg.StrOpt('image_url', + help="Default " + name + " image URL"), + cfg.StrOpt('image_file', + help="Default " + name + " image filename"), + cfg.StrOpt('container_format', + help="Default " + name + " container format"), + cfg.StrOpt('disk_format', + help="Default " + name + " disk format"), + cfg.StrOpt('username', + help="Default " + name + " username"), + cfg.StrOpt('password', + help="Default " + name + " password")])] + + return options + + +def list_options(): + options = [(GROUP_NAME, itertools.chain(OPTIONS))] + for image_options in get_images_options(): + options += [ + (image_options[0], itertools.chain(image_options[1]))] + return options diff --git a/tobiko/openstack/keystone/config.py b/tobiko/openstack/keystone/config.py index 3a002a9d8..59247e3b9 100644 --- a/tobiko/openstack/keystone/config.py +++ b/tobiko/openstack/keystone/config.py @@ -13,40 +13,48 @@ # under the License. from __future__ import absolute_import +import itertools + from oslo_config import cfg +GROUP_NAME = 'keystone' +OPTIONS = [ + cfg.IntOpt('api_version', + default=None, + help="Identity API version"), + cfg.StrOpt('auth_url', + default=None, + help="Identity service URL"), + cfg.StrOpt('username', + default=None, + help="Username"), + cfg.StrOpt('project_name', + default=None, + help="Project name"), + cfg.StrOpt('password', + default=None, + help="Password"), + cfg.StrOpt('domain_name', + default=None, + help="Domain name"), + cfg.StrOpt('user_domain_name', + default=None, + help="User domain name"), + cfg.StrOpt('project_domain_name', + default=None, + help="Project domain name"), + cfg.StrOpt('project_domain_id', + default=None, + help="Project domain ID"), + cfg.StrOpt('trust_id', + default=None, + help="Trust ID for trust scoping.")] + def register_tobiko_options(conf): - conf.register_opts( - group=cfg.OptGroup('keystone'), - opts=[cfg.IntOpt('api_version', - default=None, - help="Identity API version"), - cfg.StrOpt('auth_url', - default=None, - help="Identity service URL"), - cfg.StrOpt('username', - default=None, - help="Username"), - cfg.StrOpt('project_name', - default=None, - help="Project name"), - cfg.StrOpt('password', - default=None, - help="Password"), - cfg.StrOpt('domain_name', - default=None, - help="Domain name"), - cfg.StrOpt('user_domain_name', - default=None, - help="User domain name"), - cfg.StrOpt('project_domain_name', - default=None, - help="Project domain name"), - cfg.StrOpt('project_domain_id', - default=None, - help="Project domain ID"), - cfg.StrOpt('trust_id', - default=None, - help="Trust ID for trust scoping.")]) + conf.register_opts(group=cfg.OptGroup(GROUP_NAME), opts=OPTIONS) + + +def list_options(): + return [(GROUP_NAME, itertools.chain(OPTIONS))] diff --git a/tobiko/openstack/neutron/config.py b/tobiko/openstack/neutron/config.py index 8fb9b0ce9..755bbd421 100644 --- a/tobiko/openstack/neutron/config.py +++ b/tobiko/openstack/neutron/config.py @@ -13,33 +13,41 @@ # under the License. from __future__ import absolute_import +import itertools + from oslo_config import cfg +GROUP_NAME = 'neutron' +OPTIONS = [ + cfg.StrOpt('floating_network', + help="Network for creating floating IPs"), + cfg.StrOpt('ipv4_cidr', + default='10.100.0.0/16', + help="The CIDR block to allocate IPv4 subnets from"), + cfg.IntOpt('ipv4_prefixlen', + default=24, + help="The mask bits for IPv4 subnets"), + cfg.StrOpt('ipv6_cidr', + default='2003::/48', + help="The CIDR block to allocate IPv6 subnets from"), + cfg.IntOpt('ipv6_prefixlen', + default=64, + help="The mask bits for IPv6 subnets"), + cfg.IntOpt('custom_mtu_size', + default=1400, + help=("Customized maximum transfer unit size\n" + "Notes:\n" + " - MTU values as small as 1000 has been seen " + "breaking networking binding due to an " + "unknown cause.\n" + " - Too big MTU values (like greater than 1400)" + " may be refused during network creation")), +] + def register_tobiko_options(conf): - conf.register_opts( - group=cfg.OptGroup('neutron'), - opts=[cfg.StrOpt('floating_network', - help="Network for creating floating IPs"), - cfg.StrOpt('ipv4_cidr', - default='10.100.0.0/16', - help="The CIDR block to allocate IPv4 subnets from"), - cfg.IntOpt('ipv4_prefixlen', - default=24, - help="The mask bits for IPv4 subnets"), - cfg.StrOpt('ipv6_cidr', - default='2003::/48', - help="The CIDR block to allocate IPv6 subnets from"), - cfg.IntOpt('ipv6_prefixlen', - default=64, - help="The mask bits for IPv6 subnets"), - cfg.IntOpt('custom_mtu_size', - default=1400, - help=("Customized maximum transfer unit size\n" - "Notes:\n" - " - MTU values as small as 1000 has been seen " - "breaking networking binding due to an " - "unknown cause.\n" - " - Too big MTU values (like greater than 1400)" - " may be refused during network creation")), - ]) + conf.register_opts(group=cfg.OptGroup(GROUP_NAME), opts=OPTIONS) + + +def list_options(): + return [(GROUP_NAME, itertools.chain(OPTIONS))] diff --git a/tobiko/openstack/nova/config.py b/tobiko/openstack/nova/config.py index ada6416b0..207fdc7b0 100644 --- a/tobiko/openstack/nova/config.py +++ b/tobiko/openstack/nova/config.py @@ -13,14 +13,22 @@ # under the License. from __future__ import absolute_import +import itertools + from oslo_config import cfg +GROUP_NAME = "nova" +OPTIONS = [ + cfg.StrOpt('flavor', + help="Default flavor for new server instances"), + cfg.StrOpt('key_file', default='~/.ssh/id_rsa', + help="Default SSH key to login to server instances"), +] + def register_tobiko_options(conf): - conf.register_opts( - group=cfg.OptGroup('nova'), - opts=[cfg.StrOpt('flavor', - help="Default flavor for new server instances"), - cfg.StrOpt('key_file', default='~/.ssh/id_rsa', - help="Default SSH key to login to server instances"), - ]) + conf.register_opts(group=cfg.OptGroup('nova'), opts=OPTIONS) + + +def list_options(): + return [(GROUP_NAME, itertools.chain(OPTIONS))] diff --git a/tobiko/shell/ping/config.py b/tobiko/shell/ping/config.py index 2ff5831b1..51cf669a4 100644 --- a/tobiko/shell/ping/config.py +++ b/tobiko/shell/ping/config.py @@ -13,35 +13,42 @@ # under the License. from __future__ import absolute_import +import itertools + from oslo_config import cfg +GROUP_NAME = "ping" +OPTIONS = [ + cfg.IntOpt('count', + default=1, + help="Number of ICMP messages to wait before ending " + "ping command execution"), + cfg.IntOpt('deadline', + default=5, + help="Max seconds waited from ping command before " + "self terminating himself"), + cfg.StrOpt('fragmentation', + default=True, + help="If disable it will not allow ICMP messages to " + "be delivered in smaller fragments"), + cfg.StrOpt('interval', + default=1, + help="Seconds of time interval between " + "consecutive before ICMP messages"), + cfg.IntOpt('packet_size', + default=None, + help="Size in bytes of ICMP messages (including " + "headers and payload)"), + cfg.IntOpt('timeout', + default=90., + help="Maximum time in seconds a sequence of ICMP " + "messages is sent to a destination host before " + "reporting as a failure")] + def register_tobiko_options(conf): + conf.register_opts(group=cfg.OptGroup('ping'), opts=OPTIONS) - conf.register_opts( - group=cfg.OptGroup('ping'), - opts=[cfg.IntOpt('count', - default=1, - help="Number of ICMP messages to wait before ending " - "ping command execution"), - cfg.IntOpt('deadline', - default=5, - help="Max seconds waited from ping command before " - "self terminating himself"), - cfg.StrOpt('fragmentation', - default=True, - help="If disable it will not allow ICMP messages to " - "be delivered in smaller fragments"), - cfg.StrOpt('interval', - default=1, - help="Seconds of time interval between " - "consecutive before ICMP messages"), - cfg.IntOpt('packet_size', - default=None, - help="Size in bytes of ICMP messages (including " - "headers and payload)"), - cfg.IntOpt('timeout', - default=90., - help="Maximum time in seconds a sequence of ICMP " - "messages is sent to a destination host before " - "reporting as a failure")]) + +def list_options(): + return [(GROUP_NAME, itertools.chain(OPTIONS))] diff --git a/tobiko/shell/sh/config.py b/tobiko/shell/sh/config.py index e5dec78ca..8a7959e46 100644 --- a/tobiko/shell/sh/config.py +++ b/tobiko/shell/sh/config.py @@ -15,14 +15,22 @@ # under the License. from __future__ import absolute_import +import itertools + from oslo_config import cfg +GROUP_NAME = 'shell' +OPTIONS = [ + cfg.StrOpt('command', + default='/bin/sh -c', + help="Default shell command used for executing " + "local commands") +] + def register_tobiko_options(conf): + conf.register_opts(group=cfg.OptGroup('shell'), opts=OPTIONS) - conf.register_opts( - group=cfg.OptGroup('shell'), - opts=[cfg.StrOpt('command', - default='/bin/sh -c', - help="Default shell command used for executing " - "local commands")]) + +def list_options(): + return [(GROUP_NAME, itertools.chain(OPTIONS))] diff --git a/tobiko/shell/ssh/config.py b/tobiko/shell/ssh/config.py index b9abea243..524f6180f 100644 --- a/tobiko/shell/ssh/config.py +++ b/tobiko/shell/ssh/config.py @@ -14,58 +14,65 @@ from __future__ import absolute_import import getpass +import itertools from oslo_config import cfg from oslo_log import log +GROUP_NAME = 'ssh' +OPTIONS = [ + cfg.BoolOpt('debug', + default=False, + help=('Logout debugging messages of paramiko ' + 'library')), + cfg.StrOpt('command', + default='/usr/bin/ssh', + help=('Default SSH client command')), + cfg.StrOpt('port', + default=22, + help=('Default SSH port')), + cfg.StrOpt('username', + default=getpass.getuser(), + help=('Default SSH username')), + cfg.ListOpt('config_files', + default=['/etc/ssh/ssh_config', '~/.ssh/config'], + help="Default user SSH configuration files"), + cfg.StrOpt('key_file', + default='~/.ssh/id_rsa', + help="Default SSH private key file"), + cfg.BoolOpt('allow_agent', + default=False, + help=("Set to False to disable connecting to the " + "SSH agent")), + cfg.BoolOpt('compress', + default=False, + help="Set to True to turn on compression"), + cfg.FloatOpt('timeout', + default=5., + help="SSH connect timeout in seconds"), + cfg.IntOpt('connection_attempts', + default=60, + help=("Incremental seconds to wait after every " + "failed SSH connection attempt")), + cfg.FloatOpt('connection_interval', + default=5., + help=("Minimal seconds to wait between every " + "failed SSH connection attempt")), + cfg.StrOpt('proxy_jump', + default=None, + help="Default SSH proxy server"), + cfg.StrOpt('proxy_command', + default=None, + help="Default proxy command"), +] + def register_tobiko_options(conf): - conf.register_opts( - group=cfg.OptGroup('ssh'), - opts=[cfg.BoolOpt('debug', - default=False, - help=('Logout debugging messages of paramiko ' - 'library')), - cfg.StrOpt('command', - default='/usr/bin/ssh', - help=('Default SSH client command')), - cfg.StrOpt('port', - default=22, - help=('Default SSH port')), - cfg.StrOpt('username', - default=getpass.getuser(), - help=('Default SSH username')), - cfg.ListOpt('config_files', - default=['/etc/ssh/ssh_config', '~/.ssh/config'], - help="Default user SSH configuration files"), - cfg.StrOpt('key_file', - default='~/.ssh/id_rsa', - help="Default SSH private key file"), - cfg.BoolOpt('allow_agent', - default=False, - help=("Set to False to disable connecting to the " - "SSH agent")), - cfg.BoolOpt('compress', - default=False, - help="Set to True to turn on compression"), - cfg.FloatOpt('timeout', - default=5., - help="SSH connect timeout in seconds"), - cfg.IntOpt('connection_attempts', - default=60, - help=("Incremental seconds to wait after every " - "failed SSH connection attempt")), - cfg.FloatOpt('connection_interval', - default=5., - help=("Minimal seconds to wait between every " - "failed SSH connection attempt")), - cfg.StrOpt('proxy_jump', - default=None, - help="Default SSH proxy server"), - cfg.StrOpt('proxy_command', - default=None, - help="Default proxy command"), - ]) + conf.register_opts(group=cfg.OptGroup(GROUP_NAME), opts=OPTIONS) + + +def list_options(): + return [(GROUP_NAME, itertools.chain(OPTIONS))] def setup_tobiko_config(conf):