1
2
3 """
4 Plugin for our configuring the OMERO.web installation
5
6 Copyright 2009-2013 University of Dundee. All rights reserved.
7 Use is subject to license terms supplied in LICENSE.txt
8
9 """
10
11 from datetime import datetime
12 from omero.cli import BaseControl, CLI
13 import platform
14 import sys
15 import os
16
17 try:
18 from omeroweb import settings
19
20 CONFIG_TABLE_FMT = " %-35.35s %-8s %r\n"
21 CONFIG_TABLE = CONFIG_TABLE_FMT % ("Key", "Default?", "Current value")
22
23 for key in sorted(settings.CUSTOM_SETTINGS_MAPPINGS):
24 global_name, default_value, mapping, using_default = \
25 settings.CUSTOM_SETTINGS_MAPPINGS[key]
26 global_value = getattr(settings, global_name, "(unset)")
27 CONFIG_TABLE += CONFIG_TABLE_FMT % (key, using_default, global_value)
28 except:
29 CONFIG_TABLE = "INVALID OR LOCKED CONFIGURATION! Cannot display default"\
30 " values"
31
32 HELP = """OMERO.web configuration/deployment tools
33
34 Configuration:
35
36 Configuration for OMERO.web takes place via the omero config commands. The
37 configuration values which are checked are as below:
38
39 %s
40
41 Example Nginx usage:
42
43 omero config set omero.web.debug true
44 omero config set omero.web.application_server fastcgi
45 omero web config nginx --http=8000 >> nginx.conf
46 omero web start
47 nginx -c `pwd`/nginx.conf
48 omero web status
49 omero web stop
50 nginx -s stop
51
52 Example IIS usage:
53
54 # Install server
55 omero config set omero.web.debug true
56 omero web iis
57 iisreset
58
59 # Uninstall server
60 omero web iis --remove
61 iisreset
62
63 """ % CONFIG_TABLE
64
65
67
164
166 if not args.type:
167 self.ctx.out(
168 "Available configuration helpers:\n - nginx, apache\n")
169 else:
170 server = args.type
171 port = 8080
172 if args.http:
173 port = args.http
174 if settings.APPLICATION_SERVER == settings.FASTCGITCP:
175 if settings.APPLICATION_SERVER_PORT == port:
176 self.ctx.die(
177 678, "Port conflict: HTTP(%s) and"" fastcgi-tcp(%s)."
178 % (port, settings.APPLICATION_SERVER_PORT))
179 if server == "nginx":
180 if settings.APPLICATION_SERVER == settings.FASTCGITCP:
181 fastcgi_pass = "%s:%s" \
182 % (settings.APPLICATION_SERVER_HOST,
183 settings.APPLICATION_SERVER_PORT)
184 else:
185 fastcgi_pass = "unix:%s/var/django_fcgi.sock" \
186 % self.ctx.dir
187 if args.system:
188 c = file(self.ctx.dir / "etc" /
189 "nginx.conf.system.template").read()
190 else:
191 c = file(self.ctx.dir / "etc" /
192 "nginx.conf.template").read()
193 d = {
194 "ROOT": self.ctx.dir,
195 "OMEROWEBROOT": self.ctx.dir / "lib" / "python" /
196 "omeroweb",
197 "HTTPPORT": port,
198 "FASTCGI_PASS": fastcgi_pass,
199 }
200 if hasattr(settings, 'FORCE_SCRIPT_NAME') \
201 and len(settings.FORCE_SCRIPT_NAME) > 0:
202 d["FASTCGI_PATH_SCRIPT_INFO"] = \
203 "fastcgi_split_path_info ^(%s)(.*)$;\n" \
204 " " \
205 "fastcgi_param PATH_INFO $fastcgi_path_info;\n" \
206 " " \
207 "fastcgi_param SCRIPT_INFO $fastcgi_script_name;\n" \
208 % (settings.FORCE_SCRIPT_NAME)
209 else:
210 d["FASTCGI_PATH_SCRIPT_INFO"] = \
211 "fastcgi_param PATH_INFO $fastcgi_script_name;\n"
212 self.ctx.out(c % d)
213 if server == "apache":
214 if settings.APPLICATION_SERVER == settings.FASTCGITCP:
215 fastcgi_external = '-host %s:%s' % \
216 (settings.APPLICATION_SERVER_HOST,
217 settings.APPLICATION_SERVER_PORT)
218 else:
219 fastcgi_external = '-socket "%s/var/django_fcgi.sock"' % \
220 self.ctx.dir
221 stanza = """###
222 # apache config for omero
223 # this file should be loaded *after* ssl.conf
224 #
225 # -D options to control configurations
226 # OmeroWebClientRedirect - redirect / to /omero/webclient
227 # OmeroWebAdminRedirect - redirect / to /omero/webadmin
228 # OmeroForceSSL - redirect all http requests to https
229
230 ###
231 ### Example SSL stanza for OMERO.web created %(NOW)s
232 ###
233
234 # Eliminate overlap warnings with the default ssl vhost
235 # Requires SNI (http://wiki.apache.org/httpd/NameBasedSSLVHostsWithSNI) \
236 support
237 # most later versions of mod_ssl and OSes will support it
238 # if you see "You should not use name-based virtual hosts in conjunction \
239 with SSL!!"
240 # or similar start apache with -D DISABLE_SNI and modify ssl.conf
241 #<IfDefine !DISABLE_SNI>
242 # NameVirtualHost *:443
243 #</IfDefine>
244 #
245 ## force https/ssl
246 #<IfDefine OmeroForceSSL>
247 # RewriteEngine on
248 # RewriteCond %%{HTTPS} !on
249 # RewriteRule (.*) https://%%{HTTP_HOST}%%{REQUEST_URI} [L]
250 #</IfDefine>
251 #
252 #<VirtualHost _default_:443>
253 #
254 # ErrorLog logs/ssl_error_log
255 # TransferLog logs/ssl_access_log
256 # LogLevel warn
257 #
258 # SSLEngine on
259 # SSLProtocol all -SSLv2
260 # SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW
261 # SSLCertificateFile /etc/pki/tls/certs/server.crt
262 # SSLCertificateKeyFile /etc/pki/tls/private/server.key
263 #
264 # # SSL Protocol Adjustments:
265 # SetEnvIf User-Agent ".*MSIE.*" \
266 # nokeepalive ssl-unclean-shutdown \
267 # downgrade-1.0 force-response-1.0
268 #
269 # # Per-Server Logging:
270 # CustomLog logs/ssl_request_log \
271 # "%%t %%h %%{SSL_PROTOCOL}x %%{SSL_CIPHER}x \"%%r\" %%b"
272 #
273 #</VirtualHost>
274
275 RewriteEngine on
276 RewriteRule ^/?$ /omero/ [R]
277
278 ###
279 ### Stanza for OMERO.web created %(NOW)s
280 ###
281 FastCGIExternalServer "%(ROOT)s/var/omero.fcgi" %(FASTCGI_EXTERNAL)s
282
283 <Directory "%(ROOT)s/var">
284 Options -Indexes FollowSymLinks
285 Order allow,deny
286 Allow from all
287 </Directory>
288
289 <Directory "%(STATIC)s">
290 Options -Indexes FollowSymLinks
291 Order allow,deny
292 Allow from all
293 </Directory>
294
295 Alias /static %(STATIC)s
296 Alias /omero "%(ROOT)s/var/omero.fcgi/"
297 """
298 d = {
299 "ROOT": self.ctx.dir,
300 "STATIC": self.ctx.dir / "lib" / "python" / "omeroweb" /
301 "static",
302 "OMEROWEBROOT": self.ctx.dir / "lib" / "python" /
303 "omeroweb",
304 "FASTCGI_EXTERNAL": fastcgi_external,
305 "NOW": str(datetime.now()),
306 }
307 self.ctx.out(stanza % d)
308
311
313 location = self.ctx.dir / "lib" / "python" / "omeroweb"
314 if not args.appname:
315 apps = [x.name for x in filter(
316 lambda x: x.isdir() and
317 (x / 'scripts' / 'enable.py').exists(), location.listdir())]
318 iapps = map(lambda x: x.startswith('omeroweb.') and x[9:] or
319 x, settings.INSTALLED_APPS)
320 apps = filter(lambda x: x not in iapps, apps)
321 self.ctx.out('[enableapp] available apps:\n - ' +
322 '\n - '.join(apps) + '\n')
323 else:
324 for app in args.appname:
325 args = [sys.executable, location / app / "scripts" /
326 "enable.py"]
327 rv = self.ctx.call(args, cwd=location)
328 if rv != 0:
329 self.ctx.die(121, "Failed to enable '%s'.\n" % app)
330 else:
331 self.ctx.out("App '%s' was enabled\n" % app)
332 args = [sys.executable, "manage.py", "syncdb", "--noinput"]
333 rv = self.ctx.call(args, cwd=location)
334 self.syncmedia(None)
335
337 location = self.ctx.dir / "lib" / "python" / "omeroweb"
338 args = [sys.executable, "-i", location /
339 "../omero/gateway/scripts/dbhelpers.py"]
340 self.set_environ()
341 os.environ['DJANGO_SETTINGS_MODULE'] = \
342 os.environ.get('DJANGO_SETTINGS_MODULE', 'omeroweb.settings')
343 self.ctx.call(args, cwd=location)
344
345 - def test(self, args):
346 try:
347 pass
348 except:
349 self.ctx.die(121, 'test: wrong arguments, run test -h for a list')
350
351 cargs = ['py.test']
352
353 if args.config:
354 self.set_environ(ice_config=args.config)
355 else:
356 self.set_environ(ice_config=self.ctx.dir / 'etc' / 'ice.config')
357
358 if args.basepath:
359 cwd = args.basepath
360 else:
361 cwd = self.ctx.dir / 'lib' / 'python' / 'omeroweb'
362
363 if args.testpath:
364 cargs.extend(['-s', args.testpath])
365 if args.string:
366 cargs.extend(['-k', args.string])
367 if args.failfast:
368 cargs.append('-x')
369 if args.verbose:
370 cargs.append('-v')
371 if args.quiet:
372 cargs.append('-q')
373 if args.pdb:
374 cargs.append('--pdb')
375 for cov in args.cov:
376 cargs.extend(['--cov', cov])
377 for cov_rep in args.cov_report:
378 cargs.extend(['--cov-report', cov_rep])
379
380 os.environ['DJANGO_SETTINGS_MODULE'] = \
381 os.environ.get('DJANGO_SETTINGS_MODULE', 'omeroweb.settings')
382
383
384 os.environ['PYTHONPATH'] += ':.'
385
386 self.ctx.call(cargs, cwd=cwd)
387
389 try:
390 ice_config = args.config
391 appname = args.djangoapp
392 seleniumserver = args.seleniumserver
393 hostname = args.hostname
394 browser = args.browser
395 except:
396 self.ctx.die(121, "usage: seleniumtest [path.]{djangoapp}"
397 " [seleniumserver] [hostname] [browser]")
398
399 if appname.find('.') > 0:
400 appname = appname.split('.')
401 appbase = appname[0]
402 location = self.ctx.dir / appbase
403 appname = '.'.join(appname[1:])
404 else:
405 appbase = "omeroweb"
406 location = self.ctx.dir / "lib" / "python" / "omeroweb"
407
408 cargs = [sys.executable, location / appname / "tests" /
409 "seleniumtests.py", seleniumserver, hostname, browser]
410
411 self.set_environ(ice_config=ice_config)
412 os.environ['DJANGO_SETTINGS_MODULE'] = 'omeroweb.settings'
413 self.ctx.call(cargs, cwd=location)
414
415 - def call(self, args):
416 try:
417 location = self.ctx.dir / "lib" / "python" / "omeroweb"
418 cargs = []
419 appname = args.appname
420 scriptname = args.scriptname.split(' ')
421 if len(scriptname) > 1:
422 cargs.append(scriptname[0])
423 scriptname = ' '.join(scriptname[1:])
424 else:
425 scriptname = scriptname[0]
426 cargs.extend([location / appname / "scripts" / scriptname] +
427 args.arg)
428 print cargs
429 os.environ['DJANGO_SETTINGS_MODULE'] = 'omeroweb.settings'
430 self.set_environ()
431 self.ctx.call(cargs, cwd=location)
432 except:
433 import traceback
434 print traceback.print_exc()
435
437
438 location = self.ctx.dir / "lib" / "python" / "omeroweb"
439 args = [sys.executable, "manage.py", "collectstatic", "--noinput"]
440 rv = self.ctx.call(args, cwd=location)
441 if rv != 0:
442 self.ctx.die(607, "Failed to collect static content.\n")
443
445 self.collectstatic()
446 import omeroweb.settings as settings
447 link = ("%s:%s" % (settings.APPLICATION_SERVER_HOST,
448 settings.APPLICATION_SERVER_PORT))
449 location = self.ctx.dir / "lib" / "python" / "omeroweb"
450 self.ctx.out("Starting OMERO.web... ", newline=False)
451 cache_backend = getattr(settings, 'CACHE_BACKEND', None)
452 if cache_backend is not None and cache_backend.startswith("file:///"):
453 cache_backend = cache_backend[7:]
454 if "Windows" != platform.system() \
455 and not os.access(cache_backend, os.R_OK | os.W_OK):
456 self.ctx.out("[FAILED]")
457 self.ctx.out("CACHE_BACKEND '%s' not writable or missing." %
458 getattr(settings, 'CACHE_BACKEND'))
459 return 1
460 deploy = getattr(settings, 'APPLICATION_SERVER')
461
462
463 if deploy in (settings.FASTCGI_TYPES):
464 if "Windows" == platform.system():
465 self.ctx.out("""
466 WARNING: Unless you **really** know what you are doing you should NOT be
467 using bin\omero web start on Windows with FastCGI.
468 """)
469 pid_path = self.ctx.dir / "var" / "django.pid"
470 pid_num = None
471
472 if pid_path.exists():
473 pid_txt = pid_path.text().strip()
474 try:
475 pid_num = int(pid_txt)
476 except:
477 pid_path.remove()
478 self.ctx.err("Removed invalid %s: '%s'"
479 % (pid_path, pid_txt))
480
481 if pid_num is not None:
482 try:
483 os.kill(pid_num, 0)
484 self.ctx.die(606,
485 "%s exists! Use 'web stop' first" % pid_path)
486 except OSError:
487 pid_path.remove()
488 self.ctx.err("Removed stale %s" % pid_path)
489
490 if deploy == settings.FASTCGI:
491 cmd = "python manage.py runfcgi workdir=./"
492 cmd += " method=prefork socket=%(base)s/var/django_fcgi.sock"
493 cmd += " pidfile=%(base)s/var/django.pid daemonize=true"
494 cmd += " maxchildren=5 minspare=1 maxspare=5"
495 cmd += " maxrequests=%(maxrequests)d"
496 django = (cmd % {
497 'maxrequests': settings.APPLICATION_SERVER_MAX_REQUESTS,
498 'base': self.ctx.dir}).split()
499 rv = self.ctx.popen(args=django, cwd=location)
500 elif deploy == settings.FASTCGITCP:
501 cmd = "python manage.py runfcgi workdir=./"
502 cmd += " method=prefork host=%(host)s port=%(port)s"
503 cmd += " pidfile=%(base)s/var/django.pid daemonize=true"
504 cmd += " maxchildren=5 minspare=1 maxspare=5"
505 cmd += " maxrequests=%(maxrequests)d"
506 django = (cmd % {
507 'maxrequests': settings.APPLICATION_SERVER_MAX_REQUESTS,
508 'base': self.ctx.dir,
509 'host': settings.APPLICATION_SERVER_HOST,
510 'port': settings.APPLICATION_SERVER_PORT}).split()
511 rv = self.ctx.popen(args=django, cwd=location)
512 else:
513 django = [sys.executable, "manage.py", "runserver", link,
514 "--noreload"]
515 rv = self.ctx.call(django, cwd=location)
516 self.ctx.out("[OK]")
517 return rv
518
520 self.ctx.out("OMERO.web status... ", newline=False)
521 import omeroweb.settings as settings
522 deploy = getattr(settings, 'APPLICATION_SERVER')
523 cache_backend = getattr(settings, 'CACHE_BACKEND', None)
524 if cache_backend is not None:
525 cache_backend = ' (CACHE_BACKEND %s)' % cache_backend
526 else:
527 cache_backend = ''
528 rv = 0
529 if deploy in settings.FASTCGI_TYPES:
530 try:
531 f = open(self.ctx.dir / "var" / "django.pid", 'r')
532 pid = int(f.read())
533 except IOError:
534 self.ctx.out("[NOT STARTED]")
535 return rv
536
537 try:
538 os.kill(pid, 0)
539 self.ctx.out("[RUNNING] (PID %d)%s" % (pid, cache_backend))
540 except:
541 self.ctx.out("[NOT STARTED]")
542 return rv
543 else:
544 self.ctx.err("DEVELOPMENT: You will have to check status by hand!")
545 return rv
546
547 - def stop(self, args):
548 self.ctx.out("Stopping OMERO.web... ", newline=False)
549 import omeroweb.settings as settings
550 deploy = getattr(settings, 'APPLICATION_SERVER')
551 if deploy in settings.FASTCGI_TYPES:
552 if "Windows" == platform.system():
553 self.ctx.out("""
554 WARNING: Unless you **really** know what you are doing you should NOT be
555 using bin\omero web start on Windows with FastCGI.
556 """)
557 pid = 'Unknown'
558 pid_path = self.ctx.dir / "var" / "django.pid"
559 pid_text = "Unknown"
560 if pid_path.exists():
561 pid_text = pid_path.text().strip()
562 try:
563 try:
564 pid = int(pid_text)
565 import signal
566 os.kill(pid, 0)
567 except:
568 self.ctx.out("[FAILED]")
569 self.ctx.out(
570 "Django FastCGI workers (PID %s) not started?"
571 % pid_text)
572 return
573 os.kill(pid, signal.SIGTERM)
574 self.ctx.out("[OK]")
575 self.ctx.out("Django FastCGI workers (PID %d) killed." % pid)
576 finally:
577 if pid_path.exists():
578 pid_path.remove()
579 else:
580 self.ctx.err(
581 "DEVELOPMENT: You will have to kill processes by hand!")
582
584 os.environ['ICE_CONFIG'] = ice_config is None and \
585 str(self.ctx.dir / "etc" / "ice.config") or str(ice_config)
586 os.environ['PATH'] = str(os.environ.get('PATH', '.') + ':' +
587 self.ctx.dir / 'bin')
588
589 - def iis(self, args):
590 self.collectstatic()
591 if not (self._isWindows() or self.ctx.isdebug):
592 self.ctx.die(2, "'iis' command is for Windows only")
593
594 web_iis = self.ctx.dir / "lib" / "python" / "omero_web_iis.py"
595 cmd = [sys.executable, str(web_iis)]
596 if args.remove:
597 cmd.append("remove")
598 self.ctx.call(cmd)
599
600 try:
601 register("web", WebControl, HELP)
602 except NameError:
603 if __name__ == "__main__":
604 cli = CLI()
605 cli.register("web", WebControl, HELP)
606 cli.invoke(sys.argv[1:])
607