1 new commit in galaxy-central: https://bitbucket.org/galaxy/galaxy-central/commits/16efc5713d85/ Changeset: 16efc5713d85 User: jmchilton Date: 2015-02-02 05:54:51+00:00 Summary: Allow web-less job/workflow handlers. See notes in lib/galaxy/main.py for usage. lib/galaxy/main.py basically sets up an app (minus web controllers) very much in the way paste would but without binding it to a port. Pass --server-name to specify a server name (presumably a job handler found in job_conf.xml). An optional --daemonize mode is available - though it requires the daemonize library (install with `pip install daemonize`). Based on similar initial work in Pulsar (see https://github.com/galaxyproject/pulsar/blob/master/pulsar/daemon.py). Affected #: 2 files diff -r 57217275bebe445be7bdae43b9072bcca47ff3e7 -r 16efc5713d85f631c8ef9ad11895730f4747ea7e lib/galaxy/config.py --- a/lib/galaxy/config.py +++ b/lib/galaxy/config.py @@ -318,6 +318,9 @@ # Crummy, but PasteScript does not give you a way to determine this if arg.lower().startswith('--server-name='): self.server_name = arg.split('=', 1)[-1] + # Allow explicit override of server name in confg params + if "server_name" in kwargs: + self.server_name = kwargs.get("server_name") # Store all configured server names self.server_names = [] for section in global_conf_parser.sections(): diff -r 57217275bebe445be7bdae43b9072bcca47ff3e7 -r 16efc5713d85f631c8ef9ad11895730f4747ea7e lib/galaxy/main.py --- /dev/null +++ b/lib/galaxy/main.py @@ -0,0 +1,245 @@ +""" Entry point for starting Galaxy without starting as part of a web server. + +Example Usage: Start a job/workflow handler without a web server and with +a given name using. + +python lib/galaxy/main.py --server-name handler0 + +Start as a daemon with (requires daemonized - install with 'pip install daemonize'): + +python lib/galaxy/main.py -d --daemon-log-file=handler0-daemon.log --pid-file handler0.pid --server-name handler0 + +In daemon mode logging of Galaxy (as opposed to this script) is configured via +a loggers section in Galaxy's ini file - this can be overridden with sensible +defaults logging to a single file with the following: + +python lib/galaxy/main.py -d --server-name handler0 --daemon-log-file=handler0-daemon.log --pid-file handler0.pid --log-file handler0.log + +""" +import logging +from logging.config import fileConfig + +import functools +import os +import time +import sys + +try: + import ConfigParser as configparser +except ImportError: + import configparser + +try: + from daemonize import Daemonize +except ImportError: + Daemonize = None + +# Vaguely Python 2.6 compatibile ArgumentParser import +try: + from argparser import ArgumentParser +except ImportError: + from optparse import OptionParser + + class ArgumentParser(OptionParser): + + def __init__(self, **kwargs): + self.delegate = OptionParser(**kwargs) + + def add_argument(self, *args, **kwargs): + if "required" in kwargs: + del kwargs["required"] + return self.delegate.add_option(*args, **kwargs) + + def parse_args(self, args=None): + (options, args) = self.delegate.parse_args(args) + return options + +REQUIRES_DAEMONIZE_MESSAGE = "Attempted to use Galaxy in daemon mode, but daemonize is unavailable." + +log = logging.getLogger(__name__) + +GALAXY_ROOT_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..")) +GALAXY_LIB_DIR = os.path.join(GALAXY_ROOT_DIR, "lib") +DEFAULT_INI_APP = "main" +DEFAULT_INIS = ["config/galaxy.ini", "universe_wsgi.ini", "config/galaxy.ini.sample"] + +DEFAULT_PID = "galaxy.pid" +DEFAULT_VERBOSE = True +DESCRIPTION = "Daemonized entry point for Galaxy." + + +def load_galaxy_app( + config_builder, + config_env=False, + log=None, + **kwds +): + # Allow specification of log so daemon can reuse properly configured one. + if log is None: + log = logging.getLogger(__name__) + + # If called in daemon mode, set the ROOT directory and ensure Galaxy is on + # sys.path. + if config_env: + try: + os.chdir(GALAXY_ROOT_DIR) + except Exception: + log.exception("Failed to chdir") + raise + try: + sys.path.append(GALAXY_LIB_DIR) + except Exception: + log.exception("Failed to add Galaxy to sys.path") + raise + + config_builder.setup_logging() + from galaxy.util.properties import load_app_properties + kwds = config_builder.app_kwds() + kwds = load_app_properties(**kwds) + from galaxy.app import UniverseApplication + app = UniverseApplication( + global_conf={"__file__": config_builder.ini_path}, + **kwds + ) + return app + + +def app_loop(args, log): + try: + config_builder = GalaxyConfigBuilder(args) + galaxy_app = load_galaxy_app( + config_builder, + config_env=True, + log=log, + ) + except BaseException: + log.exception("Failed to initialize Galaxy application") + raise + sleep = True + while sleep: + try: + time.sleep(5) + except KeyboardInterrupt: + sleep = False + except SystemExit: + sleep = False + except Exception: + pass + try: + galaxy_app.shutdown() + except Exception: + log.exception("Failed to shutdown Galaxy application") + raise + + +def absolute_config_path(path, galaxy_root): + if path and not os.path.isabs(path): + path = os.path.join(galaxy_root, path) + return path + + +def find_ini(supplied_ini, galaxy_root): + if supplied_ini: + return supplied_ini + + # If not explicitly supplied an ini, check server.ini and then + # just restort to sample if that has not been configured. + for guess in DEFAULT_INIS: + ini_path = os.path.join(galaxy_root, guess) + if os.path.exists(ini_path): + return ini_path + + return guess + + +class GalaxyConfigBuilder(object): + """ Generate paste-like configuration from supplied command-line arguments. + """ + + def __init__(self, args=None, **kwds): + ini_path = kwds.get("ini_path", None) or (args and args.ini_path) + # If given app_conf_path - use that - else we need to ensure we have an + # ini path. + if not ini_path: + galaxy_root = kwds.get("galaxy_root", GALAXY_ROOT_DIR) + ini_path = find_ini(ini_path, galaxy_root) + ini_path = absolute_config_path(ini_path, galaxy_root=galaxy_root) + self.ini_path = ini_path + self.app_name = kwds.get("app") or (args and args.app) or DEFAULT_INI_APP + self.log_file = (args and args.log_file) + + @classmethod + def populate_options(cls, arg_parser): + arg_parser.add_argument("-c", "--ini-path", default=None, help="Galaxy ini config file (defaults to config/galaxy.ini)") + arg_parser.add_argument("--app", default=DEFAULT_INI_APP, help="app section in ini file (defaults to main)") + arg_parser.add_argument("-d", "--daemonize", default=False, help="Daemonzie process", action="store_true") + arg_parser.add_argument("--daemon-log-file", default=None, help="log file for daemon script ") + arg_parser.add_argument("--log-file", default=None, help="Galaxy log file (overrides log configuration in ini_path if set)") + arg_parser.add_argument("--pid-file", default=DEFAULT_PID, help="pid file (default is %s)" % DEFAULT_PID) + arg_parser.add_argument("--server-name", default=None, help="set a galaxy server name") + + def app_kwds(self): + config = dict( + ini_file=self.ini_path, + ini_section="app:%s" % self.app_name, + ) + return config + + def setup_logging(self): + # Galaxy will attempt to setup logging if loggers is not present in + # ini config file - this handles that loggers block however if present + # (the way paste normally would) + if not self.ini_path: + return + raw_config = configparser.ConfigParser() + raw_config.read([self.ini_path]) + if raw_config.has_section('loggers'): + config_file = os.path.abspath(self.ini_path) + fileConfig( + config_file, + dict(__file__=config_file, here=os.path.dirname(config_file)) + ) + + +def main(): + if Daemonize is None: + raise ImportError(REQUIRES_DAEMONIZE_MESSAGE) + + arg_parser = ArgumentParser(description=DESCRIPTION) + GalaxyConfigBuilder.populate_options(arg_parser) + args = arg_parser.parse_args() + if args.log_file: + os.environ["GALAXY_CONFIG_LOG_DESTINATION"] = os.path.abspath(args.log_file) + if args.server_name: + os.environ["GALAXY_CONFIG_SERVER_NAME"] = args.server_name + pid_file = args.pid_file + + log.setLevel(logging.DEBUG) + log.propagate = False + if args.daemonize: + keep_fds = [] + if args.daemon_log_file: + fh = logging.FileHandler(args.daemon_log_file, "w") + fh.setLevel(logging.DEBUG) + log.addHandler(fh) + keep_fds.append(fh.stream.fileno()) + else: + fh = logging.StreamHandler(sys.stderr) + fh.setLevel(logging.DEBUG) + log.addHandler(fh) + + daemon = Daemonize( + app="galaxy", + pid=pid_file, + action=functools.partial(app_loop, args, log), + verbose=DEFAULT_VERBOSE, + logger=log, + keep_fds=keep_fds, + ) + daemon.start() + else: + app_loop(args, log) + + +if __name__ == "__main__": + main() Repository URL: https://bitbucket.org/galaxy/galaxy-central/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.