~justin-fathomdb/nova/justinsb-openstack-api-volumes

« back to all changes in this revision

Viewing changes to nova/twistd.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
# Copyright [2010] [Anso Labs, LLC]
 
3
 
4
#    Licensed under the Apache License, Version 2.0 (the "License");
 
5
#    you may not use this file except in compliance with the License.
 
6
#    You may obtain a copy of the License at
 
7
 
8
#        http://www.apache.org/licenses/LICENSE-2.0
 
9
 
10
#    Unless required by applicable law or agreed to in writing, software
 
11
#    distributed under the License is distributed on an "AS IS" BASIS,
 
12
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
#    See the License for the specific language governing permissions and
 
14
#    limitations under the License.
 
15
 
 
16
"""
 
17
Twisted daemon helpers, specifically to parse out gFlags from twisted flags,
 
18
manage pid files and support syslogging.
 
19
"""
 
20
 
 
21
import logging
 
22
import os
 
23
import signal
 
24
import sys
 
25
import time
 
26
import UserDict
 
27
import logging.handlers
 
28
 
 
29
from nova import vendor
 
30
from twisted.scripts import twistd
 
31
from twisted.python import log
 
32
from twisted.python import reflect
 
33
from twisted.python import runtime
 
34
from twisted.python import usage
 
35
 
 
36
from nova import flags
 
37
 
 
38
if runtime.platformType == "win32":
 
39
    from twisted.scripts._twistw import ServerOptions
 
40
else:
 
41
    from twisted.scripts._twistd_unix import ServerOptions
 
42
 
 
43
 
 
44
FLAGS = flags.FLAGS
 
45
 
 
46
 
 
47
class TwistdServerOptions(ServerOptions):
 
48
    def parseArgs(self, *args):
 
49
        return
 
50
 
 
51
 
 
52
def WrapTwistedOptions(wrapped):
 
53
    class TwistedOptionsToFlags(wrapped):
 
54
        subCommands = None
 
55
        def __init__(self):
 
56
            # NOTE(termie): _data exists because Twisted stuff expects
 
57
            #               to be able to set arbitrary things that are
 
58
            #               not actual flags
 
59
            self._data = {}
 
60
            self._flagHandlers = {}
 
61
            self._paramHandlers = {}
 
62
 
 
63
            # Absorb the twistd flags into our FLAGS
 
64
            self._absorbFlags()
 
65
            self._absorbParameters()
 
66
            self._absorbHandlers()
 
67
 
 
68
            super(TwistedOptionsToFlags, self).__init__()
 
69
 
 
70
        def _absorbFlags(self):
 
71
            twistd_flags = []
 
72
            reflect.accumulateClassList(self.__class__, 'optFlags', twistd_flags)
 
73
            for flag in twistd_flags:
 
74
                key = flag[0].replace('-', '_')
 
75
                flags.DEFINE_boolean(key, None, str(flag[-1]))
 
76
 
 
77
        def _absorbParameters(self):
 
78
            twistd_params = []
 
79
            reflect.accumulateClassList(self.__class__, 'optParameters', twistd_params)
 
80
            for param in twistd_params:
 
81
                key = param[0].replace('-', '_')
 
82
                flags.DEFINE_string(key, param[2], str(param[-1]))
 
83
 
 
84
        def _absorbHandlers(self):
 
85
            twistd_handlers = {}
 
86
            reflect.addMethodNamesToDict(self.__class__, twistd_handlers, "opt_")
 
87
 
 
88
            # NOTE(termie): Much of the following is derived/copied from
 
89
            #               twisted.python.usage with the express purpose of
 
90
            #               providing compatibility
 
91
            for name in twistd_handlers.keys():
 
92
                method = getattr(self, 'opt_'+name)
 
93
 
 
94
                takesArg = not usage.flagFunction(method, name)
 
95
                doc = getattr(method, '__doc__', None)
 
96
                if not doc:
 
97
                    doc = 'undocumented'
 
98
 
 
99
                if not takesArg:
 
100
                    if name not in FLAGS:
 
101
                        flags.DEFINE_boolean(name, None, doc)
 
102
                    self._flagHandlers[name] = method
 
103
                else:
 
104
                    if name not in FLAGS:
 
105
                        flags.DEFINE_string(name, None, doc)
 
106
                    self._paramHandlers[name] = method
 
107
 
 
108
 
 
109
        def _doHandlers(self):
 
110
            for flag, handler in self._flagHandlers.iteritems():
 
111
                if self[flag]:
 
112
                    handler()
 
113
            for param, handler in self._paramHandlers.iteritems():
 
114
                if self[param] is not None:
 
115
                    handler(self[param])
 
116
 
 
117
        def __str__(self):
 
118
            return str(FLAGS)
 
119
 
 
120
        def parseOptions(self, options=None):
 
121
            if options is None:
 
122
                options = sys.argv
 
123
            else:
 
124
                options.insert(0, '')
 
125
 
 
126
            args = FLAGS(options)
 
127
            argv = args[1:]
 
128
            # ignore subcommands
 
129
 
 
130
            try:
 
131
                self.parseArgs(*argv)
 
132
            except TypeError:
 
133
                raise usage.UsageError("Wrong number of arguments.")
 
134
 
 
135
            self.postOptions()
 
136
            return args
 
137
 
 
138
        def parseArgs(self, *args):
 
139
            # TODO(termie): figure out a decent way of dealing with args
 
140
            #return
 
141
            super(TwistedOptionsToFlags, self).parseArgs(*args)
 
142
 
 
143
        def postOptions(self):
 
144
            self._doHandlers()
 
145
 
 
146
            super(TwistedOptionsToFlags, self).postOptions()
 
147
 
 
148
        def __getitem__(self, key):
 
149
            key = key.replace('-', '_')
 
150
            try:
 
151
                return getattr(FLAGS, key)
 
152
            except (AttributeError, KeyError):
 
153
                return self._data[key]
 
154
 
 
155
        def __setitem__(self, key, value):
 
156
            key = key.replace('-', '_')
 
157
            try:
 
158
                return setattr(FLAGS, key, value)
 
159
            except (AttributeError, KeyError):
 
160
                self._data[key] = value
 
161
 
 
162
    return TwistedOptionsToFlags
 
163
 
 
164
 
 
165
def stop(pidfile):
 
166
    """
 
167
    Stop the daemon
 
168
    """
 
169
    # Get the pid from the pidfile
 
170
    try:
 
171
        pf = file(pidfile,'r')
 
172
        pid = int(pf.read().strip())
 
173
        pf.close()
 
174
    except IOError:
 
175
        pid = None
 
176
 
 
177
    if not pid:
 
178
        message = "pidfile %s does not exist. Daemon not running?\n"
 
179
        sys.stderr.write(message % pidfile)
 
180
        return # not an error in a restart
 
181
 
 
182
    # Try killing the daemon process
 
183
    try:
 
184
        while 1:
 
185
            os.kill(pid, signal.SIGKILL)
 
186
            time.sleep(0.1)
 
187
    except OSError, err:
 
188
        err = str(err)
 
189
        if err.find("No such process") > 0:
 
190
            if os.path.exists(pidfile):
 
191
                os.remove(pidfile)
 
192
        else:
 
193
            print str(err)
 
194
            sys.exit(1)
 
195
 
 
196
 
 
197
def serve(filename):
 
198
    logging.debug("Serving %s" % filename)
 
199
    name = os.path.basename(filename)
 
200
    OptionsClass = WrapTwistedOptions(TwistdServerOptions)
 
201
    options = OptionsClass()
 
202
    argv = options.parseOptions()
 
203
    logging.getLogger('amqplib').setLevel(logging.WARN)
 
204
    FLAGS.python = filename
 
205
    FLAGS.no_save = True
 
206
    if not FLAGS.pidfile:
 
207
        FLAGS.pidfile = '%s.pid' % name
 
208
    elif FLAGS.pidfile.endswith('twistd.pid'):
 
209
        FLAGS.pidfile = FLAGS.pidfile.replace('twistd.pid', '%s.pid' % name)
 
210
 
 
211
    if not FLAGS.logfile:
 
212
        FLAGS.logfile = '%s.log' % name
 
213
 
 
214
    action = 'start'
 
215
    if len(argv) > 1:
 
216
        action = argv.pop()
 
217
 
 
218
    if action == 'stop':
 
219
        stop(FLAGS.pidfile)
 
220
        sys.exit()
 
221
    elif action == 'restart':
 
222
        stop(FLAGS.pidfile)
 
223
    elif action == 'start':
 
224
        pass
 
225
    else:
 
226
        print 'usage: %s [options] [start|stop|restart]' % argv[0]
 
227
        sys.exit(1)
 
228
 
 
229
    formatter = logging.Formatter(
 
230
        name + '(%(name)s): %(levelname)s %(message)s')
 
231
    handler = logging.StreamHandler(log.StdioOnnaStick())
 
232
    handler.setFormatter(formatter)
 
233
    logging.getLogger().addHandler(handler)
 
234
 
 
235
    if FLAGS.verbose:
 
236
        logging.getLogger().setLevel(logging.DEBUG)
 
237
    else:
 
238
        logging.getLogger().setLevel(logging.WARNING)
 
239
 
 
240
    if FLAGS.syslog:
 
241
        syslog = logging.handlers.SysLogHandler(address='/dev/log')
 
242
        syslog.setFormatter(formatter)
 
243
        logging.getLogger().addHandler(syslog)
 
244
 
 
245
    logging.debug("Full set of FLAGS:")
 
246
    for flag in FLAGS:
 
247
        logging.debug("%s : %s" % (flag, FLAGS.get(flag, None)))
 
248
 
 
249
    twistd.runApp(options)