# Copyright (c) 2013-2014 Rackspace, Inc. # # 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. """ Configuration setup for Barbican. """ import logging import os from oslo_config import cfg from oslo_log import log from oslo_middleware import cors from oslo_service import _options from barbican import i18n as u import barbican.version MAX_BYTES_REQUEST_INPUT_ACCEPTED = 15000 DEFAULT_MAX_SECRET_BYTES = 10000 KS_NOTIFICATIONS_GRP_NAME = 'keystone_notifications' context_opts = [ cfg.StrOpt('admin_role', default='admin', help=u._('Role used to identify an authenticated user as ' 'administrator.')), cfg.BoolOpt('allow_anonymous_access', default=False, help=u._('Allow unauthenticated users to access the API with ' 'read-only privileges. This only applies when using ' 'ContextMiddleware.')), ] common_opts = [ cfg.IntOpt('max_allowed_request_size_in_bytes', default=MAX_BYTES_REQUEST_INPUT_ACCEPTED, help=u._("Maximum allowed http request size against the " "barbican-api.")), cfg.IntOpt('max_allowed_secret_in_bytes', default=DEFAULT_MAX_SECRET_BYTES, help=u._("Maximum allowed secret size in bytes.")), ] host_opts = [ cfg.StrOpt('host_href', default='http://localhost:9311', help=u._("Host name, for use in HATEOAS-style references Note: " "Typically this would be the load balanced endpoint " "that clients would use to communicate back with this " "service. If a deployment wants to derive host from " "wsgi request instead then make this blank. Blank is " "needed to override default config value which is " "'http://localhost:9311'")), ] db_opts = [ cfg.StrOpt('sql_connection', default="sqlite:///barbican.sqlite", secret=True, help=u._("SQLAlchemy connection string for the reference " "implementation registry server. Any valid " "SQLAlchemy connection string is fine. See: " "http://www.sqlalchemy.org/docs/05/reference/" "sqlalchemy/connections.html#sqlalchemy." "create_engine. Note: For absolute addresses, use " "'////' slashes after 'sqlite:'.")), cfg.IntOpt('sql_idle_timeout', default=3600, help=u._("Period in seconds after which SQLAlchemy should " "reestablish its connection to the database. MySQL " "uses a default `wait_timeout` of 8 hours, after " "which it will drop idle connections. This can result " "in 'MySQL Gone Away' exceptions. If you notice this, " "you can lower this value to ensure that SQLAlchemy " "reconnects before MySQL can drop the connection.")), cfg.IntOpt('sql_max_retries', default=60, help=u._("Maximum number of database connection retries " "during startup. Set to -1 to specify an infinite " "retry count.")), cfg.IntOpt('sql_retry_interval', default=1, help=u._("Interval between retries of opening a SQL " "connection.")), cfg.BoolOpt('db_auto_create', default=True, help=u._("Create the Barbican database on service startup.")), cfg.IntOpt('max_limit_paging', default=100, help=u._("Maximum page size for the 'limit' paging URL " "parameter.")), cfg.IntOpt('default_limit_paging', default=10, help=u._("Default page size for the 'limit' paging URL " "parameter.")), cfg.StrOpt('sql_pool_class', default="QueuePool", help=u._("Accepts a class imported from the sqlalchemy.pool " "module, and handles the details of building the " "pool for you. If commented out, SQLAlchemy will " "select based on the database dialect. Other options " "are QueuePool (for SQLAlchemy-managed connections) " "and NullPool (to disabled SQLAlchemy management of " "connections). See http://docs.sqlalchemy.org/en/" "latest/core/pooling.html for more details")), cfg.BoolOpt('sql_pool_logging', default=False, help=u._("Show SQLAlchemy pool-related debugging output in " "logs (sets DEBUG log level output) if specified.")), cfg.IntOpt('sql_pool_size', default=5, help=u._("Size of pool used by SQLAlchemy. This is the largest " "number of connections that will be kept persistently " "in the pool. Can be set to 0 to indicate no size " "limit. To disable pooling, use a NullPool with " "sql_pool_class instead. Comment out to allow " "SQLAlchemy to select the default.")), cfg.IntOpt('sql_pool_max_overflow', default=10, help=u._("# The maximum overflow size of the pool used by " "SQLAlchemy. When the number of checked-out " "connections reaches the size set in sql_pool_size, " "additional connections will be returned up to this " "limit. It follows then that the total number of " "simultaneous connections the pool will allow is " "sql_pool_size + sql_pool_max_overflow. Can be set " "to -1 to indicate no overflow limit, so no limit " "will be placed on the total number of concurrent " "connections. Comment out to allow SQLAlchemy to " "select the default.")), ] retry_opt_group = cfg.OptGroup(name='retry_scheduler', title='Retry/Scheduler Options') retry_opts = [ cfg.FloatOpt( 'initial_delay_seconds', default=10.0, help=u._('Seconds (float) to wait before starting retry scheduler')), cfg.FloatOpt( 'periodic_interval_max_seconds', default=10.0, help=u._('Seconds (float) to wait between periodic schedule events')), ] queue_opt_group = cfg.OptGroup(name='queue', title='Queue Application Options') queue_opts = [ cfg.BoolOpt('enable', default=False, help=u._('True enables queuing, False invokes ' 'workers synchronously')), cfg.StrOpt('namespace', default='barbican', help=u._('Queue namespace')), cfg.StrOpt('topic', default='barbican.workers', help=u._('Queue topic name')), cfg.StrOpt('version', default='1.1', help=u._('Version of tasks invoked via queue')), cfg.StrOpt('server_name', default='barbican.queue', help=u._('Server name for RPC task processing server')), cfg.IntOpt('asynchronous_workers', default=1, help=u._('Number of asynchronous worker processes')), ] ks_queue_opt_group = cfg.OptGroup(name=KS_NOTIFICATIONS_GRP_NAME, title='Keystone Notification Options') ks_queue_opts = [ cfg.BoolOpt('enable', default=False, help=u._('True enables keystone notification listener ' ' functionality.')), cfg.StrOpt('control_exchange', default='keystone', help=u._('The default exchange under which topics are scoped. ' 'May be overridden by an exchange name specified in ' 'the transport_url option.')), cfg.StrOpt('topic', default='notifications', help=u._("Keystone notification queue topic name. This name " "needs to match one of values mentioned in Keystone " "deployment's 'notification_topics' configuration " "e.g." " notification_topics=notifications, " " barbican_notifications" "Multiple servers may listen on a topic and messages " "will be dispatched to one of the servers in a " "round-robin fashion. That's why Barbican service " "should have its own dedicated notification queue so " "that it receives all of Keystone notifications.")), cfg.BoolOpt('allow_requeue', default=False, help=u._('True enables requeue feature in case of notification' ' processing error. Enable this only when underlying ' 'transport supports this feature.')), cfg.StrOpt('version', default='1.0', help=u._('Version of tasks invoked via notifications')), cfg.IntOpt('thread_pool_size', default=10, help=u._('Define the number of max threads to be used for ' 'notification server processing functionality.')), ] quota_opt_group = cfg.OptGroup(name='quotas', title='Quota Options') quota_opts = [ cfg.IntOpt('quota_secrets', default=-1, help=u._('Number of secrets allowed per project')), cfg.IntOpt('quota_orders', default=-1, help=u._('Number of orders allowed per project')), cfg.IntOpt('quota_containers', default=-1, help=u._('Number of containers allowed per project')), cfg.IntOpt('quota_consumers', default=-1, help=u._('Number of consumers allowed per project')), cfg.IntOpt('quota_cas', default=-1, help=u._('Number of CAs allowed per project')) ] def list_opts(): yield None, context_opts yield None, common_opts yield None, host_opts yield None, db_opts yield None, _options.eventlet_backdoor_opts yield retry_opt_group, retry_opts yield queue_opt_group, queue_opts yield ks_queue_opt_group, ks_queue_opts yield quota_opt_group, quota_opts # Flag to indicate barbican configuration is already parsed once or not _CONFIG_PARSED_ONCE = False def parse_args(conf, args=None, usage=None, default_config_files=None): global _CONFIG_PARSED_ONCE conf(args=args if args else [], project='barbican', prog='barbican', version=barbican.version.__version__, usage=usage, default_config_files=default_config_files) conf.pydev_debug_host = os.environ.get('PYDEV_DEBUG_HOST') conf.pydev_debug_port = os.environ.get('PYDEV_DEBUG_PORT') # Assign cfg.CONF handle to parsed barbican configuration once at startup # only. No need to keep re-assigning it with separate plugin conf usage if not _CONFIG_PARSED_ONCE: cfg.CONF = conf _CONFIG_PARSED_ONCE = True def new_config(): conf = cfg.ConfigOpts() log.register_options(conf) conf.register_opts(context_opts) conf.register_opts(common_opts) conf.register_opts(host_opts) conf.register_opts(db_opts) conf.register_opts(_options.eventlet_backdoor_opts) conf.register_opts(_options.periodic_opts) conf.register_opts(_options.ssl_opts, "ssl") conf.register_group(retry_opt_group) conf.register_opts(retry_opts, group=retry_opt_group) conf.register_group(queue_opt_group) conf.register_opts(queue_opts, group=queue_opt_group) conf.register_group(ks_queue_opt_group) conf.register_opts(ks_queue_opts, group=ks_queue_opt_group) conf.register_group(quota_opt_group) conf.register_opts(quota_opts, group=quota_opt_group) # Update default values from libraries that carry their own oslo.config # initialization and configuration. set_middleware_defaults() return conf def setup_remote_pydev_debug(): """Required setup for remote debugging.""" if CONF.pydev_debug_host and CONF.pydev_debug_port: try: try: from pydev import pydevd except ImportError: import pydevd pydevd.settrace(CONF.pydev_debug_host, port=int(CONF.pydev_debug_port), stdoutToServer=True, stderrToServer=True) except Exception: LOG.exception('Unable to join debugger, please ' 'make sure that the debugger processes is ' 'listening on debug-host \'%(debug-host)s\' ' 'debug-port \'%(debug-port)s\'.', {'debug-host': CONF.pydev_debug_host, 'debug-port': CONF.pydev_debug_port}) raise def set_middleware_defaults(): """Update default configuration options for oslo.middleware.""" cors.set_defaults( allow_headers=['X-Auth-Token', 'X-Openstack-Request-Id', 'X-Project-Id', 'X-Identity-Status', 'X-User-Id', 'X-Storage-Token', 'X-Domain-Id', 'X-User-Domain-Id', 'X-Project-Domain-Id', 'X-Roles'], expose_headers=['X-Auth-Token', 'X-Openstack-Request-Id', 'X-Project-Id', 'X-Identity-Status', 'X-User-Id', 'X-Storage-Token', 'X-Domain-Id', 'X-User-Domain-Id', 'X-Project-Domain-Id', 'X-Roles'], allow_methods=['GET', 'PUT', 'POST', 'DELETE', 'PATCH'] ) CONF = new_config() LOG = logging.getLogger(__name__) parse_args(CONF) # Adding global scope dict for all different configs created in various # modules. In barbican, each plugin module creates its own *new* config # instance so its error prone to share/access config values across modules # as these module imports introduce a cyclic dependency. To avoid this, each # plugin can set this dict after its own config instance is created and parsed. _CONFIGS = {} def set_module_config(name, module_conf): """Each plugin can set its own conf instance with its group name.""" _CONFIGS[name] = module_conf def get_module_config(name): """Get handle to plugin specific config instance by its group name.""" return _CONFIGS[name]