[X2go-Commits] python-x2go.git - master (branch) updated: 0.2.1.1-33-g01ea13f

X2Go dev team git-admin at x2go.org
Sun Jan 20 12:14:36 CET 2013


The branch, master has been updated
       via  01ea13f9b472bf7139c0640fbddb1cf4309c0861 (commit)
      from  3133baf8a3c0e8dc6d80bad8d5aef6b0d0f34862 (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 -----------------------------------------------------------------
commit 01ea13f9b472bf7139c0640fbddb1cf4309c0861
Author: Mike Gabriel <mike.gabriel at das-netzwerkteam.de>
Date:   Sun Jan 20 12:14:34 2013 +0100

    Improve desktop sharing code. Add code to obtain version information of server-side X2Go components.

-----------------------------------------------------------------------

Summary of changes:
 debian/changelog                  |    2 +
 x2go/backends/control/_stdout.py  |   54 ++++++++++++++++++---
 x2go/backends/terminal/_stdout.py |   57 ++++++++++++++++++++++
 x2go/client.py                    |   84 ++++++++++++++++++++++++++++++--
 x2go/session.py                   |   95 ++++++++++++++++++++++++++++++-------
 5 files changed, 263 insertions(+), 29 deletions(-)

The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index 87f6a60..b9c9ed2 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -25,6 +25,8 @@ python-x2go (0.4.0.0-0~x2go1) UNRELEASED; urgency=low
     - Fix auto-starting and auto-resuming of sessions.
     - Avoid false-positive notifications of dead control session directly after
       a disconnect request from the user.
+    - Improve desktop sharing code. Add code to obtain version information of
+      server-side X2Go components.
 
  -- Mike Gabriel <mike.gabriel at das-netzwerkteam.de>  Thu, 20 Dec 2012 08:58:44 +0100
 
diff --git a/x2go/backends/control/_stdout.py b/x2go/backends/control/_stdout.py
index b8c4c10..52eee5f 100644
--- a/x2go/backends/control/_stdout.py
+++ b/x2go/backends/control/_stdout.py
@@ -194,6 +194,7 @@ class X2GoControlSessionSTDOUT(paramiko.SSHClient):
         self._remote_username = None
         self._remote_peername = None
 
+        self._server_versions = None
         self._server_features = None
 
         if logger is None:
@@ -439,18 +440,51 @@ class X2GoControlSessionSTDOUT(paramiko.SSHClient):
         return _retval
 
     @property
+    def _x2go_server_versions(self):
+        """\
+        Render a dictionary of server-side X2Go components and their versions. Results get cached
+        once there has been one successful query.
+
+        """
+        if self._server_versions is None:
+            self._server_versions = {}
+            (stdin, stdout, stderr) = self._x2go_exec_command('which x2goversion >/dev/null && x2goversion')
+            _lines = stdout.read().split('\n')
+            for _line in _lines:
+                if ':' not in _line: continue
+                comp = _line.split(':')[0].strip()
+                version = _line.split(':')[1].strip()
+                self._server_versions.update({comp: version})
+            self.logger('server-side X2Go components and their versions are: %s' % self._server_versions, loglevel=log.loglevel_DEBUG)
+        return self._server_versions
+
+    def query_server_versions(self, force=False):
+        """\
+        Do a query for the server-side list of X2Go components and their versions.
+
+        @param force: do not use the cached component list, really ask the server (again)
+        @type force: C{bool}
+
+        @return: dictionary of X2Go components (as keys) and their versions (as values)
+        @rtype: C{list}
+
+        """
+        if force:
+            self._server_versions = None
+        return self._x2go_server_versions
+    get_server_versions = query_server_versions
+
+    @property
     def _x2go_server_features(self):
         """\
-        Render a list of server-side X2Go features. Results get cached once there has been one successfull query.
+        Render a list of server-side X2Go features. Results get cached once there has been one successful query.
 
         """
         if self._server_features is None:
             (stdin, stdout, stderr) = self._x2go_exec_command('which x2gofeaturelist >/dev/null && x2gofeaturelist')
             self._server_features = stdout.read().split('\n')
             self.logger('server-side X2Go features are: %s' % self._server_features, loglevel=log.loglevel_DEBUG)
-            return self._server_features
-        else:
-            return self._server_features
+        return self._server_features
 
     def query_server_features(self, force=False):
         """\
@@ -1333,7 +1367,7 @@ class X2GoControlSessionSTDOUT(paramiko.SSHClient):
             user = desktop.split('@')[0]
             display = desktop.split('@')[1]
         if not (user and display):
-            raise x2go_exceptions.X2GoDesktopSharingException('Need user name and display number of sharable desktop.')
+            raise x2go_exceptions.X2GoDesktopSharingException('Need user name and display number of shared desktop.')
 
         cmd = '%sXSHAD%sXSHAD%s' % (share_mode, user, display)
 
@@ -1443,7 +1477,10 @@ class X2GoControlSessionSTDOUT(paramiko.SSHClient):
             be interpreted as disconnected due to connection loss
         """
         if raw:
-            (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
+            if 'X2GO_LIST_SHADOWSESSIONS' in self._x2go_server_features:
+                (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && { x2golistsessions; x2golistshadowsessions; }")
+            else:
+                (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
             return stdout.read(), stderr.read()
 
         else:
@@ -1460,7 +1497,10 @@ class X2GoControlSessionSTDOUT(paramiko.SSHClient):
             while not _success and _count < _maxwait:
                 _count += 1
                 try:
-                    (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
+                    if 'X2GO_LIST_SHADOWSESSIONS' in self._x2go_server_features:
+                        (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && { x2golistsessions; x2golistshadowsessions; }")
+                    else:
+                        (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions")
                     _stdout_read = stdout.read()
                     _listsessions = self._list_backend(_stdout_read, info_backend=self._info_backend).sessions
                     _success = True
diff --git a/x2go/backends/terminal/_stdout.py b/x2go/backends/terminal/_stdout.py
index 0156700..4872832 100644
--- a/x2go/backends/terminal/_stdout.py
+++ b/x2go/backends/terminal/_stdout.py
@@ -983,6 +983,18 @@ class X2GoTerminalSessionSTDOUT(object):
                 # session title fallback... (like X2Go server does it...)
                 self.session_title = _generic_title
 
+        elif self.params.session_type == 'S':
+            if self.set_session_title:
+
+                shared_user = _generic_title.split('XSHAD')[1]
+                shared_display = _generic_title.split('XSHAD')[2].replace('PP', ':').split("_")[0]
+
+                self.session_title = 'Desktop %s@%s shared with %s@%s' % (shared_user, shared_display, self.control_session.remote_username(), self.control_session.get_hostname())
+
+            else:
+                # session title fallback... (like X2Go server does it...)
+                self.session_title = _generic_title
+
         else:
             # do nothing for rootless sessions
             self.session_title = _generic_title
@@ -1616,3 +1628,48 @@ class X2GoTerminalSessionSTDOUT(object):
                 self._rm_desktop_dirtree()
 
             self._cleaned_up = True
+
+    def is_desktop_session(self):
+        """\
+        Test if this terminal session is a desktop session.
+
+        @return: C{True} if this session is of session type desktop ('D').
+        @rtype: C{bool}
+
+        """
+        self.params.rewrite_session_type()
+        return self.params.session_type == 'D'
+
+    def is_rootless_session(self):
+        """\
+        Test if this terminal session is a rootless session.
+
+        @return: C{True} if this session is of session type rootless ('R').
+        @rtype: C{bool}
+
+        """
+        self.params.rewrite_session_type()
+        return self.params.session_type == 'R'
+
+    def is_shadow_session(self):
+        """\
+        Test if this terminal session is a desktop sharing (aka shadow) session.
+
+        @return: C{True} if this session is of session type shadow ('S').
+        @rtype: C{bool}
+
+        """
+        self.params.rewrite_session_type()
+        return self.params.session_type == 'S'
+
+    def is_pubapp_session(self):
+        """\
+        Test if this terminal session is a published applications session.
+
+        @return: C{True} if this session is of session type published applications ('P').
+        @rtype: C{bool}
+
+        """
+        self.params.rewrite_session_type()
+        return self.params.session_type == 'P'
+
diff --git a/x2go/client.py b/x2go/client.py
index 3a091f2..b7fd646 100644
--- a/x2go/client.py
+++ b/x2go/client.py
@@ -38,6 +38,7 @@ Supported Features
         - enabling and mangaging X2Go printing (real printing, viewing as PDF, saving
           to a local folder or executing a custom »print« command
         - transparent tunneling of audio (Pulseaudio, ESD)
+        - sharing of other desktops
         - LDAP support for X2Go server clusters (NOT IMPLEMENTED YET)
 
 Non-Profile Sessions
@@ -384,6 +385,20 @@ class X2GoClient(object):
         """
         self.logger('HOOK_session_startup_failed: session startup for session profile ,,%s'' failed.' % profile_name, loglevel=log.loglevel_WARN)
 
+    def HOOK_list_desktops_timeout(self, profile_name='UNKNOWN'):
+        """\
+        HOOK method: called if the x2golistdesktops command generates a timeout due to long execution time.
+
+        """
+        self.logger('HOOK_list_desktops_timeout: the server-side x2golistdesktops command for session profile %s took too long to return results. This can happen from time to time, please try again.' % profile_name, loglevel=log.loglevel_WARN)
+
+    def HOOK_no_such_desktop(self, profile_name='UNKNOWN', desktop='UNKNOWN'):
+        """\
+        HOOK method: called if it is tried to connect to a (seen before) sharable desktop that's not available (anymore).
+
+        """
+        self.logger('HOOK_no_such_desktop: the desktop %s (via session profile %s) is not available for sharing (anymore).' % (desktop, profile_name), loglevel=log.loglevel_WARN)
+
     def HOOK_no_known_xserver_found(self):
         """\
         HOOK method: called if the Python X2Go module could not find any usable XServer
@@ -477,7 +492,6 @@ class X2GoClient(object):
         # this HOOK has to return either True (accept host connection) or False (deny host conection)
         return True
 
-
     def HOOK_on_control_session_death(self, profile_name):
         """\
         HOOK method: called if a control session (server connection) has unexpectedly encountered a failure.
@@ -1465,7 +1479,7 @@ class X2GoClient(object):
         return _retval
     __start_session = start_session
 
-    def share_desktop_session(self, session_uuid, desktop=None, user=None, display=None, share_mode=0, **sessionopts):
+    def share_desktop_session(self, session_uuid, desktop=None, user=None, display=None, share_mode=0, check_desktop_list=False, **sessionopts):
         """\
         Share another already running desktop session. Desktop sharing can be run
         in two different modes: view-only and full-access mode. Like new sessions
@@ -1503,10 +1517,8 @@ class X2GoClient(object):
         if not _desktop in self._X2GoClient__list_desktops(session_uuid):
             _orig_desktop = _desktop
             _desktop = '%s.0' % _desktop
-            if not _desktop in self._X2GoClient__list_desktops(session_uuid):
-                raise x2go_exceptions.X2GoDesktopSharingException('No such desktop ID: %s' % _orig_desktop)
 
-        return self.session_registry(session_uuid).share_desktop(desktop=_desktop, share_mode=share_mode, check_desktop_list=False, **sessionopts)
+        return self.session_registry(session_uuid).share_desktop(desktop=_desktop, share_mode=share_mode, check_desktop_list=check_desktop_list, **sessionopts)
     __share_desktop_session = share_desktop_session
 
     def resume_session(self, session_uuid=None, session_name=None, match_profile_name=None, **sessionopts):
@@ -2022,6 +2034,7 @@ class X2GoClient(object):
         @type return_profile_ids: C{bool}
         @param return_session_names: return as list of session names
         @type return_session_names: C{bool}
+
         @return: list of connected sessions
         @rtype: C{list}
 
@@ -2050,6 +2063,7 @@ class X2GoClient(object):
         @type return_profile_ids: C{bool}
         @param return_session_names: return as list of session names
         @type return_session_names: C{bool}
+
         @return: list of associated sessions
         @rtype: C{list}
 
@@ -2078,6 +2092,7 @@ class X2GoClient(object):
         @type return_profile_ids: C{bool}
         @param return_session_names: return as list of session names
         @type return_session_names: C{bool}
+
         @return: list of running sessions
         @rtype: C{list}
 
@@ -2106,6 +2121,7 @@ class X2GoClient(object):
         @type return_profile_ids: C{bool}
         @param return_session_names: return as list of session names
         @type return_session_names: C{bool}
+
         @return: list of suspended sessions
         @rtype: C{list}
 
@@ -2134,6 +2150,7 @@ class X2GoClient(object):
         @type return_profile_ids: C{bool}
         @param return_session_names: return as list of session names
         @type return_session_names: C{bool}
+
         @return: list of registered sessions
         @rtype: C{list}
 
@@ -2164,6 +2181,63 @@ class X2GoClient(object):
         return self.session_registry.control_session_of_profile_name(profile_name)
     __client_control_session_of_profile_name = client_control_session_of_profile_name
 
+    def get_server_versions(self, profile_name, component=None):
+        """\
+        Query the server configured in session profile <profile_name> for the list of install X2Go components
+        and its versions.
+
+        @param profile_name: use the control session of this profile to query the X2Go server for its component list
+        @type profile_name: C{str}
+        @param component: only return the version of a specific component
+        @type component: C{str}
+
+        @return: dictionary of server components (as keys) and their versions (as values) or the version of the given <component>
+        @rtype: C{dict} or C{str}
+
+        @raise X2GoClientException: if component is not available on the X2Go Server.
+
+        """
+        control_session = self.client_control_session_of_profile_name(profile_name)
+        if component is None:
+            return control_session.get_server_versions()
+        else:
+            try:
+                return control_session.get_server_versions()[component]
+            except KeyError:
+                raise x2go_exceptions.X2GoClientException('No such component on X2Go Server')
+
+    def get_server_features(self, profile_name):
+        """\
+        Query the server configured in session profile <profile_name> for the list of server-side
+        X2Go features.
+
+        @param profile_name: use the control session of this profile to query the X2Go server for its feature list
+        @type profile_name: C{str}
+
+        @return: list of server feature names (as returned by server-side command ,,x2gofeaturelist''
+        @rtype: C{list}
+
+        """
+        control_session = self.client_control_session_of_profile_name(profile_name)
+        return control_session.get_server_features()
+
+    def has_server_feature(self, profile_name, feature):
+        """\
+        Query the server configured in session profile <profile_name> for the availability
+        of a certain server feature.
+
+        @param profile_name: use the control session of this profile to query the X2Go server for its feature
+        @type profile_name: C{str}
+        @param feature: test the availability of this feature on the X2Go server
+        @type feature: C{str}
+
+        @return: C{True} if the feature is available on the queried server
+        @rtype: C{bool}
+
+        """
+        control_session = self.client_control_session_of_profile_name(profile_name)
+        return feature in control_session.get_server_features()
+
     def client_registered_session_of_name(self, session_name, return_object=False):
         """\
         Retrieve X2Go session of a given session name.
diff --git a/x2go/session.py b/x2go/session.py
index 2284c09..fbe1bf3 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -467,6 +467,26 @@ class X2GoSession(object):
         else:
             self.logger('HOOK_session_startup_failed: session startup for session profile ,,%s\'\' failed.' % self.profile_name, loglevel=log.loglevel_WARN)
 
+    def HOOK_list_desktops_timeout(self):
+        """\
+        HOOK method: called if the x2golistdesktops command generates a timeout due to long execution time.
+
+        """
+        if self.client_instance:
+            self.client_instance.HOOK_list_desktops_timeout(profile_name=self.profile_name)
+        else:
+            self.logger('HOOK_list_desktops_timeout: the server-side x2golistdesktops command for session profile %s took too long to return results. This can happen from time to time, please try again.' % self.profile_name, loglevel=log.loglevel_WARN)
+
+    def HOOK_no_such_desktop(self, desktop='UNKNOWN'):
+        """\
+        HOOK method: called if it is tried to connect to a shared desktop that's not available (anymore).
+
+        """
+        if self.client_instance:
+            self.client_instance.HOOK_no_such_desktop(profile_name=self.profile_name, desktop=desktop)
+        else:
+            self.logger('HOOK_no_such_desktop: the desktop %s (via session profile %s) is not available for sharing (anymore).' % (desktop, self.profile_name), loglevel=log.loglevel_WARN)
+
     def HOOK_rforward_request_denied(self, server_port=0):
         """\
         HOOK method: called if a reverse port forwarding request has been denied.
@@ -1451,6 +1471,9 @@ class X2GoSession(object):
         """
         try:
             return self.control_session.list_desktops(raw=raw)
+        except x2go_exceptions.X2GoTimeoutException:
+            if self.is_alive(): self.HOOK_list_desktop_timeout()
+            return []
         except x2go_exceptions.X2GoControlSessionException:
             if self.connected: self.HOOK_on_control_session_death()
             self._X2GoSession__disconnect()
@@ -1570,19 +1593,6 @@ class X2GoSession(object):
         return False
     __is_published_applications_provider = is_published_applications_provider
 
-    def is_desktop_session(self):
-        """\
-        Returns true if this session is configured as desktop session.
-
-        @return: returns C{True} if this session is a desktop session.
-        @rtype: C{bool}
-
-        """
-        if self.has_terminal_session():
-            return self.terminal_session.is_desktop_session()
-        return False
-    __is_desktop_session = is_desktop_session
-
     def get_published_applications(self, lang=None, refresh=False, raw=False, very_raw=False, max_no_submenus=defaults.PUBAPP_MAX_NO_SUBMENUS):
         """\
         Return a list of published menu items from the X2Go server
@@ -1928,7 +1938,6 @@ class X2GoSession(object):
 
             self._X2GoSession__disconnect()
             return False
-
     __resume = resume
 
     def start(self, cmd=None, progress_event=None):
@@ -2029,11 +2038,15 @@ class X2GoSession(object):
 
         _desktop = desktop or '%s@%s' % (user, display)
         if check_desktop_list:
-            if not _desktop in self._X2GoSession__list_desktops():
+            desktop_list = self._X2GoSession__list_desktops()
+            if not _desktop in desktop_list:
                 _orig_desktop = _desktop
                 _desktop = '%s.0' % _desktop
-                if not _desktop in self._X2GoSession__list_desktops():
-                    raise x2go_exceptions.X2GoDesktopSharingException('No such desktop ID: %s' % _orig_desktop)
+                if not _desktop in desktop_list:
+                    self.HOOK_no_such_desktop(desktop=_orig_desktop)
+                    self._progress_status = -1
+                    progress_event.set()
+                    return False
 
         self._progress_status = 33
         progress_event.set()
@@ -2101,6 +2114,54 @@ class X2GoSession(object):
         return False
     __share_desktop = share_desktop
 
+    def is_desktop_session(self):
+        """\
+        Test if this X2Go session is a desktop session.
+
+        @return: C{True} if this session is of session type desktop ('D').
+        @rtype: C{bool}
+
+        """
+        if self.has_terminal_session():
+            return self.terminal_session.is_desktop_session()
+    __is_desktop_session = is_desktop_session
+
+    def is_rootless_session(self):
+        """\
+        Test if this X2Go session is a rootless session.
+
+        @return: C{True} if this session is of session type rootless ('R').
+        @rtype: C{bool}
+
+        """
+        if self.has_terminal_session():
+            return self.terminal_session.is_rootless_session()
+    __is_rootless_session = is_rootless_session
+
+    def is_shadow_session(self):
+        """\
+        Test if this X2Go session is a desktop sharing (aka shadow) session.
+
+        @return: C{True} if this session is of session type shadow ('S').
+        @rtype: C{bool}
+
+        """
+        if self.has_terminal_session():
+            return self.terminal_session.is_shadow_session()
+    __is_shadow_session = is_shadow_session
+
+    def is_pubapp_session(self):
+        """\
+        Test if this X2Go session is a published applications session.
+
+        @return: C{True} if this session is of session type published applications ('P').
+        @rtype: C{bool}
+
+        """
+        if self.has_terminal_session():
+            return self.terminal_session.is_pubapp_session()
+    __is_pubapp_session = is_pubapp_session
+
     def suspend(self):
         """\
         Suspend this X2Go session.


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).




More information about the x2go-commits mailing list