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