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