diff --git a/tests/test_stack_dump.py b/tests/test_stack_dump.py index cc8cf8fd66..824e04c533 100644 --- a/tests/test_stack_dump.py +++ b/tests/test_stack_dump.py @@ -17,7 +17,7 @@ import logging import signal import testtools -import zuul.cmd.server +import zuul.cmd class TestStackDump(testtools.TestCase): @@ -29,6 +29,6 @@ class TestStackDump(testtools.TestCase): def test_stack_dump_logs(self): "Test that stack dumps end up in logs." - zuul.cmd.server.stack_dump_handler(signal.SIGUSR2, None) + zuul.cmd.stack_dump_handler(signal.SIGUSR2, None) self.assertIn("Thread", self.log_fixture.output) self.assertIn("test_stack_dump_logs", self.log_fixture.output) diff --git a/zuul/cmd/__init__.py b/zuul/cmd/__init__.py index e69de29bb2..e17ad5b9aa 100644 --- a/zuul/cmd/__init__.py +++ b/zuul/cmd/__init__.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# Copyright 2012 Hewlett-Packard Development Company, L.P. +# Copyright 2013 OpenStack Foundation +# +# 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. + +import ConfigParser +import logging +import logging.config +import os +import signal +import sys +import traceback + +# No zuul imports here because they pull in paramiko which must not be +# imported until after the daemonization. +# https://github.com/paramiko/paramiko/issues/59 +# Similar situation with gear and statsd. + + +def stack_dump_handler(signum, frame): + signal.signal(signal.SIGUSR2, signal.SIG_IGN) + log_str = "" + for thread_id, stack_frame in sys._current_frames().items(): + log_str += "Thread: %s\n" % thread_id + log_str += "".join(traceback.format_stack(stack_frame)) + log = logging.getLogger("zuul.stack_dump") + log.debug(log_str) + signal.signal(signal.SIGUSR2, stack_dump_handler) + + +class ZuulApp(object): + + def __init__(self): + self.args = None + self.config = None + + def _get_version(self): + from zuul.version import version_info as zuul_version_info + return "Zuul version: %s" % zuul_version_info.version_string() + + def read_config(self): + self.config = ConfigParser.ConfigParser() + if self.args.config: + locations = [self.args.config] + else: + locations = ['/etc/zuul/zuul.conf', + '~/zuul.conf'] + for fp in locations: + if os.path.exists(os.path.expanduser(fp)): + self.config.read(os.path.expanduser(fp)) + return + raise Exception("Unable to locate config file in %s" % locations) + + def setup_logging(self, section, parameter): + if self.config.has_option(section, parameter): + fp = os.path.expanduser(self.config.get(section, parameter)) + if not os.path.exists(fp): + raise Exception("Unable to read logging config file at %s" % + fp) + logging.config.fileConfig(fp) + else: + logging.basicConfig(level=logging.DEBUG) diff --git a/zuul/cmd/client.py b/zuul/cmd/client.py index 147fade9bd..766a4ef83b 100644 --- a/zuul/cmd/client.py +++ b/zuul/cmd/client.py @@ -16,26 +16,20 @@ import argparse import babel.dates -import ConfigParser import datetime import logging -import logging.config -import os import prettytable import sys import time + import zuul.rpcclient +import zuul.cmd -class Client(object): +class Client(zuul.cmd.ZuulApp): log = logging.getLogger("zuul.Client") - def __init__(self): - self.args = None - self.config = None - self.gear_server_pid = None - def parse_arguments(self): parser = argparse.ArgumentParser( description='Zuul Project Gating System Client.') @@ -89,24 +83,8 @@ class Client(object): self.args = parser.parse_args() - def _get_version(self): - from zuul.version import version_info as zuul_version_info - return "Zuul version: %s" % zuul_version_info.version_string() - - def read_config(self): - self.config = ConfigParser.ConfigParser() - if self.args.config: - locations = [self.args.config] - else: - locations = ['/etc/zuul/zuul.conf', - '~/zuul.conf'] - for fp in locations: - if os.path.exists(os.path.expanduser(fp)): - self.config.read(os.path.expanduser(fp)) - return - raise Exception("Unable to locate config file in %s" % locations) - def setup_logging(self): + """Client logging does not rely on conf file""" if self.args.verbose: logging.basicConfig(level=logging.DEBUG) diff --git a/zuul/cmd/merger.py b/zuul/cmd/merger.py index edf8da99de..dc3484a405 100644 --- a/zuul/cmd/merger.py +++ b/zuul/cmd/merger.py @@ -15,7 +15,6 @@ # under the License. import argparse -import ConfigParser import daemon import extras @@ -23,12 +22,11 @@ import extras # instead it depends on lockfile-0.9.1 which uses pidfile. pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile']) -import logging -import logging.config import os import sys import signal -import traceback + +import zuul.cmd # No zuul imports here because they pull in paramiko which must not be # imported until after the daemonization. @@ -36,21 +34,7 @@ import traceback # Similar situation with gear and statsd. -def stack_dump_handler(signum, frame): - signal.signal(signal.SIGUSR2, signal.SIG_IGN) - log_str = "" - for thread_id, stack_frame in sys._current_frames().items(): - log_str += "Thread: %s\n" % thread_id - log_str += "".join(traceback.format_stack(stack_frame)) - log = logging.getLogger("zuul.stack_dump") - log.debug(log_str) - signal.signal(signal.SIGUSR2, stack_dump_handler) - - -class Merger(object): - def __init__(self): - self.args = None - self.config = None +class Merger(zuul.cmd.ZuulApp): def parse_arguments(self): parser = argparse.ArgumentParser(description='Zuul merge worker.') @@ -58,33 +42,11 @@ class Merger(object): help='specify the config file') parser.add_argument('-d', dest='nodaemon', action='store_true', help='do not run as a daemon') - parser.add_argument('--version', dest='version', action='store_true', + parser.add_argument('--version', dest='version', action='version', + version=self._get_version(), help='show zuul version') self.args = parser.parse_args() - def read_config(self): - self.config = ConfigParser.ConfigParser() - if self.args.config: - locations = [self.args.config] - else: - locations = ['/etc/zuul/zuul.conf', - '~/zuul.conf'] - for fp in locations: - if os.path.exists(os.path.expanduser(fp)): - self.config.read(os.path.expanduser(fp)) - return - raise Exception("Unable to locate config file in %s" % locations) - - def setup_logging(self, section, parameter): - if self.config.has_option(section, parameter): - fp = os.path.expanduser(self.config.get(section, parameter)) - if not os.path.exists(fp): - raise Exception("Unable to read logging config file at %s" % - fp) - logging.config.fileConfig(fp) - else: - logging.basicConfig(level=logging.DEBUG) - def exit_handler(self, signum, frame): signal.signal(signal.SIGUSR1, signal.SIG_IGN) self.merger.stop() @@ -100,7 +62,7 @@ class Merger(object): self.merger.start() signal.signal(signal.SIGUSR1, self.exit_handler) - signal.signal(signal.SIGUSR2, stack_dump_handler) + signal.signal(signal.SIGUSR2, zuul.cmd.stack_dump_handler) while True: try: signal.pause() @@ -113,11 +75,6 @@ def main(): server = Merger() server.parse_arguments() - if server.args.version: - from zuul.version import version_info as zuul_version_info - print "Zuul version: %s" % zuul_version_info.version_string() - sys.exit(0) - server.read_config() if server.config.has_option('zuul', 'state_dir'): diff --git a/zuul/cmd/server.py b/zuul/cmd/server.py index 8caa1fd858..06ea780099 100755 --- a/zuul/cmd/server.py +++ b/zuul/cmd/server.py @@ -15,7 +15,6 @@ # under the License. import argparse -import ConfigParser import daemon import extras @@ -24,11 +23,11 @@ import extras pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile']) import logging -import logging.config import os import sys import signal -import traceback + +import zuul.cmd # No zuul imports here because they pull in paramiko which must not be # imported until after the daemonization. @@ -36,21 +35,9 @@ import traceback # Similar situation with gear and statsd. -def stack_dump_handler(signum, frame): - signal.signal(signal.SIGUSR2, signal.SIG_IGN) - log_str = "" - for thread_id, stack_frame in sys._current_frames().items(): - log_str += "Thread: %s\n" % thread_id - log_str += "".join(traceback.format_stack(stack_frame)) - log = logging.getLogger("zuul.stack_dump") - log.debug(log_str) - signal.signal(signal.SIGUSR2, stack_dump_handler) - - -class Server(object): +class Server(zuul.cmd.ZuulApp): def __init__(self): - self.args = None - self.config = None + super(Server, self).__init__() self.gear_server_pid = None def parse_arguments(self): @@ -71,33 +58,6 @@ class Server(object): help='show zuul version') self.args = parser.parse_args() - def _get_version(self): - from zuul.version import version_info as zuul_version_info - return "Zuul version: %s" % zuul_version_info.version_string() - - def read_config(self): - self.config = ConfigParser.ConfigParser() - if self.args.config: - locations = [self.args.config] - else: - locations = ['/etc/zuul/zuul.conf', - '~/zuul.conf'] - for fp in locations: - if os.path.exists(os.path.expanduser(fp)): - self.config.read(os.path.expanduser(fp)) - return - raise Exception("Unable to locate config file in %s" % locations) - - def setup_logging(self, section, parameter): - if self.config.has_option(section, parameter): - fp = os.path.expanduser(self.config.get(section, parameter)) - if not os.path.exists(fp): - raise Exception("Unable to read logging config file at %s" % - fp) - logging.config.fileConfig(fp) - else: - logging.basicConfig(level=logging.DEBUG) - def reconfigure_handler(self, signum, frame): signal.signal(signal.SIGHUP, signal.SIG_IGN) self.read_config() @@ -235,7 +195,7 @@ class Server(object): signal.signal(signal.SIGHUP, self.reconfigure_handler) signal.signal(signal.SIGUSR1, self.exit_handler) - signal.signal(signal.SIGUSR2, stack_dump_handler) + signal.signal(signal.SIGUSR2, zuul.cmd.stack_dump_handler) signal.signal(signal.SIGTERM, self.term_handler) while True: try: