Statistics
| Branch: | Tag: | Revision:

root / lib / daemon.py @ 63d44c55

History | View | Annotate | Download (7.7 kB)

1 821d9e43 Michael Hanselmann
#
2 821d9e43 Michael Hanselmann
#
3 821d9e43 Michael Hanselmann
4 821d9e43 Michael Hanselmann
# Copyright (C) 2006, 2007, 2008 Google Inc.
5 821d9e43 Michael Hanselmann
#
6 821d9e43 Michael Hanselmann
# This program is free software; you can redistribute it and/or modify
7 821d9e43 Michael Hanselmann
# it under the terms of the GNU General Public License as published by
8 821d9e43 Michael Hanselmann
# the Free Software Foundation; either version 2 of the License, or
9 821d9e43 Michael Hanselmann
# (at your option) any later version.
10 821d9e43 Michael Hanselmann
#
11 821d9e43 Michael Hanselmann
# This program is distributed in the hope that it will be useful, but
12 821d9e43 Michael Hanselmann
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 821d9e43 Michael Hanselmann
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 821d9e43 Michael Hanselmann
# General Public License for more details.
15 821d9e43 Michael Hanselmann
#
16 821d9e43 Michael Hanselmann
# You should have received a copy of the GNU General Public License
17 821d9e43 Michael Hanselmann
# along with this program; if not, write to the Free Software
18 821d9e43 Michael Hanselmann
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 821d9e43 Michael Hanselmann
# 02110-1301, USA.
20 821d9e43 Michael Hanselmann
21 821d9e43 Michael Hanselmann
22 821d9e43 Michael Hanselmann
"""Module with helper classes and functions for daemons"""
23 821d9e43 Michael Hanselmann
24 821d9e43 Michael Hanselmann
25 112d240d Guido Trotter
import asyncore
26 3b1b0cb6 Guido Trotter
import os
27 821d9e43 Michael Hanselmann
import select
28 821d9e43 Michael Hanselmann
import signal
29 821d9e43 Michael Hanselmann
import errno
30 04ccf5e9 Guido Trotter
import logging
31 a02b89cf Guido Trotter
import sched
32 a02b89cf Guido Trotter
import time
33 821d9e43 Michael Hanselmann
34 821d9e43 Michael Hanselmann
from ganeti import utils
35 04ccf5e9 Guido Trotter
from ganeti import constants
36 a02b89cf Guido Trotter
from ganeti import errors
37 a02b89cf Guido Trotter
38 a02b89cf Guido Trotter
39 a02b89cf Guido Trotter
class SchedulerBreakout(Exception):
40 a02b89cf Guido Trotter
  """Exception used to get out of the scheduler loop
41 a02b89cf Guido Trotter

42 a02b89cf Guido Trotter
  """
43 a02b89cf Guido Trotter
44 a02b89cf Guido Trotter
45 a02b89cf Guido Trotter
def AsyncoreDelayFunction(timeout):
46 a02b89cf Guido Trotter
  """Asyncore-compatible scheduler delay function.
47 a02b89cf Guido Trotter

48 a02b89cf Guido Trotter
  This is a delay function for sched that, rather than actually sleeping,
49 a02b89cf Guido Trotter
  executes asyncore events happening in the meantime.
50 a02b89cf Guido Trotter

51 a02b89cf Guido Trotter
  After an event has occurred, rather than returning, it raises a
52 a02b89cf Guido Trotter
  SchedulerBreakout exception, which will force the current scheduler.run()
53 a02b89cf Guido Trotter
  invocation to terminate, so that we can also check for signals. The main loop
54 a02b89cf Guido Trotter
  will then call the scheduler run again, which will allow it to actually
55 a02b89cf Guido Trotter
  process any due events.
56 a02b89cf Guido Trotter

57 a02b89cf Guido Trotter
  This is needed because scheduler.run() doesn't support a count=..., as
58 a02b89cf Guido Trotter
  asyncore loop, and the scheduler module documents throwing exceptions from
59 a02b89cf Guido Trotter
  inside the delay function as an allowed usage model.
60 a02b89cf Guido Trotter

61 a02b89cf Guido Trotter
  """
62 a02b89cf Guido Trotter
  asyncore.loop(timeout=timeout, count=1, use_poll=True)
63 a02b89cf Guido Trotter
  raise SchedulerBreakout()
64 a02b89cf Guido Trotter
65 a02b89cf Guido Trotter
66 a02b89cf Guido Trotter
class AsyncoreScheduler(sched.scheduler):
67 a02b89cf Guido Trotter
  """Event scheduler integrated with asyncore
68 a02b89cf Guido Trotter

69 a02b89cf Guido Trotter
  """
70 a02b89cf Guido Trotter
  def __init__(self, timefunc):
71 a02b89cf Guido Trotter
    sched.scheduler.__init__(self, timefunc, AsyncoreDelayFunction)
72 821d9e43 Michael Hanselmann
73 821d9e43 Michael Hanselmann
74 821d9e43 Michael Hanselmann
class Mainloop(object):
75 821d9e43 Michael Hanselmann
  """Generic mainloop for daemons
76 821d9e43 Michael Hanselmann

77 821d9e43 Michael Hanselmann
  """
78 821d9e43 Michael Hanselmann
  def __init__(self):
79 b14b975f Michael Hanselmann
    """Constructs a new Mainloop instance.
80 b14b975f Michael Hanselmann

81 a02b89cf Guido Trotter
    @ivar scheduler: A L{sched.scheduler} object, which can be used to register
82 a02b89cf Guido Trotter
    timed events
83 a02b89cf Guido Trotter

84 b14b975f Michael Hanselmann
    """
85 821d9e43 Michael Hanselmann
    self._signal_wait = []
86 a02b89cf Guido Trotter
    self.scheduler = AsyncoreScheduler(time.time)
87 821d9e43 Michael Hanselmann
88 9b739173 Guido Trotter
  @utils.SignalHandled([signal.SIGCHLD])
89 9b739173 Guido Trotter
  @utils.SignalHandled([signal.SIGTERM])
90 9b739173 Guido Trotter
  def Run(self, stop_on_empty=False, signal_handlers=None):
91 b14b975f Michael Hanselmann
    """Runs the mainloop.
92 b14b975f Michael Hanselmann

93 b14b975f Michael Hanselmann
    @type stop_on_empty: bool
94 b14b975f Michael Hanselmann
    @param stop_on_empty: Whether to stop mainloop once all I/O waiters
95 b14b975f Michael Hanselmann
                          unregistered
96 9b739173 Guido Trotter
    @type signal_handlers: dict
97 9b739173 Guido Trotter
    @param signal_handlers: signal->L{utils.SignalHandler} passed by decorator
98 b14b975f Michael Hanselmann

99 b14b975f Michael Hanselmann
    """
100 9b739173 Guido Trotter
    assert isinstance(signal_handlers, dict) and \
101 9b739173 Guido Trotter
           len(signal_handlers) > 0, \
102 9b739173 Guido Trotter
           "Broken SignalHandled decorator"
103 9b739173 Guido Trotter
    running = True
104 9b739173 Guido Trotter
    # Start actual main loop
105 9b739173 Guido Trotter
    while running:
106 9b739173 Guido Trotter
      # Stop if nothing is listening anymore
107 9b739173 Guido Trotter
      if stop_on_empty and not (self._io_wait):
108 9b739173 Guido Trotter
        break
109 9b739173 Guido Trotter
110 a02b89cf Guido Trotter
      if not self.scheduler.empty():
111 a02b89cf Guido Trotter
        try:
112 a02b89cf Guido Trotter
          self.scheduler.run()
113 a02b89cf Guido Trotter
        except SchedulerBreakout:
114 a02b89cf Guido Trotter
          pass
115 a02b89cf Guido Trotter
      else:
116 a02b89cf Guido Trotter
        asyncore.loop(count=1, use_poll=True)
117 9b739173 Guido Trotter
118 9b739173 Guido Trotter
      # Check whether a signal was raised
119 9b739173 Guido Trotter
      for sig in signal_handlers:
120 9b739173 Guido Trotter
        handler = signal_handlers[sig]
121 9b739173 Guido Trotter
        if handler.called:
122 9b739173 Guido Trotter
          self._CallSignalWaiters(sig)
123 9b739173 Guido Trotter
          running = (sig != signal.SIGTERM)
124 9b739173 Guido Trotter
          handler.Clear()
125 a570e2a8 Guido Trotter
126 b14b975f Michael Hanselmann
  def _CallSignalWaiters(self, signum):
127 b14b975f Michael Hanselmann
    """Calls all signal waiters for a certain signal.
128 b14b975f Michael Hanselmann

129 b14b975f Michael Hanselmann
    @type signum: int
130 b14b975f Michael Hanselmann
    @param signum: Signal number
131 b14b975f Michael Hanselmann

132 b14b975f Michael Hanselmann
    """
133 b14b975f Michael Hanselmann
    for owner in self._signal_wait:
134 b14b975f Michael Hanselmann
      owner.OnSignal(signal.SIGCHLD)
135 821d9e43 Michael Hanselmann
136 821d9e43 Michael Hanselmann
  def RegisterSignal(self, owner):
137 821d9e43 Michael Hanselmann
    """Registers a receiver for signal notifications
138 821d9e43 Michael Hanselmann

139 821d9e43 Michael Hanselmann
    The receiver must support a "OnSignal(self, signum)" function.
140 821d9e43 Michael Hanselmann

141 821d9e43 Michael Hanselmann
    @type owner: instance
142 821d9e43 Michael Hanselmann
    @param owner: Receiver
143 821d9e43 Michael Hanselmann

144 821d9e43 Michael Hanselmann
    """
145 821d9e43 Michael Hanselmann
    self._signal_wait.append(owner)
146 b11c9e5c Michael Hanselmann
147 04ccf5e9 Guido Trotter
148 04ccf5e9 Guido Trotter
def GenericMain(daemon_name, optionparser, dirs, check_fn, exec_fn):
149 04ccf5e9 Guido Trotter
  """Shared main function for daemons.
150 04ccf5e9 Guido Trotter

151 04ccf5e9 Guido Trotter
  @type daemon_name: string
152 04ccf5e9 Guido Trotter
  @param daemon_name: daemon name
153 04ccf5e9 Guido Trotter
  @type optionparser: L{optparse.OptionParser}
154 04ccf5e9 Guido Trotter
  @param optionparser: initialized optionparser with daemon-specific options
155 04ccf5e9 Guido Trotter
                       (common -f -d options will be handled by this module)
156 04ccf5e9 Guido Trotter
  @type options: object @param options: OptionParser result, should contain at
157 04ccf5e9 Guido Trotter
                 least the fork and the debug options
158 04ccf5e9 Guido Trotter
  @type dirs: list of strings
159 04ccf5e9 Guido Trotter
  @param dirs: list of directories that must exist for this daemon to work
160 04ccf5e9 Guido Trotter
  @type check_fn: function which accepts (options, args)
161 04ccf5e9 Guido Trotter
  @param check_fn: function that checks start conditions and exits if they're
162 04ccf5e9 Guido Trotter
                   not met
163 04ccf5e9 Guido Trotter
  @type exec_fn: function which accepts (options, args)
164 04ccf5e9 Guido Trotter
  @param exec_fn: function that's executed with the daemon's pid file held, and
165 04ccf5e9 Guido Trotter
                  runs the daemon itself.
166 04ccf5e9 Guido Trotter

167 04ccf5e9 Guido Trotter
  """
168 04ccf5e9 Guido Trotter
  optionparser.add_option("-f", "--foreground", dest="fork",
169 04ccf5e9 Guido Trotter
                          help="Don't detach from the current terminal",
170 04ccf5e9 Guido Trotter
                          default=True, action="store_false")
171 04ccf5e9 Guido Trotter
  optionparser.add_option("-d", "--debug", dest="debug",
172 04ccf5e9 Guido Trotter
                          help="Enable some debug messages",
173 04ccf5e9 Guido Trotter
                          default=False, action="store_true")
174 04ccf5e9 Guido Trotter
  if daemon_name in constants.DAEMONS_PORTS:
175 04ccf5e9 Guido Trotter
    # for networked daemons we also allow choosing the bind port and address.
176 04ccf5e9 Guido Trotter
    # by default we use the port provided by utils.GetDaemonPort, and bind to
177 04ccf5e9 Guido Trotter
    # 0.0.0.0 (which is represented by and empty bind address.
178 04ccf5e9 Guido Trotter
    port = utils.GetDaemonPort(daemon_name)
179 04ccf5e9 Guido Trotter
    optionparser.add_option("-p", "--port", dest="port",
180 04ccf5e9 Guido Trotter
                            help="Network port (%s default)." % port,
181 04ccf5e9 Guido Trotter
                            default=port, type="int")
182 04ccf5e9 Guido Trotter
    optionparser.add_option("-b", "--bind", dest="bind_address",
183 04ccf5e9 Guido Trotter
                            help="Bind address",
184 04ccf5e9 Guido Trotter
                            default="", metavar="ADDRESS")
185 04ccf5e9 Guido Trotter
186 3b1b0cb6 Guido Trotter
  if daemon_name in constants.DAEMONS_SSL:
187 3b1b0cb6 Guido Trotter
    default_cert, default_key = constants.DAEMONS_SSL[daemon_name]
188 3b1b0cb6 Guido Trotter
    optionparser.add_option("--no-ssl", dest="ssl",
189 3b1b0cb6 Guido Trotter
                            help="Do not secure HTTP protocol with SSL",
190 3b1b0cb6 Guido Trotter
                            default=True, action="store_false")
191 3b1b0cb6 Guido Trotter
    optionparser.add_option("-K", "--ssl-key", dest="ssl_key",
192 3b1b0cb6 Guido Trotter
                            help="SSL key",
193 3b1b0cb6 Guido Trotter
                            default=default_key, type="string")
194 3b1b0cb6 Guido Trotter
    optionparser.add_option("-C", "--ssl-cert", dest="ssl_cert",
195 3b1b0cb6 Guido Trotter
                            help="SSL certificate",
196 3b1b0cb6 Guido Trotter
                            default=default_cert, type="string")
197 3b1b0cb6 Guido Trotter
198 04ccf5e9 Guido Trotter
  multithread = utils.no_fork = daemon_name in constants.MULTITHREADED_DAEMONS
199 04ccf5e9 Guido Trotter
200 04ccf5e9 Guido Trotter
  options, args = optionparser.parse_args()
201 04ccf5e9 Guido Trotter
202 3b1b0cb6 Guido Trotter
  if hasattr(options, 'ssl') and options.ssl:
203 3b1b0cb6 Guido Trotter
    if not (options.ssl_cert and options.ssl_key):
204 3b1b0cb6 Guido Trotter
      print >> sys.stderr, "Need key and certificate to use ssl"
205 3b1b0cb6 Guido Trotter
      sys.exit(constants.EXIT_FAILURE)
206 3b1b0cb6 Guido Trotter
    for fname in (options.ssl_cert, options.ssl_key):
207 3b1b0cb6 Guido Trotter
      if not os.path.isfile(fname):
208 3b1b0cb6 Guido Trotter
        print >> sys.stderr, "Need ssl file %s to run" % fname
209 3b1b0cb6 Guido Trotter
        sys.exit(constants.EXIT_FAILURE)
210 3b1b0cb6 Guido Trotter
211 3b1b0cb6 Guido Trotter
  if check_fn is not None:
212 3b1b0cb6 Guido Trotter
    check_fn(options, args)
213 3b1b0cb6 Guido Trotter
214 04ccf5e9 Guido Trotter
  utils.EnsureDirs(dirs)
215 04ccf5e9 Guido Trotter
216 04ccf5e9 Guido Trotter
  if options.fork:
217 04ccf5e9 Guido Trotter
    utils.CloseFDs()
218 04ccf5e9 Guido Trotter
    utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
219 04ccf5e9 Guido Trotter
220 04ccf5e9 Guido Trotter
  utils.WritePidFile(daemon_name)
221 04ccf5e9 Guido Trotter
  try:
222 04ccf5e9 Guido Trotter
    utils.SetupLogging(logfile=constants.DAEMONS_LOGFILES[daemon_name],
223 04ccf5e9 Guido Trotter
                       debug=options.debug,
224 04ccf5e9 Guido Trotter
                       stderr_logging=not options.fork,
225 04ccf5e9 Guido Trotter
                       multithreaded=multithread)
226 04ccf5e9 Guido Trotter
    logging.info("%s daemon startup" % daemon_name)
227 04ccf5e9 Guido Trotter
    exec_fn(options, args)
228 04ccf5e9 Guido Trotter
  finally:
229 04ccf5e9 Guido Trotter
    utils.RemovePidFile(daemon_name)