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

Source Code for Module omero.plugins.sessions

  1  #!/usr/bin/env python 
  2  """ 
  3     Plugin for viewing and controlling active sessions for a local user. 
  4   
  5     Plugin read by omero.cli.Cli during initialization. The method(s) 
  6     defined here will be added to the Cli class for later use. 
  7   
  8     Copyright 2008 Glencoe Software, Inc. All rights reserved. 
  9     Use is subject to license terms supplied in LICENSE.txt 
 10   
 11  """ 
 12   
 13   
 14  import os 
 15  import sys 
 16  import Ice, Glacier2 
 17  import time 
 18  import traceback 
 19  import exceptions 
 20  import subprocess 
 21  import getpass 
 22  import omero.java 
 23   
 24  from omero.util.sessions import SessionsStore 
 25  from omero.cli import BaseControl, CLI 
 26  from path import path 
 27   
 28  HELP = """Control and create user sessions 
 29   
 30  Sessions are stored locally on disk. Several can 
 31  be active simultaneously, but only one will be used 
 32  for a single invocation of bin/omero. 
 33   
 34  """ 
 35   
 36  LONGHELP = """ 
 37      Uses the login parameters from %(prog)s to login. 
 38   
 39      To list these options, use "%(prog)s -h" 
 40   
 41      Sample session: 
 42          $ bin/omero -s localhost sessions login 
 43          Username: 
 44          Password: 
 45          $ bin/omero -s localhost -u john sessions login 
 46          Password 
 47          $ bin/omero -s localhost -k 8afe443f-19fc-4cc4-bf4a-850ec94f4650 sessions login 
 48          $ bin/omero sessions login 
 49          Server: 
 50          Username: 
 51          Password: 
 52          $ bin/omero sessions login user@omero.example.com 
 53          Password: 
 54          $ bin/omero sessions logout 
 55          $ bin/omero sessions login 
 56          Reuse current session? [Y/n] 
 57          $ bin/omero sessions list 
 58          $ bin/omero sessions logout 
 59          $ bin/omero sessions login omero.example.com 
 60          Username: 
 61          Password: 
 62          $ bin/omero sessions logout 
 63          $ bin/omero -p 24064 sessions login 
 64          Server: 
 65          Username: 
 66          Password: 
 67          $ bin/omero sessions login my.email@example.com@omero.example.com 
 68          Password: 
 69          $ bin/omero -k 8afe443f-19fc-4cc4-bf4a-850ec94f4650 sessions login 
 70          $ bin/omero sessions clear 
 71          $ bin/omero sessions list --session-dir=/tmp 
 72  """ 
 73   
74 -class SessionsControl(BaseControl):
75 76 FACTORY = SessionsStore 77
78 - def store(self, args):
79 try: 80 dirpath = getattr(args, "session_dir", None) 81 return self.FACTORY(dirpath) 82 except OSError, ose: 83 filename = getattr(ose, "filename", dirpath) 84 self.ctx.die(155, "Could not access session dir: %s" % filename)
85
86 - def _configure(self, parser):
87 sub = parser.sub() 88 help = parser.add(sub, self.help, "Extended help") 89 login = parser.add(sub, self.login, "Login to a given server, and store session key locally") 90 logout = parser.add(sub, self.logout, "Logout and remove current session key") 91 self._configure_login(login, logout) 92 group = parser.add(sub, self.group, "Set the group of the current session by id or name") 93 group.add_argument("target", help="Id or name of the group to switch this session to") 94 list = parser.add(sub, self.list, "List all locally stored sessions") 95 list.add_argument("--purge", action="store_true", help="Remove inactive sessions") 96 keepalive = parser.add(sub, self.keepalive, "Keeps the current session alive") 97 keepalive.add_argument("-f", "--frequency", type=int, default=60, help="Time in seconds between keep alive calls", metavar="SECS") 98 clear = parser.add(sub, self.clear, "Close and remove locally stored sessions") 99 clear.add_argument("--all", action="store_true", help="Remove all locally stored sessions not just inactive ones") 100 file = parser.add(sub, self.file, "Print the path to the current session file") 101 102 for x in (file, logout, keepalive, list, clear, group): 103 self._configure_dir(x)
104
105 - def _configure_login(self, login, logout = None):
106 login.add_argument("-t", "--timeout", help="Timeout for session. After this many inactive seconds, the session will be closed") 107 login.add_argument("connection", nargs="?", help="Connection string. See extended help for examples") 108 self._configure_dir(login)
109
110 - def _configure_dir(self, parser):
111 parser.add_argument("--session-dir", help="Use a different sessions directory (Default: $HOME/omero/sessions)")
112
113 - def help(self, args):
114 self.ctx.err(LONGHELP % {"prog":args.prog})
115
116 - def login(self, args):
117 """ 118 Goals: 119 If server and key, then don't ask any questions. 120 If nothing requested, and something's active, use it. (i.e. don't require port number) 121 Reconnect if possible (assuming parameters are the same) 122 """ 123 124 if self.ctx.conn(): 125 self.ctx.err("Active client found") 126 return # EARLY EXIT 127 128 create = getattr(args, "create", None) 129 store = self.store(args) 130 previous = store.get_current() 131 132 # Basic props, don't get fiddled with 133 props = {} 134 if args.port: 135 props["omero.port"] = args.port 136 if args.group: 137 props["omero.group"] = args.group 138 139 # 140 # Retrieving the parameters as set by the user 141 # If these are set and different from the current 142 # connection, then a new one may be created. 143 # 144 server = getattr(args, "connection", None) # May be called by another plugin 145 name = None 146 147 if args.server: 148 if server: 149 self.ctx.die(3, "Server specified twice: %s and %s" % (server, args.server)) 150 else: 151 server = args.server 152 153 if server: server, name = self._parse_conn(server) 154 155 if args.user: 156 if name: 157 self.ctx.die(4, "Username specified twice: %s and %s" % (name, args.user)) 158 else: 159 name = args.user 160 161 # 162 # If a key is provided, then that takes precedence. 163 # Unless the key is bad, there's no reason to ask 164 # the user for any more input. 165 # 166 pasw = args.password 167 if args.key: 168 if name: 169 self.ctx.err("Overriding name since session set") 170 name = args.key 171 if args.password: 172 self.ctx.err("Ignoring password since key set") 173 pasw = args.key 174 # 175 # If no key provided, then we check the last used connection 176 # by default. The only requirement is that the user can't 177 # have requested a differente server / name or conflicting 178 # props (group / port) 179 # 180 elif previous[0] and previous[1]: 181 182 server_differs = (server is not None and server != previous[0]) 183 name_differs = (name is not None and name != previous[1]) 184 185 if not create and not server_differs and not name_differs: 186 try: 187 conflicts = store.conflicts(previous[0], previous[1], previous[2], props, True) 188 if conflicts: 189 self.ctx.dbg("Not attaching because of conflicts: %s" % conflicts) 190 else: 191 rv = store.attach(*previous) 192 return self.handle(rv, "Using") 193 except exceptions.Exception, e: 194 self.ctx.dbg("Exception on attach: %s" % traceback.format_exc(e)) 195 self.ctx.dbg("Exception on attach: %s" % e) 196 197 self.ctx.out("Previously logged in to %s as %s" % (previous[0], previous[1])) 198 199 # 200 # If we've reached here, then the user either does not have 201 # an active session or has requested another (different options) 202 # If they've omitted some required value, we must ask for it. 203 # 204 if not server: server, name = self._get_server(store) 205 if not name: name = self._get_username() 206 207 props["omero.host"] = server 208 props["omero.user"] = name 209 210 rv = None 211 if not create: 212 available = store.available(server, name) 213 for uuid in available: 214 conflicts = store.conflicts(server, name, uuid, props) 215 if conflicts: 216 self.ctx.dbg("Skipping %s due to conflicts: %s" % (uuid, conflicts)) 217 continue 218 try: 219 rv = store.attach(server, name, uuid) 220 store.set_current(server, name, uuid) 221 action = "Reconnected to" 222 break 223 except: 224 self.ctx.dbg("Removing %s" % uuid) 225 store.clear(server, name, uuid) 226 continue 227 228 if not rv: 229 tries = 3 230 while True: 231 try: 232 if not pasw: 233 pasw = self.ctx.input("Password:", hidden = True, required = True) 234 rv = store.create(name, pasw, props) 235 break 236 except Glacier2.PermissionDeniedException, pde: 237 tries -= 1 238 if not tries: 239 self.ctx.die(524, "3 incorrect password attempts") 240 else: 241 self.ctx.err(pde.reason) 242 pasw = None 243 except Ice.ConnectionRefusedException: 244 self.ctx.die(554, "Ice.ConnectionRefusedException: %s isn't running" % server) 245 except Ice.DNSException: 246 self.ctx.die(555, "Ice.DNSException: bad host name: '%s'" % server) 247 except exceptions.Exception, e: 248 exc = traceback.format_exc() 249 self.ctx.dbg(exc) 250 self.ctx.die(556, "InternalException: Failed to connect: %s" % e) 251 action = "Created" 252 253 return self.handle(rv, action)
254
255 - def handle(self, rv, action):
256 """ 257 Handles a new connection 258 """ 259 client, uuid, idle, live = rv 260 sf = client.sf 261 262 # detachOnDestroy called by omero.util.sessions 263 client.enableKeepAlive(300) 264 ec = sf.getAdminService().getEventContext() 265 self.ctx._event_context = ec 266 self.ctx._client = client 267 268 msg = "%s session %s (%s@%s)." % (action, uuid, ec.userName, client.getProperty("omero.host")) 269 if idle: 270 msg = msg + " Idle timeout: %s min." % (float(idle)/60/1000) 271 if live: 272 msg = msg + " Expires in %s min." % (float(live)/60/1000) 273 274 msg += (" Current group: %s" % sf.getAdminService().getEventContext().groupName) 275 276 self.ctx.err(msg)
277
278 - def logout(self, args):
279 store = self.store(args) 280 previous = store.get_current() 281 282 import Glacier2 283 try: 284 rv = store.attach(*previous) 285 rv[0].killSession() 286 except exceptions.Exception, e: 287 self.ctx.dbg("Exception on logout: %s" % e) 288 store.set_current("", "", "")
289
290 - def group(self, args):
291 store = self.store(args) 292 client = self.ctx.conn(args) 293 sf = client.sf 294 admin = sf.getAdminService() 295 296 try: 297 group_id = long(args.target) 298 group_name = admin.getGroup(group_id).name.val 299 except ValueError, ve: 300 group_name = args.target 301 group_id = admin.lookupGroup(group_name).id.val 302 303 ec = admin.getEventContext() 304 old_id = ec.groupId 305 old_name = ec.groupName 306 if old_id == group_id: 307 self.ctx.err("Group '%s' (id=%s) is already active" % (group_name, group_id)) 308 else: 309 sf.setSecurityContext(omero.model.ExperimenterGroupI(group_id, False)) 310 self.ctx.out("Group '%s' (id=%s) switched to '%s' (id=%s)" % (old_name, old_id, group_name, group_id))
311
312 - def list(self, args):
313 import Glacier2 314 store = self.store(args) 315 s = store.contents() 316 previous = store.get_current() 317 318 #fmt = "%-16.16s\t%-12.12s\t%-12.12s\t%-40.40s\t%-30.30s\t%s" 319 #self.ctx.out(fmt % ("Server","User","Group", "Session","Active", "Started")) 320 #self.ctx.out("-"*136) 321 headers = ("Server", "User", "Group", "Session", "Active", "Started") 322 results = dict([(x,[]) for x in headers]) 323 for server, names in s.items(): 324 for name, sessions in names.items(): 325 for uuid, props in sessions.items(): 326 rv = None 327 msg = "True" 328 grp = "Unknown" 329 started = "Unknown" 330 try: 331 rv = store.attach(server, name, uuid) 332 grp = rv[0].sf.getAdminService().getEventContext().groupName 333 started = rv[0].sf.getSessionService().getSession(uuid).started.val 334 started = time.ctime(started / 1000.0) 335 rv[0].closeSession() 336 except Glacier2.PermissionDeniedException, pde: 337 msg = pde.reason 338 except exceptions.Exception, e: 339 self.ctx.dbg("Exception on attach: %s" % e) 340 msg = "Unknown exception" 341 if rv is None and args.purge: 342 self.ctx.dbg("Purging %s / %s / %s" % (server, name, uuid)) 343 store.remove(server, name, uuid) 344 345 if server == previous[0] and name == previous[1] and uuid == previous[2]: 346 msg = "Logged in" 347 348 results["Server"].append(server) 349 results["User"].append(name) 350 results["Group"].append(grp) 351 results["Session"].append(uuid) 352 results["Active"].append(msg) 353 results["Started"].append(started) 354 355 from omero.util.text import Table, Column 356 columns = tuple([Column(x, results[x]) for x in headers]) 357 self.ctx.out(str(Table(*columns)))
358
359 - def clear(self, args):
360 store = self.store(args) 361 count = store.count() 362 store.clear() 363 self.ctx.out("%s session(s) cleared" % count)
364
365 - def keepalive(self, args):
366 import threading 367 from omero.util.concurrency import get_event as get_event 368 class T(threading.Thread): 369 def run(self): 370 while self.client: 371 try: 372 self.client.sf.keepAlive(None) 373 self.event.wait(args.frequency) 374 except exceptions.Exception, e: 375 self.err("Keep alive failed: %s" % str(e)) 376 return
377 t = T() 378 t.client = self.ctx.conn(args) 379 t.event = get_event(name="keepalive") 380 t.start() 381 try: 382 self.ctx.out("Running keep alive every %s seconds" % args.frequency) 383 self.ctx.input("Press enter to cancel.") 384 finally: 385 t.client = None 386 t.event.set() 387
388 - def file(self, args):
389 store = self.store(args) 390 srv, usr, uuid = store.get_current() 391 if srv and usr and uuid: 392 self.ctx.out(str(store.dir / srv / usr / uuid))
393
394 - def conn(self, properties={}, profile=None, args=None):
395 """ 396 Either creates or returns the exiting omero.client instance. 397 Uses the comm() method with the same signature. 398 """ 399 400 if self._client: 401 return self._client 402 403 import omero 404 try: 405 data = self.initData(properties) 406 self._client = omero.client(sys.argv, id = data) 407 self._client.setAgent("OMERO.cli") 408 self._client.createSession() 409 return self._client 410 except Exc, exc: 411 self._client = None 412 raise
413 414 # 415 # Private methods 416 # 417
418 - def _parse_conn(self, server):
419 try: 420 idx = server.rindex("@") 421 return server[idx+1:], server[0:idx] # server, user which may also contain an @ 422 except ValueError: 423 return server, None
424
425 - def _get_server(self, store):
426 defserver = store.last_host() 427 rv = self.ctx.input("Server: [%s]" % defserver) 428 if not rv: 429 return defserver, None 430 else: 431 return self._parse_conn(rv)
432
433 - def _get_username(self):
434 defuser = getpass.getuser() 435 rv = self.ctx.input("Username: [%s]" % defuser) 436 if not rv: 437 return defuser 438 else: 439 return rv
440 441 442 try: 443 register("sessions", SessionsControl, HELP) 444 except NameError: 445 if __name__ == "__main__": 446 cli = CLI() 447 cli.register("sessions", SessionsControl, HELP) 448 cli.invoke(sys.argv[1:]) 449