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

Source Code for Module omero.plugins.web

  1  #!/usr/bin/env python 
  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   
68 -class WebControl(BaseControl):
69
70 - def _configure(self, parser):
71 sub = parser.sub() 72 73 parser.add(sub, self.start, "Primary start for the OMERO.web server") 74 parser.add(sub, self.stop, "Stop the OMERO.web server") 75 parser.add(sub, self.status, "Status for the OMERO.web server") 76 77 iis = parser.add(sub, self.iis, "IIS (un-)install of OMERO.web ") 78 iis.add_argument("--remove", action = "store_true", default = False) 79 80 # 81 # Advanced 82 # 83 84 config = parser.add(sub, self.config, "Output a config template for server ('nginx' or 'apache' for the moment") 85 config.add_argument("type", choices=("nginx","apache")) 86 config.add_argument("--http", type=int, help="HTTP port for web server (not fastcgi)") 87 88 parser.add(sub, self.syncmedia, "Advanced use: Creates needed symlinks for static media files") 89 90 # 91 # Developer 92 # 93 94 call = parser.add(sub, self.call, """Developer use: call appname "[executable] scriptname" args """) 95 call.add_argument("appname") 96 call.add_argument("scriptname") 97 call.add_argument("arg", nargs="*") 98 99 enableapp = parser.add(sub, self.enableapp, "Developer use: runs enable.py and then syncdb") 100 enableapp.add_argument("appname", nargs="*") 101 102 gateway = parser.add(sub, self.gateway, "Developer use: Loads the blitz gateway into a Python interpreter") 103 104 selenium = parser.add(sub, self.seleniumtest, "Developer use: runs selenium tests on a django app") 105 selenium.add_argument("--config", action="store", help = "ice.config location") 106 selenium.add_argument("djangoapp", help = "Django-app to be tested") 107 selenium.add_argument("seleniumserver", help = "E.g. localhost") 108 selenium.add_argument("hostname", help = "E.g. http://localhost:4080") 109 selenium.add_argument("browser", help = "E.g. firefox") 110 111 unittest = parser.add(sub, self.unittest, "Developer use: Runs 'coverage -x manage.py test'") 112 unittest.add_argument("--config", action="store", help = "ice.config location") 113 unittest.add_argument("--test", action="store", help = "Specific test case(-s).") 114 unittest.add_argument("--path", action="store", help = "Path to Django-app. Must include '/'.")
115 116
117 - def host_and_port(self, APPLICATION_HOST):
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
132 - def config(self, args):
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
195 - def syncmedia(self, args):
196 import shutil 197 from glob import glob 198 location = self.ctx.dir / "lib" / "python" / "omeroweb" 199 # Targets 200 apps = map(lambda x: x.startswith('omeroweb.') and x[9:] or x, settings.INSTALLED_APPS) 201 apps = filter(lambda x: os.path.exists(location / x), apps) 202 # Destination dir 203 if not os.path.exists(location / 'media'): 204 os.mkdir(location / 'media') 205 206 # Create app media links 207 for app in apps: 208 media_dir = location / app / 'media' 209 if os.path.exists(media_dir): 210 if os.path.exists(location / 'media' / app): 211 os.remove(os.path.abspath(location / 'media' / app)) 212 try: 213 # Windows does not support symlink 214 sys.getwindowsversion() 215 shutil.copytree(os.path.abspath(media_dir), location / 'media' / app) 216 except: 217 os.symlink(os.path.abspath(media_dir), location / 'media' / app)
218
219 - def enableapp(self, args):
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
238 - def gateway(self, args):
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
245 - def unittest(self, args):
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
276 - def seleniumtest (self, args):
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 #cargs += args.arg[1:] 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
322 - def start(self, args):
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 # 3216 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) # popen 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) # popen 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
388 - def status(self, args):
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) # NULL signal 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) # NULL signal 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) #kill whole group 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
450 - def set_environ(self, ice_config=None):
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