1
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
76
77 FACTORY = SessionsStore
78
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
107
112
115
116 - def help(self, args):
118
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
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
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
149
150
151
152 server = getattr(args, "connection", None)
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
171
172
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
184
185
186
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:
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
213 self.ctx.err("Failed to remove session: %s" % e)
214
215
216
217
218
219
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
230
231
232
233
234 if args.key:
235 stored_name = store.find_name_by_key(server, args.key)
236 if not stored_name:
237
238
239
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
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
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
347
348
349
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
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
379
380
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
431
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):
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
485
486
488 try:
489 idx = server.rindex("@")
490 return server[idx+1:], server[0:idx]
491 except ValueError:
492 return server, None
493
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
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