diff --git a/etc/api.cfg b/etc/api.cfg new file mode 100644 index 00000000..594505bd --- /dev/null +++ b/etc/api.cfg @@ -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] diff --git a/libra/admin_api/app.py b/libra/admin_api/app.py index 24c1f873..f38b64bc 100644 --- a/libra/admin_api/app.py +++ b/libra/admin_api/app.py @@ -292,7 +292,8 @@ def main(): MaintThreads(logger, args, drivers) 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) return 0 diff --git a/libra/api/__init__.py b/libra/api/__init__.py index 92bd912f..f5d20d10 100644 --- a/libra/api/__init__.py +++ b/libra/api/__init__.py @@ -11,3 +11,52 @@ # 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 + + +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 +) diff --git a/libra/api/acl.py b/libra/api/acl.py index c762d624..fcf68c11 100644 --- a/libra/api/acl.py +++ b/libra/api/acl.py @@ -12,10 +12,12 @@ # License for the specific language governing permissions and limitations # under the License. -import ConfigParser import importlib import logging + +from oslo.config import cfg from pecan import request + from libra.api.library.exp import NotAuthorized @@ -43,10 +45,9 @@ class AuthDirector(object): will direct intentionally unauthenticated requests to the relevant controllers. """ - def __init__(self, app, args): - self.args = args + def __init__(self, app): self.unauthed_app = app - if not args.disable_keystone: + if not cfg.CONF['api']['disable_keystone']: self.app = self._install() else: self.app = app @@ -60,9 +61,7 @@ class AuthDirector(object): def _install(self): """Install ACL check on application.""" - config = ConfigParser.SafeConfigParser() - config.read([self.args.config]) - module_details = self.args.keystone_module.split(':') + module_details = cfg.CONF['api']['keystone_module'].split(':') keystone = importlib.import_module(module_details[0]) 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']) diff --git a/libra/api/app.py b/libra/api/app.py index 40422390..5dd4fc7c 100644 --- a/libra/api/app.py +++ b/libra/api/app.py @@ -13,6 +13,7 @@ # under the License. import eventlet eventlet.monkey_patch() + import daemon import daemon.pidfile import daemon.runner @@ -20,14 +21,16 @@ import grp import pwd import pecan import sys -import os import wsme_overrides + +from eventlet import wsgi + +from libra import __version__ from libra.api import config as api_config from libra.api import model from libra.api import acl from libra.common.api import server -from libra.common.options import Options, setup_logging -from eventlet import wsgi +from libra.common.options import add_common_opts, libra_logging, CONF # Gets rid of pep8 error @@ -40,31 +43,32 @@ def get_pecan_config(): return pecan.configuration.conf_from_file(filename) -def setup_app(pecan_config, args): +def setup_app(pecan_config): model.init_model() if not pecan_config: pecan_config = get_pecan_config() config = dict(pecan_config) - config['database'] = args.db_sections - config['conffile'] = args.config + config['database'] = CONF['api']['db_sections'] + # NOTE: We support only 1 config file + config['conffile'] = CONF['config-file'][0] config['swift'] = { - 'swift_basepath': args.swift_basepath, - 'swift_endpoint': args.swift_endpoint + 'swift_basepath': CONF['api']['swift_basepath'], + 'swift_endpoint': CONF['api']['swift_endpoint'] } config['gearman'] = { - 'server': args.gearman, - 'ssl_key': args.gearman_ssl_key, - 'ssl_cert': args.gearman_ssl_cert, - 'ssl_ca': args.gearman_ssl_ca, - 'keepalive': args.gearman_keepalive, - 'keepcnt': args.gearman_keepcnt, - 'keepidle': args.gearman_keepidle, - 'keepintvl': args.gearman_keepintvl + 'server': CONF['gearman']['servers'], + 'ssl_key': CONF['gearman']['ssl_key'], + 'ssl_cert': CONF['gearman']['ssl_cert'], + 'ssl_ca': CONF['gearman']['ssl_ca'], + 'keepalive': CONF['gearman']['keepalive'], + 'keepcnt': CONF['gearman']['keepcnt'], + 'keepidle': CONF['gearman']['keepidle'], + 'keepintvl': CONF['gearman']['keepintvl'] } - config['ip_filters'] = args.ip_filters - if args.debug: + config['ip_filters'] = CONF['api']['ip_filters'] + if CONF['debug']: config['wsme'] = {'debug': True} config['app']['debug'] = True @@ -82,7 +86,7 @@ def setup_app(pecan_config, args): True) ) - final_app = acl.AuthDirector(app, args) + final_app = acl.AuthDirector(app) return final_app @@ -100,125 +104,20 @@ class LogStdout(object): def main(): - options = Options('api', 'API Server') - options.parser.add_argument( - '--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 + add_common_opts() + CONF(project='libra', version=__version__) pc = get_pecan_config() # NOTE: Let's not force anyone to actually have to use SSL, it shouldn't be # 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: - pidfile = daemon.pidfile.TimeoutPIDLockFile(args.pid, 10) + if CONF['api']['daemon']: + pidfile = daemon.pidfile.TimeoutPIDLockFile(CONF['api']['pid'], 10) if daemon.runner.is_pidfile_stale(pidfile): pidfile.break_lock() context = daemon.DaemonContext( @@ -227,17 +126,19 @@ def main(): pidfile=pidfile, files_preserve=[sock.fileno()] ) - if args.user: - context.uid = pwd.getpwnam(args.user).pw_uid - if args.group: - context.gid = grp.getgrnam(args.group).gr_gid + if CONF['user']: + context.uid = pwd.getpwnam(CONF['user']).pw_uid + if CONF['group']: + context.gid = grp.getgrnam(CONF['group']).gr_gid context.open() + # Use the root logger due to lots of services using logger - logger = setup_logging('', args) - logger.info('Starting on {0}:{1}'.format(args.host, args.port)) - api = setup_app(pc, args) + logger = libra_logging('', 'api') + logger.info('Starting on {0}:{1}'.format(CONF['api']['host'], + CONF['api']['port'])) + api = setup_app(pc) sys.stderr = LogStdout(logger) - wsgi.server(sock, api, keepalive=False, debug=args.debug) + wsgi.server(sock, api, keepalive=False, debug=CONF['debug']) return 0 diff --git a/libra/common/api/server.py b/libra/common/api/server.py index efa6bc36..33079405 100644 --- a/libra/common/api/server.py +++ b/libra/common/api/server.py @@ -14,11 +14,11 @@ import eventlet -def make_socket(args): - sock = eventlet.listen((args.host, args.port)) +def make_socket(host, port, ssl_keyfile=None, ssl_certfile=None): + sock = eventlet.listen((host, port)) # TODO: set ca_certs and cert_reqs=CERT_REQUIRED - if args.ssl_keyfile and args.ssl_certfile: - sock = eventlet.wrap_ssl(sock, certfile=args.ssl_certfile, - keyfile=args.ssl_keyfile, + if ssl_keyfile and ssl_certfile: + sock = eventlet.wrap_ssl(sock, certfile=ssl_certfile, + keyfile=ssl_keyfile, server_side=True) return sock