1
2 """
3 Plugin for our configuring the OMERO.web installation
4
5 Copyright 2009 University of Dundee. All rights reserved.
6 Use is subject to license terms supplied in LICENSE.txt
7
8 """
9
10 from exceptions import Exception
11 from datetime import datetime
12 from omero.cli import BaseControl, CLI
13 import omero.java
14 import platform
15 import time
16 import sys
17 import os
18 import re
19
20 try:
21 from omeroweb import settings
22
23 CONFIG_TABLE_FMT = " %-35.35s %-8s %r\n"
24 CONFIG_TABLE = CONFIG_TABLE_FMT % ("Key", "Default?", "Current value")
25
26 for key in sorted(settings.CUSTOM_SETTINGS_MAPPINGS):
27 global_name, default_value, mapping, using_default = settings.CUSTOM_SETTINGS_MAPPINGS[key]
28 global_value = getattr(settings, global_name, "(unset)")
29 CONFIG_TABLE += CONFIG_TABLE_FMT % (key, using_default, global_value)
30 except:
31 CONFIG_TABLE="INVALID OR LOCKED CONFIGURATION! Cannot display default values"
32
33 HELP="""OMERO.web configuration/deployment tools
34
35 Configuration:
36
37 Configuration for OMERO.web takes place via the
38 omero config commands. The configuration values
39 which are checked are as below:
40
41 %s
42
43 Example Nginx usage:
44
45 omero config set omero.web.debug true
46 omero config set omero.web.application_server fastcgi
47 omero web config nginx --http=8000 >> nginx.conf
48 omero web start
49 nginx -c `pwd`/nginx.conf
50 omero web status
51 omero web stop
52 nginx -s stop
53
54 Example IIS usage:
55
56 # Install server
57 omero config set omero.web.debug true
58 omero web iis
59 iisreset
60
61 # Uninstall server
62 omero web iis --remove
63 iisreset
64
65 """ % CONFIG_TABLE
66
67
69
115
116
118 parts = APPLICATION_HOST.split(':')
119 if len(parts) != 3:
120 self.ctx.die(656, "Invalid application host: %s" % ":".join(parts))
121 try:
122 host = parts[1]
123 while host.startswith(r"/"):
124 host = host[1:]
125 port = parts[2]
126 port = re.search(r'^(\d+).*', port).group(1)
127 port = int(port)
128 return (host, port)
129 except Exception, e:
130 self.ctx.die(567, "Badly formed domain: %s -- %s" % (":".join(parts), e))
131
133 if not args.type:
134 self.ctx.out("Available configuration helpers:\n - nginx, apache\n")
135 else:
136 server = args.type
137 host, port = self.host_and_port(settings.APPLICATION_HOST)
138 if args.http:
139 port = args.http
140 if settings.APPLICATION_SERVER == settings.FASTCGITCP:
141 if settings.APPLICATION_SERVER_PORT == port:
142 self.ctx.die(678, "Port conflict: HTTP(%s) and fastcgi-tcp(%s)." % \
143 (port, settings.APPLICATION_SERVER_PORT))
144 if server == "nginx":
145 if settings.APPLICATION_SERVER == settings.FASTCGITCP:
146 fastcgi_pass = "%s:%s" % (settings.APPLICATION_SERVER_HOST,
147 settings.APPLICATION_SERVER_PORT)
148 else:
149 fastcgi_pass = "unix:%s/var/django_fcgi.sock" % self.ctx.dir
150 c = file(self.ctx.dir / "etc" / "nginx.conf.template").read()
151 d = {
152 "ROOT":self.ctx.dir,
153 "OMEROWEBROOT":self.ctx.dir / "lib" / "python" / "omeroweb",
154 "HTTPPORT":port,
155 "FASTCGI_PASS":fastcgi_pass,
156 }
157 self.ctx.out(c % d)
158 if server == "apache":
159 if settings.APPLICATION_SERVER == settings.FASTCGITCP:
160 fastcgi_external = '-host %s:%s' % \
161 (settings.APPLICATION_SERVER_HOST,
162 settings.APPLICATION_SERVER_PORT)
163 else:
164 fastcgi_external = '-socket "%s/var/django_fcgi.sock"' % \
165 self.ctx.dir
166 stanza = """###
167 ### Stanza for OMERO.web created %(NOW)s
168 ###
169 FastCGIExternalServer "%(ROOT)s/var/omero.fcgi" %(FASTCGI_EXTERNAL)s
170
171 <Directory "%(ROOT)s/var">
172 Options -Indexes FollowSymLinks
173 Order allow,deny
174 Allow from all
175 </Directory>
176
177 <Directory "%(MEDIA)s">
178 Options -Indexes FollowSymLinks
179 Order allow,deny
180 Allow from all
181 </Directory>
182
183 Alias /appmedia %(MEDIA)s
184 Alias / "%(ROOT)s/var/omero.fcgi/"
185 """
186 d = {
187 "ROOT":self.ctx.dir,
188 "MEDIA":self.ctx.dir / "lib" / "python" / "omeroweb" / "media",
189 "OMEROWEBROOT":self.ctx.dir / "lib" / "python" / "omeroweb",
190 "FASTCGI_EXTERNAL":fastcgi_external,
191 "NOW":str(datetime.now()),
192 }
193 self.ctx.out(stanza % d)
194
218
220 location = self.ctx.dir / "lib" / "python" / "omeroweb"
221 if not args.appname:
222 apps = [x.name for x in filter(lambda x: x.isdir() and (x / 'scripts' / 'enable.py').exists(), location.listdir())]
223 iapps = map(lambda x: x.startswith('omeroweb.') and x[9:] or x, settings.INSTALLED_APPS)
224 apps = filter(lambda x: x not in iapps, apps)
225 self.ctx.out('[enableapp] available apps:\n - ' + '\n - '.join(apps) + '\n')
226 else:
227 for app in args.appname:
228 args = [sys.executable, location / app / "scripts" / "enable.py"]
229 rv = self.ctx.call(args, cwd = location)
230 if rv != 0:
231 self.ctx.die(121, "Failed to enable '%s'.\n" % app)
232 else:
233 self.ctx.out("App '%s' was enabled\n" % app)
234 args = [sys.executable, "manage.py", "syncdb", "--noinput"]
235 rv = self.ctx.call(args, cwd = location)
236 self.syncmedia(None)
237
239 location = self.ctx.dir / "lib" / "python" / "omeroweb"
240 args = [sys.executable, "-i", location / "../omero/gateway/scripts/dbhelpers.py"]
241 self.set_environ()
242 os.environ['DJANGO_SETTINGS_MODULE'] = os.environ.get('DJANGO_SETTINGS_MODULE', 'omeroweb.settings')
243 rv = self.ctx.call(args, cwd = location)
244
246 try:
247 ice_config = args.config
248 test = args.test
249 testpath = args.path
250 except:
251 self.ctx.die(121, "usage: unittest --config=/path/to/ice.config --test=appname.TestCase --path=/external/path/")
252
253 if testpath is not None and testpath.find('/') >= 0:
254 path = testpath.split('/')
255 test = path[len(path)-1]
256 if testpath.startswith('/'):
257 location = "/".join(path[:(len(path)-1)])
258 else:
259 appbase = test.split('.')[0]
260 location = self.ctx.dir / "/".join(path[:(len(path)-1)])
261
262 if testpath is None:
263 location = self.ctx.dir / "lib" / "python" / "omeroweb"
264
265 if testpath is not None and len(testpath) > 1:
266 cargs = [testpath]
267 else:
268 cargs = [sys.executable]
269
270 cargs.extend([ "manage.py", "test"])
271 if test:
272 cargs.append(test)
273 self.set_environ(ice_config=ice_config)
274 rv = self.ctx.call(cargs, cwd = location)
275
277 try:
278 ice_config = args.config
279 appname = args.djangoapp
280 seleniumserver = args.seleniumserver
281 hostname = args.hostname
282 browser = args.browser
283 except:
284 self.ctx.die(121, "usage: seleniumtest [path.]{djangoapp} [seleniumserver] [hostname] [browser]")
285
286 if appname.find('.') > 0:
287 appname = appname.split('.')
288 appbase = appname[0]
289 location = self.ctx.dir / appbase
290 appname = '.'.join(appname[1:])
291 else:
292 appbase = "omeroweb"
293 location = self.ctx.dir / "lib" / "python" / "omeroweb"
294
295 cargs = [sys.executable, location / appname / "tests" / "seleniumtests.py", seleniumserver, hostname, browser]
296
297 self.set_environ(ice_config=ice_config)
298 os.environ['DJANGO_SETTINGS_MODULE'] = 'omeroweb.settings'
299 rv = self.ctx.call(cargs, cwd = location )
300
301 - def call (self, args):
302 try:
303 location = self.ctx.dir / "lib" / "python" / "omeroweb"
304 cargs = []
305 appname = args.appname
306 scriptname = args.scriptname.split(' ')
307 if len(scriptname) > 1:
308 cargs.append(scriptname[0])
309 scriptname = ' '.join(scriptname[1:])
310 else:
311 scriptname = scriptname[0]
312 cargs.extend([location / appname / "scripts" / scriptname] + args.arg)
313 print cargs
314 os.environ['DJANGO_SETTINGS_MODULE'] = 'omeroweb.settings'
315 self.set_environ()
316 rv = self.ctx.call(cargs, cwd = location)
317 except:
318 import traceback
319 print traceback.print_exc()
320
321
323 import omeroweb.settings as settings
324 link = ("%s:%s" % (settings.APPLICATION_SERVER_HOST,
325 settings.APPLICATION_SERVER_PORT))
326 location = self.ctx.dir / "lib" / "python" / "omeroweb"
327 self.ctx.out("Starting OMERO.web... ", newline=False)
328 cache_backend = getattr(settings, 'CACHE_BACKEND', None)
329 if cache_backend is not None and cache_backend.startswith("file:///"):
330 cache_backend = cache_backend[7:]
331 if "Windows" != platform.system() \
332 and not os.access(cache_backend, os.R_OK|os.W_OK):
333 self.ctx.out("[FAILED]")
334 self.ctx.out("CACHE_BACKEND '%s' not writable or missing." % \
335 getattr(settings, 'CACHE_BACKEND'))
336 return 1
337 deploy = getattr(settings, 'APPLICATION_SERVER')
338
339
340 if deploy in (settings.FASTCGI_TYPES):
341 if "Windows" == platform.system():
342 self.ctx.out("""
343 WARNING: Unless you **really** know what you are doing you should NOT be
344 using bin\omero web start on Windows with FastCGI.
345 """)
346 pid_path = self.ctx.dir / "var" / "django.pid"
347 pid_num = None
348
349 if pid_path.exists():
350 pid_txt = pid_path.text().strip()
351 try:
352 pid_num = int(pid_txt)
353 except:
354 pid_path.remove()
355 self.ctx.err("Removed invalid %s: '%s'" % (pid_path, pid_txt))
356
357 if pid_num is not None:
358 try:
359 os.kill(pid_num, 0)
360 self.ctx.die(606, "%s exists! Use 'web stop' first" % pid_path)
361 except OSError:
362 pid_path.remove()
363 self.ctx.err("Removed stale %s" % pid_path)
364
365 if deploy == settings.FASTCGI:
366 cmd = "python manage.py runfcgi workdir=./"
367 cmd += " method=prefork socket=%(base)s/var/django_fcgi.sock"
368 cmd += " pidfile=%(base)s/var/django.pid daemonize=true"
369 cmd += " maxchildren=5 minspare=1 maxspare=5 maxrequests=400"
370 django = (cmd % {'base': self.ctx.dir}).split()
371 rv = self.ctx.popen(args=django, cwd=location)
372 elif deploy == settings.FASTCGITCP:
373 cmd = "python manage.py runfcgi workdir=./"
374 cmd += " method=prefork host=%(host)s port=%(port)s"
375 cmd += " pidfile=%(base)s/var/django.pid daemonize=true"
376 cmd += " maxchildren=5 minspare=1 maxspare=5 maxrequests=400"
377 django = (cmd % {'base': self.ctx.dir,
378 'host': settings.APPLICATION_SERVER_HOST,
379 'port': settings.APPLICATION_SERVER_PORT}).split()
380 rv = self.ctx.popen(args=django, cwd=location)
381 else:
382 django = [sys.executable,"manage.py","runserver", link, "--noreload"]
383 rv = self.ctx.call(django, cwd = location)
384 self.ctx.out("[OK]")
385 return rv
386
387
389 location = self.ctx.dir / "lib" / "python" / "omeroweb"
390 self.ctx.out("OMERO.web status... ", newline=False)
391 import omeroweb.settings as settings
392 deploy = getattr(settings, 'APPLICATION_SERVER')
393 cache_backend = getattr(settings, 'CACHE_BACKEND', None)
394 if cache_backend is not None:
395 cache_backend = ' (CACHE_BACKEND %s)' % cache_backend
396 else:
397 cache_backend = ''
398 rv = 0
399 if deploy in settings.FASTCGI_TYPES:
400 try:
401 f=open(self.ctx.dir / "var" / "django.pid", 'r')
402 pid = int(f.read())
403 except IOError:
404 self.ctx.out("[NOT STARTED]")
405 return rv
406 import signal
407 try:
408 os.kill(pid, 0)
409 self.ctx.out("[RUNNING] (PID %d)%s" % (pid, cache_backend))
410 except:
411 self.ctx.out("[NOT STARTED]")
412 return rv
413 else:
414 self.ctx.err("DEVELOPMENT: You will have to check status by hand!")
415 return rv
416
417 - def stop(self, args):
418 self.ctx.out("Stopping OMERO.web... ", newline=False)
419 import omeroweb.settings as settings
420 deploy = getattr(settings, 'APPLICATION_SERVER')
421 if deploy in settings.FASTCGI_TYPES:
422 if "Windows" == platform.system():
423 self.ctx.out("""
424 WARNING: Unless you **really** know what you are doing you should NOT be
425 using bin\omero web start on Windows with FastCGI.
426 """)
427 pid = 'Unknown'
428 pid_path = self.ctx.dir / "var" / "django.pid"
429 pid_text = "Unknown"
430 if pid_path.exists():
431 pid_text = pid_path.text().strip()
432 try:
433 try:
434 pid = int(pid_text)
435 import signal
436 os.kill(pid, 0)
437 except:
438 self.ctx.out("[FAILED]")
439 self.ctx.out("Django FastCGI workers (PID %s) not started?" % pid_text)
440 return
441 os.kill(pid, signal.SIGTERM)
442 self.ctx.out("[OK]")
443 self.ctx.out("Django FastCGI workers (PID %d) killed." % pid)
444 finally:
445 if pid_path.exists():
446 pid_path.remove()
447 else:
448 self.ctx.err("DEVELOPMENT: You will have to kill processes by hand!")
449
451 os.environ['ICE_CONFIG'] = ice_config is None and str(self.ctx.dir / "etc" / "ice.config") or str(ice_config)
452 os.environ['PATH'] = str(os.environ.get('PATH', '.') + ':' + self.ctx.dir / 'bin')
453
454 - def iis(self, args):
455
456 if not (self._isWindows() or self.ctx.isdebug):
457 self.ctx.die(2, "'iis' command is for Windows only")
458
459 web_iis = self.ctx.dir / "lib" / "python" / "omero_web_iis.py"
460 cmd = [sys.executable, str(web_iis)]
461 if args.remove:
462 cmd.append("remove")
463 rv = self.ctx.call(cmd)
464
465 try:
466 register("web", WebControl, HELP)
467 except NameError:
468 if __name__ == "__main__":
469 cli = CLI()
470 cli.register("web", WebControl, HELP)
471 cli.invoke(sys.argv[1:])
472