Factor out common code between cli utilities

The client, merger and server share common code. Factor it out to the
new class zuul.cmd.ZuulApp().

* Moved stack_dump_handler there. It is still a function.

* setup_logging() is shared by merger and server. The client simply
  override it (--verbose simply set the debug level).  We might want one
  day to have the client look at zuul.conf for its logging
  configuration.

* The merger now reports the Zuul version via the argparse action
  'version'.  The action asks argparse to invokes a method, print its
  result and exit immediately.  That brings it on par with client and
  server which have been using that action since commit aabb686b

* Client.gear_server_pid property is gone.  Seems to be a left over when
  the client got created out of the server code.

Change-Id: I0a3984a5650408ac5f5d6ecdb7518c339b392492
This commit is contained in:
Antoine Musso 2014-04-05 23:01:35 +02:00
parent 11041d2130
commit 3775875a42
5 changed files with 90 additions and 122 deletions

View File

@ -17,7 +17,7 @@ import logging
import signal import signal
import testtools import testtools
import zuul.cmd.server import zuul.cmd
class TestStackDump(testtools.TestCase): class TestStackDump(testtools.TestCase):
@ -29,6 +29,6 @@ class TestStackDump(testtools.TestCase):
def test_stack_dump_logs(self): def test_stack_dump_logs(self):
"Test that stack dumps end up in logs." "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("Thread", self.log_fixture.output)
self.assertIn("test_stack_dump_logs", self.log_fixture.output) self.assertIn("test_stack_dump_logs", self.log_fixture.output)

View File

@ -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)

View File

@ -16,26 +16,20 @@
import argparse import argparse
import babel.dates import babel.dates
import ConfigParser
import datetime import datetime
import logging import logging
import logging.config
import os
import prettytable import prettytable
import sys import sys
import time import time
import zuul.rpcclient import zuul.rpcclient
import zuul.cmd
class Client(object): class Client(zuul.cmd.ZuulApp):
log = logging.getLogger("zuul.Client") log = logging.getLogger("zuul.Client")
def __init__(self):
self.args = None
self.config = None
self.gear_server_pid = None
def parse_arguments(self): def parse_arguments(self):
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Zuul Project Gating System Client.') description='Zuul Project Gating System Client.')
@ -89,24 +83,8 @@ class Client(object):
self.args = parser.parse_args() 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): def setup_logging(self):
"""Client logging does not rely on conf file"""
if self.args.verbose: if self.args.verbose:
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)

View File

@ -15,7 +15,6 @@
# under the License. # under the License.
import argparse import argparse
import ConfigParser
import daemon import daemon
import extras import extras
@ -23,12 +22,11 @@ import extras
# instead it depends on lockfile-0.9.1 which uses pidfile. # instead it depends on lockfile-0.9.1 which uses pidfile.
pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile']) pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile'])
import logging
import logging.config
import os import os
import sys import sys
import signal import signal
import traceback
import zuul.cmd
# No zuul imports here because they pull in paramiko which must not be # No zuul imports here because they pull in paramiko which must not be
# imported until after the daemonization. # imported until after the daemonization.
@ -36,21 +34,7 @@ import traceback
# Similar situation with gear and statsd. # Similar situation with gear and statsd.
def stack_dump_handler(signum, frame): class Merger(zuul.cmd.ZuulApp):
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
def parse_arguments(self): def parse_arguments(self):
parser = argparse.ArgumentParser(description='Zuul merge worker.') parser = argparse.ArgumentParser(description='Zuul merge worker.')
@ -58,33 +42,11 @@ class Merger(object):
help='specify the config file') help='specify the config file')
parser.add_argument('-d', dest='nodaemon', action='store_true', parser.add_argument('-d', dest='nodaemon', action='store_true',
help='do not run as a daemon') 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') help='show zuul version')
self.args = parser.parse_args() 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): def exit_handler(self, signum, frame):
signal.signal(signal.SIGUSR1, signal.SIG_IGN) signal.signal(signal.SIGUSR1, signal.SIG_IGN)
self.merger.stop() self.merger.stop()
@ -100,7 +62,7 @@ class Merger(object):
self.merger.start() self.merger.start()
signal.signal(signal.SIGUSR1, self.exit_handler) 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: while True:
try: try:
signal.pause() signal.pause()
@ -113,11 +75,6 @@ def main():
server = Merger() server = Merger()
server.parse_arguments() 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() server.read_config()
if server.config.has_option('zuul', 'state_dir'): if server.config.has_option('zuul', 'state_dir'):

View File

@ -15,7 +15,6 @@
# under the License. # under the License.
import argparse import argparse
import ConfigParser
import daemon import daemon
import extras import extras
@ -24,11 +23,11 @@ import extras
pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile']) pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile'])
import logging import logging
import logging.config
import os import os
import sys import sys
import signal import signal
import traceback
import zuul.cmd
# No zuul imports here because they pull in paramiko which must not be # No zuul imports here because they pull in paramiko which must not be
# imported until after the daemonization. # imported until after the daemonization.
@ -36,21 +35,9 @@ import traceback
# Similar situation with gear and statsd. # Similar situation with gear and statsd.
def stack_dump_handler(signum, frame): class Server(zuul.cmd.ZuulApp):
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):
def __init__(self): def __init__(self):
self.args = None super(Server, self).__init__()
self.config = None
self.gear_server_pid = None self.gear_server_pid = None
def parse_arguments(self): def parse_arguments(self):
@ -71,33 +58,6 @@ class Server(object):
help='show zuul version') help='show zuul version')
self.args = parser.parse_args() 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): def reconfigure_handler(self, signum, frame):
signal.signal(signal.SIGHUP, signal.SIG_IGN) signal.signal(signal.SIGHUP, signal.SIG_IGN)
self.read_config() self.read_config()
@ -235,7 +195,7 @@ class Server(object):
signal.signal(signal.SIGHUP, self.reconfigure_handler) signal.signal(signal.SIGHUP, self.reconfigure_handler)
signal.signal(signal.SIGUSR1, self.exit_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) signal.signal(signal.SIGTERM, self.term_handler)
while True: while True:
try: try: