~ttx/nova/d4-merge

« back to all changes in this revision

Viewing changes to nova/twistd.py

  • Committer: Thierry Carrez
  • Date: 2011-08-23 12:23:07 UTC
  • mfrom: (1130.75.258 nova)
  • Revision ID: thierry@openstack.org-20110823122307-f0vtuyg1ikc14n87
Merge diablo-4 development from trunk (rev1479)

Show diffs side-by-side

added added

removed removed

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