The branch, twofactorauth has been updated via bcb1bc06b3f2ceb51f4b8a14f18c04b91f171855 (commit) from 61081683ba395feec7eb1b2ca0ccc8aa4e41e127 (commit) Those revisions listed above that are new to this repository have not appeared on any other notification email; so we list those revisions in full, below. - Log ----------------------------------------------------------------- ----------------------------------------------------------------------- Summary of changes: x2go/backends/control/stdout.py | 42 +++++++++++---- x2go/cache.py | 111 +++++++++++++++++++++++++++++++++++++++ x2go/cleanup.py | 12 ++--- x2go/client.py | 8 +-- x2go/guardian.py | 19 ++++--- x2go/session.py | 6 ++- 6 files changed, 168 insertions(+), 30 deletions(-) create mode 100644 x2go/cache.py The diff of changes is: diff --git a/x2go/backends/control/stdout.py b/x2go/backends/control/stdout.py index a38d55c..28bddb6 100644 --- a/x2go/backends/control/stdout.py +++ b/x2go/backends/control/stdout.py @@ -37,6 +37,7 @@ import x2go.log as log import x2go.utils as utils import x2go.x2go_exceptions as x2go_exceptions import x2go.defaults as defaults +import x2go.cache as cache from x2go.backends.terminal import X2goTerminalSession as _X2goTerminalSession from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo @@ -62,11 +63,14 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): _remote_home = None _remote_group = {} + x2go_listsessions_cache = None + def __init__(self, terminal_backend=_X2goTerminalSession, info_backend=_X2goServerSessionInfo, list_backend=_X2goServerSessionList, proxy_backend=_X2goProxy, + use_listsessions_cache=True, logger=None, loglevel=log.loglevel_DEFAULT, *args, **kwargs): """\ @@ -85,6 +89,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): self._info_backend = info_backend self._list_backend = list_backend self._proxy_backend = proxy_backend + self.use_listsessions_cache = use_listsessions_cache paramiko.SSHClient.__init__(self, *args, **kwargs) def __del__(self): @@ -263,6 +268,10 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): ssh_transport._x2go_session_marker = True self._session_password = password + if self.use_listsessions_cache: + self.x2go_listsessions_cache = cache.X2goListSessionsCache(self, logger=self.logger) + self.x2go_listsessions_cache.start() + return (self.get_transport() is not None) def disconnect(self): @@ -270,6 +279,8 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): STILL UNDOCUMENTED """ + self.x2go_listsessions_cache.stop_thread() + del self.x2go_listsessions_cache t_names = self.associated_terminals.keys() for t_obj in self.associated_terminals.values(): t_obj.suspend() @@ -305,7 +316,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group x2gousers' % self.get_transport().get_username()) if session_name is not None: - session_info = self.list_sessions()[session_name] + session_info = self.list_sessions(refresh_cache=true)[session_name] else: session_info = None @@ -342,7 +353,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): return _terminal or None - def list_sessions(self, raw=False): + def list_sessions(self, raw=False, no_cache=False, refresh_cache=False): """\ List all sessions of current user on the connected server. @@ -356,13 +367,23 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @rtype: L{X2goServerSessionList} instance or str """ - (stdin, stdout, stderr) = self._x2go_exec_command("x2golistsessions") - if raw: return stdout.read(), stderr.read() - _stdout_read = stdout.read() - return self._list_backend(_stdout_read, info_backend=self._info_backend).sessions + elif no_cache or refresh_cache or (self.use_listsessions_cache is False) or (self.x2go_listsessions_cache is None): + + (stdin, stdout, stderr) = self._x2go_exec_command("x2golistsessions") + + _stdout_read = stdout.read() + _listsessions = self._list_backend(_stdout_read, info_backend=self._info_backend).sessions + if refresh_cache and self.x2go_listsessions_cache is not None: + self.x2go_listsessions_cache.update_session_list(session_list=_listsessions) + + return _listsessions + + else: + + return self.x2go_listsessions_cache.list_sessions() def clean_sessions(self): """\ @@ -370,7 +391,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): connected user on the remote X2go server and terminate them. """ - session_infos = self.list_sessions() + session_infos = self.list_sessions(refresh_cache=True) for session_info in session_infos.values(): self.terminate(session_name=session_info) @@ -400,7 +421,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @rtype: C{bool} """ - session_infos = self.list_sessions() + session_infos = self.list_sessions(refresh_cache=True) if session_name in session_infos.keys(): return session_infos[session_name].is_running() return False @@ -414,7 +435,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @rtype: C{bool} """ - session_infos = self.list_sessions() + session_infos = self.list_sessions(refresh_cache=True) if session_name in session_infos.keys(): return session_infos[session_name].is_suspended() return False @@ -431,8 +452,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @rtype: C{bool} """ - session_infos = self.list_sessions() - + session_infos = self.list_sessions(refresh_cache=True) if session_name not in session_infos.keys(): if session_name in self.terminated_terminals: diff --git a/x2go/cache.py b/x2go/cache.py new file mode 100644 index 0000000..147c5b4 --- /dev/null +++ b/x2go/cache.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2010 by Mike Gabriel <m.gabriel@das-netzwerkteam.de> +# +# Python X2go 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 3 of the License, or +# (at your option) any later version. +# +# Python X2go 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 St, Fifth Floor, Boston, MA 02110-1301, USA. + +"""\ +X2goListSessionCache class - caching X2go session information. + +""" +__NAME__ = 'x2gocache-pylib' + +# modules +import gevent +import threading +import copy + +# Python X2go modules +import log +import x2go_exceptions + +class X2goListSessionsCache(threading.Thread): + """\ + STILL UNDOCUMENTED + + """ + + x2go_listsessions_cache = None + + def __init__(self, control_session, refresh_interval=5, logger=None, loglevel=log.loglevel_DEFAULT): + """\ + @param control: the L{X2goControlSession} that uses this L{X2goListSessionsCache} + @type control: C{instance} + @param refresh_interval: refresh interval of the list sessions cache in seconds + @type refresh_interval: C{int} + @param logger: you can pass an L{X2goLogger} object to the L{X2goSessionGuardian} constructor + @type logger: C{instance} + @param loglevel: if no L{X2goLogger} object has been supplied a new one will be + constructed with the given loglevel + @type loglevel: C{int} + + """ + if logger is None: + self.logger = log.X2goLogger(loglevel=loglevel) + else: + self.logger = copy.deepcopy(logger) + self.logger.tag = __NAME__ + + self.control_session = control_session + self.refresh_interval = refresh_interval + threading.Thread.__init__(self, target=self.refresh_cache) + self.daemon = True + + def refresh_cache(self): + """\ + The handler of this L{X2goListSessionsCache} thread. + + """ + seconds = 0 + self._keepalive = True + while self._keepalive: + gevent.sleep(1) + seconds += 1 + if seconds % self.refresh_interval == 0: + self.update_session_list() + + def update_session_list(self, session_list=None): + """\ + Retrieve a recent session list either from the control session's C{list_sessions()} + method or as an argument. + + @param session_list: an L{X2goServerSessionList} backend instance + @type session_list C{instance} + + """ + if session_list is None: + try: + self.x2go_listsessions_cache = self.control_session.list_sessions(no_cache=True) + except x2go_exceptions.X2goSessionException: + pass + else: + self.x2go_listsessions_cache = session_list + + def list_sessions(self): + """\ + Retrieve the current cache content of L{X2goListSessionsCache}. + + """ + return self.x2go_listsessions_cache + + def stop_thread(self): + """\ + Stop this L{X2goListSessionsCache} thread. + + """ + self._keepalive = False + + diff --git a/x2go/cleanup.py b/x2go/cleanup.py index 72aa038..04949df 100644 --- a/x2go/cleanup.py +++ b/x2go/cleanup.py @@ -27,7 +27,7 @@ import guardian import rforward -def x2go_cleanup(e=None, session=None, threads=[]): +def x2go_cleanup(e=None, terminal_session=None, threads=[]): """\ For every Python X2go application you write, please make sure to capture the KeyboardInterrupt and the SystemExit exceptions and @@ -49,16 +49,16 @@ def x2go_cleanup(e=None, session=None, threads=[]): @param e: if L{x2go_cleanup} got called as we caught an exception this can be the C{Exception} that we might process at the end of the clean-up (or if clean-up failed or was not appropriate) @type e: C{exception} - @param session: an L{X2goSession} object - @type session: C{instance} + @param terminal_session: an L{X2goSession} object + @type terminal_session: C{instance} @param threads: a list of threads to clean up @type threads: C{list} """ - if session is None: + if terminal_session is None: active_threads = threading.enumerate() else: - session.logger('cleaning up threads of session: %s' % session.session_info.name) + terminal_session.logger('cleaning up threads of terminal session: %s' % terminal_session.session_info.name) active_threads = threads # stop X2go reverse forwarding tunnels @@ -66,7 +66,7 @@ def x2go_cleanup(e=None, session=None, threads=[]): if type(t) == rforward.X2goRevFwTunnel: t.stop_thread() - # stop X2go paramiko transports used by X2goSession objects + # stop X2go paramiko transports used by X2goTerminalSession objects for t in active_threads: if type(t) == paramiko.Transport: if hasattr(t, '_x2go_session_marker'): diff --git a/x2go/client.py b/x2go/client.py index 2b12c78..488468c 100644 --- a/x2go/client.py +++ b/x2go/client.py @@ -861,7 +861,7 @@ class X2goClient(object): """ if self._X2goClient__is_session_connected(session_uuid): - session_list = self._X2goClient__list_sessions(session_uuid) + session_list = self._X2goClient__list_sessions(session_uuid, refresh_cache=True) return [ key for key in session_list.keys() if session_list[key].status == 'R' ] else: raise X2goClientException('X2go session with UUID %s is not connected' % session_uuid) @@ -890,7 +890,7 @@ class X2goClient(object): """ if self._X2goClient__is_session_connected(session_uuid): - session_list = self._X2goClient__list_sessions(session_uuid) + session_list = self._X2goClient__list_sessions(session_uuid, refresh_cache=True) return [ key for key in session_list.keys() if session_list[key].status == 'S' ] else: raise X2goClientException('X2go session with UUID %s is not connected' % session_uuid) @@ -934,7 +934,7 @@ class X2goClient(object): session.clean_sessions() __clean_sessions = clean_sessions - def list_sessions(self, session_uuid): + def list_sessions(self, session_uuid, no_cache=False, refresh_cache=False): """\ Use the X2go session registered under C{session_uuid} to retrieve a list of running or suspended X2go sessions on the @@ -950,7 +950,7 @@ class X2goClient(object): """ session = self.session_registry(session_uuid) - return session.list_sessions() + return session.list_sessions(no_cache=no_cache, refresh_cache=refresh_cache) __list_sessions = list_sessions ### diff --git a/x2go/guardian.py b/x2go/guardian.py index 4c2fa8d..7c483f6 100644 --- a/x2go/guardian.py +++ b/x2go/guardian.py @@ -56,10 +56,10 @@ class X2goSessionGuardian(threading.Thread): """ - def __init__(self, session, logger=None, loglevel=log.loglevel_DEFAULT): + def __init__(self, terminal_session, logger=None, loglevel=log.loglevel_DEFAULT): """\ - @param session: the L{X2goSession} that is controlled by this L{X2goSessionGuardian} - @type session: C{instance} + @param terminal_session: the L{X2goTerminalSession} that is controlled by this L{X2goSessionGuardian} + @type terminal_session: C{instance} @param logger: you can pass an L{X2goLogger} object to the L{X2goSessionGuardian} constructor @type logger: C{instance} @param loglevel: if no L{X2goLogger} object has been supplied a new one will be @@ -73,8 +73,7 @@ class X2goSessionGuardian(threading.Thread): self.logger = copy.deepcopy(logger) self.logger.tag = __NAME__ - self.session = session - self._keepalive = True + self.terminal_session = terminal_session threading.Thread.__init__(self, target=self.guardian) self.daemon = True @@ -84,11 +83,17 @@ class X2goSessionGuardian(threading.Thread): """ global _sigterm_received + + seconds = 0 + self._keepalive = True while not _sigterm_received and self._keepalive: gevent.sleep(1) - self.logger('X2go session guardian thread waking up, calling session cleanup for session: %s' % self.session.session_info.name, loglevel=log.loglevel_DEBUG) + seconds += 1 + + self.logger('X2go session guardian thread waking up after %s seconds' % seconds, loglevel=log.loglevel_DEBUG) + self.logger('calling terminal session cleanup for terminal session: %s' % self.terminal_session.session_info.name, loglevel=log.loglevel_DEBUG) - x2go_cleanup(session=self.session, threads=self.active_threads) + x2go_cleanup(terminal_session=self.terminal_session, threads=self.active_threads) def stop_thread(self): """\ diff --git a/x2go/session.py b/x2go/session.py index 354069a..045245c 100644 --- a/x2go/session.py +++ b/x2go/session.py @@ -120,6 +120,7 @@ class X2goSession(object): self.control_session = control_session self.terminal_session = None + self.guardian_thread = None self.logger('starting X2goSession', loglevel=log.loglevel_DEBUG) if known_hosts: self.control_session.load_host_keys(known_hosts) @@ -283,8 +284,8 @@ class X2goSession(object): def clean_sessions(self): self.control_session.clean_sessions() - def list_sessions(self): - return self.control_session.list_sessions() + def list_sessions(self, no_cache=False, refresh_cache=False): + return self.control_session.list_sessions(no_cache=no_cache, refresh_cache=refresh_cache) def resume(self, session_name=None): """\ @@ -297,6 +298,7 @@ class X2goSession(object): """ _control = self.control_session _terminal = _control.resume(session_name=session_name, logger=self.logger, **self.terminal_params) + self.guardian_thread = _terminal.guardian_thread self.terminal_session = _terminal if _terminal is not None: hooks/post-receive -- python-x2go.git (Python X2Go Client API) This is an automated email from the git hooks/post-receive script. It was generated because a ref change was pushed to the repository containing the project "python-x2go.git" (Python X2Go Client API).