James E. Blair | 1f4c2bb | 2013-04-26 08:40:46 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python |
James E. Blair | 1ce97ad | 2012-05-29 15:38:19 -0700 | [diff] [blame] | 2 | # Copyright 2012 Hewlett-Packard Development Company, L.P. |
James E. Blair | 4795838 | 2013-01-10 17:26:02 -0800 | [diff] [blame] | 3 | # Copyright 2013 OpenStack Foundation |
James E. Blair | 1ce97ad | 2012-05-29 15:38:19 -0700 | [diff] [blame] | 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 | |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 17 | import argparse |
James E. Blair | d3bbe00 | 2012-05-31 13:06:31 -0700 | [diff] [blame] | 18 | import daemon |
Monty Taylor | a94bf17 | 2012-11-25 13:00:03 -0800 | [diff] [blame] | 19 | import extras |
Antoine Musso | 80925f5 | 2012-09-22 21:37:45 +0200 | [diff] [blame] | 20 | |
Monty Taylor | a94bf17 | 2012-11-25 13:00:03 -0800 | [diff] [blame] | 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. |
James E. Blair | 138d336 | 2012-11-30 15:10:43 -0800 | [diff] [blame] | 23 | pid_file_module = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile']) |
Antoine Musso | 80925f5 | 2012-09-22 21:37:45 +0200 | [diff] [blame] | 24 | |
Clark Boylan | fba9b24 | 2013-08-20 10:11:17 -0700 | [diff] [blame] | 25 | import logging |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 26 | import os |
James E. Blair | 4795838 | 2013-01-10 17:26:02 -0800 | [diff] [blame] | 27 | import sys |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 28 | import signal |
Antoine Musso | 3775875 | 2014-04-05 23:01:35 +0200 | [diff] [blame] | 29 | |
| 30 | import zuul.cmd |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 31 | |
James E. Blair | 3266340 | 2012-06-01 10:04:18 -0700 | [diff] [blame] | 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 |
James E. Blair | dda083b | 2014-01-09 07:04:37 +0800 | [diff] [blame] | 35 | # Similar situation with gear and statsd. |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 36 | |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 37 | |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 38 | class Scheduler(zuul.cmd.ZuulApp): |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 39 | def __init__(self): |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 40 | super(Scheduler, self).__init__() |
James E. Blair | 922a9f6 | 2013-05-22 14:44:58 -0700 | [diff] [blame] | 41 | self.gear_server_pid = None |
James E. Blair | 1e8dd89 | 2012-05-30 09:15:05 -0700 | [diff] [blame] | 42 | |
Clint Byrum | 44430ec | 2017-05-01 12:49:32 -0700 | [diff] [blame^] | 43 | def parse_arguments(self, args=None): |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 44 | parser = argparse.ArgumentParser(description='Project gating system.') |
| 45 | parser.add_argument('-c', dest='config', |
| 46 | help='specify the config file') |
James E. Blair | d3bbe00 | 2012-05-31 13:06:31 -0700 | [diff] [blame] | 47 | parser.add_argument('-d', dest='nodaemon', action='store_true', |
| 48 | help='do not run as a daemon') |
Clint Byrum | 240d860 | 2017-02-20 14:10:39 -0500 | [diff] [blame] | 49 | parser.add_argument('-t', dest='validate', action='store_true', |
| 50 | help='validate config file syntax (Does not' |
| 51 | 'validate config repo validity)') |
Antoine Musso | aabb686 | 2014-01-14 17:32:09 +0100 | [diff] [blame] | 52 | parser.add_argument('--version', dest='version', action='version', |
| 53 | version=self._get_version(), |
Antoine Musso | 26b1fb8 | 2013-03-21 16:31:40 +0100 | [diff] [blame] | 54 | help='show zuul version') |
Clint Byrum | 44430ec | 2017-05-01 12:49:32 -0700 | [diff] [blame^] | 55 | self.args = parser.parse_args(args) |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 56 | |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 57 | def reconfigure_handler(self, signum, frame): |
| 58 | signal.signal(signal.SIGHUP, signal.SIG_IGN) |
Joshua Hesketh | 352264b | 2015-08-11 23:42:08 +1000 | [diff] [blame] | 59 | self.log.debug("Reconfiguration triggered") |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 60 | self.read_config() |
James E. Blair | f9eeb94 | 2013-06-18 08:36:07 -0700 | [diff] [blame] | 61 | self.setup_logging('zuul', 'log_config') |
Evgeny Antyshev | 0051cd4 | 2015-07-03 11:39:10 +0000 | [diff] [blame] | 62 | try: |
| 63 | self.sched.reconfigure(self.config) |
| 64 | except Exception: |
| 65 | self.log.exception("Reconfiguration failed:") |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 66 | signal.signal(signal.SIGHUP, self.reconfigure_handler) |
James E. Blair | 1e8dd89 | 2012-05-30 09:15:05 -0700 | [diff] [blame] | 67 | |
James E. Blair | 5d5bc2b | 2012-07-06 10:24:01 -0700 | [diff] [blame] | 68 | def exit_handler(self, signum, frame): |
| 69 | signal.signal(signal.SIGUSR1, signal.SIG_IGN) |
| 70 | self.sched.exit() |
James E. Blair | 6767905 | 2013-08-26 17:05:00 -0700 | [diff] [blame] | 71 | self.sched.join() |
| 72 | self.stop_gear_server() |
James E. Blair | 5d5bc2b | 2012-07-06 10:24:01 -0700 | [diff] [blame] | 73 | |
James E. Blair | 922a9f6 | 2013-05-22 14:44:58 -0700 | [diff] [blame] | 74 | def term_handler(self, signum, frame): |
| 75 | self.stop_gear_server() |
| 76 | os._exit(0) |
| 77 | |
Clint Byrum | 240d860 | 2017-02-20 14:10:39 -0500 | [diff] [blame] | 78 | def test_config(self): |
James E. Blair | 4795838 | 2013-01-10 17:26:02 -0800 | [diff] [blame] | 79 | # See comment at top of file about zuul imports |
| 80 | import zuul.scheduler |
Paul Belanger | 174a827 | 2017-03-14 13:20:10 -0400 | [diff] [blame] | 81 | import zuul.executor.client |
James E. Blair | 4795838 | 2013-01-10 17:26:02 -0800 | [diff] [blame] | 82 | |
| 83 | logging.basicConfig(level=logging.DEBUG) |
Clint Byrum | 240d860 | 2017-02-20 14:10:39 -0500 | [diff] [blame] | 84 | try: |
| 85 | self.sched = zuul.scheduler.Scheduler(self.config, |
| 86 | testonly=True) |
| 87 | except Exception as e: |
| 88 | self.log.error("%s" % e) |
| 89 | return -1 |
| 90 | return 0 |
James E. Blair | 4795838 | 2013-01-10 17:26:02 -0800 | [diff] [blame] | 91 | |
James E. Blair | 922a9f6 | 2013-05-22 14:44:58 -0700 | [diff] [blame] | 92 | def start_gear_server(self): |
| 93 | pipe_read, pipe_write = os.pipe() |
| 94 | child_pid = os.fork() |
| 95 | if child_pid == 0: |
| 96 | os.close(pipe_write) |
| 97 | self.setup_logging('gearman_server', 'log_config') |
James E. Blair | d437159 | 2016-06-08 16:55:04 -0700 | [diff] [blame] | 98 | import zuul.lib.gearserver |
James E. Blair | a77f28c | 2014-02-21 08:02:00 -0800 | [diff] [blame] | 99 | statsd_host = os.environ.get('STATSD_HOST') |
| 100 | statsd_port = int(os.environ.get('STATSD_PORT', 8125)) |
James E. Blair | 0ac452e | 2015-07-22 09:05:16 -0700 | [diff] [blame] | 101 | if self.config.has_option('gearman_server', 'listen_address'): |
| 102 | host = self.config.get('gearman_server', 'listen_address') |
| 103 | else: |
| 104 | host = None |
James E. Blair | d437159 | 2016-06-08 16:55:04 -0700 | [diff] [blame] | 105 | zuul.lib.gearserver.GearServer(4730, |
| 106 | host=host, |
| 107 | statsd_host=statsd_host, |
| 108 | statsd_port=statsd_port, |
| 109 | statsd_prefix='zuul.geard') |
James E. Blair | a77f28c | 2014-02-21 08:02:00 -0800 | [diff] [blame] | 110 | |
James E. Blair | 922a9f6 | 2013-05-22 14:44:58 -0700 | [diff] [blame] | 111 | # Keep running until the parent dies: |
| 112 | pipe_read = os.fdopen(pipe_read) |
| 113 | pipe_read.read() |
| 114 | os._exit(0) |
| 115 | else: |
| 116 | os.close(pipe_read) |
| 117 | self.gear_server_pid = child_pid |
| 118 | self.gear_pipe_write = pipe_write |
| 119 | |
| 120 | def stop_gear_server(self): |
| 121 | if self.gear_server_pid: |
| 122 | os.kill(self.gear_server_pid, signal.SIGKILL) |
| 123 | |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 124 | def main(self): |
James E. Blair | 3266340 | 2012-06-01 10:04:18 -0700 | [diff] [blame] | 125 | # See comment at top of file about zuul imports |
| 126 | import zuul.scheduler |
Paul Belanger | 174a827 | 2017-03-14 13:20:10 -0400 | [diff] [blame] | 127 | import zuul.executor.client |
James E. Blair | 4076e2b | 2014-01-28 12:42:20 -0800 | [diff] [blame] | 128 | import zuul.merger.client |
James E. Blair | 8d69239 | 2016-04-08 17:47:58 -0700 | [diff] [blame] | 129 | import zuul.nodepool |
James E. Blair | 1f4c2bb | 2013-04-26 08:40:46 -0700 | [diff] [blame] | 130 | import zuul.webapp |
James E. Blair | ad28e91 | 2013-11-27 10:43:22 -0800 | [diff] [blame] | 131 | import zuul.rpclistener |
Paul Belanger | bbb4875 | 2017-02-21 10:56:40 -0500 | [diff] [blame] | 132 | import zuul.zk |
James E. Blair | 3266340 | 2012-06-01 10:04:18 -0700 | [diff] [blame] | 133 | |
Antoine Musso | 29eab01 | 2014-10-28 21:35:22 +0100 | [diff] [blame] | 134 | signal.signal(signal.SIGUSR2, zuul.cmd.stack_dump_handler) |
James E. Blair | 922a9f6 | 2013-05-22 14:44:58 -0700 | [diff] [blame] | 135 | if (self.config.has_option('gearman_server', 'start') and |
| 136 | self.config.getboolean('gearman_server', 'start')): |
| 137 | self.start_gear_server() |
| 138 | |
| 139 | self.setup_logging('zuul', 'log_config') |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 140 | self.log = logging.getLogger("zuul.Scheduler") |
James E. Blair | 922a9f6 | 2013-05-22 14:44:58 -0700 | [diff] [blame] | 141 | |
Joshua Hesketh | 352264b | 2015-08-11 23:42:08 +1000 | [diff] [blame] | 142 | self.sched = zuul.scheduler.Scheduler(self.config) |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 143 | |
Paul Belanger | 9cd1286 | 2017-03-15 13:03:59 -0400 | [diff] [blame] | 144 | gearman = zuul.executor.client.ExecutorClient(self.config, self.sched) |
James E. Blair | 4076e2b | 2014-01-28 12:42:20 -0800 | [diff] [blame] | 145 | merger = zuul.merger.client.MergeClient(self.config, self.sched) |
James E. Blair | 8d69239 | 2016-04-08 17:47:58 -0700 | [diff] [blame] | 146 | nodepool = zuul.nodepool.Nodepool(self.sched) |
Joshua Hesketh | 850ccb6 | 2014-11-27 11:31:02 +1100 | [diff] [blame] | 147 | |
Paul Belanger | bbb4875 | 2017-02-21 10:56:40 -0500 | [diff] [blame] | 148 | zookeeper = zuul.zk.ZooKeeper() |
| 149 | if self.config.has_option('zuul', 'zookeeper_hosts'): |
| 150 | zookeeper_hosts = self.config.get('zuul', 'zookeeper_hosts') |
| 151 | else: |
| 152 | zookeeper_hosts = '127.0.0.1:2181' |
| 153 | |
| 154 | zookeeper.connect(zookeeper_hosts) |
| 155 | |
Clark Boylan | e0b4bdb | 2014-06-03 17:01:25 -0700 | [diff] [blame] | 156 | if self.config.has_option('zuul', 'status_expiry'): |
| 157 | cache_expiry = self.config.getint('zuul', 'status_expiry') |
| 158 | else: |
| 159 | cache_expiry = 1 |
Paul Belanger | 88ef0ea | 2015-12-23 11:57:02 -0500 | [diff] [blame] | 160 | |
| 161 | if self.config.has_option('webapp', 'listen_address'): |
| 162 | listen_address = self.config.get('webapp', 'listen_address') |
| 163 | else: |
| 164 | listen_address = '0.0.0.0' |
| 165 | |
| 166 | if self.config.has_option('webapp', 'port'): |
| 167 | port = self.config.getint('webapp', 'port') |
| 168 | else: |
| 169 | port = 8001 |
| 170 | |
| 171 | webapp = zuul.webapp.WebApp( |
| 172 | self.sched, port=port, cache_expiry=cache_expiry, |
| 173 | listen_address=listen_address) |
James E. Blair | ad28e91 | 2013-11-27 10:43:22 -0800 | [diff] [blame] | 174 | rpc = zuul.rpclistener.RPCListener(self.config, self.sched) |
James E. Blair | 1e8dd89 | 2012-05-30 09:15:05 -0700 | [diff] [blame] | 175 | |
James E. Blair | fef7894 | 2016-03-11 16:28:56 -0800 | [diff] [blame] | 176 | self.configure_connections() |
Paul Belanger | 174a827 | 2017-03-14 13:20:10 -0400 | [diff] [blame] | 177 | self.sched.setExecutor(gearman) |
James E. Blair | 4076e2b | 2014-01-28 12:42:20 -0800 | [diff] [blame] | 178 | self.sched.setMerger(merger) |
James E. Blair | 8d69239 | 2016-04-08 17:47:58 -0700 | [diff] [blame] | 179 | self.sched.setNodepool(nodepool) |
Paul Belanger | bbb4875 | 2017-02-21 10:56:40 -0500 | [diff] [blame] | 180 | self.sched.setZooKeeper(zookeeper) |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 181 | |
Antoine Musso | 8f30d57 | 2014-01-15 21:57:09 +0100 | [diff] [blame] | 182 | self.log.info('Starting scheduler') |
James E. Blair | 99f9310 | 2017-02-20 17:27:40 -0500 | [diff] [blame] | 183 | try: |
| 184 | self.sched.start() |
Jan Hruban | 7083edd | 2015-08-21 14:00:54 +0200 | [diff] [blame] | 185 | self.sched.registerConnections(self.connections, webapp) |
James E. Blair | 99f9310 | 2017-02-20 17:27:40 -0500 | [diff] [blame] | 186 | self.sched.reconfigure(self.config) |
| 187 | self.sched.resume() |
| 188 | except Exception: |
| 189 | self.log.exception("Error starting Zuul:") |
| 190 | # TODO(jeblair): If we had all threads marked as daemon, |
| 191 | # we might be able to have a nicer way of exiting here. |
| 192 | sys.exit(1) |
Antoine Musso | 8f30d57 | 2014-01-15 21:57:09 +0100 | [diff] [blame] | 193 | self.log.info('Starting Webapp') |
James E. Blair | 1f4c2bb | 2013-04-26 08:40:46 -0700 | [diff] [blame] | 194 | webapp.start() |
Antoine Musso | 8f30d57 | 2014-01-15 21:57:09 +0100 | [diff] [blame] | 195 | self.log.info('Starting RPC') |
James E. Blair | ad28e91 | 2013-11-27 10:43:22 -0800 | [diff] [blame] | 196 | rpc.start() |
James E. Blair | 1f4c2bb | 2013-04-26 08:40:46 -0700 | [diff] [blame] | 197 | |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 198 | signal.signal(signal.SIGHUP, self.reconfigure_handler) |
James E. Blair | 5d5bc2b | 2012-07-06 10:24:01 -0700 | [diff] [blame] | 199 | signal.signal(signal.SIGUSR1, self.exit_handler) |
James E. Blair | 922a9f6 | 2013-05-22 14:44:58 -0700 | [diff] [blame] | 200 | signal.signal(signal.SIGTERM, self.term_handler) |
James E. Blair | e9d45c3 | 2012-05-31 09:56:45 -0700 | [diff] [blame] | 201 | while True: |
Antoine Musso | 6ad5d5b | 2012-09-24 13:25:28 +0200 | [diff] [blame] | 202 | try: |
| 203 | signal.pause() |
| 204 | except KeyboardInterrupt: |
Morgan Fainberg | 4c6a774 | 2016-05-27 08:42:17 -0700 | [diff] [blame] | 205 | print("Ctrl + C: asking scheduler to exit nicely...\n") |
Monty Taylor | 7df9e7f | 2012-11-25 12:12:28 -0800 | [diff] [blame] | 206 | self.exit_handler(signal.SIGINT, None) |
James E. Blair | ee74361 | 2012-05-29 14:49:32 -0700 | [diff] [blame] | 207 | |
Monty Taylor | 7df9e7f | 2012-11-25 12:12:28 -0800 | [diff] [blame] | 208 | |
| 209 | def main(): |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 210 | scheduler = Scheduler() |
| 211 | scheduler.parse_arguments() |
Antoine Musso | 26b1fb8 | 2013-03-21 16:31:40 +0100 | [diff] [blame] | 212 | |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 213 | scheduler.read_config() |
James E. Blair | d3bbe00 | 2012-05-31 13:06:31 -0700 | [diff] [blame] | 214 | |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 215 | if scheduler.args.validate: |
Clint Byrum | 240d860 | 2017-02-20 14:10:39 -0500 | [diff] [blame] | 216 | sys.exit(scheduler.test_config()) |
James E. Blair | 4795838 | 2013-01-10 17:26:02 -0800 | [diff] [blame] | 217 | |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 218 | if scheduler.config.has_option('zuul', 'pidfile'): |
| 219 | pid_fn = os.path.expanduser(scheduler.config.get('zuul', 'pidfile')) |
James E. Blair | d3bbe00 | 2012-05-31 13:06:31 -0700 | [diff] [blame] | 220 | else: |
Clark Boylan | b80fae0 | 2017-02-20 16:26:43 -0500 | [diff] [blame] | 221 | pid_fn = '/var/run/zuul-scheduler/zuul-scheduler.pid' |
Antoine Musso | 80925f5 | 2012-09-22 21:37:45 +0200 | [diff] [blame] | 222 | pid = pid_file_module.TimeoutPIDLockFile(pid_fn, 10) |
James E. Blair | d3bbe00 | 2012-05-31 13:06:31 -0700 | [diff] [blame] | 223 | |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 224 | if scheduler.args.nodaemon: |
| 225 | scheduler.main() |
James E. Blair | d3bbe00 | 2012-05-31 13:06:31 -0700 | [diff] [blame] | 226 | else: |
| 227 | with daemon.DaemonContext(pidfile=pid): |
James E. Blair | 5f0f514 | 2017-02-14 15:36:31 -0800 | [diff] [blame] | 228 | scheduler.main() |
James E. Blair | 1f4c2bb | 2013-04-26 08:40:46 -0700 | [diff] [blame] | 229 | |
| 230 | |
| 231 | if __name__ == "__main__": |
| 232 | sys.path.insert(0, '.') |
| 233 | main() |