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

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

View File

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

View File

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