blob: 5469773ea615df9037741a83dfc58ead029fb374 [file] [log] [blame]
Joshua Hesketh0ddd6382013-07-26 10:33:36 +10001#!/usr/bin/python2
2#
Joshua Hesketh39a0fee2013-07-31 12:00:53 +10003# Copyright 2013 Rackspace Australia
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.
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100016
Joshua Hesketh1377f6f2013-07-26 12:02:15 +100017
18""" worker_server.py is an executable worker server that loads and runs
19task_plugins. """
20
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100021import argparse
22import daemon
23import extras
24import imp
25import json
26import logging
27import os
28import signal
29import sys
30
31import worker_manager
32
33# as of python-daemon 1.6 it doesn't bundle pidlockfile anymore
34# instead it depends on lockfile-0.9.1 which uses pidfile.
Joshua Hesketh1377f6f2013-07-26 12:02:15 +100035PID_FILE_MODULE = extras.try_imports(['daemon.pidlockfile', 'daemon.pidfile'])
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100036
37
38class Server(object):
39
40 """ This is the worker server object to be daemonized """
Joshua Hesketh363d0042013-07-26 11:44:07 +100041 log = logging.getLogger("worker_server.Server")
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100042
43 def __init__(self, config):
44 # Config init
45 self.config = config
46 self.manager = None
47 self.plugins = []
48 self.load_plugins()
49
50 # Python logging output file.
51 self.debug_log = self.config['debug_log']
52
53 self.tasks = {}
54
55 def setup_logging(self):
56 if self.debug_log:
Joshua Hesketh6b6700b2013-07-26 16:47:32 +100057 if not os.path.isdir(os.path.dirname(self.debug_log)):
58 os.makedirs(os.path.dirname(self.debug_log))
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100059 logging.basicConfig(format='%(asctime)s %(message)s',
60 filename=self.debug_log, level=logging.DEBUG)
61 else:
62 logging.basicConfig(format='%(asctime)s %(message)s',
63 level=logging.WARN)
64 self.log.debug('Log pusher starting.')
65
66 def load_plugins(self):
67 """ Load the available plugins from task_plugins """
68 # Load plugins
Joshua Hesketh6b6700b2013-07-26 16:47:32 +100069 for plugin in self.config['plugins']:
70 print
71 plugin_info = imp.find_module('task',
72 [(os.path.dirname(
73 os.path.realpath(__file__)) +
74 '/task_plugins/' + plugin)])
75 self.plugins.append(imp.load_module('task', *plugin_info))
Joshua Hesketh0ddd6382013-07-26 10:33:36 +100076
77 def run_tasks(self):
78 """ Run the tasks """
79 for plugin in self.plugins:
80 self.tasks[plugin.__worker_name__] = plugin.Runner(self.config)
81 self.tasks[plugin.__worker_name__].daemon = True
82 self.tasks[plugin.__worker_name__].start()
83
84 self.manager = worker_manager.GearmanManager(self.config, self.tasks)
85 self.manager.daemon = True
86 self.manager.start()
87
88 def exit_handler(self, signum):
89 signal.signal(signal.SIGUSR1, signal.SIG_IGN)
90 for task_name, task in self.tasks.items():
91 task.stop()
92 self.manager.stop()
93 sys.exit(0)
94
95 def main(self):
96 self.setup_logging()
97 self.run_tasks()
98
99 while True:
100 try:
101 signal.pause()
102 except KeyboardInterrupt:
103 print "Ctrl + C: asking tasks to exit nicely...\n"
104 self.exit_handler(signal.SIGINT)
105
106
107def main():
108 parser = argparse.ArgumentParser()
109 parser.add_argument('-c', '--config',
110 default=
Joshua Hesketh363d0042013-07-26 11:44:07 +1000111 '/etc/turbo-hipster/config.json',
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000112 help='Path to json config file.')
Joshua Hesketh478f1512013-07-26 11:47:27 +1000113 parser.add_argument('--background', action='store_true',
114 help='Run in the background.')
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000115 parser.add_argument('-p', '--pidfile',
Joshua Hesketh363d0042013-07-26 11:44:07 +1000116 default='/var/run/turbo-hipster/'
Joshua Heskethe58ecb22013-09-06 12:39:46 +1000117 'turbo-hipster-worker-server.pid',
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000118 help='PID file to lock during daemonization.')
119 args = parser.parse_args()
120
121 with open(args.config, 'r') as config_stream:
122 config = json.load(config_stream)
123
124 server = Server(config)
125
Joshua Hesketh478f1512013-07-26 11:47:27 +1000126 if args.background:
Joshua Hesketh1377f6f2013-07-26 12:02:15 +1000127 pidfile = PID_FILE_MODULE.TimeoutPIDLockFile(args.pidfile, 10)
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000128 with daemon.DaemonContext(pidfile=pidfile):
129 server.main()
Joshua Hesketh478f1512013-07-26 11:47:27 +1000130 else:
131 server.main()
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000132
133
134if __name__ == '__main__':
Joshua Heskethac445b52013-08-14 12:55:22 +1000135 sys.path.insert(0, os.path.abspath(
136 os.path.join(os.path.dirname(__file__), '../')))
Joshua Hesketh0ddd6382013-07-26 10:33:36 +1000137 main()