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  from omeroweb import settings 
 14  import omero.java 
 15  import platform 
 16  import time 
 17  import sys 
 18  import os 
 19  import re 
 20   
 21   
 22  CONFIG_TABLE_FMT = "    %-35.35s  %-8s  %r\n" 
 23  CONFIG_TABLE = CONFIG_TABLE_FMT % ("Key", "Default?", "Current value") 
 24   
 25  for key in sorted(settings.CUSTOM_SETTINGS_MAPPINGS): 
 26      global_name, default_value, mapping, using_default = settings.CUSTOM_SETTINGS_MAPPINGS[key] 
 27      global_value = getattr(settings, global_name, "(unset)") 
 28      CONFIG_TABLE += CONFIG_TABLE_FMT  % (key, using_default, global_value) 
 29   
 30  HELP="""OMERO.web configuration/deployment tools 
 31   
 32  Configuration: 
 33   
 34      Configuration for OMERO.web takes place via the 
 35      omero config commands. The configuration values 
 36      which are checked are as below: 
 37   
 38  %s 
 39   
 40  Example Nginx usage: 
 41   
 42      omero config set omero.web.debug true 
 43      omero config set omero.web.application_server fastcgi 
 44      omero web config nginx --http=8000 >> nginx.conf 
 45      omero web start 
 46      nginx -c `pwd`/nginx.conf 
 47      omero web status 
 48      omero web stop 
 49      nginx -s stop 
 50   
 51  """ % CONFIG_TABLE 
 52   
 53   
54 -class WebControl(BaseControl):
55
56 - def _configure(self, parser):
57 sub = parser.sub() 58 59 parser.add(sub, self.start, "Primary start for the OMERO.web server") 60 parser.add(sub, self.stop, "Stop the OMERO.web server") 61 parser.add(sub, self.status, "Status for the OMERO.web server") 62 63 # 64 # Advanced 65 # 66 67 config = parser.add(sub, self.config, "Output a config template for server ('nginx' or 'apache' for the moment") 68 config.add_argument("type", choices=("nginx","apache")) 69 config.add_argument("--http", type=int, help="HTTP port for web server (not fastcgi)") 70 71 parser.add(sub, self.syncmedia, "Advanced use: Creates needed symlinks for static media files") 72 73 # 74 # Developer 75 # 76 77 call = parser.add(sub, self.call, """Developer use: call appname "[executable] scriptname" args """) 78 call.add_argument("appname") 79 call.add_argument("scriptname") 80 call.add_argument("arg", nargs="*") 81 82 enableapp = parser.add(sub, self.enableapp, "Developer use: runs enable.py and then syncdb") 83 enableapp.add_argument("appname", nargs="*") 84 85 gateway = parser.add(sub, self.gateway, "Developer use: Loads the blitz gateway into a Python interpreter") 86 87 selenium = parser.add(sub, self.seleniumtest, "Developer use: runs selenium tests on a django app") 88 selenium.add_argument("djangoapp", help = "Django-app to be tested") 89 90 test = parser.add(sub, self.test, "Developer use: Runs 'coverage -x manage.py test'") 91 test.add_argument("arg", nargs="*")
92
93 - def host_and_port(self, APPLICATION_HOST):
94 parts = APPLICATION_HOST.split(':') 95 if len(parts) != 3: 96 self.ctx.die(656, "Invalid application host: %s" % ":".join(parts)) 97 try: 98 host = parts[1] 99 while host.startswith(r"/"): 100 host = host[1:] 101 port = parts[2] 102 port = re.search(r'^(\d+).*', port).group(1) 103 port = int(port) 104 return (host, port) 105 except Exception, e: 106 self.ctx.die(567, "Badly formed domain: %s -- %s" % (":".join(parts), e))
107
108 - def config(self, args):
109 if not args.type: 110 self.ctx.out("Available configuration helpers:\n - nginx, apache\n") 111 else: 112 server = args.type 113 host, port = self.host_and_port(settings.APPLICATION_HOST) 114 if args.http: 115 port = args.http 116 if settings.APPLICATION_SERVER == settings.FASTCGITCP: 117 if settings.APPLICATION_SERVER_PORT == port: 118 self.ctx.die(678, "Port conflict: HTTP(%s) and fastcgi-tcp(%s)." % \ 119 (port, settings.APPLICATION_SERVER_PORT)) 120 if server == "nginx": 121 if settings.APPLICATION_SERVER == settings.FASTCGITCP: 122 fastcgi_pass = "%s:%s" % (settings.APPLICATION_SERVER_HOST, 123 settings.APPLICATION_SERVER_PORT) 124 else: 125 fastcgi_pass = "unix:%s/var/django_fcgi.sock" % self.ctx.dir 126 c = file(self.ctx.dir / "etc" / "nginx.conf.template").read() 127 d = { 128 "ROOT":self.ctx.dir, 129 "OMEROWEBROOT":self.ctx.dir / "lib" / "python" / "omeroweb", 130 "HTTPPORT":port, 131 "FASTCGI_PASS":fastcgi_pass, 132 } 133 self.ctx.out(c % d) 134 if server == "apache": 135 if settings.APPLICATION_SERVER == settings.FASTCGITCP: 136 fastcgi_external = '-host %s:%s' % \ 137 (settings.APPLICATION_SERVER_HOST, 138 settings.APPLICATION_SERVER_PORT) 139 else: 140 fastcgi_external = '-socket "%s/var/django_fcgi.sock"' % \ 141 self.ctx.dir 142 stanza = """### 143 ### Stanza for OMERO.web created %(NOW)s 144 ### 145 FastCGIExternalServer "%(ROOT)s/var/omero.fcgi" %(FASTCGI_EXTERNAL)s 146 147 <Directory "%(ROOT)s/var"> 148 Options -Indexes FollowSymLinks 149 Order allow,deny 150 Allow from all 151 </Directory> 152 153 <Directory "%(MEDIA)s"> 154 Options -Indexes FollowSymLinks 155 Order allow,deny 156 Allow from all 157 </Directory> 158 159 Alias /appmedia %(MEDIA)s 160 Alias / "%(ROOT)s/var/omero.fcgi/" 161 """ 162 d = { 163 "ROOT":self.ctx.dir, 164 "MEDIA":self.ctx.dir / "lib" / "python" / "omeroweb" / "media", 165 "OMEROWEBROOT":self.ctx.dir / "lib" / "python" / "omeroweb", 166 "FASTCGI_EXTERNAL":fastcgi_external, 167 "NOW":str(datetime.now()), 168 } 169 self.ctx.out(stanza % d)
170
171 - def syncmedia(self, args):
172 import shutil 173 from glob import glob 174 location = self.ctx.dir / "lib" / "python" / "omeroweb" 175 # Targets 176 apps = map(lambda x: x.startswith('omeroweb.') and x[9:] or x, settings.INSTALLED_APPS) 177 apps = filter(lambda x: os.path.exists(location / x), apps) 178 # Destination dir 179 if not os.path.exists(location / 'media'): 180 os.mkdir(location / 'media') 181 182 # Create app media links 183 for app in apps: 184 media_dir = location / app / 'media' 185 if os.path.exists(media_dir): 186 if os.path.exists(location / 'media' / app): 187 os.remove(os.path.abspath(location / 'media' / app)) 188 try: 189 # Windows does not support symlink 190 sys.getwindowsversion() 191 shutil.copytree(os.path.abspath(media_dir), location / 'media' / app) 192 except: 193 os.symlink(os.path.abspath(media_dir), location / 'media' / app)
194
195 - def enableapp(self, args):
196 location = self.ctx.dir / "lib" / "python" / "omeroweb" 197 if not args.appname: 198 apps = [x.name for x in filter(lambda x: x.isdir() and (x / 'scripts' / 'enable.py').exists(), location.listdir())] 199 iapps = map(lambda x: x.startswith('omeroweb.') and x[9:] or x, settings.INSTALLED_APPS) 200 apps = filter(lambda x: x not in iapps, apps) 201 self.ctx.out('[enableapp] available apps:\n - ' + '\n - '.join(apps) + '\n') 202 else: 203 for app in args.appname: 204 args = ["python", location / app / "scripts" / "enable.py"] 205 rv = self.ctx.call(args, cwd = location) 206 if rv != 0: 207 self.ctx.die(121, "Failed to enable '%s'.\n" % app) 208 else: 209 self.ctx.out("App '%s' was enabled\n" % app) 210 args = ["python", "manage.py", "syncdb", "--noinput"] 211 rv = self.ctx.call(args, cwd = location) 212 self.syncmedia(None)
213
214 - def gateway(self, args):
215 location = self.ctx.dir / "lib" / "python" / "omeroweb" 216 args = ["python", "-i", location / "../omero/gateway/scripts/dbhelpers.py"] 217 os.environ['ICE_CONFIG'] = self.ctx.dir / "etc" / "ice.config" 218 os.environ['PATH'] = os.environ.get('PATH', '.') + ':' + self.ctx.dir / 'bin' 219 os.environ['DJANGO_SETTINGS_MODULE'] = os.environ.get('DJANGO_SETTINGS_MODULE', 'omeroweb.settings') 220 rv = self.ctx.call(args, cwd = location)
221
222 - def test(self, args):
223 location = self.ctx.dir / "lib" / "python" / "omeroweb" 224 cargs = ["coverage","-x", "manage.py", "test"] 225 if args.arg: 226 cargs.append(args.arg) 227 os.environ['ICE_CONFIG'] = self.ctx.dir / "etc" / "ice.config" 228 os.environ['PATH'] = os.environ.get('PATH', '.') + ':' + self.ctx.dir / 'bin' 229 rv = self.ctx.call(cargs, cwd = location)
230
231 - def seleniumtest (self, args):
232 location = self.ctx.dir / "lib" / "python" / "omeroweb" 233 cargs = ["python", "seleniumtests.py"] 234 location = location / args.djangoapp / "tests" 235 print location 236 rv = self.ctx.call(cargs, cwd = location )
237
238 - def call (self, args):
239 try: 240 location = self.ctx.dir / "lib" / "python" / "omeroweb" 241 cargs = [] 242 appname = args.appname 243 scriptname = args.scriptname.split(' ') 244 if len(scriptname) > 1: 245 cargs.append(scriptname[0]) 246 scriptname = ' '.join(scriptname[1:]) 247 else: 248 scriptname = scriptname[0] 249 cargs.extend([location / appname / "scripts" / scriptname] + args.arg) 250 print cargs 251 os.environ['DJANGO_SETTINGS_MODULE'] = 'omeroweb.settings' 252 os.environ['ICE_CONFIG'] = self.ctx.dir / "etc" / "ice.config" 253 os.environ['PATH'] = os.environ.get('PATH', '.') + ':' + self.ctx.dir / 'bin' 254 rv = self.ctx.call(cargs, cwd = location) 255 except: 256 import traceback 257 print traceback.print_exc()
258 259
260 - def start(self, args):
261 262 import omeroweb.settings as settings 263 link = ("%s:%s" % (settings.APPLICATION_SERVER_HOST, 264 settings.APPLICATION_SERVER_PORT)) 265 location = self.ctx.dir / "lib" / "python" / "omeroweb" 266 self.ctx.out("Starting OMERO.web... ", newline=False) 267 cache_backend = getattr(settings, 'CACHE_BACKEND', None) 268 if cache_backend is not None and cache_backend.startswith("file:///"): 269 cache_backend = cache_backend[7:] 270 if "Windows" != platform.system() \ 271 and not os.access(cache_backend, os.R_OK|os.W_OK): 272 self.ctx.out("[FAILED]") 273 self.ctx.out("CACHE_BACKEND '%s' not writable or missing." % \ 274 getattr(settings, 'CACHE_BACKEND')) 275 return 1 276 deploy = getattr(settings, 'APPLICATION_SERVER') 277 278 # 3216 279 if deploy in (settings.FASTCGI_TYPES): 280 if "Windows" == platform.system(): 281 self.ctx.out(""" 282 WARNING: Unless you **really** know what you are doing you should NOT be 283 using bin\omero web start on Windows with FastCGI. 284 """) 285 pid_path = self.ctx.dir / "var" / "django.pid" 286 pid_num = None 287 288 if pid_path.exists(): 289 pid_txt = pid_path.text().strip() 290 try: 291 pid_num = int(pid_txt) 292 except: 293 pid_path.remove() 294 self.ctx.err("Removed invalid %s: '%s'" % (pid_path, pid_txt)) 295 296 if pid_num is not None: 297 try: 298 os.kill(pid_num, 0) 299 self.ctx.die(606, "%s exists! Use 'web stop' first" % pid_path) 300 except OSError: 301 pid_path.remove() 302 self.ctx.err("Removed stale %s" % pid_path) 303 304 if deploy == settings.FASTCGI: 305 cmd = "python manage.py runfcgi workdir=./" 306 cmd += " method=prefork socket=%(base)s/var/django_fcgi.sock" 307 cmd += " pidfile=%(base)s/var/django.pid daemonize=true" 308 cmd += " maxchildren=5 minspare=1 maxspare=5 maxrequests=400" 309 django = (cmd % {'base': self.ctx.dir}).split() 310 rv = self.ctx.popen(args=django, cwd=location) # popen 311 elif deploy == settings.FASTCGITCP: 312 cmd = "python manage.py runfcgi workdir=./" 313 cmd += " method=prefork host=%(host)s port=%(port)s" 314 cmd += " pidfile=%(base)s/var/django.pid daemonize=true" 315 cmd += " maxchildren=5 minspare=1 maxspare=5 maxrequests=400" 316 django = (cmd % {'base': self.ctx.dir, 317 'host': settings.APPLICATION_SERVER_HOST, 318 'port': settings.APPLICATION_SERVER_PORT}).split() 319 rv = self.ctx.popen(args=django, cwd=location) # popen 320 else: 321 django = ["python","manage.py","runserver", link, "--noreload"] 322 rv = self.ctx.call(django, cwd = location) 323 self.ctx.out("[OK]") 324 return rv
325 326
327 - def status(self, args):
328 location = self.ctx.dir / "lib" / "python" / "omeroweb" 329 self.ctx.out("OMERO.web status... ", newline=False) 330 import omeroweb.settings as settings 331 deploy = getattr(settings, 'APPLICATION_SERVER') 332 cache_backend = getattr(settings, 'CACHE_BACKEND', None) 333 if cache_backend is not None: 334 cache_backend = ' (CACHE_BACKEND %s)' % cache_backend 335 else: 336 cache_backend = '' 337 rv = 0 338 if deploy in settings.FASTCGI_TYPES: 339 try: 340 f=open(self.ctx.dir / "var" / "django.pid", 'r') 341 pid = int(f.read()) 342 except IOError: 343 self.ctx.out("[NOT STARTED]") 344 return rv 345 import signal 346 try: 347 os.kill(pid, 0) # NULL signal 348 self.ctx.out("[RUNNING] (PID %d)%s" % (pid, cache_backend)) 349 except: 350 self.ctx.out("[NOT STARTED]") 351 return rv 352 else: 353 self.ctx.err("DEVELOPMENT: You will have to check status by hand!") 354 return rv
355
356 - def stop(self, args):
357 self.ctx.out("Stopping OMERO.web... ", newline=False) 358 import omeroweb.settings as settings 359 deploy = getattr(settings, 'APPLICATION_SERVER') 360 if deploy in settings.FASTCGI_TYPES: 361 if "Windows" == platform.system(): 362 self.ctx.out(""" 363 WARNING: Unless you **really** know what you are doing you should NOT be 364 using bin\omero web start on Windows with FastCGI. 365 """) 366 pid = 'Unknown' 367 pid_path = self.ctx.dir / "var" / "django.pid" 368 pid_text = pid_path.text().strip() 369 try: 370 try: 371 pid = int(pid_text) 372 import signal 373 os.kill(pid, 0) # NULL signal 374 except: 375 self.ctx.out("[FAILED]") 376 self.ctx.out("Django FastCGI workers (PID %s) not started?" % pid_text) 377 return 378 os.kill(pid, signal.SIGTERM) #kill whole group 379 self.ctx.out("[OK]") 380 self.ctx.out("Django FastCGI workers (PID %d) killed." % pid) 381 finally: 382 pid_path.remove() 383 else: 384 self.ctx.err("DEVELOPMENT: You will have to kill processes by hand!")
385 386 try: 387 register("web", WebControl, HELP) 388 except NameError: 389 if __name__ == "__main__": 390 cli = CLI() 391 cli.register("web", WebControl, HELP) 392 cli.invoke(sys.argv[1:]) 393