From 0d8bc572339bc8defe5ea5fba16eedcd79c5472b Mon Sep 17 00:00:00 2001 From: Ziad Sawalha Date: Sun, 4 Dec 2011 03:03:22 -0600 Subject: Refactor: move initialization code to class - Each script (keystone, keystone-admin, keystone-auth) had a copy of the initialization code that handled options, arguments, and starting the paste apps. The code was also duplicated in the test SetUp code. This code has now been consolidated in one class. - Created the keystone.Server class that has start/stop methods to manage keystone servers. It also handles initializing options and arguments. - This provides one point of entry into keystone where we can now start refactoring logging and backend code. Change-Id: Ie4e2a9aae9578c3657c0459381d1618d30b8ca32 --- bin/keystone | 81 +++++++--------------------- bin/keystone-admin | 35 ++----------- bin/keystone-auth | 35 ++----------- keystone/__init__.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++ keystone/test/__init__.py | 75 +++++++------------------- 5 files changed, 176 insertions(+), 181 deletions(-) diff --git a/bin/keystone b/bin/keystone index db4d1dca..d79189db 100755 --- a/bin/keystone +++ b/bin/keystone @@ -36,16 +36,18 @@ if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): import keystone.tools.tracer # @UnusedImport # module runs on import import keystone -from keystone.common import config, wsgi +from keystone.common import config if __name__ == '__main__': + # Initialize a parser for our configuration paramaters + # since we have special handling for --admin-port argument parser = optparse.OptionParser(version='%%prog %s' % keystone.version) common_group = config.add_common_options(parser) config.add_log_options(parser) # Handle a special argument to support starting two endpoints - common_group.add_option( + option = common_group.add_option( '-a', '--admin-port', dest="admin_port", metavar="PORT", help="specifies port for Admin API to listen on (default is 35357)") @@ -54,67 +56,22 @@ if __name__ == '__main__': # Start services try: - # Load Service API server - conf, app = config.load_paste_app( - 'keystone-legacy-auth', options, args) - admin_conf, admin_app = config.load_paste_app( - 'admin', options, args) - - debug = options.get('debug') or conf.get('debug', False) - debug = debug in [True, "True", "1"] - verbose = options.get('verbose') or conf.get('verbose', False) - verbose = verbose in [True, "True", "1"] - - # Safely get SSL options - service_ssl = conf.get('service_ssl', False) - service_ssl = service_ssl in [True, "True", "1"] - admin_ssl = conf.get('admin_ssl', False) - admin_ssl = admin_ssl in [True, "True", "1"] - cert_required = conf.get('cert_required', False) - cert_required = cert_required in [True, "True", "1"] - certfile = conf.get('certfile') - keyfile = conf.get('keyfile') - ca_certs = conf.get('ca_certs') - - if debug or verbose: - config_file = config.find_config_file(options, args) - print "Using config file:", config_file - - port = int(options.get('bind_port') or conf.get('service_port', 5000)) - host = options.get('bind_host', conf.get('service_host', '0.0.0.0')) - - # Load Service API server - if service_ssl: - server = wsgi.SslServer() - server.start(app, port, host, - certfile=certfile, keyfile=keyfile, - ca_certs=ca_certs, - cert_required=cert_required) - else: - server = wsgi.Server() - server.start(app, port, host) - - print "Service API (ssl=%s) listening on %s:%s" % ( - service_ssl, host, port) + # Load Service API Server + service = keystone.Server(name="Service API", + config_name='keystone-legacy-auth', + options=options, args=args) + service.start(wait=False) + except RuntimeError, e: + sys.exit("ERROR: %s" % e) + try: # Load Admin API server - port = int(options.get('admin_port') or conf.get('admin_port', 35357)) - host = options.get('bind_host', conf.get('admin_host', '0.0.0.0')) - - if admin_ssl: - admin_server = wsgi.SslServer() - admin_server.start(admin_app, port, host, - certfile=certfile, keyfile=keyfile, - ca_certs=ca_certs, - cert_required=cert_required) - else: - admin_server = wsgi.Server() - admin_server.start(admin_app, port, host) - - print "Admin API (ssl=%s) listening on %s:%s" % ( - admin_ssl, host, port) - - # Wait until done - server.wait() + port = options.get('admin_port', None) + host = options.get('bind_host', None) + admin = keystone.Server(name='Admin API', config_name='admin', + options=options, args=args) + admin.start(host=host, port=port, wait=True) except RuntimeError, e: sys.exit("ERROR: %s" % e) + finally: + service.stop() diff --git a/bin/keystone-admin b/bin/keystone-admin index fd5c6ef9..68549303 100755 --- a/bin/keystone-admin +++ b/bin/keystone-admin @@ -19,7 +19,7 @@ # under the License. """ -Keystone Identity Server - Admin API +Keystone Identity Server - Admin API """ import optparse @@ -36,40 +36,11 @@ if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): import keystone.tools.tracer # @UnusedImport # module runs on import import keystone -from keystone.common import config, wsgi - if __name__ == '__main__': - # Initialize a parser for our configuration paramaters - parser = optparse.OptionParser(version='%%prog %s' % keystone.version) - common_group = config.add_common_options(parser) - config.add_log_options(parser) - - # Parse arguments and load config - (options, args) = config.parse_options(parser) - config_file = config.find_config_file(options, args) - - # Start services try: # Load Admin API server - conf, app = config.load_paste_app('admin', options, args) - - debug = options.get('debug') or conf.get('debug', False) - debug = debug in [True, "True", "1"] - verbose = options.get('verbose') or conf.get('verbose', False) - verbose = verbose in [True, "True", "1"] - if debug or verbose: - print "Using config file:", config_file - - server = wsgi.Server() - - port = int(options['bind_port'] or conf['admin_port'] or 35357) - host = options['bind_host'] or conf['admin_host'] - - server.start(app, port, host) - - print "Admin API listening on %s:%s" % (host, port) - - server.wait() + admin = keystone.Server(name='Admin API', config_name='admin') + admin.start(wait=True) except RuntimeError, e: sys.exit("ERROR: %s" % e) diff --git a/bin/keystone-auth b/bin/keystone-auth index 73d43863..287482a0 100755 --- a/bin/keystone-auth +++ b/bin/keystone-auth @@ -36,41 +36,12 @@ if os.path.exists(os.path.join(possible_topdir, 'keystone', '__init__.py')): import keystone.tools.tracer # @UnusedImport # module runs on import import keystone -from keystone.common import config, wsgi - if __name__ == '__main__': - # Initialize a parser for our configuration paramaters - parser = optparse.OptionParser(version='%%prog %s' % keystone.version) - common_group = config.add_common_options(parser) - config.add_log_options(parser) - - # Parse arguments and load config - (options, args) = config.parse_options(parser) - - # Start services try: # Load Service API server - conf, app = config.load_paste_app('keystone-legacy-auth', - options, args) - - debug = options.get('debug') or conf.get('debug', False) - debug = debug in [True, "True", "1"] - verbose = options.get('verbose') or conf.get('verbose', False) - verbose = verbose in [True, "True", "1"] - if debug or verbose: - config_file = config.find_config_file(options, args) - print "Using config file:", config_file - - server = wsgi.Server() - - port = int(options['bind_port'] or conf['service_port'] or 5000) - host = options['bind_host'] or conf['service_host'] - - server.start(app, port, host) - - print "Service API listening on %s:%s" % (host, port) - - server.wait() + admin = keystone.Server(name='Service API', + config_name='keystone-legacy-auth') + admin.start(wait=True) except RuntimeError, e: sys.exit("ERROR: %s" % e) diff --git a/keystone/__init__.py b/keystone/__init__.py index 226b09ae..30ee337f 100644 --- a/keystone/__init__.py +++ b/keystone/__init__.py @@ -13,6 +13,10 @@ # limitations under the License. import gettext +import optparse +import sys + +from keystone.common import config, wsgi API_VERSION = "2.0" @@ -37,3 +41,130 @@ def version(): # This installs the _(...) function as a built-in so all other modules # don't need to. gettext.install('keystone') + + +class Server(): + """Used to start and stop Keystone servers + + This class is called from shell and command-line scripts and is the + entry-point for starting and stopping Keystone servers. + + The initializer can take option and argument overrides, but otherwise will + parse arguments and configuration files itself to determine how to start + the server. + """ + + def __init__(self, name='admin', config_name=None, + options=None, args=None): + """Initizalizer which takes the following paramaters: + + :param name: A cosmetic name for the server (ex. Admin API) + :param config: the paste config name to look for when starting the + server + :param options: a mapping of option key/str(value) pairs passed to + config.load_paste_app + :param args: override for sys.argv (otherwise sys.argv is used) + """ + self.options = options + if args: + self.args = args + else: + self.args = sys.argv + + if options is None or args is None: + # Initialize a parser for our configuration paramaters + parser = optparse.OptionParser(version='%%prog %s' % + version) + common_group = config.add_common_options(parser) + config.add_log_options(parser) + + # Parse arguments and load config + (poptions, pargs) = config.parse_options(parser) + + if options is None: + self.options = poptions + else: + self.options = options + + if args is None: + self.args = pargs + else: + self.args = args + + self.name = name + self.config = config_name or self.name + + def start(self, host=None, port=None, wait=True): + """Starts the Keystone server + + :param host: the IP address to listen on + :param port: the TCP/IP port to listen on + :param wait: whether to wait (block) for the server to terminate or + return to the caller without waiting + """ + # Load Service API server + conf, app = config.load_paste_app( + self.config, self.options, self.args) + + debug = self.options.get('debug') or conf.get('debug', False) + debug = debug in [True, "True", "1"] + verbose = self.options.get('verbose') or conf.get('verbose', False) + verbose = verbose in [True, "True", "1"] + + if debug or verbose: + config_file = config.find_config_file(self.options, self.args) + print "Starting '%s' with config: %s" % (self.config, config_file) + + if port is None: + if self.config == 'admin': + # Legacy + port = int(self.options.get('bind_port') or + conf.get('admin_port', 35357)) + else: + port = int(self.options.get('bind_port') or + conf.get('service_port', 5000)) + if host is None: + host = self.options.get('bind_host', + conf.get('service_host', '0.0.0.0')) + + self.key = "%s-%s:%s" % (self.name, host, port) + + # Safely get SSL options + service_ssl = conf.get('service_ssl', False) + service_ssl = service_ssl in [True, "True", "1"] + + # Load the server + if service_ssl: + cert_required = conf.get('cert_required', False) + cert_required = cert_required in [True, "True", "1"] + certfile = conf.get('certfile') + keyfile = conf.get('keyfile') + ca_certs = conf.get('ca_certs') + + self.server = wsgi.SslServer() + self.server.start(app, port, host, + certfile=certfile, keyfile=keyfile, + ca_certs=ca_certs, + cert_required=cert_required, + key=self.key) + else: + self.server = wsgi.Server() + self.server.start(app, port, host, + key="%s-%s:%s" % (self.config, host, port)) + + print "%s listening on %s://%s:%s" % ( + self.name, ['http', 'https'][service_ssl], host, port) + + # Wait until done + if wait: + self.server.wait() + + def stop(self): + """Stops the Keystone server + + This should be called always to release the network socket + """ + if self.server is not None: + if self.key in self.server.threads: + self.server.threads[self.key].kill() + self.server = None diff --git a/keystone/test/__init__.py b/keystone/test/__init__.py index 915eaf15..3765bfba 100644 --- a/keystone/test/__init__.py +++ b/keystone/test/__init__.py @@ -65,7 +65,7 @@ cgitb.enable(format="text") from functional.common import HttpTestCase import keystone from keystone.common import config, wsgi -from keystone import backends +from keystone import backends, Server TEST_DIR = os.path.abspath(os.path.dirname(__file__)) BASE_DIR = os.path.abspath(os.path.join(TEST_DIR, os.pardir, os.pardir)) @@ -399,61 +399,28 @@ class KeystoneTest(object): (options, args) = config.parse_options(parser) options['config_file'] = self.conf_fp.name - # Start services try: - # Load Service API server - conf, app = config.load_paste_app( - 'keystone-legacy-auth', options, args) - admin_conf, admin_app = config.load_paste_app( - 'admin', options, args) - - port = int(options['bind_port'] or conf['service_port'] or 5000) - host = options['bind_host'] or conf['service_host'] - - if (self.isSsl == True): - server = wsgi.SslServer() - server.start(app, port, host, - certfile=conf['certfile'], - keyfile=conf['keyfile'], - ca_certs=conf['ca_certs'], - cert_required=conf['cert_required']) - # Load Admin API server - port = int(options['admin_port'] or conf['admin_port'] - or 35357) - host = options['bind_host'] or conf['admin_host'] - - admin_server = wsgi.SslServer() - admin_server.start(admin_app, - port, host, - certfile=conf['certfile'], - keyfile=conf['keyfile'], - ca_certs=conf['ca_certs'], - cert_required=conf['cert_required']) - - else: - server = wsgi.Server() - server.start(app, port, host, key="Test") - - print "Service API (ssl=%s) listening on %s:%s" % ( - conf['service_ssl'], host, port) - - # Load Admin API server - port = int(options['admin_port'] or conf['admin_port'] - or 35357) - host = options['bind_host'] or conf['admin_host'] - - admin_server = wsgi.Server() - admin_server.start(admin_app, port, host, key="Test") - - print "Admin API (ssl=%s) listening on %s:%s" % ( - conf['admin_ssl'], host, port) + # Load Service API Server + service = keystone.Server(name="Service API", + config_name='keystone-legacy-auth', + options=options, args=args) + service.start(wait=False) + except RuntimeError, e: + sys.exit("ERROR: %s" % e) + try: + # Load Admin API server + port = options.get('admin_port', None) + host = options.get('bind_host', None) + admin = keystone.Server(name='Admin API', config_name='admin', + options=options, args=args) + admin.start(host=host, port=port, wait=False) except RuntimeError, e: - print e + service.stop() sys.exit("ERROR: %s" % e) - self.server = server - self.admin_server = admin_server + self.server = service + self.admin_server = admin # Load sample data from keystone.test import sampledata @@ -465,12 +432,10 @@ class KeystoneTest(object): print "Stopping the keystone server..." try: if self.server is not None: - if 'Test' in self.server.threads: - self.server.threads['Test'].kill() + self.server.stop() self.server = None if self.admin_server is not None: - if 'Test' in self.admin_server.threads: - self.admin_server.threads['Test'].kill() + self.admin_server.stop() self.admin_server = None self.conf_fp.close() self.conf_fp = None -- cgit