blob: c9516f8d5c1a4b52a7d94687608841b558d95f78 [file] [log] [blame]
James E. Blair9f365192016-04-18 10:34:48 -07001#!/usr/bin/env python
2# Copyright 2012 Hewlett-Packard Development Company, L.P.
3# Copyright 2013-2014 OpenStack Foundation
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may
6# not use this file except in compliance with the License. You may obtain
7# a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations
15# under the License.
16
17import argparse
18import daemon
19import extras
20
21# as of python-daemon 1.6 it doesn't bundle pidlockfile anymore
22# instead it depends on lockfile-0.9.1 which uses pidfile.
23pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile'])
24
James E. Blair119acf32016-04-18 15:34:36 -070025import logging
James E. Blair9f365192016-04-18 10:34:48 -070026import os
27import sys
28import signal
29
30import zuul.cmd
31
32# No zuul imports here because they pull in paramiko which must not be
33# imported until after the daemonization.
34# https://github.com/paramiko/paramiko/issues/59
35# Similar situation with gear and statsd.
36
37
38class Launcher(zuul.cmd.ZuulApp):
39
40 def parse_arguments(self):
41 parser = argparse.ArgumentParser(description='Zuul launch worker.')
42 parser.add_argument('-c', dest='config',
43 help='specify the config file')
44 parser.add_argument('-d', dest='nodaemon', action='store_true',
45 help='do not run as a daemon')
46 parser.add_argument('--version', dest='version', action='version',
47 version=self._get_version(),
48 help='show zuul version')
James E. Blairf87c5ce2016-05-25 08:43:37 -070049 parser.add_argument('--keep-jobdir', dest='keep_jobdir',
50 action='store_true',
51 help='keep local jobdirs after run completes')
James E. Blair9f365192016-04-18 10:34:48 -070052 self.args = parser.parse_args()
53
James E. Blair119acf32016-04-18 15:34:36 -070054 def reconfigure_handler(self, signum, frame):
55 signal.signal(signal.SIGHUP, signal.SIG_IGN)
56 self.log.debug("Reconfiguration triggered")
57 self.read_config()
58 self.setup_logging('launcher', 'log_config')
59 try:
60 self.launcher.reconfigure(self.config)
61 except Exception:
62 self.log.exception("Reconfiguration failed:")
63 signal.signal(signal.SIGHUP, self.reconfigure_handler)
64
James E. Blair9f365192016-04-18 10:34:48 -070065 def exit_handler(self, signum, frame):
66 signal.signal(signal.SIGUSR1, signal.SIG_IGN)
67 self.launcher.stop()
68 self.launcher.join()
69
70 def main(self):
71 # See comment at top of file about zuul imports
72 import zuul.launcher.ansiblelaunchserver
73
74 self.setup_logging('launcher', 'log_config')
75
James E. Blair119acf32016-04-18 15:34:36 -070076 self.log = logging.getLogger("zuul.Launcher")
77
James E. Blair9f365192016-04-18 10:34:48 -070078 LaunchServer = zuul.launcher.ansiblelaunchserver.LaunchServer
James E. Blairf87c5ce2016-05-25 08:43:37 -070079 self.launcher = LaunchServer(self.config,
80 keep_jobdir=self.args.keep_jobdir)
James E. Blair9f365192016-04-18 10:34:48 -070081 self.launcher.start()
82
James E. Blair119acf32016-04-18 15:34:36 -070083 signal.signal(signal.SIGHUP, self.reconfigure_handler)
James E. Blair9f365192016-04-18 10:34:48 -070084 signal.signal(signal.SIGUSR1, self.exit_handler)
85 signal.signal(signal.SIGUSR2, zuul.cmd.stack_dump_handler)
86 while True:
87 try:
88 signal.pause()
89 except KeyboardInterrupt:
90 print "Ctrl + C: asking launcher to exit nicely...\n"
91 self.exit_handler(signal.SIGINT, None)
92 sys.exit(0)
93
94
95def main():
96 server = Launcher()
97 server.parse_arguments()
98
99 server.read_config()
100 server.configure_connections()
101
102 if server.config.has_option('launcher', 'pidfile'):
103 pid_fn = os.path.expanduser(server.config.get('launcher', 'pidfile'))
104 else:
105 pid_fn = '/var/run/zuul-launcher/zuul-launcher.pid'
106 pid = pid_file_module.TimeoutPIDLockFile(pid_fn, 10)
107
108 if server.args.nodaemon:
109 server.main()
110 else:
111 with daemon.DaemonContext(pidfile=pid):
112 server.main()
113
114
115if __name__ == "__main__":
116 sys.path.insert(0, '.')
117 main()