The branch, twofactorauth has been updated via 35ec0740ece713767617c85c90c32c044dd23338 (commit) from e3fe57d1ad10f04c05565d3dbb712bc1d3f0caf5 (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 | 80 ++++++---------- x2go/backends/profiles/sessions_file.py | 21 ---- x2go/backends/terminal/stdout.py | 20 ++-- x2go/cache.py | 95 ++++++++++--------- x2go/cleanup.py | 22 ++--- x2go/client.py | 158 +++++++++++++++++++++++-------- x2go/defaults.py | 3 +- x2go/guardian.py | 23 ++--- x2go/registry.py | 66 ++++++++++++- x2go/session.py | 33 ++++--- x2go/utils.py | 1 - x2go/x2go_exceptions.py | 5 +- 12 files changed, 308 insertions(+), 219 deletions(-) The diff of changes is: diff --git a/x2go/backends/control/stdout.py b/x2go/backends/control/stdout.py index 42ae7c9..088b48d 100644 --- a/x2go/backends/control/stdout.py +++ b/x2go/backends/control/stdout.py @@ -37,7 +37,6 @@ 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 @@ -56,13 +55,13 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @type loglevel: int """ + associated_terminals = None + def __init__(self, terminal_backend=_X2goTerminalSession, info_backend=_X2goServerSessionInfo, list_backend=_X2goServerSessionList, proxy_backend=_X2goProxy, - use_listsessions_cache=True, - controlled_session=None, logger=None, loglevel=log.loglevel_DEFAULT, *args, **kwargs): """\ @@ -73,28 +72,21 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): """ self.associated_terminals = {} self.terminated_terminals = [] - self.controlled_sessions = {} self._session_auth_rsakey = None self._remote_home = None self._remote_group = {} - self.x2go_listsessions_cache = None - if logger is None: self.logger = log.X2goLogger(loglevel=loglevel) else: self.logger = copy.deepcopy(logger) self.logger.tag = __NAME__ - if controlled_session is not None: - self.controlled_sessions[controlled_session._X2goSession__get_uuid()] = controlled_session - self._terminal_backend = terminal_backend 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): @@ -131,10 +123,10 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): self.logger('executing command on X2go server: %s' % cmd, loglevel) return self.exec_command(cmd, **kwargs) except AttributeError: - raise x2go_exceptions.X2goSessionException('a Paramiko/SSH control session has died') + raise x2go_exceptions.X2goControlSessionException('a Paramiko/SSH control session has died') else: - raise x2go_exceptions.X2goSessionException('the Paramiko/SSH client is not connected') + raise x2go_exceptions.X2goControlSessionException('the Paramiko/SSH client is not connected') @property def _x2go_remote_home(self): @@ -276,10 +268,6 @@ 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): @@ -287,33 +275,34 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): STILL UNDOCUMENTED """ - if self.x2go_listsessions_cache is not None: - self.x2go_listsessions_cache.stop_thread() - del self.x2go_listsessions_cache - self.x2go_listsessions_cache = None - - t_names = self.associated_terminals.keys() - for t_obj in self.associated_terminals.values(): - try: - t_obj.suspend() - except x2go_exceptions.X2goSessionException: - pass - del t_obj - for t_name in t_names: - del self.associated_terminals[t_name] + if self.associated_terminals is not None: + t_names = self.associated_terminals.keys() + for t_obj in self.associated_terminals.values(): + try: + t_obj.suspend() + except x2go_exceptions.X2goTerminalSessionException: + pass + del t_obj + for t_name in t_names: + del self.associated_terminals[t_name] self._remote_home = None self._remote_group = {} self._session_auth_rsakey = None - if self.get_transport() is not None: - self.get_transport().close() + + try: + if self.get_transport() is not None: + self.get_transport().close() + except AttributeError: + # if the Paramiko _transport object has not yet been initialized, ignore it + pass def is_alive(self): try: self._x2go_exec_command('echo', loglevel=log.loglevel_DEBUG) return True - except x2go_exceptions.X2goSessionException: + except x2go_exceptions.X2goControlSessionException: return False def start(self, **kwargs): @@ -342,7 +331,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): raise x2go_exceptions.X2goUserException('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(refresh_cache=True)[session_name] + session_info = self.list_sessions()[session_name] else: session_info = None @@ -379,7 +368,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): return _terminal or None - def list_sessions(self, raw=False, no_cache=False, refresh_cache=False): + def list_sessions(self, raw=False): """\ List all sessions of current user on the connected server. @@ -396,25 +385,18 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): if raw: return stdout.read(), stderr.read() - elif no_cache or refresh_cache or (self.use_listsessions_cache is False) or (self.x2go_listsessions_cache is None): + else: try: (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 - except X2goSessionException, e: - self.logger('encountered X2goSessionsException: %s' % str(e), loglevel=log.loglevel_ERROR) - self._X2goSessions__disconnect() - - else: - - return self.x2go_listsessions_cache.list_sessions() + except X2goSessionControlException, e: + self.logger('encountered X2goSessionsControlException: %s' % str(e), loglevel=log.loglevel_ERROR) def clean_sessions(self): """\ @@ -422,7 +404,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): connected user on the remote X2go server and terminate them. """ - session_infos = self.list_sessions(refresh_cache=True) + session_infos = self.list_sessions() for session_info in session_infos.values(): self.terminate(session_name=session_info) @@ -452,7 +434,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @rtype: C{bool} """ - session_infos = self.list_sessions(refresh_cache=True) + session_infos = self.list_sessions() if session_name in session_infos.keys(): return session_infos[session_name].is_running() return False @@ -466,7 +448,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @rtype: C{bool} """ - session_infos = self.list_sessions(refresh_cache=True) + session_infos = self.list_sessions() if session_name in session_infos.keys(): return session_infos[session_name].is_suspended() return False @@ -483,7 +465,7 @@ class X2goControlSessionSTDOUT(paramiko.SSHClient): @rtype: C{bool} """ - session_infos = self.list_sessions(refresh_cache=True) + session_infos = self.list_sessions() if session_name not in session_infos.keys(): if session_name in self.terminated_terminals: diff --git a/x2go/backends/profiles/sessions_file.py b/x2go/backends/profiles/sessions_file.py index 4d1644b..fe2c457 100644 --- a/x2go/backends/profiles/sessions_file.py +++ b/x2go/backends/profiles/sessions_file.py @@ -180,27 +180,6 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile): self.write_user_config = True self.writeIniFile() - def has_default_profiles(self): - """\ - STILL UNDOCUMENTED - - """ - return self.get_default_profiles() and True or False - - def get_default_profiles(self): - """\ - Find profiles marked as default profiles. If appropriate, default - profiles may be started immediately on client startup. - - """ - _default_profiles = [] - for profile_name in self.profile_names: - _profile_id = self.to_profile_id(profile_name) - _profile_config = self.get_profile_config(profile_name) - if _profile_config['default']: - _default_profiles.append(_profile_id) - return _default_profiles - def check_profile_id_or_name(self, profile_id_or_name): """\ STILL UNDOCUMENTED diff --git a/x2go/backends/terminal/stdout.py b/x2go/backends/terminal/stdout.py index 5e0d259..b992f0c 100644 --- a/x2go/backends/terminal/stdout.py +++ b/x2go/backends/terminal/stdout.py @@ -44,7 +44,6 @@ import x2go.log as log import x2go.defaults as defaults import x2go.utils as utils import x2go.x2go_exceptions as x2go_exceptions -import x2go.guardian as guardian from x2go.cleanup import x2go_cleanup @@ -212,7 +211,7 @@ class X2goTerminalSessionSTDOUT(object): self.proxy = None self.proxy_subprocess = None - self.guardian_thread = None + self.active_threads = [] self.reverse_tunnels = {} self.print_queue = None @@ -253,15 +252,10 @@ class X2goTerminalSessionSTDOUT(object): if self.session_info.name: self.session_info.local_container = os.path.join(self.params.rootdir, 'S-%s' % self.session_info.name) else: - raise X2goSessionException('no valid session info availble') + raise X2goTerminalSessionException('no valid session info availble') else: self.session_info = info_backend() - # each terminal session has its own guardian - self.guardian_thread = guardian.X2goSessionGuardian(self, logger=self.logger) - self.guardian_thread.start() - - def __del__(self): self._x2go_tidy_up() @@ -361,7 +355,7 @@ class X2goTerminalSessionSTDOUT(object): if _tunnel is not None: self.reverse_tunnels[self.session_info.name]['snd'] = (self.session_info.snd_port, _tunnel) _tunnel.start() - self.guardian_thread.active_threads.append(_tunnel) + self.active_threads.append(_tunnel) else: # tunnel has already been started and might simply need a resume call @@ -385,7 +379,7 @@ class X2goTerminalSessionSTDOUT(object): if _tunnel is not None: self.reverse_tunnels[self.session_info.name]['sshfs'] = (self.session_info.sshfs_port, _tunnel) _tunnel.start() - self.guardian_thread.active_threads.append(_tunnel) + self.active_threads.append(_tunnel) else: # tunnel has already been started and might simply need a resume call @@ -430,7 +424,7 @@ class X2goTerminalSessionSTDOUT(object): logger=self.logger, ) self.print_queue.start() - self.guardian_thread.active_threads.append(self.print_queue) + self.active_threads.append(self.print_queue) def set_print_action(self, print_action, **kwargs): """\ @@ -464,7 +458,7 @@ class X2goTerminalSessionSTDOUT(object): """ if self.session_info.username not in self.control_session._x2go_remote_group('fuse'): - raise x2go_exceptions.X2goSessionException('remote user %s is not member of X2go server group fuse' % self.session_info.username) + raise x2go_exceptions.X2goUserException('remote user %s is not member of X2go server group fuse' % self.session_info.username) if folder_name is None: self.logger('no folder name given...', log.loglevel_WARN) @@ -647,7 +641,7 @@ class X2goTerminalSessionSTDOUT(object): # set up SSH tunnel for X11 graphical elements self.proxy = self.proxy_class(session_info=self.session_info, ssh_transport=self.control_session.get_transport(), logger=self.logger) self.proxy_subprocess = self.proxy.start_proxy() - self.guardian_thread.active_threads.append(self.proxy) + self.active_threads.append(self.proxy) return self.ok() diff --git a/x2go/cache.py b/x2go/cache.py index b1e8e89..647d000 100644 --- a/x2go/cache.py +++ b/x2go/cache.py @@ -24,35 +24,34 @@ 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): +class X2goListSessionsCache(object): """\ STILL UNDOCUMENTED """ + x2go_listsessions_cache = {} - def __init__(self, control_session, refresh_interval=5, logger=None, loglevel=log.loglevel_DEFAULT): + def __init__(self, client, refresh_interval=5, logger=None, loglevel=log.loglevel_DEFAULT): """\ - @param control: the L{X2goControlSession} that uses this L{X2goListSessionsCache} - @type control: C{instance} + @param client: the L{X2goClient} instance that uses this L{X2goListSessionsCache} + @type client: 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 + @param logger: you can pass an L{X2goLogger} object to the L{X2goListSessionsCache} 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} """ - self.x2go_listsessions_cache = None - self.last_listsessions_cache = None + self.x2go_listsessions_cache = {} + self.last_listsessions_cache = {} if logger is None: self.logger = log.X2goLogger(loglevel=loglevel) @@ -60,57 +59,65 @@ class X2goListSessionsCache(threading.Thread): self.logger = copy.deepcopy(logger) self.logger.tag = __NAME__ - self.control_session = control_session - self.last_sessionlist = None + self.client = client self.refresh_interval = refresh_interval - threading.Thread.__init__(self, target=self.refresh_cache) - self.daemon = True - def refresh_cache(self): + def check_cache(self, seconds=None): + + if seconds and (seconds % self.refresh_interval != 0): + return + + for profile_name in self.x2go_listsessions_cache.keys(): + if profile_name not in [ self.client.to_profile_name(p_id) for p_id in self.client.connected_profiles ]: + del self.x2go_listsessions_cache[profile_name] + + def update_all(self, seconds=None): """\ - The handler of this L{X2goListSessionsCache} thread. + Update L{X2goListSessionsCache} for all connected profiles. + + @param profile_name: name of profile to update + @type profile_name: C{str} """ - seconds = 0 - self._keepalive = True - while self._keepalive: - gevent.sleep(1) - seconds += 1 - if seconds % self.refresh_interval == 0: - self.list_listsessions_cache = copy.deepcopy(self.x2go_listsessions_cache) - self.update_session_list() - - def update_session_list(self, session_list=None): + if seconds and (seconds % self.refresh_interval != 0): + return + + for profile_name in self.client.connected_profiles: + self.update(profile_name, seconds=seconds) + + self.check_cache(seconds=seconds) + + def update(self, profile_name, seconds=None): """\ - Retrieve a recent session list either from the control session's C{list_sessions()} - method or as an argument. + Update the L{X2goListSessionsCache} for profile C{profile_name}. - @param session_list: an L{X2goServerSessionList} backend instance - @type session_list C{instance} + @param profile_name: name of profile to update + @type profile_name: C{str} """ - if session_list is None: - try: - self.x2go_listsessions_cache = self.control_session.list_sessions(no_cache=True) - except x2go_exceptions.X2goSessionException: - for s in self.control_session.controlled_sessions.values(): - s.__hook_session_has_died() - s._X2goSession__disconnect() + if seconds and (seconds % self.refresh_interval != 0): + return + + self.last_listsessions_cache = copy.deepcopy(self.x2go_listsessions_cache) + control_session = self.client.client_control_session_of_name(profile_name) + if control_session is not None: + self.x2go_listsessions_cache[profile_name] = control_session.list_sessions() else: - self.x2go_listsessions_cache = session_list + del self.x2go_listsessions_cache[profile_name] - def list_sessions(self): + def list_sessions(self, session_uuid): """\ Retrieve the current cache content of L{X2goListSessionsCache}. """ - return self.x2go_listsessions_cache + profile_name = self.client.get_session_profile_name(session_uuid) + return self.x2go_listsessions_cache[profile_name] - def stop_thread(self): + def is_cached(self, session_uuid=None, profile_name=None): """\ - Stop this L{X2goListSessionsCache} thread. + Retrieve the current cache content of L{X2goListSessionsCache}. """ - self._keepalive = False - - + if profile_name is None and session_uuid: + profile_name = self.client.get_session_profile_name(session_uuid) + return self.x2go_listsessions_cache.has_key(profile_name) diff --git a/x2go/cleanup.py b/x2go/cleanup.py index 04949df..a6f3219 100644 --- a/x2go/cleanup.py +++ b/x2go/cleanup.py @@ -27,7 +27,7 @@ import guardian import rforward -def x2go_cleanup(e=None, terminal_session=None, threads=[]): +def x2go_cleanup(e=None, threads=None): """\ For every Python X2go application you write, please make sure to capture the KeyboardInterrupt and the SystemExit exceptions and @@ -49,33 +49,27 @@ def x2go_cleanup(e=None, terminal_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 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 terminal_session is None: - active_threads = threading.enumerate() + if threads is None: + threads = threading.enumerate() else: - terminal_session.logger('cleaning up threads of terminal session: %s' % terminal_session.session_info.name) - active_threads = threads + threads = threads # stop X2go reverse forwarding tunnels - for t in active_threads: + for t in threads: if type(t) == rforward.X2goRevFwTunnel: t.stop_thread() + del t # stop X2go paramiko transports used by X2goTerminalSession objects - for t in active_threads: + for t in threads: if type(t) == paramiko.Transport: if hasattr(t, '_x2go_session_marker'): t.stop_thread() - - # stop the X2goGuardian threads (one per X2goSession - for t in active_threads: - if type(t) == guardian.X2goSessionGuardian: - t.stop_thread() + del t if e is not None: raise e diff --git a/x2go/client.py b/x2go/client.py index c1ea7b2..be1ae0e 100644 --- a/x2go/client.py +++ b/x2go/client.py @@ -126,6 +126,8 @@ from settings import X2goClientSettings from printing import X2goClientPrinting from registry import X2goSessionRegistry from guardian import X2goSessionGuardian +from cache import X2goListSessionsCache +import x2go_exceptions import log import utils @@ -146,7 +148,7 @@ class X2goClient(object): session object etc.) and connected to it (authentication). For these two steps use these methods: L{X2goClient.register_session()} and L{X2goClient.connect_session()}. """ - def __init__(self, logger=None, loglevel=log.loglevel_DEFAULT): + def __init__(self, use_cache=True, logger=None, loglevel=log.loglevel_DEFAULT): """\ @param logger: you can pass an L{X2goLogger} object to the L{X2goClient} constructor @@ -156,6 +158,8 @@ class X2goClient(object): @type loglevel: C{int} """ + self.listsessions_cache = None + if logger is None: self.logger = log.X2goLogger(loglevel=loglevel) else: @@ -166,9 +170,22 @@ class X2goClient(object): self.session_profiles = X2goSessionProfiles(logger=self.logger) self.session_registry = X2goSessionRegistry(logger=self.logger) + self.session_guardian = X2goSessionGuardian(self, enable_cache=use_cache, logger=self.logger) + if use_cache: + self.listsessions_cache = X2goListSessionsCache(self, logger=self.logger) self.client_settings = X2goClientSettings(logger=self.logger) self.client_printing = X2goClientPrinting(logger=self.logger) + self.use_cache = use_cache + + # user hooks for detecting/notifying what happened with this session + def HOOK_on_control_session_death(self, profile_name): + self.logger('the control session of profile %s has died unexpectedly' % profile_name) + def HOOK_on_session_got_suspended_from_within(self, session_uuid): + self.logger('session %s has been suspended from within the application' % self.session_registry(session_uuid).get_session_name()) + def HOOK_on_session_got_terminated_from_within(self, session_uuid): + self.logger('session %s has been terminated from within the application' % self.session_registry(session_uuid).get_session_name()) + def __get_client_username(self): """\ Query the local user's username (i.e. the user running the X2go client). @@ -180,19 +197,6 @@ class X2goClient(object): return _CURRENT_LOCAL_USER get_client_username = __get_client_username - def __is_valid_username(self): - """\ - Check if user is allowed to start an X2go session on a remote server. - - @return User allowed to start a session? - @rtype: C{str} - - """ - return _CURRENT_LOCAL_USER - get_client_username = __get_client_username - - - def register_all_session_profiles(self, return_objects=False): """\ Register all session profiles found in the C{sessions} configuration file @@ -328,7 +332,18 @@ class X2goClient(object): __register_session = register_session ### - ### WRAPPER METHODS FOR X2goRegisteredSession objects + ### WRAPPER METHODS FOR X2goSessionRegistry objects + ### + + def get_session_summary(self, session_uuid): + """\ + STILL UNDOCUMENTED + + """ + return self.session_registry.session_summary(session_uuid) + + ### + ### WRAPPER METHODS FOR X2goSession objects ### def get_session_username(self, session_uuid): @@ -388,27 +403,6 @@ class X2goClient(object): with_session = __get_session """Alias for L{get_session()}.""" - def get_registered_session(self, session_uuid): - """\ - Retrieve the complete L{X2goRegisteredSession} object that has been - registered under the given session registry hash. - - L{X2goRegisteredSession} is one of Python X2go's public API classes - and may safely be used in user applications to operate on individual - sessions. - - @param session_uuid: the X2go session's UUID registry hash - @type session_uuid: C{str} - - @return: the L{X2goRegisteredSession} instance - @rtype: obj - - """ - return self.session_registry(session_uuid) - __get_registered_session = get_registered_session - with_registered_session = __get_registered_session - """Alias for L{get_registered_session()}.""" - def get_session_name(self, session_uuid): """\ Retrieve the server-side X2go session name for the session that has @@ -853,6 +847,23 @@ class X2goClient(object): return self.session_registry.registered_sessions __client_registered_sessions = client_registered_sessions + @property + def client_control_sessions(self): + """\ + STILL UNDOCUMENTED + + """ + return self.session_registry.control_sessions + __client_control_sessions = client_control_sessions + + def client_control_session_of_name(self, profile_name): + """\ + STILL UNDOCUMENTED + + """ + return self.session_registry.control_session_of_name(profile_name) + __client_control_session_of_name = client_control_session_of_name + def client_registered_sessions_of_name(self, profile_name): """\ STILL UNDOCUMENTED @@ -890,7 +901,7 @@ class X2goClient(object): """ if self._X2goClient__is_session_connected(session_uuid): - session_list = self._X2goClient__list_sessions(session_uuid, refresh_cache=True) + session_list = self._X2goClient__list_sessions(session_uuid) 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) @@ -919,7 +930,7 @@ class X2goClient(object): """ if self._X2goClient__is_session_connected(session_uuid): - session_list = self._X2goClient__list_sessions(session_uuid, refresh_cache=True) + session_list = self._X2goClient__list_sessions(session_uuid) 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) @@ -979,7 +990,18 @@ class X2goClient(object): """ session = self.session_registry(session_uuid) - return session.list_sessions(no_cache=no_cache, refresh_cache=refresh_cache) + + if not self.use_cache or no_cache: + return session.list_sessions() + elif refresh_cache: + self.update_cache_by_session_uuid(session_uuid) + return self.listsessions_cache.list_sessions(session_uuid) + else: + # if there is no cache for this session_uuid available, make sure the cache gets updated + # before reading from it... + if self.use_cache and (not self.listsessions_cache.is_cached(session_uuid=session_uuid)): + self.update_cache_by_session_uuid(session_uuid) + return self.listsessions_cache.list_sessions(session_uuid) __list_sessions = list_sessions ### @@ -1066,7 +1088,7 @@ class X2goClient(object): @rtype: C{str} """ - return self.session_profiles.get_profile_id(profile_name) + return self.session_profiles.to_profile_id(profile_name) __to_profile_id = to_profile_id def to_profile_name(self, profile_id): @@ -1081,7 +1103,61 @@ class X2goClient(object): @rtype: C{str} """ - return self.session_profiles.get_profile_name(profile_id) + return self.session_profiles.to_profile_name(profile_id) __to_profile_name = to_profile_name + @property + def connected_profiles(self): + """\ + STILL UNDOCUMENTED + + """ + return self.session_registry.connected_profiles + __connected_profiles = connected_profiles + + def disconnect_profile(self, profile_name): + """\ + Disconnect all L{X2goSession} instances that relate to C{profile_name} by closing down their + Paramiko/SSH Transport thread. + + @param profile_name: the X2go session profile name + @type session_uuid: C{str} + """ + for s in self.session_registry.registered_sessions_of_name(profile_name): + s.disconnect() + __disconnect_profile = disconnect_profile + + def update_cache_by_profile(self, profile_name, seconds=None): + """\ + STILL UNDOCUMENTED + + """ + if self.listsessions_cache is not None: + try: + self.listsessions_cache.update(profile_name, seconds=seconds) + except x2go_exceptions.X2goControlSessionException: + self.HOOK_on_control_session_death(profile_name) + self.disconnect_profile(profile_name, seconds=seconds) + __update_cache_by_profile = update_cache_by_profile + + def update_cache_by_session_uuid(self, session_uuid, seconds=None): + """\ + STILL UNDOCUMENTED + + """ + profile_name = self.get_session_profile_name(session_uuid) + self.__update_cache_by_profile(profile_name, seconds=seconds) + __update_cache_by_session_uuid = update_cache_by_session_uuid + + def update_cache_all_profiles(self, seconds=None): + """\ + STILL UNDOCUMENTED + + """ + if self.listsessions_cache is not None: + for profile_id in self.connected_profiles: + profile_name = self.to_profile_name(profile_id) + self.__update_cache_by_profile(profile_name, seconds=seconds) + self.listsessions_cache.check_cache(seconds=seconds) + __update_cache_all_profiles = update_cache_all_profiles diff --git a/x2go/defaults.py b/x2go/defaults.py index 1a04b5f..5f61493 100644 --- a/x2go/defaults.py +++ b/x2go/defaults.py @@ -46,7 +46,7 @@ if X2GOCLIENT_OS == "Windows": SUPPORTED_SOUND = False SUPPORTED_PRINTING = True SUPPORTED_FOLDERSHARING = True - DISPLAY='localhost:0.0' + DISPLAY='127.0.0.1:0.0' os.environ['DISPLAY'] = DISPLAY elif X2GOCLIENT_OS == "Linux": @@ -182,7 +182,6 @@ X2GO_SESSIONPROFILE_DEFAULTS = { 'host': None, 'user': None, 'key': None, 'sshport': 22, 'add_to_known_hosts': True, 'rootless': True, 'applications': 'WWWBROWSER, MAILCLIENT, OFFICE, TERMINAL', 'command':'TERMINAL', 'session_type': 'application', 'rdpoptions':None, 'rdpserver':None, - 'default':False, 'print': True, 'xdmcpserver': 'localhost', } diff --git a/x2go/guardian.py b/x2go/guardian.py index 176c274..0195c73 100644 --- a/x2go/guardian.py +++ b/x2go/guardian.py @@ -49,14 +49,7 @@ class X2goSessionGuardian(threading.Thread): """ - active_threads = [] - """\ - List of active threads that this L{X2goSessionGuardian} instance will monitor. Whenever - an L{X2goSession} starts a new sub-thread, it will be appended to this list. - - """ - - def __init__(self, terminal_session, logger=None, loglevel=log.loglevel_DEFAULT): + def __init__(self, client, enable_cache=True, logger=None, loglevel=log.loglevel_DEFAULT): """\ @param terminal_session: the L{X2goTerminalSession} that is controlled by this L{X2goSessionGuardian} @type terminal_session: C{instance} @@ -67,17 +60,17 @@ class X2goSessionGuardian(threading.Thread): @type loglevel: C{int} """ - self.active_threads = [] - if logger is None: self.logger = log.X2goLogger(loglevel=loglevel) else: self.logger = copy.deepcopy(logger) self.logger.tag = __NAME__ - self.terminal_session = terminal_session + self.client = client + self.enable_cache = enable_cache threading.Thread.__init__(self, target=self.guardian) self.daemon = True + self.start() def guardian(self): """\ @@ -91,11 +84,15 @@ class X2goSessionGuardian(threading.Thread): while not _sigterm_received and self._keepalive: gevent.sleep(1) seconds += 1 + if self.enable_cache: + self.client.update_cache_all_profiles(seconds) 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(terminal_session=self.terminal_session, threads=self.active_threads) + for session_uuid in self.client.session_registry.keys(): + session_summary = self.client.get_session_summary(session_uuid) + self.logger('calling session cleanup on profile %s for terminal session: %s' % (session_summary['profile_name'], session_summary['session_name']), loglevel=log.loglevel_DEBUG) + x2go_cleanup(threads=session_summary['active_threads']) def stop_thread(self): """\ diff --git a/x2go/registry.py b/x2go/registry.py index ec6587c..95f25e3 100644 --- a/x2go/registry.py +++ b/x2go/registry.py @@ -46,7 +46,7 @@ class X2goSessionRegistry(object): STILL UNDOCUMENTED """ - def __init__(self, logger=None, loglevel=log.loglevel_DEFAULT): + def __init__(self, use_cache=True, logger=None, loglevel=log.loglevel_DEFAULT): """\ STILL UNDOCUMENTED @@ -88,6 +88,34 @@ class X2goSessionRegistry(object): """ return self(session_uuid).profile.profile_name + def session_summary(self, session_uuid): + """\ + STILL UNDOCUMENTED + + """ + _session_summary = {} + _session_summary['uuid'] = session_uuid + _session_summary['profile_id'] = self.get_profile_id(session_uuid) + _session_summary['profile_name'] = self.get_profile_name(session_uuid) + _session_summary['session_name'] = self(session_uuid).get_session_name() + _session_summary['control_session'] = self(session_uuid).get_control_session() + _session_summary['control_params'] = self(session_uuid).control_params + _session_summary['terminal_session'] = self(session_uuid).get_terminal_session() + _session_summary['terminal_params'] = self(session_uuid).terminal_params + _session_summary['active_threads'] = self(session_uuid).get_terminal_session().active_threads + _session_summary['connected'] = self(session_uuid).connected + _session_summary['running'] = self(session_uuid).running + _session_summary['suspended'] = self(session_uuid).suspended + _session_summary['terminated'] = self(session_uuid).terminated + _session_summary['backends'] = { + 'control': self(session_uuid)._control_backend, + 'terminal': self(session_uuid)._terminal_backend, + 'info': self(session_uuid)._info_backend, + 'list': self(session_uuid)._list_backend, + 'proxy': self(session_uuid)._proxy_backend, + } + return _session_summary + def register(self, server, profile_id, profile_name, control_backend=X2goControlSession, terminal_backend=X2goTerminalSession, @@ -101,7 +129,7 @@ class X2goSessionRegistry(object): control_session = self.control_sessions[profile_id] s = session.X2goSession(server=server, control_session=control_session, - profile_id=profile_id, profile_name=profile_name, + profile_id=profile_id, profile_name=profile_name, control_backend=control_backend, terminal_backend=terminal_backend, info_backend=info_backend, @@ -131,6 +159,14 @@ class X2goSessionRegistry(object): return self._sessionsWithState('connected') @property + def connected_sessions(self): + """\ + STILL UNDOCUMENTED + + """ + return self._sessionsWithState('connected') + + @property def running_sessions(self): """\ STILL UNDOCUMENTED @@ -206,3 +242,29 @@ class X2goSessionRegistry(object): """ return self.running_sessions and [ s for s in self.running_sessions if s.profile_name == profile_name ] + + def control_session_of_name(self, profile_name): + """\ + STILL UNDOCUMENTED + + """ + if self.registered_sessions_of_name(profile_name): + session = self.registered_sessions_of_name(profile_name)[0] + return session.control_session + return None + + @property + def connected_control_sessions(self): + """\ + STILL UNDOCUMENTED + + """ + return [ c for c in self.control_sessions.values() if c.is_connected() ] + + @property + def connected_profiles(self): + """\ + STILL UNDOCUMENTED + + """ + return [ p for p in self.control_sessions.keys() if self.control_sessions[p].is_connected() ] diff --git a/x2go/session.py b/x2go/session.py index 2c6129f..42018fc 100644 --- a/x2go/session.py +++ b/x2go/session.py @@ -54,15 +54,7 @@ _X2GO_SESSION_PARAMS = ('geometry', 'depth', 'link', 'pack', class X2goSession(object): - # user hooks for detecting/notifying what happened with this session - def __hook_session_has_died(self): - self.logger('the control session of session %s has unexpectedly died' % self.get_session_name()) - def __hook_session_suspended(self): - self.logger('session %s has been suspended from within the application' % self.get_session_name()) - def __hook_session_terminated(self): - self.logger('session %s has been terminated from within the application' % self.get_session_name()) - - def __init__(self, server, control_session=None, + def __init__(self, server=None, control_session=None, profile_id=None, profile_name=None, printing=None, share_local_folders=[], control_backend=X2goControlSession, @@ -123,29 +115,36 @@ class X2goSession(object): info_backend=info_backend, list_backend=list_backend, proxy_backend=proxy_backend, - controlled_session=self, logger=logger) else: self.control_session = control_session - self.control_session.controlled_sessions[self.get_uuid()] = self 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) def __str__(self): return self.__get_uuid() + def __repr__(self): result = 'X2goSession(' for p in dir(self): if '__' in p or not p in self.__dict__ or type(p) is types.InstanceType: continue result += p + '=' + str(self.__dict__[p]) + ', ' return result + ')' + def __call__(self): return self.__get_uuid() + # user hooks for detecting/notifying what happened with this session + def HOOK_session_has_died(self): + self.logger('the control session of session %s has unexpectedly died' % self.get_session_name()) + def HOOK_session_suspended(self): + self.logger('session %s has been suspended from within the application' % self.get_session_name()) + def HOOK_session_terminated(self): + self.logger('session %s has been terminated from within the application' % self.get_session_name()) + def get_uuid(self): """\ STILL UNDOCUMENTED @@ -320,10 +319,10 @@ class X2goSession(object): self._X2goSession__disconnect() __clean_sessions = clean_sessions - def list_sessions(self, no_cache=False, refresh_cache=False): - if (no_cache or refresh_cache) and not self.is_alive(): + def list_sessions(self): + if not self.is_alive(): self._X2goSession__disconnect() - return self.control_session.list_sessions(no_cache=no_cache, refresh_cache=refresh_cache) + return self.control_session.list_sessions() __list_sessions = list_sessions def resume(self, session_name=None): @@ -337,8 +336,8 @@ class X2goSession(object): """ if self.is_alive(): _control = self.control_session - _terminal = _control.resume(session_name=session_name, logger=self.logger, **self.terminal_params) - self.guardian_thread = _terminal.guardian_thread + _terminal = _control.resume(session_name=session_name, + logger=self.logger, **self.terminal_params) self.terminal_session = _terminal if _terminal is not None: diff --git a/x2go/utils.py b/x2go/utils.py index bee7149..58c4c7a 100644 --- a/x2go/utils.py +++ b/x2go/utils.py @@ -203,7 +203,6 @@ def _convert_SessionProfileOptions_2_SessionParams(_options): 'rdpoptions', 'rdpserver', 'xdmcpserver', - 'default', ] for i in _ignored_options: del _params[i] diff --git a/x2go/x2go_exceptions.py b/x2go/x2go_exceptions.py index 950a0c7..3b4e396 100644 --- a/x2go/x2go_exceptions.py +++ b/x2go/x2go_exceptions.py @@ -39,8 +39,9 @@ SSHException = paramiko.SSHException class _X2goException(exceptions.BaseException): pass class X2goClientException(_X2goException): pass -class X2goSessionException(_X2goException): pass -class X2goSessionException(_X2goException): pass +class X2goControlSessionException(_X2goException): pass +class X2goTerminalSessionException(_X2goException): pass +class X2goSessionCacheException(_X2goException): pass class X2goUserException(_X2goException): pass class X2goProfileException(_X2goException): pass class X2goSettingsException(_X2goException): pass 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).