1 |
|
#!/usr/bin/env python |
2 |
|
from Framework import BonsaiServer |
3 |
+ |
from Framework import Context |
4 |
+ |
from Framework.Logger import Logger |
5 |
+ |
from Framework.Logger import g_Logger |
6 |
+ |
from optparse import OptionParser |
7 |
+ |
from Framework import CmdLineArgs |
8 |
+ |
import sys |
9 |
+ |
import os |
10 |
+ |
import getpass |
11 |
+ |
from os.path import abspath |
12 |
+ |
import errno |
13 |
+ |
|
14 |
+ |
class Cfg: |
15 |
+ |
def __init__ (self): |
16 |
+ |
# TODO: make it a property. |
17 |
+ |
self.installRoot = __file__.rsplit ("/", 1)[0] |
18 |
+ |
|
19 |
+ |
class CommandFactory (object): |
20 |
+ |
def __init__ (self, context, opts, args): |
21 |
+ |
self.context = context |
22 |
+ |
self.registry = {"start": StartCommand, |
23 |
+ |
"status": StatusCommand, |
24 |
+ |
"stop": StopCommand} |
25 |
+ |
self.opts, self.args = opts, args |
26 |
+ |
|
27 |
+ |
def createByName (self, name): |
28 |
+ |
try: |
29 |
+ |
obj = self.registry[name] () |
30 |
+ |
except KeyError: |
31 |
+ |
return None |
32 |
+ |
obj.context = self.context |
33 |
+ |
obj.opts = self.opts |
34 |
+ |
obj.args = self.args |
35 |
+ |
return obj |
36 |
+ |
|
37 |
+ |
class Command (object): |
38 |
+ |
def __init__ (self): |
39 |
+ |
self.context = None |
40 |
+ |
self.opts = None |
41 |
+ |
self.args = None |
42 |
+ |
|
43 |
+ |
def finish (self): |
44 |
+ |
pass |
45 |
+ |
|
46 |
+ |
def checkIfPidRunning (pid): |
47 |
+ |
try: |
48 |
+ |
os.kill(pid, 0) |
49 |
+ |
return True |
50 |
+ |
except OSError, err: |
51 |
+ |
return err.errno == errno.EPERM |
52 |
+ |
|
53 |
+ |
def getPidFromFile (filename): |
54 |
+ |
try: |
55 |
+ |
return int (open (filename).read ().strip ()) |
56 |
+ |
except ValueError: |
57 |
+ |
print "Invalid lockfile format for %s." % filename |
58 |
+ |
print "Cannot detect status." |
59 |
+ |
sys.exit (2) |
60 |
+ |
|
61 |
+ |
class StatusCommand (Command): |
62 |
+ |
def run (self): |
63 |
+ |
filename = abspath (self.opts.pidFile) |
64 |
+ |
try: |
65 |
+ |
pid = getPidFromFile (filename) |
66 |
+ |
if checkIfPidRunning (pid): |
67 |
+ |
print "cmsWeb is running as pid %s" % pid |
68 |
+ |
sys.exit (0) |
69 |
+ |
print "cmsWeb not running." |
70 |
+ |
except IOError: |
71 |
+ |
print "File %s does not exists." % self.opts.pidFile |
72 |
+ |
print "Cannot detect status." |
73 |
+ |
sys.exit (2) |
74 |
+ |
|
75 |
+ |
class StopCommand (Command): |
76 |
+ |
def run (self): |
77 |
+ |
filename = abspath (self.opts.pidFile) |
78 |
+ |
username = getpass.getuser () |
79 |
+ |
|
80 |
+ |
try: |
81 |
+ |
pid = getPidFromFile (filename) |
82 |
+ |
if self.opts.forceKill == True: |
83 |
+ |
print "Sending SIGKILL to pid %s" % pid |
84 |
+ |
os.kill (pid, 9) |
85 |
+ |
sys.exit (0) |
86 |
+ |
print "Sending SIGTERM to pid %s" % pid |
87 |
+ |
os.kill (pid, 15) |
88 |
+ |
except OSError, err: |
89 |
+ |
if err.errno == errno.EPERM: |
90 |
+ |
print "Pid %s is not owned by user %s. Cannot stop it." % (pid, username) |
91 |
+ |
sys.exit (2) |
92 |
+ |
else: |
93 |
+ |
print "Pid %s does not exists. Please remove the lock file %s." % (pid, filename) |
94 |
+ |
except IOError: |
95 |
+ |
print "File %s does not exists." % self.opts.pidFile |
96 |
+ |
print "Cannot detect status." |
97 |
+ |
sys.exit (2) |
98 |
+ |
|
99 |
+ |
|
100 |
+ |
class StartCommand (Command): |
101 |
+ |
def run (self): |
102 |
+ |
app = BonsaiServer (self.context) |
103 |
+ |
opts, args = self.context.OptionParser ().parse_args () |
104 |
+ |
filename = abspath (self.opts.pidFile) |
105 |
+ |
try: |
106 |
+ |
pid = getPidFromFile (filename) |
107 |
+ |
if checkIfPidRunning (pid): |
108 |
+ |
print "A process %s is alredy running using lock file %s. Nothing is done." % (pid, filename) |
109 |
+ |
sys.exit (0) |
110 |
+ |
else: |
111 |
+ |
print "Process %s died unexpectedly. Removing lockfile %s." % (pid, filename) |
112 |
+ |
os.unlink (filename) |
113 |
+ |
except IOError: |
114 |
+ |
# TODO: this should be a warning. |
115 |
+ |
g_Logger.trace ("File %s does not exists. Will be created." % filename) |
116 |
+ |
|
117 |
+ |
open (filename, 'w').write (str (os.getpid ())) |
118 |
+ |
self.context.addService (CmdLineArgs (self.context.OptionParser ())) |
119 |
+ |
self.context.addService (Cfg ()) |
120 |
+ |
if opts.profile: |
121 |
+ |
import pstats |
122 |
+ |
try: |
123 |
+ |
import cProfile as profile |
124 |
+ |
except ImportError: |
125 |
+ |
import profile |
126 |
+ |
profile.run ('app.start ()', 'bonsaiProfiler') |
127 |
+ |
|
128 |
+ |
p = pstats.Stats ('bonsaiProfiler') |
129 |
+ |
p.strip_dirs().sort_stats (-1).print_stats() |
130 |
+ |
else: |
131 |
+ |
app.start () |
132 |
+ |
|
133 |
+ |
def finish (self): |
134 |
+ |
filename = abspath (self.opts.pidFile) |
135 |
+ |
try: |
136 |
+ |
os.unlink (filename) |
137 |
+ |
except IOError: |
138 |
+ |
print "Unable to remove lock file %s" % filename |
139 |
+ |
|
140 |
+ |
def getValidOptions (args): |
141 |
+ |
validArguments = ["start", |
142 |
+ |
"stop", |
143 |
+ |
"restart", |
144 |
+ |
"status"] |
145 |
+ |
validOptions = ["--cfg", "--force-kill", "--pid-file", |
146 |
+ |
"--log-file", "--log-level"] |
147 |
+ |
|
148 |
+ |
result = [] |
149 |
+ |
for i in range (0, len (args)): |
150 |
+ |
arg = sys.argv[i] |
151 |
+ |
if arg in validArguments: |
152 |
+ |
result.append (args[i]) |
153 |
+ |
|
154 |
+ |
for i in range (0, len (args)): |
155 |
+ |
option = args[i] |
156 |
+ |
if option in validOptions: |
157 |
+ |
result.append (option) |
158 |
+ |
result.append (args[i+1]) |
159 |
+ |
return result |
160 |
+ |
|
161 |
+ |
class CmsWebApplication (object): |
162 |
+ |
def __init__ (self): |
163 |
+ |
self.context = Context () |
164 |
+ |
self.context.addService (OptionParser ()) |
165 |
+ |
self.parser = self.context.OptionParser () |
166 |
+ |
self.__addOptions () |
167 |
+ |
|
168 |
+ |
def __addOptions (self): |
169 |
+ |
self.parser.add_option ("--profile", |
170 |
+ |
help="start server in profiler mode", |
171 |
+ |
default=False, |
172 |
+ |
action="store_true", |
173 |
+ |
dest="profile") |
174 |
+ |
|
175 |
+ |
self.parser.add_option ("--pid-file", |
176 |
+ |
help="File in which it is specified the pid of wanted instance", |
177 |
+ |
default="pid.txt", |
178 |
+ |
dest="pidFile", |
179 |
+ |
metavar="FILE") |
180 |
+ |
|
181 |
+ |
self.parser.add_option ("--force-kill", |
182 |
+ |
help="Uses SIGKILL rather than SIGTERM", |
183 |
+ |
default=False, |
184 |
+ |
action="store_true", |
185 |
+ |
dest="forceKill", |
186 |
+ |
metavar="FILE") |
187 |
+ |
|
188 |
+ |
def openFilename (option, opt_str, value, parser, *args, **kwargs): |
189 |
+ |
try: |
190 |
+ |
f=open (value, 'a') |
191 |
+ |
except IOError: |
192 |
+ |
print "WARNING: Unable to open log file %s. Using stderr." % value |
193 |
+ |
f=sys.stderr |
194 |
+ |
setattr (parser.values, option.dest, f) |
195 |
+ |
|
196 |
+ |
self.parser.add_option ("--log-file", |
197 |
+ |
help="FILE to which redirect log messages", |
198 |
+ |
dest="logFile", |
199 |
+ |
default=sys.stderr, |
200 |
+ |
action="callback", |
201 |
+ |
callback=openFilename, |
202 |
+ |
metavar="FILENAME", |
203 |
+ |
type="str", |
204 |
+ |
nargs=1) |
205 |
+ |
|
206 |
+ |
self.parser.add_option ("--log-level", |
207 |
+ |
help="detail LEVEL for the main log", |
208 |
+ |
dest="logLevel", |
209 |
+ |
default=10, |
210 |
+ |
metavar="LEVEL", |
211 |
+ |
type="int") |
212 |
+ |
|
213 |
+ |
def run (self): |
214 |
+ |
if "--help" in sys.argv: |
215 |
+ |
g_Logger.detailLevel = -100 |
216 |
+ |
validOptions = getValidOptions (sys.argv) |
217 |
+ |
|
218 |
+ |
opts, args = self.parser.parse_args (args=validOptions) |
219 |
+ |
|
220 |
+ |
g_Logger.stream = opts.logFile |
221 |
+ |
if "--help" not in sys.argv: |
222 |
+ |
g_Logger.detailLevel = opts.logLevel |
223 |
+ |
|
224 |
+ |
if not len (args): |
225 |
+ |
args = ["start"] |
226 |
+ |
|
227 |
+ |
factory = CommandFactory (self.context, opts, args) |
228 |
+ |
startCommand = factory.createByName (args[0]) |
229 |
+ |
if not startCommand: |
230 |
+ |
"Command %s not known." % args[0] |
231 |
+ |
sys.exit (1) |
232 |
+ |
startCommand.run () |
233 |
+ |
startCommand.finish () |
234 |
|
|
235 |
|
if __name__ == '__main__': |
236 |
< |
app = BonsaiServer (socket_port=8030) |
237 |
< |
app.start () |
236 |
> |
app = CmsWebApplication () |
237 |
> |
app.run () |