Merge "Centralized configuration"
This commit is contained in:
commit
66a6f36d2d
|
@ -20,11 +20,11 @@ Senlin uses `oslo.config` to define and manage configuration options to
|
||||||
allow the deployer to control many aspects of the service API and the service
|
allow the deployer to control many aspects of the service API and the service
|
||||||
engine.
|
engine.
|
||||||
|
|
||||||
.. show-options:: senlin.config
|
.. show-options:: senlin.conf
|
||||||
|
|
||||||
Options
|
Options
|
||||||
=======
|
=======
|
||||||
|
|
||||||
.. currentmodule:: senlin.common.config
|
.. currentmodule:: senlin.conf.opts
|
||||||
|
|
||||||
.. autofunction:: list_opts
|
.. autofunction:: list_opts
|
|
@ -51,58 +51,6 @@ DEFAULT_API_VERSION = '1.0'
|
||||||
API_VERSION_KEY = 'OpenStack-API-Version'
|
API_VERSION_KEY = 'OpenStack-API-Version'
|
||||||
VER_METHOD_ATTR = 'versioned_methods'
|
VER_METHOD_ATTR = 'versioned_methods'
|
||||||
|
|
||||||
# senlin_api, api opts
|
|
||||||
api_opts = [
|
|
||||||
cfg.IPOpt('bind_host', default='0.0.0.0',
|
|
||||||
help=_('Address to bind the server. Useful when '
|
|
||||||
'selecting a particular network interface.')),
|
|
||||||
cfg.PortOpt('bind_port', default=8778,
|
|
||||||
help=_('The port on which the server will listen.')),
|
|
||||||
cfg.IntOpt('backlog', default=4096,
|
|
||||||
help=_("Number of backlog requests "
|
|
||||||
"to configure the socket with.")),
|
|
||||||
cfg.StrOpt('cert_file',
|
|
||||||
help=_("Location of the SSL certificate file "
|
|
||||||
"to use for SSL mode.")),
|
|
||||||
cfg.StrOpt('key_file',
|
|
||||||
help=_("Location of the SSL key file to use "
|
|
||||||
"for enabling SSL mode.")),
|
|
||||||
cfg.IntOpt('workers', min=0, default=0,
|
|
||||||
help=_("Number of workers for Senlin service.")),
|
|
||||||
cfg.IntOpt('max_header_line', default=16384,
|
|
||||||
help=_('Maximum line size of message headers to be accepted. '
|
|
||||||
'max_header_line may need to be increased when using '
|
|
||||||
'large tokens (typically those generated by the '
|
|
||||||
'Keystone v3 API with big service catalogs).')),
|
|
||||||
cfg.IntOpt('tcp_keepidle', default=600,
|
|
||||||
help=_('The value for the socket option TCP_KEEPIDLE. This is '
|
|
||||||
'the time in seconds that the connection must be idle '
|
|
||||||
'before TCP starts sending keepalive probes.')),
|
|
||||||
cfg.StrOpt('api_paste_config', default="api-paste.ini",
|
|
||||||
deprecated_group='paste_deploy',
|
|
||||||
help=_("The API paste config file to use.")),
|
|
||||||
cfg.BoolOpt('wsgi_keep_alive', default=True,
|
|
||||||
deprecated_group='eventlet_opts',
|
|
||||||
help=_("If false, closes the client socket explicitly.")),
|
|
||||||
cfg.IntOpt('client_socket_timeout', default=900,
|
|
||||||
deprecated_group='eventlet_opts',
|
|
||||||
help=_("Timeout for client connections' socket operations. "
|
|
||||||
"If an incoming connection is idle for this number of "
|
|
||||||
"seconds it will be closed. A value of '0' indicates "
|
|
||||||
"waiting forever.")),
|
|
||||||
cfg.IntOpt('max_json_body_size', default=1048576,
|
|
||||||
deprecated_group='DEFAULT',
|
|
||||||
help=_('Maximum raw byte size of JSON request body.'))
|
|
||||||
|
|
||||||
]
|
|
||||||
api_group = cfg.OptGroup('senlin_api')
|
|
||||||
cfg.CONF.register_group(api_group)
|
|
||||||
cfg.CONF.register_opts(api_opts, group=api_group)
|
|
||||||
|
|
||||||
|
|
||||||
def wsgi_opts():
|
|
||||||
yield api_group.name, api_opts
|
|
||||||
|
|
||||||
|
|
||||||
def get_bind_addr(conf, default_port=None):
|
def get_bind_addr(conf, default_port=None):
|
||||||
return (conf.bind_host, conf.bind_port or default_port)
|
return (conf.bind_host, conf.bind_port or default_port)
|
||||||
|
|
|
@ -17,7 +17,6 @@ Senlin API Server.
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_reports import guru_meditation_report as gmr
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
from oslo_service import systemd
|
from oslo_service import systemd
|
||||||
|
@ -27,31 +26,30 @@ from senlin.api.common import wsgi
|
||||||
from senlin.common import config
|
from senlin.common import config
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
|
import senlin.conf
|
||||||
from senlin import objects
|
from senlin import objects
|
||||||
from senlin import version
|
from senlin import version
|
||||||
|
|
||||||
|
CONF = senlin.conf.CONF
|
||||||
LOG = logging.getLogger('senlin.api')
|
LOG = logging.getLogger('senlin.api')
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
try:
|
try:
|
||||||
logging.register_options(cfg.CONF)
|
config.parse_args(sys.argv, 'senlin-api')
|
||||||
cfg.CONF(project='senlin', prog='senlin-api',
|
logging.setup(CONF, 'senlin-api')
|
||||||
version=version.version_info.version_string())
|
|
||||||
config.set_config_defaults()
|
|
||||||
logging.setup(cfg.CONF, 'senlin-api')
|
|
||||||
gmr.TextGuruMeditation.setup_autorun(version)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
objects.register_all()
|
objects.register_all()
|
||||||
messaging.setup()
|
messaging.setup()
|
||||||
|
|
||||||
app = wsgi.load_paste_app()
|
app = wsgi.load_paste_app()
|
||||||
|
|
||||||
host = cfg.CONF.senlin_api.bind_host
|
host = CONF.senlin_api.bind_host
|
||||||
port = cfg.CONF.senlin_api.bind_port
|
port = CONF.senlin_api.bind_port
|
||||||
LOG.info('Starting Senlin API on %(host)s:%(port)s',
|
LOG.info('Starting Senlin API on %(host)s:%(port)s',
|
||||||
{'host': host, 'port': port})
|
{'host': host, 'port': port})
|
||||||
profiler.setup('senlin-api', host)
|
profiler.setup('senlin-api', host)
|
||||||
server = wsgi.Server('senlin-api', cfg.CONF.senlin_api)
|
server = wsgi.Server('senlin-api', CONF.senlin_api)
|
||||||
server.start(app, default_port=port)
|
server.start(app, default_port=port)
|
||||||
systemd.notify_once()
|
systemd.notify_once()
|
||||||
server.wait()
|
server.wait()
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
Use this file for deploying senlin-api under Apache2(mode-wsgi).
|
Use this file for deploying senlin-api under Apache2(mode-wsgi).
|
||||||
"""
|
"""
|
||||||
|
import sys
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
@ -26,15 +26,11 @@ from senlin.common import config
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
from senlin import objects
|
from senlin import objects
|
||||||
from senlin import version
|
|
||||||
|
|
||||||
|
|
||||||
def init_app():
|
def init_app():
|
||||||
logging.register_options(cfg.CONF)
|
config.parse_args(sys.argv, 'senlin-api')
|
||||||
cfg.CONF(project='senlin', prog='senlin-api',
|
|
||||||
version=version.version_info.version_string())
|
|
||||||
logging.setup(cfg.CONF, 'senlin-api')
|
logging.setup(cfg.CONF, 'senlin-api')
|
||||||
config.set_config_defaults()
|
|
||||||
objects.register_all()
|
objects.register_all()
|
||||||
messaging.setup()
|
messaging.setup()
|
||||||
|
|
||||||
|
|
|
@ -15,22 +15,26 @@
|
||||||
"""
|
"""
|
||||||
Senlin Conductor.
|
Senlin Conductor.
|
||||||
"""
|
"""
|
||||||
from oslo_config import cfg
|
import sys
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_reports import guru_meditation_report as gmr
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
from oslo_service import service
|
from oslo_service import service
|
||||||
|
|
||||||
|
from senlin.common import config
|
||||||
from senlin.common import consts
|
from senlin.common import consts
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
|
import senlin.conf
|
||||||
from senlin import objects
|
from senlin import objects
|
||||||
from senlin import version
|
from senlin import version
|
||||||
|
|
||||||
|
CONF = senlin.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.register_options(cfg.CONF)
|
config.parse_args(sys.argv, 'senlin-conductor')
|
||||||
cfg.CONF(project='senlin', prog='senlin-conductor')
|
logging.setup(CONF, 'senlin-conductor')
|
||||||
logging.setup(cfg.CONF, 'senlin-conductor')
|
|
||||||
logging.set_defaults()
|
logging.set_defaults()
|
||||||
gmr.TextGuruMeditation.setup_autorun(version)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
objects.register_all()
|
objects.register_all()
|
||||||
|
@ -38,10 +42,10 @@ def main():
|
||||||
|
|
||||||
from senlin.conductor import service as conductor
|
from senlin.conductor import service as conductor
|
||||||
|
|
||||||
profiler.setup('senlin-conductor', cfg.CONF.host)
|
profiler.setup('senlin-conductor', CONF.host)
|
||||||
srv = conductor.ConductorService(cfg.CONF.host, consts.CONDUCTOR_TOPIC)
|
srv = conductor.ConductorService(CONF.host, consts.CONDUCTOR_TOPIC)
|
||||||
launcher = service.launch(cfg.CONF, srv,
|
launcher = service.launch(CONF, srv,
|
||||||
workers=cfg.CONF.conductor.workers,
|
workers=CONF.conductor.workers,
|
||||||
restart_method='mutate')
|
restart_method='mutate')
|
||||||
# the following periodic tasks are intended serve as HA checking
|
# the following periodic tasks are intended serve as HA checking
|
||||||
# srv.create_periodic_tasks()
|
# srv.create_periodic_tasks()
|
||||||
|
|
|
@ -15,22 +15,26 @@
|
||||||
"""
|
"""
|
||||||
Senlin Engine.
|
Senlin Engine.
|
||||||
"""
|
"""
|
||||||
from oslo_config import cfg
|
import sys
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_reports import guru_meditation_report as gmr
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
from oslo_service import service
|
from oslo_service import service
|
||||||
|
|
||||||
|
from senlin.common import config
|
||||||
from senlin.common import consts
|
from senlin.common import consts
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
|
import senlin.conf
|
||||||
from senlin import objects
|
from senlin import objects
|
||||||
from senlin import version
|
from senlin import version
|
||||||
|
|
||||||
|
CONF = senlin.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.register_options(cfg.CONF)
|
config.parse_args(sys.argv, 'senlin-engine')
|
||||||
cfg.CONF(project='senlin', prog='senlin-engine')
|
logging.setup(CONF, 'senlin-engine')
|
||||||
logging.setup(cfg.CONF, 'senlin-engine')
|
|
||||||
logging.set_defaults()
|
logging.set_defaults()
|
||||||
gmr.TextGuruMeditation.setup_autorun(version)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
objects.register_all()
|
objects.register_all()
|
||||||
|
@ -38,10 +42,10 @@ def main():
|
||||||
|
|
||||||
from senlin.engine import service as engine
|
from senlin.engine import service as engine
|
||||||
|
|
||||||
profiler.setup('senlin-engine', cfg.CONF.host)
|
profiler.setup('senlin-engine', CONF.host)
|
||||||
srv = engine.EngineService(cfg.CONF.host,
|
srv = engine.EngineService(CONF.host,
|
||||||
consts.ENGINE_TOPIC)
|
consts.ENGINE_TOPIC)
|
||||||
launcher = service.launch(cfg.CONF, srv,
|
launcher = service.launch(CONF, srv,
|
||||||
workers=cfg.CONF.engine.workers,
|
workers=CONF.engine.workers,
|
||||||
restart_method='mutate')
|
restart_method='mutate')
|
||||||
launcher.wait()
|
launcher.wait()
|
||||||
|
|
|
@ -15,22 +15,26 @@
|
||||||
"""
|
"""
|
||||||
Senlin Health-Manager.
|
Senlin Health-Manager.
|
||||||
"""
|
"""
|
||||||
from oslo_config import cfg
|
import sys
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_reports import guru_meditation_report as gmr
|
from oslo_reports import guru_meditation_report as gmr
|
||||||
from oslo_service import service
|
from oslo_service import service
|
||||||
|
|
||||||
|
from senlin.common import config
|
||||||
from senlin.common import consts
|
from senlin.common import consts
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
|
import senlin.conf
|
||||||
from senlin import objects
|
from senlin import objects
|
||||||
from senlin import version
|
from senlin import version
|
||||||
|
|
||||||
|
CONF = senlin.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.register_options(cfg.CONF)
|
config.parse_args(sys.argv, 'senlin-health-manager')
|
||||||
cfg.CONF(project='senlin', prog='senlin-health-manager')
|
logging.setup(CONF, 'senlin-health-manager')
|
||||||
logging.setup(cfg.CONF, 'senlin-health-manager')
|
|
||||||
logging.set_defaults()
|
logging.set_defaults()
|
||||||
gmr.TextGuruMeditation.setup_autorun(version)
|
gmr.TextGuruMeditation.setup_autorun(version)
|
||||||
objects.register_all()
|
objects.register_all()
|
||||||
|
@ -38,10 +42,10 @@ def main():
|
||||||
|
|
||||||
from senlin.health_manager import service as health_manager
|
from senlin.health_manager import service as health_manager
|
||||||
|
|
||||||
profiler.setup('senlin-health-manager', cfg.CONF.host)
|
profiler.setup('senlin-health-manager', CONF.host)
|
||||||
srv = health_manager.HealthManagerService(cfg.CONF.host,
|
srv = health_manager.HealthManagerService(CONF.host,
|
||||||
consts.HEALTH_MANAGER_TOPIC)
|
consts.HEALTH_MANAGER_TOPIC)
|
||||||
launcher = service.launch(cfg.CONF, srv,
|
launcher = service.launch(CONF, srv,
|
||||||
workers=cfg.CONF.health_manager.workers,
|
workers=CONF.health_manager.workers,
|
||||||
restart_method='mutate')
|
restart_method='mutate')
|
||||||
launcher.wait()
|
launcher.wait()
|
||||||
|
|
|
@ -20,11 +20,11 @@ from oslo_config import cfg
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_utils import timeutils
|
from oslo_utils import timeutils
|
||||||
|
|
||||||
|
from senlin.common import config
|
||||||
from senlin.common import context
|
from senlin.common import context
|
||||||
from senlin.common.i18n import _
|
from senlin.common.i18n import _
|
||||||
from senlin.db import api
|
from senlin.db import api
|
||||||
from senlin.objects import service as service_obj
|
from senlin.objects import service as service_obj
|
||||||
from senlin import version
|
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class ServiceManageCommand(object):
|
||||||
return
|
return
|
||||||
|
|
||||||
status = 'up'
|
status = 'up'
|
||||||
CONF.import_opt('periodic_interval', 'senlin.common.config')
|
CONF.import_opt('periodic_interval', 'senlin.conf')
|
||||||
max_interval = 2 * CONF.periodic_interval
|
max_interval = 2 * CONF.periodic_interval
|
||||||
if timeutils.is_older_than(service.updated_at, max_interval):
|
if timeutils.is_older_than(service.updated_at, max_interval):
|
||||||
status = 'down'
|
status = 'down'
|
||||||
|
@ -218,15 +218,11 @@ command_opt = cfg.SubCommandOpt('command',
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
logging.register_options(CONF)
|
|
||||||
logging.setup(CONF, 'senlin-manage')
|
|
||||||
CONF.register_cli_opt(command_opt)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
default_config_files = cfg.find_config_files('senlin', 'senlin-engine')
|
CONF.register_cli_opt(command_opt)
|
||||||
CONF(sys.argv[1:], project='senlin', prog='senlin-manage',
|
default_config_files = cfg.find_config_files('senlin', 'senlin-manage')
|
||||||
version=version.version_info.version_string(),
|
config.parse_args(sys.argv, 'senlin-manage', default_config_files)
|
||||||
default_config_files=default_config_files)
|
logging.setup(CONF, 'senlin-manage')
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
sys.exit("ERROR: %s" % e)
|
sys.exit("ERROR: %s" % e)
|
||||||
|
|
||||||
|
|
|
@ -13,318 +13,33 @@
|
||||||
"""
|
"""
|
||||||
Routines for configuring Senlin
|
Routines for configuring Senlin
|
||||||
"""
|
"""
|
||||||
import socket
|
from oslo_log import log
|
||||||
|
|
||||||
from keystoneauth1 import loading as ks_loading
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_middleware import cors
|
from oslo_middleware import cors
|
||||||
from osprofiler import opts as profiler
|
from oslo_utils import importutils
|
||||||
|
|
||||||
from senlin.api.common import wsgi
|
import senlin.conf
|
||||||
from senlin.common.i18n import _
|
from senlin import version
|
||||||
|
|
||||||
|
profiler = importutils.try_import('osprofiler.opts')
|
||||||
|
|
||||||
|
CONF = senlin.conf.CONF
|
||||||
|
|
||||||
|
|
||||||
# DEFAULT, service
|
def parse_args(argv, name, default_config_files=None):
|
||||||
service_opts = [
|
log.register_options(CONF)
|
||||||
cfg.StrOpt('default_region_name',
|
|
||||||
help=_('Default region name used to get services endpoints.')),
|
|
||||||
cfg.IntOpt('max_response_size',
|
|
||||||
default=524288,
|
|
||||||
help=_('Maximum raw byte size of data from web response.'))
|
|
||||||
]
|
|
||||||
|
|
||||||
cfg.CONF.register_opts(service_opts)
|
if profiler:
|
||||||
|
profiler.set_defaults(CONF)
|
||||||
|
|
||||||
# DEFAULT, engine
|
set_config_defaults()
|
||||||
default_engine_opts = [
|
|
||||||
cfg.IntOpt('periodic_interval',
|
|
||||||
default=60,
|
|
||||||
help=_('Seconds between running periodic tasks.')),
|
|
||||||
cfg.IntOpt('periodic_interval_max',
|
|
||||||
default=120,
|
|
||||||
help=_('Maximum seconds between periodic tasks to be called.')),
|
|
||||||
cfg.IntOpt('check_interval_max',
|
|
||||||
default=3600,
|
|
||||||
help=_('Maximum seconds between cluster check to be called.')),
|
|
||||||
cfg.IntOpt('health_check_interval_min',
|
|
||||||
default=60,
|
|
||||||
help=_('Minimum seconds between health check to be called.')),
|
|
||||||
cfg.IntOpt('periodic_fuzzy_delay',
|
|
||||||
default=10,
|
|
||||||
help=_('Range of seconds to randomly delay when starting the '
|
|
||||||
'periodic task scheduler to reduce stampeding. '
|
|
||||||
'(Disable by setting to 0)')),
|
|
||||||
cfg.StrOpt('environment_dir',
|
|
||||||
default='/etc/senlin/environments',
|
|
||||||
help=_('The directory to search for environment files.')),
|
|
||||||
cfg.IntOpt('max_nodes_per_cluster',
|
|
||||||
default=1000,
|
|
||||||
help=_('Maximum nodes allowed per top-level cluster.')),
|
|
||||||
cfg.IntOpt('max_clusters_per_project',
|
|
||||||
default=100,
|
|
||||||
help=_('Maximum number of clusters any one project may have'
|
|
||||||
' active at one time.')),
|
|
||||||
cfg.IntOpt('default_action_timeout',
|
|
||||||
default=3600,
|
|
||||||
help=_('Timeout in seconds for actions.')),
|
|
||||||
cfg.IntOpt('default_nova_timeout',
|
|
||||||
default=600,
|
|
||||||
help=_('Timeout in seconds for nova API calls.')),
|
|
||||||
cfg.IntOpt('max_actions_per_batch',
|
|
||||||
default=0,
|
|
||||||
help=_('Maximum number of node actions that each engine worker '
|
|
||||||
'can schedule consecutively per batch. 0 means no '
|
|
||||||
'limit.')),
|
|
||||||
cfg.IntOpt('batch_interval',
|
|
||||||
default=3,
|
|
||||||
help=_('Seconds to pause between scheduling two consecutive '
|
|
||||||
'batches of node actions.')),
|
|
||||||
cfg.IntOpt('lock_retry_times',
|
|
||||||
default=3,
|
|
||||||
help=_('Number of times trying to grab a lock.')),
|
|
||||||
cfg.IntOpt('lock_retry_interval',
|
|
||||||
default=10,
|
|
||||||
help=_('Number of seconds between lock retries.')),
|
|
||||||
cfg.IntOpt('database_retry_limit',
|
|
||||||
default=10,
|
|
||||||
help=_('Number of times retrying a failed operation on the '
|
|
||||||
'database.')),
|
|
||||||
cfg.IntOpt('database_retry_interval',
|
|
||||||
default=0.3,
|
|
||||||
help=_('Initial number of seconds between database retries.')),
|
|
||||||
cfg.IntOpt('database_max_retry_interval',
|
|
||||||
default=2,
|
|
||||||
help=_('Maximum number of seconds between database retries.')),
|
|
||||||
cfg.IntOpt('engine_life_check_timeout',
|
|
||||||
default=2,
|
|
||||||
help=_('RPC timeout for the engine liveness check that is used'
|
|
||||||
' for cluster locking.')),
|
|
||||||
cfg.BoolOpt('name_unique',
|
|
||||||
default=False,
|
|
||||||
help=_('Flag to indicate whether to enforce unique names for '
|
|
||||||
'Senlin objects belonging to the same project.')),
|
|
||||||
cfg.IntOpt('service_down_time',
|
|
||||||
default=60,
|
|
||||||
help=_('Maximum time since last check-in for a service to be '
|
|
||||||
'considered up.')),
|
|
||||||
cfg.ListOpt('trust_roles',
|
|
||||||
default=[],
|
|
||||||
help=_('The roles which are delegated to the trustee by the '
|
|
||||||
'trustor when a cluster is created.')),
|
|
||||||
]
|
|
||||||
cfg.CONF.register_opts(default_engine_opts)
|
|
||||||
|
|
||||||
# DEFAULT, host
|
CONF(
|
||||||
rpc_opts = [
|
argv[1:],
|
||||||
cfg.HostAddressOpt('host',
|
project='senlin',
|
||||||
default=socket.gethostname(),
|
prog=name,
|
||||||
help=_('Name of the engine node. This can be an opaque '
|
version=version.version_info.version_string(),
|
||||||
'identifier. It is not necessarily a hostname, '
|
default_config_files=default_config_files,
|
||||||
'FQDN or IP address.'))
|
)
|
||||||
]
|
|
||||||
cfg.CONF.register_opts(rpc_opts)
|
|
||||||
|
|
||||||
# DEFAULT, cloud_backend
|
|
||||||
cloud_backend_opts = [
|
|
||||||
cfg.StrOpt('cloud_backend', default='openstack',
|
|
||||||
choices=("openstack", "openstack_test"),
|
|
||||||
help=_('Default cloud backend to use.'))]
|
|
||||||
cfg.CONF.register_opts(cloud_backend_opts)
|
|
||||||
|
|
||||||
# DEFAULT, event dispatchers
|
|
||||||
event_opts = [
|
|
||||||
cfg.MultiStrOpt("event_dispatchers", default=['database'],
|
|
||||||
help=_("Event dispatchers to enable."))]
|
|
||||||
cfg.CONF.register_opts(event_opts)
|
|
||||||
|
|
||||||
# Dispatcher section
|
|
||||||
dispatcher_group = cfg.OptGroup('dispatchers')
|
|
||||||
dispatcher_opts = [
|
|
||||||
cfg.StrOpt('priority', default='info',
|
|
||||||
choices=("critical", "error", "warning", "info", "debug"),
|
|
||||||
help=_("Lowest event priorities to be dispatched.")),
|
|
||||||
cfg.BoolOpt("exclude_derived_actions", default=True,
|
|
||||||
help=_("Exclude derived actions from events dumping.")),
|
|
||||||
]
|
|
||||||
|
|
||||||
cfg.CONF.register_group(dispatcher_group)
|
|
||||||
cfg.CONF.register_opts(dispatcher_opts, group=dispatcher_group)
|
|
||||||
|
|
||||||
# Authentication section
|
|
||||||
authentication_group = cfg.OptGroup('authentication')
|
|
||||||
authentication_opts = [
|
|
||||||
cfg.StrOpt('auth_url', default='',
|
|
||||||
help=_('Complete public identity V3 API endpoint.')),
|
|
||||||
cfg.StrOpt('service_username', default='senlin',
|
|
||||||
help=_('Senlin service user name.')),
|
|
||||||
cfg.StrOpt('service_password', default='', secret=True,
|
|
||||||
help=_('Password specified for the Senlin service user.')),
|
|
||||||
cfg.StrOpt('service_project_name', default='service',
|
|
||||||
help=_('Name of the service project.')),
|
|
||||||
cfg.StrOpt('service_user_domain', default='Default',
|
|
||||||
help=_('Name of the domain for the service user.')),
|
|
||||||
cfg.StrOpt('service_project_domain', default='Default',
|
|
||||||
help=_('Name of the domain for the service project.')),
|
|
||||||
]
|
|
||||||
cfg.CONF.register_group(authentication_group)
|
|
||||||
cfg.CONF.register_opts(authentication_opts, group=authentication_group)
|
|
||||||
|
|
||||||
# Conductor group
|
|
||||||
conductor_group = cfg.OptGroup('conductor')
|
|
||||||
conductor_opts = [
|
|
||||||
cfg.IntOpt('workers',
|
|
||||||
default=1,
|
|
||||||
help=_('Number of senlin-conductor processes.')),
|
|
||||||
cfg.IntOpt('threads',
|
|
||||||
default=1000,
|
|
||||||
help=_('Number of senlin-conductor threads.')),
|
|
||||||
]
|
|
||||||
cfg.CONF.register_group(conductor_group)
|
|
||||||
cfg.CONF.register_opts(conductor_opts, group=conductor_group)
|
|
||||||
|
|
||||||
# Engine group
|
|
||||||
engine_group = cfg.OptGroup('engine')
|
|
||||||
engine_opts = [
|
|
||||||
cfg.IntOpt('workers',
|
|
||||||
default=1,
|
|
||||||
deprecated_name='num_engine_workers',
|
|
||||||
deprecated_group="DEFAULT",
|
|
||||||
help=_('Number of senlin-engine processes.')),
|
|
||||||
cfg.IntOpt('threads',
|
|
||||||
default=1000,
|
|
||||||
deprecated_name='scheduler_thread_pool_size',
|
|
||||||
deprecated_group="DEFAULT",
|
|
||||||
help=_('Number of senlin-engine threads.')),
|
|
||||||
]
|
|
||||||
cfg.CONF.register_group(engine_group)
|
|
||||||
cfg.CONF.register_opts(engine_opts, group=engine_group)
|
|
||||||
|
|
||||||
# Health Manager Group
|
|
||||||
healthmgr_group = cfg.OptGroup('health_manager')
|
|
||||||
healthmgr_opts = [
|
|
||||||
cfg.StrOpt('nova_control_exchange', default='nova',
|
|
||||||
help=_("Exchange name for nova notifications.")),
|
|
||||||
cfg.StrOpt('nova_notification_topic', default='versioned_notifications',
|
|
||||||
help=_("Topic name for nova notifications.")),
|
|
||||||
cfg.StrOpt('heat_control_exchange', default='heat',
|
|
||||||
help=_("Exchange name for heat notifications.")),
|
|
||||||
cfg.StrOpt('heat_notification_topic', default='notifications',
|
|
||||||
help=_("Topic name for heat notifications.")),
|
|
||||||
cfg.MultiStrOpt("enabled_endpoints", default=['nova', 'heat'],
|
|
||||||
help=_("Notification endpoints to enable.")),
|
|
||||||
cfg.IntOpt('workers',
|
|
||||||
default=1,
|
|
||||||
help=_('Number of senlin-health-manager processes.')),
|
|
||||||
cfg.IntOpt('threads',
|
|
||||||
default=1000,
|
|
||||||
deprecated_name='health_manager_thread_pool_size',
|
|
||||||
deprecated_group="DEFAULT",
|
|
||||||
help=_('Number of senlin-health-manager threads.')),
|
|
||||||
]
|
|
||||||
cfg.CONF.register_group(healthmgr_group)
|
|
||||||
cfg.CONF.register_opts(healthmgr_opts, group=healthmgr_group)
|
|
||||||
|
|
||||||
# Revision group
|
|
||||||
revision_group = cfg.OptGroup('revision')
|
|
||||||
revision_opts = [
|
|
||||||
cfg.StrOpt('senlin_api_revision', default='1.0',
|
|
||||||
help=_('Senlin API revision.')),
|
|
||||||
cfg.StrOpt('senlin_engine_revision', default='1.0',
|
|
||||||
help=_('Senlin engine revision.'))
|
|
||||||
]
|
|
||||||
cfg.CONF.register_group(revision_group)
|
|
||||||
cfg.CONF.register_opts(revision_opts, group=revision_group)
|
|
||||||
|
|
||||||
# Receiver group
|
|
||||||
receiver_group = cfg.OptGroup('receiver')
|
|
||||||
receiver_opts = [
|
|
||||||
cfg.StrOpt('host', deprecated_group='webhook',
|
|
||||||
help=_('The address for notifying and triggering receivers. '
|
|
||||||
'It is useful for case Senlin API service is running '
|
|
||||||
'behind a proxy.')),
|
|
||||||
cfg.PortOpt('port', default=8778, deprecated_group='webhook',
|
|
||||||
help=_('The port for notifying and triggering receivers. '
|
|
||||||
'It is useful for case Senlin API service is running '
|
|
||||||
'behind a proxy.')),
|
|
||||||
cfg.IntOpt('max_message_size', default=65535,
|
|
||||||
help=_('The max size(bytes) of message can be posted to '
|
|
||||||
'receiver queue.'))
|
|
||||||
]
|
|
||||||
cfg.CONF.register_group(receiver_group)
|
|
||||||
cfg.CONF.register_opts(receiver_opts, group=receiver_group)
|
|
||||||
|
|
||||||
# Notification group
|
|
||||||
notification_group = cfg.OptGroup('notification')
|
|
||||||
notification_opts = [
|
|
||||||
cfg.IntOpt('max_message_size', default=65535,
|
|
||||||
help=_('The max size(bytes) of message can be posted to '
|
|
||||||
'notification queue.')),
|
|
||||||
cfg.IntOpt('ttl', default=300,
|
|
||||||
help=_('The ttl in seconds of a message posted to '
|
|
||||||
'notification queue.'))
|
|
||||||
]
|
|
||||||
cfg.CONF.register_group(notification_group)
|
|
||||||
cfg.CONF.register_opts(notification_opts, group=notification_group)
|
|
||||||
|
|
||||||
# Notification topic
|
|
||||||
notification_topic_opts = [
|
|
||||||
cfg.ListOpt('notification_topics',
|
|
||||||
default=['versioned_notifications'],
|
|
||||||
help=_('Default notification topic.'))]
|
|
||||||
cfg.CONF.register_opts(notification_topic_opts)
|
|
||||||
|
|
||||||
# Zaqar group
|
|
||||||
zaqar_group = cfg.OptGroup(
|
|
||||||
'zaqar', title='Zaqar Options',
|
|
||||||
help=_('Configuration options for zaqar trustee.'))
|
|
||||||
|
|
||||||
zaqar_opts = (
|
|
||||||
ks_loading.get_auth_common_conf_options() +
|
|
||||||
ks_loading.get_auth_plugin_conf_options('password'))
|
|
||||||
|
|
||||||
cfg.CONF.register_group(zaqar_group)
|
|
||||||
ks_loading.register_session_conf_options(cfg.CONF, 'zaqar')
|
|
||||||
ks_loading.register_auth_conf_options(cfg.CONF, 'zaqar')
|
|
||||||
|
|
||||||
# OSProfiler
|
|
||||||
group, opts = profiler.list_opts()[0]
|
|
||||||
cfg.CONF.register_opts(opts, group=group)
|
|
||||||
|
|
||||||
|
|
||||||
def list_opts():
|
|
||||||
"""Return a list of oslo.config options available.
|
|
||||||
|
|
||||||
The purpose of this function is to allow tools like the Oslo sample config
|
|
||||||
file generator to discover the options exposed to users by this service.
|
|
||||||
The returned list includes all oslo.config options which may be registered
|
|
||||||
at runtime by the service api/engine.
|
|
||||||
|
|
||||||
Each element of the list is a tuple. The first element is the name of the
|
|
||||||
group under which the list of elements in the second element will be
|
|
||||||
registered. A group name of None corresponds to the [DEFAULT] group in
|
|
||||||
config files.
|
|
||||||
|
|
||||||
This function is also discoverable via the 'senlin.config' entry point
|
|
||||||
under the 'oslo.config.opts' namespace.
|
|
||||||
|
|
||||||
:returns: a list of (group_name, opts) tuples
|
|
||||||
"""
|
|
||||||
for g, o in wsgi.wsgi_opts():
|
|
||||||
yield g, o
|
|
||||||
yield 'DEFAULT', cloud_backend_opts
|
|
||||||
yield 'DEFAULT', rpc_opts
|
|
||||||
yield 'DEFAULT', default_engine_opts
|
|
||||||
yield 'DEFAULT', service_opts
|
|
||||||
yield 'DEFAULT', event_opts
|
|
||||||
yield authentication_group.name, authentication_opts
|
|
||||||
yield conductor_group.name, conductor_opts
|
|
||||||
yield dispatcher_group.name, dispatcher_opts
|
|
||||||
yield engine_group.name, engine_opts
|
|
||||||
yield healthmgr_group.name, healthmgr_opts
|
|
||||||
yield revision_group.name, revision_opts
|
|
||||||
yield receiver_group.name, receiver_opts
|
|
||||||
yield zaqar_group.name, zaqar_opts
|
|
||||||
yield profiler.list_opts()[0]
|
|
||||||
|
|
||||||
|
|
||||||
def set_config_defaults():
|
def set_config_defaults():
|
||||||
|
|
|
@ -20,7 +20,7 @@ import osprofiler.web
|
||||||
from senlin.common import context
|
from senlin.common import context
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
|
|
||||||
cfg.CONF.import_opt('enabled', 'senlin.common.config', group='profiler')
|
cfg.CONF.import_opt('enabled', 'senlin.conf', group='profiler')
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,17 @@
|
||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_service import service
|
from oslo_service import service
|
||||||
|
|
||||||
|
from oslo_service import sslutils
|
||||||
|
from oslo_service import wsgi
|
||||||
|
from oslo_utils import netutils
|
||||||
|
|
||||||
|
import senlin.conf
|
||||||
from senlin import version
|
from senlin import version
|
||||||
|
|
||||||
|
CONF = senlin.conf.CONF
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,3 +45,42 @@ class Service(service.Service):
|
||||||
def stop(self, graceful=True):
|
def stop(self, graceful=True):
|
||||||
LOG.info('Stopping %(name)s service', {'name': self.name})
|
LOG.info('Stopping %(name)s service', {'name': self.name})
|
||||||
super(Service, self).stop(graceful)
|
super(Service, self).stop(graceful)
|
||||||
|
|
||||||
|
|
||||||
|
class WSGIService(service.Service):
|
||||||
|
def __init__(self, app, name, listen, max_url_len=None):
|
||||||
|
super(WSGIService, self).__init__(CONF.senlin_api.threads)
|
||||||
|
self.app = app
|
||||||
|
self.name = name
|
||||||
|
|
||||||
|
self.listen = listen
|
||||||
|
|
||||||
|
self.servers = []
|
||||||
|
|
||||||
|
for address in self.listen:
|
||||||
|
host, port = netutils.parse_host_port(address)
|
||||||
|
server = wsgi.Server(
|
||||||
|
CONF, name, app,
|
||||||
|
host=host,
|
||||||
|
port=port,
|
||||||
|
pool_size=CONF.senlin_api.threads,
|
||||||
|
use_ssl=sslutils.is_enabled(CONF),
|
||||||
|
max_url_len=max_url_len
|
||||||
|
)
|
||||||
|
|
||||||
|
self.servers.append(server)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
for server in self.servers:
|
||||||
|
server.start()
|
||||||
|
super(WSGIService, self).start()
|
||||||
|
|
||||||
|
def stop(self, graceful=True):
|
||||||
|
for server in self.servers:
|
||||||
|
server.stop()
|
||||||
|
super(WSGIService, self).stop(graceful)
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
for server in self.servers:
|
||||||
|
server.wait()
|
||||||
|
super(WSGIService, self).wait()
|
||||||
|
|
|
@ -32,8 +32,8 @@ from senlin.common import exception
|
||||||
from senlin.common.i18n import _
|
from senlin.common.i18n import _
|
||||||
from senlin.objects import service as service_obj
|
from senlin.objects import service as service_obj
|
||||||
|
|
||||||
cfg.CONF.import_opt('max_response_size', 'senlin.common.config')
|
cfg.CONF.import_opt('max_response_size', 'senlin.conf')
|
||||||
cfg.CONF.import_opt('periodic_interval', 'senlin.common.config')
|
cfg.CONF.import_opt('periodic_interval', 'senlin.conf')
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
|
_ISO8601_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.conf import api
|
||||||
|
from senlin.conf import authentication
|
||||||
|
from senlin.conf import base
|
||||||
|
from senlin.conf import conductor
|
||||||
|
from senlin.conf import dispatchers
|
||||||
|
from senlin.conf import engine
|
||||||
|
from senlin.conf import health_manager
|
||||||
|
from senlin.conf import notification
|
||||||
|
from senlin.conf import receiver
|
||||||
|
from senlin.conf import revision
|
||||||
|
from senlin.conf import zaqar
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
|
||||||
|
api.register_opts(CONF)
|
||||||
|
authentication.register_opts(CONF)
|
||||||
|
base.register_opts(CONF)
|
||||||
|
conductor.register_opts(CONF)
|
||||||
|
dispatchers.register_opts(CONF)
|
||||||
|
engine.register_opts(CONF)
|
||||||
|
health_manager.register_opts(CONF)
|
||||||
|
notification.register_opts(CONF)
|
||||||
|
receiver.register_opts(CONF)
|
||||||
|
revision.register_opts(CONF)
|
||||||
|
zaqar.register_opts(CONF)
|
|
@ -0,0 +1,70 @@
|
||||||
|
# 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.
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
API_GROUP = cfg.OptGroup('senlin_api')
|
||||||
|
API_OPTS = [
|
||||||
|
cfg.IPOpt('bind_host', default='0.0.0.0',
|
||||||
|
help=_('Address to bind the server. Useful when '
|
||||||
|
'selecting a particular network interface.')),
|
||||||
|
cfg.PortOpt('bind_port', default=8778,
|
||||||
|
help=_('The port on which the server will listen.')),
|
||||||
|
cfg.IntOpt('backlog', default=4096,
|
||||||
|
help=_("Number of backlog requests "
|
||||||
|
"to configure the socket with.")),
|
||||||
|
cfg.StrOpt('cert_file',
|
||||||
|
help=_("Location of the SSL certificate file "
|
||||||
|
"to use for SSL mode.")),
|
||||||
|
cfg.StrOpt('key_file',
|
||||||
|
help=_("Location of the SSL key file to use "
|
||||||
|
"for enabling SSL mode.")),
|
||||||
|
cfg.IntOpt('workers', min=0, default=0,
|
||||||
|
help=_("Number of workers for Senlin service.")),
|
||||||
|
cfg.IntOpt('max_header_line', default=16384,
|
||||||
|
help=_('Maximum line size of message headers to be accepted. '
|
||||||
|
'max_header_line may need to be increased when using '
|
||||||
|
'large tokens (typically those generated by the '
|
||||||
|
'Keystone v3 API with big service catalogs).')),
|
||||||
|
cfg.IntOpt('tcp_keepidle', default=600,
|
||||||
|
help=_('The value for the socket option TCP_KEEPIDLE. This is '
|
||||||
|
'the time in seconds that the connection must be idle '
|
||||||
|
'before TCP starts sending keepalive probes.')),
|
||||||
|
cfg.StrOpt('api_paste_config', default="api-paste.ini",
|
||||||
|
deprecated_group='paste_deploy',
|
||||||
|
help=_("The API paste config file to use.")),
|
||||||
|
cfg.BoolOpt('wsgi_keep_alive', default=True,
|
||||||
|
deprecated_group='eventlet_opts',
|
||||||
|
help=_("If false, closes the client socket explicitly.")),
|
||||||
|
cfg.IntOpt('client_socket_timeout', default=900,
|
||||||
|
deprecated_group='eventlet_opts',
|
||||||
|
help=_("Timeout for client connections' socket operations. "
|
||||||
|
"If an incoming connection is idle for this number of "
|
||||||
|
"seconds it will be closed. A value of '0' indicates "
|
||||||
|
"waiting forever.")),
|
||||||
|
cfg.IntOpt('max_json_body_size', default=1048576,
|
||||||
|
deprecated_group='DEFAULT',
|
||||||
|
help=_('Maximum raw byte size of JSON request body.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(API_GROUP)
|
||||||
|
conf.register_opts(API_OPTS, group=API_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
API_GROUP: API_OPTS,
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
# 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.
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
AUTHENTICATION_GROUP = cfg.OptGroup('authentication')
|
||||||
|
AUTHENTICATION_OPTS = [
|
||||||
|
cfg.StrOpt('auth_url', default='',
|
||||||
|
help=_('Complete public identity V3 API endpoint.')),
|
||||||
|
cfg.StrOpt('service_username', default='senlin',
|
||||||
|
help=_('Senlin service user name.')),
|
||||||
|
cfg.StrOpt('service_password', default='', secret=True,
|
||||||
|
help=_('Password specified for the Senlin service user.')),
|
||||||
|
cfg.StrOpt('service_project_name', default='service',
|
||||||
|
help=_('Name of the service project.')),
|
||||||
|
cfg.StrOpt('service_user_domain', default='Default',
|
||||||
|
help=_('Name of the domain for the service user.')),
|
||||||
|
cfg.StrOpt('service_project_domain', default='Default',
|
||||||
|
help=_('Name of the domain for the service project.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(AUTHENTICATION_GROUP)
|
||||||
|
conf.register_opts(AUTHENTICATION_OPTS, group=AUTHENTICATION_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
AUTHENTICATION_GROUP: AUTHENTICATION_OPTS
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
# 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.
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
SENLIN_OPTS = [
|
||||||
|
cfg.HostAddressOpt('host',
|
||||||
|
default=socket.gethostname(),
|
||||||
|
help=_('Name of the engine node. This can be an opaque '
|
||||||
|
'identifier. It is not necessarily a hostname, '
|
||||||
|
'FQDN or IP address.')),
|
||||||
|
cfg.StrOpt('default_region_name',
|
||||||
|
help=_('Default region name used to get services endpoints.')),
|
||||||
|
cfg.IntOpt('max_response_size',
|
||||||
|
default=524288,
|
||||||
|
help=_('Maximum raw byte size of data from web response.')),
|
||||||
|
cfg.ListOpt('notification_topics',
|
||||||
|
default=['versioned_notifications'],
|
||||||
|
help=_('Default notification topic.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
ENGINE_OPTS = [
|
||||||
|
cfg.IntOpt('periodic_interval',
|
||||||
|
default=60,
|
||||||
|
help=_('Seconds between running periodic tasks.')),
|
||||||
|
cfg.IntOpt('periodic_interval_max',
|
||||||
|
default=120,
|
||||||
|
help=_('Maximum seconds between periodic tasks to be called.')),
|
||||||
|
cfg.IntOpt('check_interval_max',
|
||||||
|
default=3600,
|
||||||
|
help=_('Maximum seconds between cluster check to be called.')),
|
||||||
|
cfg.IntOpt('health_check_interval_min',
|
||||||
|
default=60,
|
||||||
|
help=_('Minimum seconds between health check to be called.')),
|
||||||
|
cfg.IntOpt('periodic_fuzzy_delay',
|
||||||
|
default=10,
|
||||||
|
help=_('Range of seconds to randomly delay when starting the '
|
||||||
|
'periodic task scheduler to reduce stampeding. '
|
||||||
|
'(Disable by setting to 0)')),
|
||||||
|
cfg.StrOpt('environment_dir',
|
||||||
|
default='/etc/senlin/environments',
|
||||||
|
help=_('The directory to search for environment files.')),
|
||||||
|
cfg.IntOpt('max_nodes_per_cluster',
|
||||||
|
default=1000,
|
||||||
|
help=_('Maximum nodes allowed per top-level cluster.')),
|
||||||
|
cfg.IntOpt('max_clusters_per_project',
|
||||||
|
default=100,
|
||||||
|
help=_('Maximum number of clusters any one project may have'
|
||||||
|
' active at one time.')),
|
||||||
|
cfg.IntOpt('default_action_timeout',
|
||||||
|
default=3600,
|
||||||
|
help=_('Timeout in seconds for actions.')),
|
||||||
|
cfg.IntOpt('default_nova_timeout',
|
||||||
|
default=600,
|
||||||
|
help=_('Timeout in seconds for nova API calls.')),
|
||||||
|
cfg.IntOpt('max_actions_per_batch',
|
||||||
|
default=0,
|
||||||
|
help=_('Maximum number of node actions that each engine worker '
|
||||||
|
'can schedule consecutively per batch. 0 means no '
|
||||||
|
'limit.')),
|
||||||
|
cfg.IntOpt('batch_interval',
|
||||||
|
default=3,
|
||||||
|
help=_('Seconds to pause between scheduling two consecutive '
|
||||||
|
'batches of node actions.')),
|
||||||
|
cfg.IntOpt('lock_retry_times',
|
||||||
|
default=3,
|
||||||
|
help=_('Number of times trying to grab a lock.')),
|
||||||
|
cfg.IntOpt('lock_retry_interval',
|
||||||
|
default=10,
|
||||||
|
help=_('Number of seconds between lock retries.')),
|
||||||
|
cfg.IntOpt('database_retry_limit',
|
||||||
|
default=10,
|
||||||
|
help=_('Number of times retrying a failed operation on the '
|
||||||
|
'database.')),
|
||||||
|
cfg.IntOpt('database_retry_interval',
|
||||||
|
default=0.3,
|
||||||
|
help=_('Initial number of seconds between database retries.')),
|
||||||
|
cfg.IntOpt('database_max_retry_interval',
|
||||||
|
default=2,
|
||||||
|
help=_('Maximum number of seconds between database retries.')),
|
||||||
|
cfg.IntOpt('engine_life_check_timeout',
|
||||||
|
default=2,
|
||||||
|
help=_('RPC timeout for the engine liveness check that is used'
|
||||||
|
' for cluster locking.')),
|
||||||
|
cfg.BoolOpt('name_unique',
|
||||||
|
default=False,
|
||||||
|
help=_('Flag to indicate whether to enforce unique names for '
|
||||||
|
'Senlin objects belonging to the same project.')),
|
||||||
|
cfg.IntOpt('service_down_time',
|
||||||
|
default=60,
|
||||||
|
help=_('Maximum time since last check-in for a service to be '
|
||||||
|
'considered up.')),
|
||||||
|
cfg.ListOpt('trust_roles',
|
||||||
|
default=[],
|
||||||
|
help=_('The roles which are delegated to the trustee by the '
|
||||||
|
'trustor when a cluster is created.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
CLOUD_BACKEND_OPTS = [
|
||||||
|
cfg.StrOpt('cloud_backend', default='openstack',
|
||||||
|
choices=("openstack", "openstack_test"),
|
||||||
|
help=_('Default cloud backend to use.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
EVENT_OPTS = [
|
||||||
|
cfg.MultiStrOpt("event_dispatchers", default=['database'],
|
||||||
|
help=_("Event dispatchers to enable.")),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_opts(SENLIN_OPTS)
|
||||||
|
conf.register_opts(ENGINE_OPTS)
|
||||||
|
conf.register_opts(CLOUD_BACKEND_OPTS)
|
||||||
|
conf.register_opts(EVENT_OPTS)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
'DEFAULT': SENLIN_OPTS + ENGINE_OPTS + CLOUD_BACKEND_OPTS + EVENT_OPTS
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
# 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.
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
CONDUCTOR_GROUP = cfg.OptGroup('conductor')
|
||||||
|
CONDUCTOR_OPTS = [
|
||||||
|
cfg.IntOpt('workers',
|
||||||
|
default=1,
|
||||||
|
help=_('Number of senlin-conductor processes.')),
|
||||||
|
cfg.IntOpt('threads',
|
||||||
|
default=1000,
|
||||||
|
help=_('Number of senlin-conductor threads.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(CONDUCTOR_GROUP)
|
||||||
|
conf.register_opts(CONDUCTOR_OPTS, group=CONDUCTOR_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
CONDUCTOR_GROUP: CONDUCTOR_OPTS,
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
# 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.
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
DISPATCHERS_GROUP = cfg.OptGroup('dispatchers')
|
||||||
|
DISPATCHERS_OPTS = [
|
||||||
|
cfg.StrOpt('priority', default='info',
|
||||||
|
choices=("critical", "error", "warning", "info", "debug"),
|
||||||
|
help=_("Lowest event priorities to be dispatched.")),
|
||||||
|
cfg.BoolOpt("exclude_derived_actions", default=True,
|
||||||
|
help=_("Exclude derived actions from events dumping.")),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(DISPATCHERS_GROUP)
|
||||||
|
conf.register_opts(DISPATCHERS_OPTS, group=DISPATCHERS_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
DISPATCHERS_GROUP: DISPATCHERS_OPTS
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
# 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.
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
|
||||||
|
ENGINE_GROUP = cfg.OptGroup('engine')
|
||||||
|
ENGINE_OPTS = [
|
||||||
|
cfg.IntOpt('workers',
|
||||||
|
default=1,
|
||||||
|
deprecated_name='num_engine_workers',
|
||||||
|
deprecated_group="DEFAULT",
|
||||||
|
help=_('Number of senlin-engine processes.')),
|
||||||
|
cfg.IntOpt('threads',
|
||||||
|
default=1000,
|
||||||
|
deprecated_name='scheduler_thread_pool_size',
|
||||||
|
deprecated_group="DEFAULT",
|
||||||
|
help=_('Number of senlin-engine threads.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(ENGINE_GROUP)
|
||||||
|
conf.register_opts(ENGINE_OPTS, group=ENGINE_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
ENGINE_GROUP: ENGINE_OPTS,
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
# 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.
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
HEALTH_MANAGER_GROUP = cfg.OptGroup('health_manager')
|
||||||
|
HEALTH_MANAGER_OPTS = [
|
||||||
|
cfg.StrOpt('nova_control_exchange', default='nova',
|
||||||
|
help=_("Exchange name for nova notifications.")),
|
||||||
|
cfg.StrOpt('nova_notification_topic', default='versioned_notifications',
|
||||||
|
help=_("Topic name for nova notifications.")),
|
||||||
|
cfg.StrOpt('heat_control_exchange', default='heat',
|
||||||
|
help=_("Exchange name for heat notifications.")),
|
||||||
|
cfg.StrOpt('heat_notification_topic', default='notifications',
|
||||||
|
help=_("Topic name for heat notifications.")),
|
||||||
|
cfg.MultiStrOpt("enabled_endpoints", default=['nova', 'heat'],
|
||||||
|
help=_("Notification endpoints to enable.")),
|
||||||
|
cfg.IntOpt('workers',
|
||||||
|
default=1,
|
||||||
|
help=_('Number of senlin-health-manager processes.')),
|
||||||
|
cfg.IntOpt('threads',
|
||||||
|
default=1000,
|
||||||
|
deprecated_name='health_manager_thread_pool_size',
|
||||||
|
deprecated_group="DEFAULT",
|
||||||
|
help=_('Number of senlin-health-manager threads.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(HEALTH_MANAGER_GROUP)
|
||||||
|
conf.register_opts(HEALTH_MANAGER_OPTS, group=HEALTH_MANAGER_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
HEALTH_MANAGER_GROUP: HEALTH_MANAGER_OPTS
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
NOTIFICATION_GROUP = cfg.OptGroup(
|
||||||
|
name='notification',
|
||||||
|
)
|
||||||
|
|
||||||
|
NOTIFICATION_OPTS = [
|
||||||
|
cfg.IntOpt('max_message_size', default=65535,
|
||||||
|
help=_('The max size(bytes) of message can be posted to '
|
||||||
|
'notification queue.')),
|
||||||
|
cfg.IntOpt('ttl', default=300,
|
||||||
|
help=_('The ttl in seconds of a message posted to '
|
||||||
|
'notification queue.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(NOTIFICATION_GROUP)
|
||||||
|
conf.register_opts(NOTIFICATION_OPTS, group=NOTIFICATION_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
NOTIFICATION_GROUP: NOTIFICATION_OPTS,
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
# Copyright 2015 OpenStack Foundation
|
||||||
|
# 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.
|
||||||
|
# Copied from nova
|
||||||
|
|
||||||
|
"""
|
||||||
|
This is the single point of entry to generate the sample configuration
|
||||||
|
file for Senlin. It collects all the necessary info from the other modules
|
||||||
|
in this package. It is assumed that:
|
||||||
|
|
||||||
|
* every other module in this package has a 'list_opts' function which
|
||||||
|
return a dict where
|
||||||
|
* the keys are strings which are the group names
|
||||||
|
* the value of each key is a list of config options for that group
|
||||||
|
* the senlin.conf package doesn't have further packages with config options
|
||||||
|
* this module is only used in the context of sample file generation
|
||||||
|
"""
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import importlib
|
||||||
|
import os
|
||||||
|
import pkgutil
|
||||||
|
|
||||||
|
LIST_OPTS_FUNC_NAME = "list_opts"
|
||||||
|
|
||||||
|
|
||||||
|
def _tupleize(dct):
|
||||||
|
"""Take the dict of options and convert to the 2-tuple format."""
|
||||||
|
return [(key, val) for key, val in dct.items()]
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
"""Return a list of oslo.config options available.
|
||||||
|
|
||||||
|
The purpose of this function is to allow tools like the Oslo sample config
|
||||||
|
file generator to discover the options exposed to users by this service.
|
||||||
|
The returned list includes all oslo.config options which may be registered
|
||||||
|
at runtime by the service api/engine.
|
||||||
|
|
||||||
|
This function is also discoverable via the 'senlin.conf' entry point
|
||||||
|
under the 'oslo.config.opts' namespace.
|
||||||
|
|
||||||
|
:returns: a list of (group_name, opts) tuples
|
||||||
|
"""
|
||||||
|
opts = collections.defaultdict(list)
|
||||||
|
module_names = _list_module_names()
|
||||||
|
imported_modules = _import_modules(module_names)
|
||||||
|
_append_config_options(imported_modules, opts)
|
||||||
|
return _tupleize(opts)
|
||||||
|
|
||||||
|
|
||||||
|
def _list_module_names():
|
||||||
|
module_names = []
|
||||||
|
package_path = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
for _, modname, ispkg in pkgutil.iter_modules(path=[package_path]):
|
||||||
|
if modname == "opts" or ispkg:
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
module_names.append(modname)
|
||||||
|
return module_names
|
||||||
|
|
||||||
|
|
||||||
|
def _import_modules(module_names):
|
||||||
|
imported_modules = []
|
||||||
|
for modname in module_names:
|
||||||
|
mod = importlib.import_module("senlin.conf." + modname)
|
||||||
|
if not hasattr(mod, LIST_OPTS_FUNC_NAME):
|
||||||
|
msg = ("The module 'senlin.conf.%s' should have a '%s' "
|
||||||
|
"function which returns the config options." %
|
||||||
|
(modname, LIST_OPTS_FUNC_NAME))
|
||||||
|
raise Exception(msg)
|
||||||
|
else:
|
||||||
|
imported_modules.append(mod)
|
||||||
|
return imported_modules
|
||||||
|
|
||||||
|
|
||||||
|
def _append_config_options(imported_modules, config_options):
|
||||||
|
for mod in imported_modules:
|
||||||
|
configs = mod.list_opts()
|
||||||
|
for key, val in configs.items():
|
||||||
|
config_options[key].extend(val)
|
|
@ -0,0 +1,44 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
RECEIVER_GROUP = cfg.OptGroup(
|
||||||
|
name='receiver',
|
||||||
|
)
|
||||||
|
|
||||||
|
RECEIVER_OPTS = [
|
||||||
|
cfg.StrOpt('host', deprecated_group='webhook',
|
||||||
|
help=_('The address for notifying and triggering receivers. '
|
||||||
|
'It is useful for case Senlin API service is running '
|
||||||
|
'behind a proxy.')),
|
||||||
|
cfg.PortOpt('port', default=8778, deprecated_group='webhook',
|
||||||
|
help=_('The port for notifying and triggering receivers. '
|
||||||
|
'It is useful for case Senlin API service is running '
|
||||||
|
'behind a proxy.')),
|
||||||
|
cfg.IntOpt('max_message_size', default=65535,
|
||||||
|
help=_('The max size(bytes) of message can be posted to '
|
||||||
|
'receiver queue.')),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(RECEIVER_GROUP)
|
||||||
|
conf.register_opts(RECEIVER_OPTS, group=RECEIVER_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
RECEIVER_GROUP: RECEIVER_OPTS,
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
# 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.
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
REVISION_GROUP = cfg.OptGroup('revision')
|
||||||
|
REVISION_OPTS = [
|
||||||
|
cfg.StrOpt('senlin_api_revision', default='1.0',
|
||||||
|
help=_('Senlin API revision.')),
|
||||||
|
cfg.StrOpt('senlin_engine_revision', default='1.0',
|
||||||
|
help=_('Senlin engine revision.'))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(REVISION_GROUP)
|
||||||
|
conf.register_opts(REVISION_OPTS, group=REVISION_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
REVISION_GROUP: REVISION_OPTS
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
# 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.
|
||||||
|
from keystoneauth1 import loading as ksa_loading
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
from senlin.common.i18n import _
|
||||||
|
|
||||||
|
ZAQAR_GROUP = cfg.OptGroup(
|
||||||
|
name='zaqar',
|
||||||
|
title=_('Configuration options for zaqar trustee.')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def register_opts(conf):
|
||||||
|
conf.register_group(ZAQAR_GROUP)
|
||||||
|
ksa_loading.register_session_conf_options(conf, ZAQAR_GROUP)
|
||||||
|
ksa_loading.register_auth_conf_options(conf, ZAQAR_GROUP)
|
||||||
|
|
||||||
|
|
||||||
|
def list_opts():
|
||||||
|
return {
|
||||||
|
ZAQAR_GROUP: (ksa_loading.get_auth_common_conf_options() +
|
||||||
|
ksa_loading.get_auth_plugin_conf_options('password'))
|
||||||
|
}
|
|
@ -44,16 +44,16 @@ CONF = cfg.CONF
|
||||||
_main_context_manager = None
|
_main_context_manager = None
|
||||||
_CONTEXT = threading.local()
|
_CONTEXT = threading.local()
|
||||||
|
|
||||||
cfg.CONF.import_opt('database_retry_limit', 'senlin.common.config')
|
cfg.CONF.import_opt('database_retry_limit', 'senlin.conf')
|
||||||
cfg.CONF.import_opt('database_retry_interval', 'senlin.common.config')
|
cfg.CONF.import_opt('database_retry_interval', 'senlin.conf')
|
||||||
cfg.CONF.import_opt('database_max_retry_interval', 'senlin.common.config')
|
cfg.CONF.import_opt('database_max_retry_interval', 'senlin.conf')
|
||||||
|
|
||||||
|
|
||||||
def _get_main_context_manager():
|
def _get_main_context_manager():
|
||||||
global _main_context_manager
|
global _main_context_manager
|
||||||
if not _main_context_manager:
|
if not _main_context_manager:
|
||||||
_main_context_manager = enginefacade.transaction_context()
|
_main_context_manager = enginefacade.transaction_context()
|
||||||
cfg.CONF.import_group('profiler', 'senlin.common.config')
|
cfg.CONF.import_group('profiler', 'senlin.conf')
|
||||||
if cfg.CONF.profiler.enabled:
|
if cfg.CONF.profiler.enabled:
|
||||||
if cfg.CONF.profiler.trace_sqlalchemy:
|
if cfg.CONF.profiler.trace_sqlalchemy:
|
||||||
eng = _main_context_manager.writer.get_engine()
|
eng = _main_context_manager.writer.get_engine()
|
||||||
|
|
|
@ -78,7 +78,7 @@ def get_sort_params(value, default_key=None):
|
||||||
|
|
||||||
def is_service_dead(service):
|
def is_service_dead(service):
|
||||||
"""Check if a given service is dead."""
|
"""Check if a given service is dead."""
|
||||||
cfg.CONF.import_opt("periodic_interval", "senlin.common.config")
|
cfg.CONF.import_opt("periodic_interval", "senlin.conf")
|
||||||
max_elapse = 2 * cfg.CONF.periodic_interval
|
max_elapse = 2 * cfg.CONF.periodic_interval
|
||||||
|
|
||||||
return timeutils.is_older_than(service.updated_at, max_elapse)
|
return timeutils.is_older_than(service.updated_at, max_elapse)
|
||||||
|
|
|
@ -169,7 +169,7 @@ class Environment(object):
|
||||||
def read_global_environment(self):
|
def read_global_environment(self):
|
||||||
"""Read and parse global environment files."""
|
"""Read and parse global environment files."""
|
||||||
|
|
||||||
cfg.CONF.import_opt('environment_dir', 'senlin.common.config')
|
cfg.CONF.import_opt('environment_dir', 'senlin.conf')
|
||||||
env_dir = cfg.CONF.environment_dir
|
env_dir = cfg.CONF.environment_dir
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -27,8 +27,8 @@ from senlin.objects import node_lock as nl_obj
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
|
|
||||||
CONF.import_opt('lock_retry_times', 'senlin.common.config')
|
CONF.import_opt('lock_retry_times', 'senlin.conf')
|
||||||
CONF.import_opt('lock_retry_interval', 'senlin.common.config')
|
CONF.import_opt('lock_retry_interval', 'senlin.conf')
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ class Capacity(fields.Integer):
|
||||||
|
|
||||||
def __init__(self, minimum=0, maximum=None):
|
def __init__(self, minimum=0, maximum=None):
|
||||||
super(Capacity, self).__init__()
|
super(Capacity, self).__init__()
|
||||||
CONF.import_opt("max_nodes_per_cluster", "senlin.common.config")
|
CONF.import_opt("max_nodes_per_cluster", "senlin.conf")
|
||||||
|
|
||||||
if minimum > CONF.max_nodes_per_cluster:
|
if minimum > CONF.max_nodes_per_cluster:
|
||||||
err = _("The value of 'minimum' cannot be greater than the global "
|
err = _("The value of 'minimum' cannot be greater than the global "
|
||||||
|
|
|
@ -18,7 +18,7 @@ from senlin.objects import base
|
||||||
from senlin.objects import fields
|
from senlin.objects import fields
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.import_opt('default_action_timeout', 'senlin.common.config')
|
CONF.import_opt('default_action_timeout', 'senlin.conf')
|
||||||
|
|
||||||
|
|
||||||
@base.SenlinObjectRegistry.register
|
@base.SenlinObjectRegistry.register
|
||||||
|
|
|
@ -13,6 +13,7 @@ import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from senlin.cmd import conductor
|
from senlin.cmd import conductor
|
||||||
|
from senlin.common import config
|
||||||
from senlin.common import consts
|
from senlin.common import consts
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
|
@ -26,31 +27,26 @@ class TestConductor(base.SenlinTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestConductor, self).setUp()
|
super(TestConductor, self).setUp()
|
||||||
|
|
||||||
@mock.patch('oslo_config.cfg.CONF')
|
|
||||||
@mock.patch('oslo_log.log.register_options')
|
|
||||||
@mock.patch('oslo_log.log.setup')
|
@mock.patch('oslo_log.log.setup')
|
||||||
@mock.patch('oslo_log.log.set_defaults')
|
@mock.patch('oslo_log.log.set_defaults')
|
||||||
@mock.patch('oslo_service.service.launch')
|
@mock.patch('oslo_service.service.launch')
|
||||||
|
@mock.patch.object(config, 'parse_args')
|
||||||
@mock.patch.object(messaging, 'setup')
|
@mock.patch.object(messaging, 'setup')
|
||||||
@mock.patch.object(profiler, 'setup')
|
@mock.patch.object(profiler, 'setup')
|
||||||
@mock.patch.object(service, 'ConductorService')
|
@mock.patch.object(service, 'ConductorService')
|
||||||
def test_main(self, mock_service, mock_profiler_setup,
|
def test_main(self, mock_service, mock_profiler_setup,
|
||||||
mock_messaging_setup, mock_launch,
|
mock_messaging_setup, mock_parse_args, mock_launch,
|
||||||
mock_log_set_defaults, mock_log_setup,
|
mock_log_set_defaults, mock_log_setup):
|
||||||
mock_register_opts, mock_conf):
|
|
||||||
mock_conf.conductor.workers = 1
|
|
||||||
mock_conf.host = 'hostname'
|
|
||||||
|
|
||||||
conductor.main()
|
conductor.main()
|
||||||
|
|
||||||
mock_register_opts.assert_called_once()
|
mock_parse_args.assert_called_once()
|
||||||
mock_log_setup.assert_called_once()
|
mock_log_setup.assert_called_once()
|
||||||
mock_log_set_defaults.assert_called_once()
|
mock_log_set_defaults.assert_called_once()
|
||||||
mock_messaging_setup.assert_called_once()
|
mock_messaging_setup.assert_called_once()
|
||||||
mock_profiler_setup.assert_called_once()
|
mock_profiler_setup.assert_called_once()
|
||||||
|
|
||||||
mock_service.assert_called_once_with(
|
mock_service.assert_called_once_with(
|
||||||
'hostname', consts.CONDUCTOR_TOPIC
|
mock.ANY, consts.CONDUCTOR_TOPIC
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_launch.assert_called_once_with(
|
mock_launch.assert_called_once_with(
|
||||||
|
|
|
@ -13,6 +13,7 @@ import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from senlin.cmd import engine
|
from senlin.cmd import engine
|
||||||
|
from senlin.common import config
|
||||||
from senlin.common import consts
|
from senlin.common import consts
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
|
@ -26,31 +27,26 @@ class TestEngine(base.SenlinTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestEngine, self).setUp()
|
super(TestEngine, self).setUp()
|
||||||
|
|
||||||
@mock.patch('oslo_config.cfg.CONF')
|
|
||||||
@mock.patch('oslo_log.log.register_options')
|
|
||||||
@mock.patch('oslo_log.log.setup')
|
@mock.patch('oslo_log.log.setup')
|
||||||
@mock.patch('oslo_log.log.set_defaults')
|
@mock.patch('oslo_log.log.set_defaults')
|
||||||
@mock.patch('oslo_service.service.launch')
|
@mock.patch('oslo_service.service.launch')
|
||||||
|
@mock.patch.object(config, 'parse_args')
|
||||||
@mock.patch.object(messaging, 'setup')
|
@mock.patch.object(messaging, 'setup')
|
||||||
@mock.patch.object(profiler, 'setup')
|
@mock.patch.object(profiler, 'setup')
|
||||||
@mock.patch.object(service, 'EngineService')
|
@mock.patch.object(service, 'EngineService')
|
||||||
def test_main(self, mock_service, mock_profiler_setup,
|
def test_main(self, mock_service, mock_profiler_setup,
|
||||||
mock_messaging_setup, mock_launch,
|
mock_messaging_setup, mock_parse_args, mock_launch,
|
||||||
mock_log_set_defaults, mock_log_setup,
|
mock_log_set_defaults, mock_log_setup):
|
||||||
mock_register_opts, mock_conf):
|
|
||||||
mock_conf.engine.workers = 1
|
|
||||||
mock_conf.host = 'hostname'
|
|
||||||
|
|
||||||
engine.main()
|
engine.main()
|
||||||
|
|
||||||
mock_register_opts.assert_called_once()
|
mock_parse_args.assert_called_once()
|
||||||
mock_log_setup.assert_called_once()
|
mock_log_setup.assert_called_once()
|
||||||
mock_log_set_defaults.assert_called_once()
|
mock_log_set_defaults.assert_called_once()
|
||||||
mock_messaging_setup.assert_called_once()
|
mock_messaging_setup.assert_called_once()
|
||||||
mock_profiler_setup.assert_called_once()
|
mock_profiler_setup.assert_called_once()
|
||||||
|
|
||||||
mock_service.assert_called_once_with(
|
mock_service.assert_called_once_with(
|
||||||
'hostname', consts.ENGINE_TOPIC
|
mock.ANY, consts.ENGINE_TOPIC
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_launch.assert_called_once_with(
|
mock_launch.assert_called_once_with(
|
||||||
|
|
|
@ -13,6 +13,7 @@ import mock
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from senlin.cmd import health_manager
|
from senlin.cmd import health_manager
|
||||||
|
from senlin.common import config
|
||||||
from senlin.common import consts
|
from senlin.common import consts
|
||||||
from senlin.common import messaging
|
from senlin.common import messaging
|
||||||
from senlin.common import profiler
|
from senlin.common import profiler
|
||||||
|
@ -26,31 +27,26 @@ class TestHealthManager(base.SenlinTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestHealthManager, self).setUp()
|
super(TestHealthManager, self).setUp()
|
||||||
|
|
||||||
@mock.patch('oslo_config.cfg.CONF')
|
|
||||||
@mock.patch('oslo_log.log.register_options')
|
|
||||||
@mock.patch('oslo_log.log.setup')
|
@mock.patch('oslo_log.log.setup')
|
||||||
@mock.patch('oslo_log.log.set_defaults')
|
@mock.patch('oslo_log.log.set_defaults')
|
||||||
@mock.patch('oslo_service.service.launch')
|
@mock.patch('oslo_service.service.launch')
|
||||||
|
@mock.patch.object(config, 'parse_args')
|
||||||
@mock.patch.object(messaging, 'setup')
|
@mock.patch.object(messaging, 'setup')
|
||||||
@mock.patch.object(profiler, 'setup')
|
@mock.patch.object(profiler, 'setup')
|
||||||
@mock.patch.object(service, 'HealthManagerService')
|
@mock.patch.object(service, 'HealthManagerService')
|
||||||
def test_main(self, mock_service, mock_profiler_setup,
|
def test_main(self, mock_service, mock_profiler_setup,
|
||||||
mock_messaging_setup, mock_launch,
|
mock_messaging_setup, mock_parse_args, mock_launch,
|
||||||
mock_log_set_defaults, mock_log_setup,
|
mock_log_set_defaults, mock_log_setup):
|
||||||
mock_register_opts, mock_conf):
|
|
||||||
mock_conf.health_manager.workers = 1
|
|
||||||
mock_conf.host = 'hostname'
|
|
||||||
|
|
||||||
health_manager.main()
|
health_manager.main()
|
||||||
|
|
||||||
mock_register_opts.assert_called_once()
|
mock_parse_args.assert_called_once()
|
||||||
mock_log_setup.assert_called_once()
|
mock_log_setup.assert_called_once()
|
||||||
mock_log_set_defaults.assert_called_once()
|
mock_log_set_defaults.assert_called_once()
|
||||||
mock_messaging_setup.assert_called_once()
|
mock_messaging_setup.assert_called_once()
|
||||||
mock_profiler_setup.assert_called_once()
|
mock_profiler_setup.assert_called_once()
|
||||||
|
|
||||||
mock_service.assert_called_once_with(
|
mock_service.assert_called_once_with(
|
||||||
'hostname', consts.HEALTH_MANAGER_TOPIC
|
mock.ANY, consts.HEALTH_MANAGER_TOPIC
|
||||||
)
|
)
|
||||||
|
|
||||||
mock_launch.assert_called_once_with(
|
mock_launch.assert_called_once_with(
|
||||||
|
|
|
@ -19,8 +19,8 @@ from senlin.objects.requests import clusters
|
||||||
from senlin.tests.unit.common import base as test_base
|
from senlin.tests.unit.common import base as test_base
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.import_opt('default_action_timeout', 'senlin.common.config')
|
CONF.import_opt('default_action_timeout', 'senlin.conf')
|
||||||
CONF.import_opt('max_nodes_per_cluster', 'senlin.common.config')
|
CONF.import_opt('max_nodes_per_cluster', 'senlin.conf')
|
||||||
|
|
||||||
|
|
||||||
class TestClusterCreate(test_base.SenlinTestCase):
|
class TestClusterCreate(test_base.SenlinTestCase):
|
||||||
|
|
|
@ -17,7 +17,7 @@ from senlin.objects.requests import nodes
|
||||||
from senlin.tests.unit.common import base as test_base
|
from senlin.tests.unit.common import base as test_base
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.import_opt('default_action_timeout', 'senlin.common.config')
|
CONF.import_opt('default_action_timeout', 'senlin.conf')
|
||||||
|
|
||||||
|
|
||||||
class TestNodeCreate(test_base.SenlinTestCase):
|
class TestNodeCreate(test_base.SenlinTestCase):
|
||||||
|
|
|
@ -20,7 +20,7 @@ from senlin.objects.requests import receivers
|
||||||
from senlin.tests.unit.common import base as test_base
|
from senlin.tests.unit.common import base as test_base
|
||||||
|
|
||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.import_opt('default_action_timeout', 'senlin.common.config')
|
CONF.import_opt('default_action_timeout', 'senlin.conf')
|
||||||
|
|
||||||
|
|
||||||
class TestReceiverCreate(test_base.SenlinTestCase):
|
class TestReceiverCreate(test_base.SenlinTestCase):
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# 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.
|
||||||
|
import mock
|
||||||
|
import oslotest.base
|
||||||
|
|
||||||
|
from senlin.conf import engine
|
||||||
|
from senlin.conf import opts
|
||||||
|
|
||||||
|
|
||||||
|
class TestConfOpts(oslotest.base.BaseTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(TestConfOpts, self).setUp()
|
||||||
|
|
||||||
|
def test_opts_tupleize(self):
|
||||||
|
self.assertEqual([('a', 'b')], opts._tupleize({'a': 'b'}))
|
||||||
|
|
||||||
|
def test_opts_list(self):
|
||||||
|
self.assertIsInstance(opts.list_opts(), list)
|
||||||
|
|
||||||
|
@mock.patch('pkgutil.iter_modules')
|
||||||
|
def test_opts_list_module_names(self, mock_iter_modules):
|
||||||
|
mock_iter_modules.return_value = iter(
|
||||||
|
[
|
||||||
|
(None, 'api', False),
|
||||||
|
(None, 'authentication', False),
|
||||||
|
(None, 'unknown', True),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(['api', 'authentication'], opts._list_module_names())
|
||||||
|
|
||||||
|
def test_opts_import_modules(self):
|
||||||
|
self.assertEqual([engine], opts._import_modules(['engine']))
|
||||||
|
|
||||||
|
@mock.patch('importlib.import_module')
|
||||||
|
def test_opts_import_invalid_module(self, mock_import_module):
|
||||||
|
mock_import_module.return_value = None
|
||||||
|
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
Exception,
|
||||||
|
"The module 'senlin.conf.invalid' should have a 'list_opts' "
|
||||||
|
"function which returns the config options.",
|
||||||
|
opts._import_modules, ['invalid']
|
||||||
|
)
|
|
@ -37,10 +37,10 @@ wsgi_scripts =
|
||||||
senlin-wsgi-api = senlin.cmd.api_wsgi:init_app
|
senlin-wsgi-api = senlin.cmd.api_wsgi:init_app
|
||||||
|
|
||||||
oslo.config.opts =
|
oslo.config.opts =
|
||||||
senlin.config = senlin.common.config:list_opts
|
senlin.conf = senlin.conf.opts:list_opts
|
||||||
|
|
||||||
oslo.config.opts.defaults =
|
oslo.config.opts.defaults =
|
||||||
senlin.config = senlin.common.config:set_config_defaults
|
senlin.conf = senlin.common.config:set_config_defaults
|
||||||
|
|
||||||
oslo.policy.policies =
|
oslo.policy.policies =
|
||||||
senlin = senlin.common.policies:list_rules
|
senlin = senlin.common.policies:list_rules
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
output_file = etc/senlin/senlin.conf.sample
|
output_file = etc/senlin/senlin.conf.sample
|
||||||
wrap_width = 119
|
wrap_width = 119
|
||||||
namespace = senlin.config
|
namespace = senlin.conf
|
||||||
namespace = keystonemiddleware.auth_token
|
namespace = keystonemiddleware.auth_token
|
||||||
namespace = oslo.db
|
namespace = oslo.db
|
||||||
namespace = oslo.log
|
namespace = oslo.log
|
||||||
|
@ -12,3 +12,4 @@ namespace = oslo.policy
|
||||||
namespace = oslo.service.periodic_task
|
namespace = oslo.service.periodic_task
|
||||||
namespace = oslo.service.service
|
namespace = oslo.service.service
|
||||||
namespace = oslo.service.sslutils
|
namespace = oslo.service.sslutils
|
||||||
|
namespace = osprofiler
|
||||||
|
|
Loading…
Reference in New Issue