#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (C) 2008-2014 Glencoe Software, Inc. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
"""
Plugin for viewing and controlling active sessions for a local user.
Plugin read by omero.cli.Cli during initialization. The method(s)
defined here will be added to the Cli class for later use.
"""
import datetime
import os
import sys
import Ice
import IceImport
import time
import traceback
import warnings
import omero.java
IceImport.load("Glacier2_Router_ice")
from Glacier2 import PermissionDeniedException
from omero.rtypes import rlong
from omero.rtypes import unwrap
from omero.util import get_user
from omero.util.sessions import SessionsStore
from omero.cli import UserGroupControl, CLI, admin_only
from omero_ext.argparse import SUPPRESS
from omero.model.enums import AdminPrivilegeSudo
HELP = """Control and create user sessions
Sessions are stored locally on disk. Several can
be active simultaneously, but only one will be used
for a single invocation of omero.
"""
LONGHELP = """
Uses the login parameters from %(prog)s to login.
To list these options, use "%(prog)s -h"
Options for logging in:
# Provide all values interactively
$ omero sessions login
Server: [localhost:4064]
Username: [root]
Password:
# Pass values as the target
$ omero sessions login user@omero.example.com
Password:
$ omero sessions login user@omero.example.com:24064
Password:
# Pass some values via arguments
$ omero -s localhost sessions login
Username: [user]
Password:
$ omero -p 24064 sessions login
Server: [localhost:24064]
Username: [user]
Password:
# Pass all non-password values via arguments
$ omero -s localhost -u john sessions login
Password:
# Use a session ID to login without a password
$ omero -s localhost -k 8afe443f-19fc-4cc4-bf4a-850ec94f4650 \
sessions login
# Arguments can also go earlier
$ omero -k 8afe443f-19fc-4cc4-bf4a-850ec94f4650 sessions login
# The *last* "@" symbol is used
$ omero sessions login my.email@example.com@omero.example.com
Password:
# System administrators can use "--sudo" to login as others
$ omero sessions login --sudo=root example@localhost
Password for root:
Other sessions commands:
# Logging out of the currently active sessions
$ omero sessions logout
# List all locally available sessions (purging the expired ones)
$ omero sessions list
# List all local sessions
$ omero sessions list --no-purge
# List all active server sessions
$ omero sessions who
# List or change the group for the session
$ omero sessions group
$ omero sessions group mygroup
$ omero sessions group 123
# List or change the timeToLive for the session
$ omero sessions timeout
$ omero sessions timeout 300 # Seconds
$ omero sessions timeout 300 --session=$UUID
Custom sessions directory:
# Specify a custom session directory using OMERO_SESSIONDIR
$ export OMERO_SESSIONDIR=/tmp/my_sessions
# Create a new session stored under OMERO_SESSIONDIR
$ omero sessions login
$ omero sessions file
$ omero sessions list
"""
LISTHELP = """
By default, inactive sessions are purged from the local sessions store and
removed from the listing. To list all sessions stored locally independently of
their status, use the --no-purge argument.
"""
GROUPHELP = """
If any current services are open, the command will fail.
"""
WHOHELP = """
Administrators will receive a list of all active sessions
along with critical information on last activity. This is
useful for determining whether or not the server can be
restarted.
Other users will only see a list of names, i.e. users who
can be considered "online".
"""
[docs]
class SessionsControl(UserGroupControl):
FACTORY = SessionsStore
[docs]
def store(self, args):
try:
# Read base directory from deprecated --session-dir argument
base_dir = getattr(args, "session_dir", None)
if base_dir:
warnings.warn(
"--session-dir is deprecated. Use OMERO_SESSIONDIR"
" instead.", DeprecationWarning)
# Read base directory from deprecated OMERO_SESSION_DIR envvar
base_dir = os.environ.get('OMERO_SESSION_DIR', base_dir)
if 'OMERO_SESSION_DIR' in os.environ:
warnings.warn(
"OMERO_SESSION_DIR is deprecated. Use OMERO_SESSIONDIR"
" instead.", DeprecationWarning)
# Read sessions directory from OMERO_SESSIONDIR envvar
session_dir = None
if base_dir:
from omero_ext.path import path
session_dir = path(base_dir) / "omero" / "sessions"
sessions_dir = os.environ.get('OMERO_SESSIONDIR', session_dir)
return self.FACTORY(sessions_dir)
except OSError as ose:
filename = getattr(ose, "filename", sessions_dir)
self.ctx.die(155, "Could not access session dir: %s" % filename)
def _configure(self, parser):
parser.add_login_arguments()
sub = parser.sub()
parser.add(sub, self.help, "Extended help")
login = parser.add(
sub, self.login, self.login.__doc__)
logout = parser.add(
sub, self.logout, "Logout and remove current session key")
self._configure_login(login)
group = parser.add(
sub, self.group,
"Set the group of the given session by id or name" + GROUPHELP)
group.add_argument(
"target",
nargs="?",
help="Id or name of the group to switch this session to")
timeout = parser.add(
sub, self.timeout,
"Query or set the timeToIdle for the given session")
timeout.add_argument(
"seconds",
nargs="?",
type=int,
help="Number of seconds to set the timeToIdle value to")
timeout.add_argument(
"--session",
help="Session other than the current to update")
list = parser.add(sub, self.list, (
"List all available sessions stored locally\n\n" + LISTHELP))
list.add_argument(
"--no-purge", dest="purge", action="store_false",
help="Do not remove inactive sessions")
who = parser.add(sub, self.who, (
"List all active server sessions\n\n" + WHOHELP))
who.add_argument(
"--show-uuid", help="Show uuids for sessions",
action="store_true")
keepalive = parser.add(
sub, self.keepalive, "Keeps the current session alive")
keepalive.add_argument(
"-f", "--frequency", type=int, default=60,
help="Time in seconds between keep alive calls", metavar="SECS")
clear = parser.add(
sub, self.clear, "Close and remove locally stored sessions")
clear.add_argument(
"--all", action="store_true",
help="Remove all locally stored sessions not just inactive ones")
file = parser.add(
sub, self.file, "Print the path to the current session file")
key = parser.add(
sub, self.key, "Print the key of the current active session")
for x in (file, key, logout, keepalive, list, clear, group):
self._configure_dir(x)
open = parser.add(sub, self.open, "Create a session for "
"the given user and group")
self.add_single_user_argument(open)
self.add_single_group_argument(open, required=False)
open.add_argument("--timeout", nargs="?", type=int, default=0,
help="Timeout in seconds (optional; default: "
"maximum possible)")
close = parser.add(sub, self.close, "Close the session with "
"the given session ID")
close.add_argument("sessionId", nargs="?", help="The session ID")
def _configure_login(self, login):
login.add_login_arguments()
login.add_argument(
"-t", "--timeout", type=int,
help="Timeout for session. After this many inactive seconds, the"
" session will be closed")
login.add_argument(
"--retry", nargs="?", type=int, default=0,
help="Number of seconds to retry login (default: no retry)")
login.add_argument(
"connection", nargs="?",
help="Connection string. See extended help for examples")
self._configure_dir(login)
def _configure_dir(self, parser):
parser.add_argument("--session-dir", help=SUPPRESS)
[docs]
def help(self, args):
self.ctx.out(LONGHELP % {"prog": args.prog})
@admin_only(AdminPrivilegeSudo)
def open(self, args):
client = self.ctx.conn(args)
admin = client.sf.getAdminService()
user, group = self.get_single_user_group(args, admin)
if not user:
self.ctx.die(156, "No user found")
username = user.omeName.val
groupname = None
if group:
groupname = group.name.val
p = omero.sys.Principal()
p.name = username
if group:
p.group = groupname
p.eventType = "User"
svc = client.sf.getSessionService()
sess = svc.createSessionWithTimeout(p, (int(args.timeout)
* 1000))
sessId = sess.getUuid().val
tti = sess.getTimeToIdle().val / 1000
ttl = sess.getTimeToLive().val / 1000
msg = "Session created for user %s" % username
if groupname:
msg += " in group %s" % groupname
msg += " (timeToIdle: %d sec, timeToLive: %d sec)" % (tti, ttl)
self.ctx.err(msg)
self.ctx.out(sessId) # Ok, must share
[docs]
def close(self, args):
client = self.ctx.conn(args)
svc = client.sf.getSessionService()
session = None
try:
session = svc.getSession(args.sessionId)
except Exception:
self.ctx.err("No session with the given ID found.")
if session:
client.destroySession(args.sessionId)
self.ctx.out("Session %s closed." % args.sessionId)
[docs]
def login(self, args):
("Login to a given server, and store session key locally.\n\n"
"USER, HOST, and PORT are set as args or in a ssh-style "
"connection string.\n"
"PASSWORD can be entered interactively, or passed via "
"-w (insecure!).\n"
"Alternatively, a session KEY can be passed with '-k'.\n"
"Admin users can use --sudo=ADMINUSER to login for others.\n\n"
"Examples:\n"
" omero login example.com\n"
" omero login user@example.com\n"
" omero login user@example.com:24064\n"
" omero login -k SESSIONKEY example.com\n"
" omero login --sudo=root user@example\n"
"\n")
"""
Goals:
If server and key, then don't ask any questions.
If nothing requested, and something's active, use it. (i.e. don't
require port number)
Reconnect if possible (assuming parameters are the same)
"""
if self.ctx.conn() and not self.ctx.isquiet:
self.ctx.err("Active client found")
return # EARLY EXIT
create = getattr(args, "create", None)
store = self.store(args)
previous = store.get_current()
# Basic props, don't get fiddled with
props = {}
if args.group:
props["omero.group"] = args.group
#
# Retrieving the parameters as set by the user
# If these are set and different from the current
# connection, then a new one may be created.
#
# May be called by another plugin
server = getattr(args, "connection", None)
name = None
port = None
if args.server:
if server:
self.ctx.die(3, "Server specified twice: %s and %s"
% (server, args.server))
else:
server = args.server
if server:
server, name, port = self._parse_conn(server, name)
if args.user:
if name:
self.ctx.die(4, "Username specified twice: %s and %s"
% (name, args.user))
else:
name = args.user
if args.port:
if port:
self.ctx.die(5, "Port specified twice: %s and %s"
% (port, args.port))
else:
port = args.port
#
# If a key is provided, then that takes precedence.
# Unless the key is bad, there's no reason to ask
# the user for any more input.
#
pasw = args.password
if args.key:
if name and not self.ctx.isquiet:
self.ctx.err("Overriding name since session key set")
name = args.key
if args.group and not self.ctx.isquiet:
self.ctx.err("Ignoring group since session key set")
if args.password and not self.ctx.isquiet:
self.ctx.err("Ignoring password since session key set")
pasw = args.key
#
# If no key provided, then we check the last used connection
# by default. The only requirement is that the user can't
# have requested a differente server / name or conflicting
# props (group / port)
#
elif previous[0] and previous[1]:
server_differs = (server is not None and server != previous[0])
name_differs = (name is not None and name != previous[1])
port_differs = (port is not None and port != previous[3])
if not create and not server_differs and not name_differs \
and not port_differs:
try:
if previous[2] is not None:
# Missing session uuid file. Deleted? See #4199
conflicts = store.conflicts(
previous[0], previous[1], previous[2], props,
True)
if conflicts:
self.ctx.dbg("Not attaching because of"
" conflicts: %s" % conflicts)
else:
rv = store.attach(*previous[:-1])
return self.handle(rv, "Using")
if not self.ctx.isquiet:
self.ctx.out("Previously logged in to %s:%s as %s"
% (previous[0], previous[3],
previous[1]))
except Exception as e:
self.ctx.out("Previous session expired for %s on"
" %s:%s" % (previous[1], previous[0],
previous[3]))
self.ctx.dbg("Exception on attach: %s"
% traceback.format_exception(None, e, sys.exc_info()[2]))
try:
store.remove(*previous[:-1])
except OSError as ose:
self.ctx.dbg("Session file missing: %s" % ose)
except:
self.ctx.dbg("Exception on remove: %s"
% traceback.format_exception(None, e, sys.exc_info()[2]))
# Could tell user to manually clear here and then
# self.ctx.die()
self.ctx.err("Failed to remove session: %s" % e)
#
# If we've reached here, then the user either does not have
# an active session or has requested another (different options)
# If they've omitted some required value, we must ask for it.
#
if not server:
server, name, port = self._get_server(store, name, port)
if not name:
name = self._get_username(previous[1])
props["omero.host"] = server
props["omero.user"] = name
if port:
props["omero.port"] = port
if "timeout" in args and args.timeout:
props["omero.timeout"] = args.timeout
rv = None
#
# For session key access, we now need to lookup the stored_name
# since otherwise, all access to the directory under ~/omero/sessions
# will fail. Then, if no session can be found, we exit immediately
# rather than asking for a password. See #4223
#
if args.key:
stored_name = store.find_name_by_key(server, args.key)
if not stored_name:
# ticket:5975 : If this is the case, then this session key
# did not come from a CLI login, and so we're not going to
# modify the value returned by store.get_current()
self.ctx.dbg("No local session file found for %s." % args.key)
rv = self.attach(store, server, args.key, args.key, props,
False, set_current=False)
else:
rv = self.check_and_attach(store, server, stored_name,
args.key, props, check_group=False)
action = "Joined"
if not rv:
if port:
msg = "Cannot join %s on %s:%s." % (args.key, server, port)
else:
msg = "Cannot join %s on %s." % (args.key, server)
self.ctx.die(523, "Bad session key. %s" % msg)
elif not create:
available = store.available(server, name)
for uuid in available:
rv = self.check_and_attach(store, server, name, uuid, props,
check_group=True)
action = "Reconnected to"
if not rv:
if not pasw:
pasw = os.getenv("OMERO_PASSWORD")
tries = 3
while True:
try:
if not pasw:
# Handle absent and incorrect passwords in
# non-interactive mode
self._require_tty("cannot request password")
if args.sudo:
prompt = "Password for %s:" % args.sudo
else:
prompt = "Password:"
pasw = self.ctx.input(prompt, hidden=True,
required=True)
### Retry logic ############################################
start = time.time()
retries = 0
while True:
try:
rv = store.create(name, pasw, props, sudo=args.sudo)
break
except Exception as e:
elapsed = (time.time() - start)
cannot_retry = (
'retry' not in vars(args) or
not args.retry or
elapsed > args.retry)
if cannot_retry:
raise
else:
retries += 1
msg = str(datetime.datetime.now().time())
msg += ": Login retry #%d in 3s" % (retries)
self.ctx.err(msg)
time.sleep(3)
### End retry ##############################################
break
except PermissionDeniedException as pde:
tries -= 1
if not tries:
self.ctx.die(524, "3 incorrect password attempts")
else:
self.ctx.err(pde.reason)
pasw = None
except omero.RemovedSessionException as rse:
self.ctx.die(525, "User account error: %s." % rse.message)
except Ice.ConnectionRefusedException:
if port:
self.ctx.die(554, "Ice.ConnectionRefusedException:"
" %s:%s isn't running" % (server, port))
else:
self.ctx.die(554, "Ice.ConnectionRefusedException: %s"
" isn't running" % server)
except Ice.DNSException:
self.ctx.die(555, "Ice.DNSException: bad host name: '%s'"
% server)
except omero.SecurityViolation as sv:
self.ctx.die(557, "SecurityViolation: %s" % sv.message)
except Exception as e:
exc = traceback.format_exc()
self.ctx.dbg(exc)
self.ctx.die(556, "InternalException: Failed to connect:"
" %s" % e)
action = "Created"
return self.handle(rv, action)
[docs]
def check_and_attach(self, store, server, name, uuid, props,
check_group=False):
"""
Checks for conflicts in the settings for this session,
and if there are none, then attempts an "attach()". If
that fails, the session is removed.
"""
exists = store.exists(server, name, uuid)
if exists:
conflicts = store.conflicts(server, name, uuid, props,
check_group=check_group)
if conflicts:
if "omero.port" in conflicts:
self.ctx.dbg("Skipping session %s due to mismatching"
" ports: %s " % (uuid, conflicts))
elif not self.ctx.isquiet:
self.ctx.err("Skipped session %s due to property"
" conflicts: %s" % (uuid, conflicts))
return None
return self.attach(store, server, name, uuid, props, exists)
[docs]
def attach(self, store, server, name, uuid, props, exists,
set_current=True):
rv = None
try:
if exists:
rv = store.attach(server, name, uuid, set_current=set_current)
else:
rv = store.create(name, name, props, set_current=set_current)
except Exception as e:
self.ctx.dbg("Removing %s: %s" % (uuid, e))
store.clear(server, name, uuid)
return rv
[docs]
def handle(self, rv, action):
"""
Handles a new connection
"""
client, uuid, idle, live = rv
sf = client.sf
# detachOnDestroy called by omero.util.sessions
client.enableKeepAlive(300)
ec = sf.getAdminService().getEventContext()
self.ctx.set_event_context(ec)
self.ctx.set_client(client)
host = client.getProperty("omero.host")
port = client.getProperty("omero.port")
msg = "%s session for %s@%s:%s." \
% (action, ec.userName, host, port)
msg += self._parse_timeout(idle, " Idle timeout: ")
msg += self._parse_timeout(live, " Expires in : ")
msg += (" Current group: %s" % ec.groupName)
if not self.ctx.isquiet:
self.ctx.err(msg)
def _parse_timeout(self, timeout, msg=""):
timeout = unwrap(timeout)
if not timeout:
return ""
unit = "min."
val = float(timeout) / 60 / 1000
if val < 5:
unit = "s."
val = val * 60
return "%s%.f %s" % (msg, val, unit)
[docs]
def logout(self, args):
store = self.store(args)
previous = store.get_current()
try:
rv = store.attach(*previous[:-1])
rv[0].killSession()
except Exception as e:
self.ctx.dbg("Exception on logout: %s" % e)
store.remove(*previous[:-1])
# Last is still useful. Not resetting.
# store.set_current("", "", "")
[docs]
def group(self, args):
client = self.ctx.conn(args)
sf = client.sf
admin = sf.getAdminService()
if args.target is None:
ec = self.ctx.get_event_context()
self.ctx.out("ExperimenterGroup:%s" % ec.groupId)
return ec.groupName
try:
group_id = int(args.target)
group_name = admin.getGroup(group_id).name.val
except ValueError:
group_name = args.target
group_id = admin.lookupGroup(group_name).id.val
ec = self.ctx.get_event_context() # 5711
old_id = ec.groupId
old_name = ec.groupName
if old_id == group_id and not self.ctx.isquiet:
self.ctx.err("Group '%s' (id=%s) is already active"
% (group_name, group_id))
else:
try:
sf.setSecurityContext(omero.model.ExperimenterGroupI(
group_id, False))
self.ctx.set_event_context(
sf.getAdminService().getEventContext())
self.ctx.out("Group '%s' (id=%s) switched to '%s' (id=%s)" % (
old_name, old_id, group_name, group_id))
except omero.SecurityViolation as sv:
self.ctx.die(564, "SecurityViolation: %s" % sv.message)
[docs]
def timeout(self, args):
client = self.ctx.conn(args)
svc = client.sf.getSessionService()
uuid = args.session
if uuid is None:
uuid = self.ctx.get_event_context().sessionUuid
try:
obj = svc.getSession(uuid)
except:
self.ctx.dbg(traceback.format_exc())
self.ctx.die(557, "cannot get session: %s" % uuid)
if args.seconds is None:
# Query only
secs = unwrap(obj.timeToIdle) / 1000.0
self.ctx.out(secs)
return secs
req = omero.cmd.UpdateSessionTimeoutRequest()
req.session = uuid
req.timeToIdle = rlong(args.seconds * 1000)
try:
cb = client.submit(req) # Response is "OK"
cb.close(True)
except omero.CmdError as ce:
self.ctx.dbg(str(ce.err))
self.ctx.die(558, "CmdError: %s" % ce.err.name)
except:
self.ctx.dbg(traceback.format_exc())
self.ctx.die(559, "cannot update timeout for %s" % uuid)
[docs]
def list(self, args):
store = self.store(args)
s = store.contents()
previous = store.get_current()
headers = ("Server", "User", "Group", "Session", "Active", "Started")
results = dict([(x, []) for x in headers])
for server, names in list(s.items()):
for name, sessions in list(names.items()):
for uuid, props in list(sessions.items()):
rv = None
msg = "True"
grp = "Unknown"
started = "Unknown"
port = None
try:
if props:
port = props.get("omero.port", port)
rv = store.attach(server, name, uuid)
try:
a_s = rv[0].sf.getAdminService()
grp = a_s.getEventContext().groupName
s_s = rv[0].sf.getSessionService()
started = s_s.getSession(uuid).started.val
started = time.ctime(started / 1000.0)
finally:
if rv:
rv[0].closeSession()
except PermissionDeniedException as pde:
msg = pde.reason
except Exception as e:
self.ctx.dbg("Exception on attach: %s" % e)
msg = "Unknown exception"
if rv is None and args.purge:
try:
self.ctx.dbg("Purging %s / %s / %s"
% (server, name, uuid))
store.remove(server, name, uuid)
continue
except IOError as ioe:
self.ctx.dbg("Aborting session purging. %s" % ioe)
break
if server == previous[0] and name == previous[1] and \
uuid == previous[2]:
msg = "Logged in"
if port:
results["Server"].append("%s:%s" % (server, port))
else:
results["Server"].append(server)
results["User"].append(name)
results["Group"].append(grp)
results["Session"].append(uuid)
results["Active"].append(msg)
results["Started"].append(started)
from omero.util.text import Table, Column
columns = tuple([Column(x, results[x]) for x in headers])
self.ctx.out(str(Table(*columns)))
[docs]
def who(self, args):
show_uuid = args.show_uuid
client = self.ctx.conn(args)
uuid = self.ctx.get_event_context().sessionUuid
req = omero.cmd.CurrentSessionsRequest()
try:
cb = client.submit(req)
try:
rsp = cb.getResponse()
finally:
cb.close(True)
headers = ["name", "group", "logged in", "agent", "timeout"]
extra = set()
results = {"name": [], "group": [],
"logged in": [], "agent": [],
"timeout": [], "uuid": []}
# Preparse data to find extra columns
for idx, s in enumerate(rsp.sessions):
for k in list(rsp.data[idx].keys()):
extra.add(k)
if show_uuid:
headers.append("uuid")
for add in sorted(extra):
headers.append(add)
results[add] = []
for idx, s in enumerate(rsp.sessions):
ec = rsp.contexts[idx]
data = unwrap(rsp.data[idx])
# Handle missing keys
for k in extra:
if k not in list(data.keys()):
results[k].append("---")
for k, v in sorted(data.items()):
try:
if k.endswith("Time"):
t = v / 1000.0
t = time.localtime(t)
v = time.strftime('%Y-%m-%d %H:%M:%S', t)
except:
pass
results[k].append(v)
results["name"].append(ec.userName)
results["group"].append(ec.groupName)
if s is not None:
t = s.started.val / 1000.0
t = time.localtime(t)
t = time.strftime("%Y-%m-%d %H:%M:%S", t)
if uuid == ec.sessionUuid:
t = t + " (*)"
results["logged in"].append(t)
results["agent"].append(unwrap(s.userAgent))
results["timeout"].append(
self._parse_timeout(s.timeToIdle))
results["uuid"].append(ec.sessionUuid)
else:
# Insufficient privileges. The EventContext
# will be missing fields as well.
msg = "---"
results["logged in"].append(msg)
results["agent"].append(msg)
results["timeout"].append(msg)
results["uuid"].append(msg)
from omero.util.text import Table, Column
columns = tuple([Column(x, results[x]) for x in headers])
self.ctx.out(str(Table(*columns)))
except omero.CmdError as ce:
self.ctx.dbg(str(ce.err))
self.ctx.die(560, "CmdError: %s" % ce.err.name)
except omero.ClientError as ce:
if ce.message == "Null handle":
v = client.sf.getConfigService().getVersion()
self.ctx.die(561,
"Operation unsupported. Server version: %s" % v)
else:
exc = traceback.format_exc()
self.ctx.dbg(exc)
self.ctx.die(562, "ClientError: %s" % ce.err.name)
except omero.LockTimeout:
exc = traceback.format_exc()
self.ctx.dbg(exc)
self.ctx.die(563, "LockTimeout: operation took too long")
[docs]
def clear(self, args):
store = self.store(args)
count = store.count()
store.clear()
self.ctx.out("%s session(s) cleared" % count)
[docs]
def keepalive(self, args):
import threading
from omero.util.concurrency import get_event as get_event
class T(threading.Thread):
def run(self):
while self.client:
try:
self.client.sf.keepAlive(None)
self.event.wait(args.frequency)
except Exception as e:
self.err("Keep alive failed: %s" % str(e))
return
t = T()
t.client = self.ctx.conn(args)
t.err = self.ctx.err
t.event = get_event(name="keepalive")
t.start()
try:
self.ctx.out("Running keep alive every %s seconds"
% args.frequency)
self.ctx.input("Press enter to cancel.")
finally:
t.client = None
t.event.set()
[docs]
def file(self, args):
"""Return the file associated with the current active session"""
store = self.store(args)
srv, usr, uuid, port = store.get_current()
if srv and usr and uuid:
self.ctx.out(str(store.dir / srv / usr / uuid))
[docs]
def key(self, args):
"""Return the key associated with the current active session"""
store = self.store(args)
srv, usr, uuid, port = store.get_current()
if uuid:
self.ctx.out(uuid)
[docs]
def conn(self, properties=None, profile=None, args=None):
"""
Either creates or returns the exiting omero.client instance.
Uses the comm() method with the same signature.
"""
if properties is None:
properties = {}
if self.get_client():
return self.get_client()
import omero
try:
data = self.initData(properties)
self.set_client(omero.client(sys.argv, id=data))
self.get_client().setAgent("OMERO.cli")
self.get_client().createSession()
return self.get_client()
except Exception:
self.set_client(None)
raise
#
# Private methods
#
@staticmethod
def _parse_conn(server, default_name):
"""Parse a connection string of form (user@)server(:port)"""
import re
pat = r'^((?P<name>.+)@)?(?P<server>.*?)(:(?P<port>\d{1,5}))?$'
match = re.match(pat, server)
server = match.group('server')
name = match.group('name')
port = match.group('port')
if not name:
name = default_name
return server, name, port
def _get_server(self, store, name, port):
defserver = store.last_host()
if not port:
port = store.last_port()
self._require_tty("cannot request server")
rv = self.ctx.input("Server: [%s:%s]" % (defserver, port))
if not rv:
return defserver, name, port
else:
return self._parse_conn(rv, name)
def _get_username(self, defuser):
if defuser is None:
defuser = get_user("root")
self._require_tty("cannot request username")
rv = self.ctx.input("Username: [%s]" % defuser)
if not rv:
return defuser
else:
return rv
def _require_tty(self, msg):
if not sys.stdin.isatty():
self.ctx.die(564, "stdin is not a terminal: %s" % msg)
elif not sys.stdout.isatty():
self.ctx.die(564, "stdout is not a terminal: %s" % msg)
else:
return
try:
register("sessions", SessionsControl, HELP)
except NameError:
if __name__ == "__main__":
cli = CLI()
cli.register("sessions", SessionsControl, HELP)
cli.invoke(sys.argv[1:])