Package omero :: Package plugins :: Module web
[hide private]
[frames] | no frames]

Source Code for Module omero.plugins.web

  1  #!/usr/bin/env python 
  2  # -*- coding: utf-8 -*- 
  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   
66 -class WebControl(BaseControl):
67
68 - def _configure(self, parser):
69 sub = parser.sub() 70 71 parser.add(sub, self.start, "Primary start for the OMERO.web server") 72 parser.add(sub, self.stop, "Stop the OMERO.web server") 73 parser.add(sub, self.status, "Status for the OMERO.web server") 74 75 iis = parser.add(sub, self.iis, "IIS (un-)install of OMERO.web ") 76 iis.add_argument("--remove", action="store_true", default=False) 77 78 # 79 # Advanced 80 # 81 82 config = parser.add( 83 sub, self.config, 84 "Output a config template for server" 85 " ('nginx' or 'apache' for the moment") 86 config.add_argument("type", choices=("nginx", "apache")) 87 config.add_argument( 88 "--http", type=int, 89 help="HTTP port for web server (not fastcgi)") 90 config.add_argument( 91 "--system", action="store_true", 92 help="System appropriate configuration file") 93 94 parser.add( 95 sub, self.syncmedia, 96 "Advanced use: Creates needed symlinks for static media" 97 " files\n(Performed automatically by 'start')") 98 99 # 100 # Developer 101 # 102 103 call = parser.add( 104 sub, self.call, 105 """Developer use: call appname "[executable] scriptname" args""") 106 call.add_argument("appname") 107 call.add_argument("scriptname") 108 call.add_argument("arg", nargs="*") 109 110 enableapp = parser.add( 111 sub, self.enableapp, 112 "Developer use: runs enable.py and then syncdb") 113 enableapp.add_argument("appname", nargs="*") 114 115 parser.add( 116 sub, self.gateway, 117 "Developer use: Loads the blitz gateway into a Python" 118 " interpreter") 119 120 selenium = parser.add( 121 sub, self.seleniumtest, 122 "Developer use: runs selenium tests on a django app") 123 selenium.add_argument( 124 "--config", action="store", help="ice.config location") 125 selenium.add_argument("djangoapp", help="Django-app to be tested") 126 selenium.add_argument("seleniumserver", help="E.g. localhost") 127 selenium.add_argument("hostname", help="E.g. http://localhost:4080") 128 selenium.add_argument("browser", help="E.g. firefox") 129 130 test = parser.add( 131 sub, self.test, "Developer use: Runs omero web tests" 132 " (py.test)\n--cov* options depend on pytest-cov plugin") 133 test.add_argument( 134 "--config", action="store", help="ice.config location") 135 test.add_argument( 136 "--basepath", action="store", 137 help="Base omeroweb path (default lib/python/omeroweb)") 138 test.add_argument( 139 "--testpath", action="store", 140 help="Path for test collection (relative to basepath)") 141 test.add_argument( 142 "--string", action="store", 143 help="Only run tests including string.") 144 test.add_argument( 145 "--failfast", action="store_true", default=False, 146 help="Exit on first error") 147 test.add_argument( 148 "--verbose", action="store_true", default=False, 149 help="More verbose output") 150 test.add_argument( 151 "--quiet", action="store_true", default=False, 152 help="Less verbose output") 153 test.add_argument( 154 "--pdb", action="store_true", default=False, 155 help="Fallback to pdb on error") 156 test.add_argument( 157 '--cov', action='append', default=[], 158 help='measure coverage for filesystem path (multi-allowed)') 159 test.add_argument( 160 '--cov-report', action='append', default=[], 161 choices=['term', 'term-missing', 'annotate', 'html', 'xml'], 162 help="type of report to generate: term, term-missing, annotate," 163 " html, xml (multi-allowed)")
164
165 - def config(self, args):
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
309 - def syncmedia(self, args):
310 self.collectstatic()
311
312 - def enableapp(self, args):
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
336 - def gateway(self, args):
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 # The following is needed so the cwd is included in the python path 383 # when using --testpath 384 os.environ['PYTHONPATH'] += ':.' 385 386 self.ctx.call(cargs, cwd=cwd)
387
388 - def seleniumtest(self, args):
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 # cargs += args.arg[1:] 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
436 - def collectstatic(self):
437 # Ensure that static media is copied to the correct location 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
444 - def start(self, args):
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 # 3216 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) # popen 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) # popen 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
519 - def status(self, args):
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) # NULL signal 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) # NULL signal 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) # kill whole group 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
583 - def set_environ(self, ice_config=None):
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