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