[API] Migrate to oslo.config

Major configuration change. See examples in etc/ directory.

Change-Id: I8090e365f7c1d48ffeede55651ee54801f9d3b90
This commit is contained in:
David Shrewsbury
2013-10-16 11:11:28 -04:00
parent 3d36a4248e
commit cb3fe316b5
6 changed files with 176 additions and 154 deletions

72
etc/api.cfg Normal file
View File

@@ -0,0 +1,72 @@
########################################################################
# A sample configuration file read by the Libra pool manager utility.
########################################################################
#-----------------------------------------------------------------------
# The [DEFAULT] section contains options common to the various Libra
# utilities (worker, mgm, etc).
#-----------------------------------------------------------------------
[DEFAULT]
# Options to enable more verbose output
#verbose = false
#debug = false
# Daemon process options
#daemon = true
#user = libra
#group = libra
# Other logging options
#syslog = false
#syslog_socket = /dev/log
#syslog_faciltiy = local7
#logstash = HOST:PORT
#-----------------------------------------------------------------------
# Options for utilities that are Gearman workers or clients.
#-----------------------------------------------------------------------
[gearman]
#servers = localhost:4730, HOST:PORT
#keepalive = false
#keepcnt = COUNT
#keepidle = SECONDS
#keepintvl = SECONDS
#poll = 1
#reconnect_sleep = 60
#ssl_ca = /path/to/ssl_ca
#ssl_cert = /path/to/ssl_cert
#ssl_key = /path/to/ssl_key
#-----------------------------------------------------------------------
# The [api] section is specific to the libra_api utility.
#-----------------------------------------------------------------------
[api]
# Options with defaults
#disable_keystone=False
#host=0.0.0.0
#port=443
#keystone_module=keystoneclient.middleware.auth_token:AuthProtocol
#logfile=/var/log/libra/libra_api.log
#pid=/var/run/libra/libra_api.pid
# Required options
db_sections=mysql1
swift_basepath=lbaaslogs
swift_endpoint=https://host.com:443/v1/
# Others
ssl_certfile=certfile.crt
ssl_keyfile=keyfile.key
ip_filters=192.168.0.0/24
[mysql1]
username=root
password=
schema=lbaas
host=localhost
# Keystone options go here
[keystone]

View File

@@ -292,7 +292,8 @@ def main():
MaintThreads(logger, args, drivers) MaintThreads(logger, args, drivers)
sys.stderr = LogStdout(logger) sys.stderr = LogStdout(logger)
sock = server.make_socket(args) sock = server.make_socket(args.host, args.port,
args.ssl_keyfile, args.ssl_certfile)
wsgi.server(sock, api, keepalive=False) wsgi.server(sock, api, keepalive=False)
return 0 return 0

View File

@@ -11,3 +11,52 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# 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.config import cfg
api_group = cfg.OptGroup('api', 'Libra API options')
cfg.CONF.register_group(api_group)
cfg.CONF.register_opts(
[
cfg.ListOpt('db_sections',
required=True,
help='MySQL config sections in the config file'),
cfg.BoolOpt('disable_keystone',
default=False,
help='Unauthenticated server, for testing only'),
cfg.StrOpt('host',
default='0.0.0.0',
help='IP address to bind to, 0.0.0.0 for all IPs'),
cfg.ListOpt('ip_filters',
help='IP filters for backend nodes in the form '
'xxx.xxx.xxx.xxx/yy'),
cfg.StrOpt('keystone_module',
default='keystoneclient.middleware.auth_token:AuthProtocol',
help='A colon separated module and class for keystone '
' middleware'),
cfg.StrOpt('logfile',
default='/var/log/libra/libra_api.log',
help='Log file'),
cfg.StrOpt('pid',
default='/var/run/libra/libra_api.pid',
help='PID file'),
cfg.IntOpt('port',
default=443,
help='Port number for API server'),
cfg.StrOpt('ssl_certfile',
help='Path to an SSL certificate file'),
cfg.StrOpt('ssl_keyfile',
help='Path to an SSL key file'),
cfg.StrOpt('swift_basepath',
required=True,
help='Default Swift container to place log files'),
cfg.StrOpt('swift_endpoint',
required=True,
help='Default endpoint URL (tenant ID will be appended'
' to this)'),
],
group=api_group
)

View File

@@ -12,10 +12,12 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import ConfigParser
import importlib import importlib
import logging import logging
from oslo.config import cfg
from pecan import request from pecan import request
from libra.api.library.exp import NotAuthorized from libra.api.library.exp import NotAuthorized
@@ -43,10 +45,9 @@ class AuthDirector(object):
will direct intentionally unauthenticated requests to the relevant will direct intentionally unauthenticated requests to the relevant
controllers. """ controllers. """
def __init__(self, app, args): def __init__(self, app):
self.args = args
self.unauthed_app = app self.unauthed_app = app
if not args.disable_keystone: if not cfg.CONF['api']['disable_keystone']:
self.app = self._install() self.app = self._install()
else: else:
self.app = app self.app = app
@@ -60,9 +61,7 @@ class AuthDirector(object):
def _install(self): def _install(self):
"""Install ACL check on application.""" """Install ACL check on application."""
config = ConfigParser.SafeConfigParser() module_details = cfg.CONF['api']['keystone_module'].split(':')
config.read([self.args.config])
module_details = self.args.keystone_module.split(':')
keystone = importlib.import_module(module_details[0]) keystone = importlib.import_module(module_details[0])
auth_class = getattr(keystone, module_details[1]) auth_class = getattr(keystone, module_details[1])
return auth_class(self.unauthed_app, config._sections['keystone']) return auth_class(self.unauthed_app, cfg.CONF['keystone'])

View File

@@ -13,6 +13,7 @@
# under the License. # under the License.
import eventlet import eventlet
eventlet.monkey_patch() eventlet.monkey_patch()
import daemon import daemon
import daemon.pidfile import daemon.pidfile
import daemon.runner import daemon.runner
@@ -20,14 +21,16 @@ import grp
import pwd import pwd
import pecan import pecan
import sys import sys
import os
import wsme_overrides import wsme_overrides
from eventlet import wsgi
from libra import __version__
from libra.api import config as api_config from libra.api import config as api_config
from libra.api import model from libra.api import model
from libra.api import acl from libra.api import acl
from libra.common.api import server from libra.common.api import server
from libra.common.options import Options, setup_logging from libra.common.options import add_common_opts, libra_logging, CONF
from eventlet import wsgi
# Gets rid of pep8 error # Gets rid of pep8 error
@@ -40,31 +43,32 @@ def get_pecan_config():
return pecan.configuration.conf_from_file(filename) return pecan.configuration.conf_from_file(filename)
def setup_app(pecan_config, args): def setup_app(pecan_config):
model.init_model() model.init_model()
if not pecan_config: if not pecan_config:
pecan_config = get_pecan_config() pecan_config = get_pecan_config()
config = dict(pecan_config) config = dict(pecan_config)
config['database'] = args.db_sections config['database'] = CONF['api']['db_sections']
config['conffile'] = args.config # NOTE: We support only 1 config file
config['conffile'] = CONF['config-file'][0]
config['swift'] = { config['swift'] = {
'swift_basepath': args.swift_basepath, 'swift_basepath': CONF['api']['swift_basepath'],
'swift_endpoint': args.swift_endpoint 'swift_endpoint': CONF['api']['swift_endpoint']
} }
config['gearman'] = { config['gearman'] = {
'server': args.gearman, 'server': CONF['gearman']['servers'],
'ssl_key': args.gearman_ssl_key, 'ssl_key': CONF['gearman']['ssl_key'],
'ssl_cert': args.gearman_ssl_cert, 'ssl_cert': CONF['gearman']['ssl_cert'],
'ssl_ca': args.gearman_ssl_ca, 'ssl_ca': CONF['gearman']['ssl_ca'],
'keepalive': args.gearman_keepalive, 'keepalive': CONF['gearman']['keepalive'],
'keepcnt': args.gearman_keepcnt, 'keepcnt': CONF['gearman']['keepcnt'],
'keepidle': args.gearman_keepidle, 'keepidle': CONF['gearman']['keepidle'],
'keepintvl': args.gearman_keepintvl 'keepintvl': CONF['gearman']['keepintvl']
} }
config['ip_filters'] = args.ip_filters config['ip_filters'] = CONF['api']['ip_filters']
if args.debug: if CONF['debug']:
config['wsme'] = {'debug': True} config['wsme'] = {'debug': True}
config['app']['debug'] = True config['app']['debug'] = True
@@ -82,7 +86,7 @@ def setup_app(pecan_config, args):
True) True)
) )
final_app = acl.AuthDirector(app, args) final_app = acl.AuthDirector(app)
return final_app return final_app
@@ -100,125 +104,20 @@ class LogStdout(object):
def main(): def main():
options = Options('api', 'API Server') add_common_opts()
options.parser.add_argument( CONF(project='libra', version=__version__)
'--host', help='IP address to bind to, 0.0.0.0 for all IPs',
default='0.0.0.0'
)
options.parser.add_argument(
'--port', help='Port number for API server', type=int, default=443
)
options.parser.add_argument(
'--disable_keystone', help='Unauthenticated server, for testing only',
action='store_true'
)
options.parser.add_argument(
'--db_sections', action='append', default=[],
help='MySQL config sections in the config file'
)
options.parser.add_argument(
'--gearman', action='append', metavar='HOST:PORT', default=[],
help='Gearman job servers'
)
options.parser.add_argument(
'--gearman_keepalive', action="store_true",
help='use KEEPALIVE to Gearman server'
)
options.parser.add_argument(
'--gearman_keepcnt', type=int, metavar='COUNT',
help='max keepalive probes to send before killing connection'
)
options.parser.add_argument(
'--gearman_keepidle', type=int, metavar='SECONDS',
help='seconds of idle time before sending keepalive probes'
)
options.parser.add_argument(
'--gearman_keepintvl', type=int, metavar='SECONDS',
help='seconds between TCP keepalive probes'
)
options.parser.add_argument(
'--gearman_ssl_ca', metavar='FILE',
help='Gearman SSL certificate authority'
)
options.parser.add_argument(
'--gearman_ssl_cert', metavar='FILE',
help='Gearman SSL certificate'
)
options.parser.add_argument(
'--gearman_ssl_key', metavar='FILE',
help='Gearman SSL key'
)
options.parser.add_argument(
'--keystone_module',
default='keystoneclient.middleware.auth_token:AuthProtocol',
help='A colon separated module and class for keystone middleware'
)
options.parser.add_argument(
'--swift_basepath',
help='Default swift container to use for pushing log files to'
)
options.parser.add_argument(
'--swift_endpoint',
help='Default endpoint URL (tenant ID will be appended to this)'
)
options.parser.add_argument(
'--ssl_certfile',
help='Path to an SSL certificate file'
)
options.parser.add_argument(
'--ssl_keyfile',
help='Path to an SSL key file'
)
options.parser.add_argument(
'--ip_filters', action='append', default=[],
help='IP filters for backend nodes in the form xxx.xxx.xxx.xxx/yy'
)
args = options.run()
required_args = [
'db_sections', 'swift_basepath',
'swift_endpoint'
]
missing_args = 0
for req in required_args:
test_var = getattr(args, req)
if test_var is None:
missing_args += 1
sys.stderr.write(
'{app}: error: argument --{test_var} is required\n'
.format(app=os.path.basename(sys.argv[0]), test_var=req))
if missing_args:
return 2
if not args.gearman:
# NOTE(shrews): Can't set a default in argparse method because the
# value is appended to the specified default.
args.gearman.append('localhost:4730')
elif not isinstance(args.gearman, list):
# NOTE(shrews): The Options object cannot intelligently handle
# creating a list from an option that may have multiple values.
# We convert it to the expected type here.
svr_list = args.gearman.split()
args.gearman = svr_list
if not isinstance(args.db_sections, list):
db_list = args.db_sections.split()
args.db_sections = db_list
if not isinstance(args.ip_filters, list):
ip_list = args.ip_filters.split()
args.ip_filters = ip_list
pc = get_pecan_config() pc = get_pecan_config()
# NOTE: Let's not force anyone to actually have to use SSL, it shouldn't be # NOTE: Let's not force anyone to actually have to use SSL, it shouldn't be
# up to us to decide. # up to us to decide.
sock = server.make_socket(args) sock = server.make_socket(CONF['api']['host'],
CONF['api']['port'],
CONF['api']['ssl_keyfile'],
CONF['api']['ssl_certfile'])
if not args.nodaemon: if CONF['api']['daemon']:
pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10) pidfile = daemon.pidfile.TimeoutPIDLockFile(CONF['api']['pid'], 10)
if daemon.runner.is_pidfile_stale(pidfile): if daemon.runner.is_pidfile_stale(pidfile):
pidfile.break_lock() pidfile.break_lock()
context = daemon.DaemonContext( context = daemon.DaemonContext(
@@ -227,17 +126,19 @@ def main():
pidfile=pidfile, pidfile=pidfile,
files_preserve=[sock.fileno()] files_preserve=[sock.fileno()]
) )
if args.user: if CONF['user']:
context.uid = pwd.getpwnam(args.user).pw_uid context.uid = pwd.getpwnam(CONF['user']).pw_uid
if args.group: if CONF['group']:
context.gid = grp.getgrnam(args.group).gr_gid context.gid = grp.getgrnam(CONF['group']).gr_gid
context.open() context.open()
# Use the root logger due to lots of services using logger # Use the root logger due to lots of services using logger
logger = setup_logging('', args) logger = libra_logging('', 'api')
logger.info('Starting on {0}:{1}'.format(args.host, args.port)) logger.info('Starting on {0}:{1}'.format(CONF['api']['host'],
api = setup_app(pc, args) CONF['api']['port']))
api = setup_app(pc)
sys.stderr = LogStdout(logger) sys.stderr = LogStdout(logger)
wsgi.server(sock, api, keepalive=False, debug=args.debug) wsgi.server(sock, api, keepalive=False, debug=CONF['debug'])
return 0 return 0

View File

@@ -14,11 +14,11 @@
import eventlet import eventlet
def make_socket(args): def make_socket(host, port, ssl_keyfile=None, ssl_certfile=None):
sock = eventlet.listen((args.host, args.port)) sock = eventlet.listen((host, port))
# TODO: set ca_certs and cert_reqs=CERT_REQUIRED # TODO: set ca_certs and cert_reqs=CERT_REQUIRED
if args.ssl_keyfile and args.ssl_certfile: if ssl_keyfile and ssl_certfile:
sock = eventlet.wrap_ssl(sock, certfile=args.ssl_certfile, sock = eventlet.wrap_ssl(sock, certfile=ssl_certfile,
keyfile=args.ssl_keyfile, keyfile=ssl_keyfile,
server_side=True) server_side=True)
return sock return sock