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

Source Code for Module omero.plugins.sessions

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