[X2Go-Commits] python-x2go.git - release/0.4.0.x (branch) updated: 0.2.0.10-82-g8d835dc
X2Go dev team
git-admin at x2go.org
Tue Jan 7 16:18:21 CET 2014
The branch, release/0.4.0.x has been updated
via 8d835dcdec07d7f620a25cbcd4aafd88a1a044c7 (commit)
from 19142c8cf8165b553780cfc33048df5ae11890ef (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:
debian/changelog | 2 +
x2go/backends/profiles/_file.py | 42 ++++++++++++--
x2go/cache.py | 1 +
x2go/client.py | 36 +++++++++++-
x2go/defaults.py | 2 +-
x2go/registry.py | 10 ++++
x2go/session.py | 121 +++++++++++++++++++++++++++++----------
x2go/utils.py | 1 +
8 files changed, 178 insertions(+), 37 deletions(-)
The diff of changes is:
diff --git a/debian/changelog b/debian/changelog
index 3cd247b..871adcb 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -48,6 +48,8 @@ python-x2go (0.2.1.0-0~x2go1) UNRELEASED; urgency=low
get_published_applications() of control sessions.
- Implement some internal locking for X2goSession objects.
- Add option to disable auto-registration of pubapp sessions.
+ - Implement functionality for restoring mounted shares on session
+ resumption / re-start. Sponsored by Dick Kniep, LinDix NL.
* /debian/rules:
+ Allow package build on systems with missing dh_python2.
* /debian/control:
diff --git a/x2go/backends/profiles/_file.py b/x2go/backends/profiles/_file.py
index 586f262..24c2912 100644
--- a/x2go/backends/profiles/_file.py
+++ b/x2go/backends/profiles/_file.py
@@ -28,6 +28,7 @@ __NAME__ = 'x2gosessionprofiles-pylib'
import copy
import types
+import re
# Python X2Go modules
from x2go.defaults import X2GO_SESSIONPROFILES_CONFIGFILES as _X2GO_SESSIONPROFILES_CONFIGFILES
@@ -177,12 +178,14 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
# we have to handle the get_type method separately...
return self.get_profile_option_type(key)
- def get_profile_config(self, profile_id_or_name=None, profile_id=None):
+ def get_profile_config(self, profile_id_or_name=None, parameter=None, profile_id=None):
"""\
The configuration options for a single session profile.
@param profile_id_or_name: either profile ID or profile name is accepted
@type profile_id_or_name: C{str}
+ @param parameter: if specified, only the value for the given parameter is returned
+ @type parameter: C{str}
@param profile_id: profile ID (fast than specifying C{profile_id_or_name})
@type profile_id: C{str}
@@ -194,6 +197,27 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
_profile_config = {}
for option in self.iniConfig.options(_profile_id):
_profile_config[option] = self.get(_profile_id, option, key_type=self.get_profile_option_type(option))
+
+ if parameter is not None:
+ if parameter in _profile_config.keys():
+
+ value = _profile_config[parameter]
+
+ if parameter == 'export':
+ _strvalue = value.replace(',', ';').strip().strip('"').strip().strip(';').strip()
+ value = {}
+ if _strvalue:
+ _export_paths = _strvalue.split(';')
+ for _path in _export_paths:
+ if not re.match('.*:(0|1)$', _path): _path = '%s:1' % _path
+ _auto_export_path = re.match('.*:1$', _path) and True or False
+ _export_path = ':'.join(_path.split(':')[:-1])
+ value[_export_path] = _auto_export_path
+
+ return value
+
+ else:
+ raise X2goProfileException('no such session profile parameter: %s' % parameter)
return _profile_config
def default_profile_config(self):
@@ -362,17 +386,27 @@ class X2goSessionProfilesFILE(inifiles.X2goIniFile):
@type value: any type, depends on the session profile option
"""
- profile_id = section
+ profile_id = self.check_profile_id_or_name(section)
if key == 'name':
profile_name = value
- current_profile_name = self.get_value(section, key)
+ current_profile_name = self.get_value(profile_id, key)
if not profile_name:
raise X2goProfileException('profile name for profile id %s may not be empty' % profile_id)
else:
if profile_name != current_profile_name and profile_name in self.profile_names:
raise X2goProfileException('a profile of name ,,%s'' already exists' % profile_name)
self._cached_profile_names = []
- inifiles.X2goIniFile.update_value(self, section, key, value)
+
+ if key == 'export' and type(value) == types.DictType:
+
+ _strvalue = '"'
+ for folder in value.keys():
+ _strvalue += "%s:%s;" % (folder, int(value[folder]))
+ _strvalue += '"'
+ _strvalue = _strvalue.replace('""', '')
+ value = _strvalue
+
+ inifiles.X2goIniFile.update_value(self, profile_id, key, value)
def check_profile_id_or_name(self, profile_id_or_name):
"""\
diff --git a/x2go/cache.py b/x2go/cache.py
index 5d90191..7c73692 100644
--- a/x2go/cache.py
+++ b/x2go/cache.py
@@ -149,6 +149,7 @@ class X2goListSessionsCache(object):
@raise X2goControlSessionException: if the control session's C{list_mounts} method fails
"""
try:
+ self.x2go_listsessions_cache[profile_name]['mounts'] = {}
if self.x2go_listsessions_cache[profile_name]['sessions']:
for session_name in self.x2go_listsessions_cache[profile_name]['sessions']:
self.x2go_listsessions_cache[profile_name]['mounts'].update(control_session.list_mounts(session_name))
diff --git a/x2go/client.py b/x2go/client.py
index 1dd219b..142e970 100644
--- a/x2go/client.py
+++ b/x2go/client.py
@@ -1919,6 +1919,13 @@ class X2goClient(object):
Get a list of local folders mounted within X2Go session with session hash <session_uuid>
from this client.
+ @param session_uuid: the X2Go session's UUID registry hash
+ @type session_uuid: C{str}
+ @param profile_name: alternatively, the profile name can be used to get mounted folders of a session connected profile
+ @type profile_name: C{str}
+ @param check_list_mounts: query the server-side mount list for up-to-date information
+ @type check_list_mounts: C{bool}
+
@return: returns a C{list} of those local folder names that are mounted within X2Go session <session_uuid>.
@rtype: C{list}
@@ -2710,7 +2717,7 @@ class X2goClient(object):
### Session profile oriented methods
###
- def get_profile_config(self, profile_id_or_name):
+ def get_profile_config(self, profile_id_or_name, parameter=None):
"""\
Returns a dictionary with session options and values that represent
the session profile for C{profile_id_or_name}.
@@ -2718,15 +2725,38 @@ class X2goClient(object):
@param profile_id_or_name: name or id of an X2Go session profile as found
in the sessions configuration file
@type profile_id_or_name: C{str}
+ @param parameter: if specified, only the value for the given parameter is returned
+ @type parameter: C{str}
@return: a Python dictionary with session profile options
- @rtype: C{dict}
+ @rtype: C{dict} or C{bool}, C{int}, C{str}
"""
- return self.session_profiles.get_profile_config(profile_id_or_name)
+ return self.session_profiles.get_profile_config(profile_id_or_name, parameter=parameter)
__get_profile_config = get_profile_config
with_profile_config = get_profile_config
+ def set_profile_config(self, profile_id_or_name, parameter, value):
+ """\
+ Set individual session profile parameters for session profile C{profile_id_or_name}.
+
+ @param profile_id_or_name: name or id of an X2Go session profile as found
+ in the sessions configuration file
+ @type profile_id_or_name: C{str}
+ @param parameter: set this parameter with the given C{value}
+ @type parameter: C{str}
+ @param value: set this value for the given C{parameter}
+ @type value: C{bool}, C{int}, C{str}, C{list} or C{dict}
+
+ @return: returns C{True} if this operation has been successful
+ @rtype: C{dict}
+
+ """
+ self.session_profiles.update_value(profile_id_or_name, parameter, value)
+ self.session_profiles.write_user_config = True
+ self.session_profiles.write()
+ __set_profile_config = set_profile_config
+
def to_profile_id(self, profile_name):
"""\
Retrieve the session profile ID of the session whose profile name
diff --git a/x2go/defaults.py b/x2go/defaults.py
index b158bb2..58a0615 100644
--- a/x2go/defaults.py
+++ b/x2go/defaults.py
@@ -297,7 +297,7 @@ X2GO_SESSIONPROFILE_DEFAULTS = {
'iconvto': 'UTF-8', 'iconvfrom': 'UTF-8', 'useiconv': False,
'usesshproxy': False, 'sshproxyhost': 'proxyhost.mydomain', 'sshproxyport': 22, 'sshproxyuser': '', 'sshproxytunnel': 'localhost:44444:server.mydomain.private:22', 'sshproxykeyfile': '',
'sshproxytype': 'SSH', 'sshproxysameuser': False, 'sshproxysamepass': False, 'sshproxyautologin': True,
- 'useexports': True, 'fstunnel': True, 'export': '',
+ 'useexports': True, 'restoreexports': False, 'fstunnel': True, 'export': '',
'usemimebox': False, 'mimeboxextensions': '', 'mimeboxaction': 'OPEN',
'fullscreen': False,
'width': 800,'height': 600, 'maxdim': False, 'dpi': 96, 'setdpi': False, 'xinerama': False, 'multidisp': False,
diff --git a/x2go/registry.py b/x2go/registry.py
index e7b6732..5b4d67f 100644
--- a/x2go/registry.py
+++ b/x2go/registry.py
@@ -332,6 +332,11 @@ class X2goSessionRegistry(object):
# unregister as master session
if _profile_name in self.master_sessions.keys():
if self.master_sessions[_profile_name] == self(_session_uuid):
+
+ # save exported folders to session profile config if requested by session profile parameter ,,restoreexports''...
+ if self.client_instance and self(_session_uuid).restore_shared_local_folders:
+ self.client_instance.set_profile_config(_profile_name, 'export', self(_session_uuid)._restore_exported_folders)
+
self(_session_uuid).unset_master_session()
del self.master_sessions[_profile_name]
@@ -344,6 +349,11 @@ class X2goSessionRegistry(object):
# unregister as master session
if _profile_name in self.master_sessions.keys():
if self.master_sessions[_profile_name] == self(_session_uuid):
+
+ # save exported folders to session profile config if requested by session profile parameter ,,restoreexports''...
+ if self.client_instance and self(_session_uuid).restore_shared_local_folders:
+ self.client_instance.set_profile_config(_profile_name, 'export', self(_session_uuid)._restore_exported_folders)
+
self(_session_uuid).unset_master_session()
del self.master_sessions[_profile_name]
diff --git a/x2go/session.py b/x2go/session.py
index da67aaf..31d7aea 100644
--- a/x2go/session.py
+++ b/x2go/session.py
@@ -89,7 +89,7 @@ _X2GO_SESSION_PARAMS = ('use_sshproxy', 'sshproxy_reuse_authinfo',
'auto_start_or_resume', 'auto_connect',
'printing', 'allow_mimebox',
'mimebox_extensions', 'mimebox_action',
- 'allow_share_local_folders', 'share_local_folders',
+ 'allow_share_local_folders', 'share_local_folders', 'restore_shared_local_folders',
'control_backend', 'terminal_backend', 'info_backend', 'list_backend', 'proxy_backend', 'settings_backend', 'printing_backend',
'client_rootdir', 'sessions_rootdir', 'ssh_rootdir',
'keep_controlsession_alive', 'add_to_known_hosts', 'known_hosts', 'forward_sshagent',
@@ -145,6 +145,7 @@ class X2goSession(object):
mimebox_action='OPEN',
allow_share_local_folders=False,
share_local_folders=[],
+ restore_shared_local_folders=False,
control_backend=_X2goControlSession,
terminal_backend=_X2goTerminalSession,
info_backend=_X2goServerSessionInfo,
@@ -194,6 +195,8 @@ class X2goSession(object):
@type allow_share_local_folders: C{bool}
@param share_local_folders: list of local folders to share with the remote X2Go session
@type share_local_folders: C{list}
+ @param restore_shared_local_folders: store actual list of shared local folders after session has been suspended or terminated
+ @type restore_shared_local_folders: C{bool}
@param control_backend: X2Go control session backend to use
@type control_backend: C{class}
@param terminal_backend: X2Go terminal session backend to use
@@ -276,6 +279,7 @@ class X2goSession(object):
self.printing = printing
self.allow_share_local_folders = allow_share_local_folders
self.share_local_folders = share_local_folders
+ self.restore_shared_local_folders = restore_shared_local_folders
self.allow_mimebox = allow_mimebox
self.mimebox_extensions = mimebox_extensions
self.mimebox_action = mimebox_action
@@ -364,6 +368,9 @@ class X2goSession(object):
self._progress_status = 0
self._lock = threading.Lock()
+ if self.client_instance and self.restore_shared_local_folders:
+ self._restore_exported_folders = self.client_instance.get_profile_config(self.profile_name, 'export')
+
def get_client_instance(self):
"""\
Return parent L{X2goClient} instance if avaiable.
@@ -569,9 +576,9 @@ class X2goSession(object):
gevent.sleep(1)
if wait:
- gevent.spawn_later(wait, self.share_all_local_folders)
+ gevent.spawn_later(wait, self.share_all_local_folders, update_exported_folders=False)
else:
- gevent.spawn(self.share_all_local_folders)
+ gevent.spawn(self.share_all_local_folders, update_exported_folders=False)
__set_master_session = set_master_session
def unset_master_session(self):
@@ -581,7 +588,7 @@ class X2goSession(object):
"""
# unmount shared folders
if self.has_terminal_session():
- self.unshare_all_local_folders()
+ self.unshare_all_local_folders(update_exported_folders=False)
self.master_session = False
__unset_master_session = unset_master_session
@@ -719,6 +726,10 @@ class X2goSession(object):
del params['share_local_folders']
except KeyError: pass
try:
+ self.restore_shared_local_folders = params['restore_shared_local_folders']
+ del params['restore_shared_local_folders']
+ except KeyError: pass
+ try:
self.allow_mimebox = params['allow_mimebox']
del params['allow_mimebox']
except KeyError: pass
@@ -1290,6 +1301,7 @@ class X2goSession(object):
self.terminated = None
self.faults = None
self.active = False
+ self._lock.release()
self.unset_master_session()
try:
self.update_status(force_update=True)
@@ -1894,7 +1906,7 @@ class X2goSession(object):
self._progress_status = 90
progress_event.set()
- # if there is a client instance for X2Go sessions that the client instance will handle the mounting of shared folders
+ # if self.client_instance exists than the folder sharing is handled via the self.set_master_session() evoked by the session registry
if (not self.client_instance) and \
self._SUPPORTED_FOLDERSHARING and \
self.allow_share_local_folders:
@@ -2104,6 +2116,10 @@ class X2goSession(object):
@raise X2goSessionException: if the session could not be suspended
"""
+ _store_shared_folders = {}
+ for folder in self.shared_folders:
+ _store_shared_folders.update({ folder: True })
+
self._lock.acquire()
try:
_retval = self._suspend()
@@ -2111,6 +2127,7 @@ class X2goSession(object):
raise
finally:
self._lock.release()
+
return _retval
def _suspend(self):
@@ -2137,12 +2154,12 @@ class X2goSession(object):
self.unset_master_session()
- if self.terminal_session.suspend():
-
- self.session_cleanup()
- del self.terminal_session
- self.terminal_session = None
- return True
+ if self.has_terminal_session():
+ if self.terminal_session.suspend():
+ self.session_cleanup()
+ del self.terminal_session
+ self.terminal_session = None
+ return True
elif self.has_control_session() and self.session_name:
if self.control_session.suspend(session_name=self.session_name):
@@ -2181,6 +2198,7 @@ class X2goSession(object):
raise
finally:
self._lock.release()
+
return _retval
def _terminate(self):
@@ -2207,11 +2225,12 @@ class X2goSession(object):
self.unset_master_session()
- if self.terminal_session.terminate():
- self.session_cleanup()
- del self.terminal_session
- self.terminal_session = None
- return True
+ if self.has_terminal_session():
+ if self.terminal_session.terminate():
+ self.session_cleanup()
+ del self.terminal_session
+ self.terminal_session = None
+ return True
elif self.has_control_session() and self.session_name:
if self.control_session.terminate(session_name=self.session_name):
@@ -2397,7 +2416,19 @@ class X2goSession(object):
return False
__is_folder_sharing_available = is_folder_sharing_available
- def share_local_folder(self, local_path=None, folder_name=None):
+ def _update_restore_exported_folders(self):
+
+ # remember exported folders for restoring them on session suspension/termination
+ if self.client_instance and self.restore_shared_local_folders:
+ _exported_folders = self.client_instance.get_profile_config(self.profile_name, 'export')
+ for folder in self.shared_folders:
+ _exported_folders.update({ unicode(folder): True })
+ for folder in _exported_folders.keys():
+ if (folder not in self.shared_folders) and _exported_folders[folder]:
+ del _exported_folders[folder]
+ self._restore_exported_folders = _exported_folders
+
+ def share_local_folder(self, local_path=None, folder_name=None, update_exported_folders=True):
"""\
Share a local folder with this registered X2Go session.
@@ -2406,6 +2437,8 @@ class X2goSession(object):
@type local_path: C{str}
@param folder_name: synonymous to C{local_path}
@type folder_name: C{str}
+ @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+ @type update_exported_folders: C{bool}
@return: returns C{True} if the local folder has been successfully mounted within
this X2Go session
@@ -2419,18 +2452,26 @@ class X2goSession(object):
if self.has_terminal_session():
if self.is_folder_sharing_available() and self.is_master_session():
+ # append local_path to list of shared folders before mounting
+ self.shared_folders.append(local_path)
if self.terminal_session.share_local_folder(local_path=local_path):
- self.shared_folders.append(local_path)
+ if update_exported_folders:
+ self._update_restore_exported_folders()
return True
+ # if we failed remove the local_path element again...
+ self.shared_folders.remove(local_path)
return False
else:
raise x2go_exceptions.X2goSessionException('this X2goSession object does not have any associated terminal')
__share_local_folder = share_local_folder
- def share_all_local_folders(self):
+ def share_all_local_folders(self, update_exported_folders=True):
"""\
Share all local folders configured to be mounted within this X2Go session.
+ @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+ @type update_exported_folders: C{bool}
+
@return: returns C{True} if all local folders could be successfully mounted
inside this X2Go session
@rtype: C{bool}
@@ -2444,21 +2485,26 @@ class X2goSession(object):
_retval = True
for _folder in self.share_local_folders:
try:
- _retval = self.share_local_folder(_folder) and _retval
+ _retval = self.share_local_folder(_folder, update_exported_folders=False) and _retval
except x2go_exceptions.X2goUserException, e:
self.logger('%s' % str(e), loglevel=log.loglevel_WARN)
+ if update_exported_folders:
+ self._update_restore_exported_folders()
+
else:
self.HOOK_foldersharing_not_available()
return _retval
__share_all_local_folders = share_all_local_folders
- def unshare_all_local_folders(self, force_all=False):
+ def unshare_all_local_folders(self, force_all=False, update_exported_folders=True):
"""\
Unshare all local folders mounted within this X2Go session.
@param force_all: Really unmount _all_ shared folders, including the print spool folder and
the MIME box spool dir (not recommended).
@type force_all: C{bool}
+ @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+ @type update_exported_folders: C{bool}
@return: returns C{True} if all local folders could be successfully unmounted
inside this X2Go session
@@ -2471,19 +2517,23 @@ class X2goSession(object):
if self.is_folder_sharing_available() and self.is_master_session():
if force_all:
self.shared_folders = []
- return self.terminal_session.unshare_all_local_folders()
+ retval = self.terminal_session.unshare_all_local_folders()
+ return retval
else:
+ _shared_folders = copy.deepcopy(self.shared_folders)
+ self.shared_folders = []
retval = 0
- for _shared_folder in self.shared_folders:
+ for _shared_folder in _shared_folders:
retval = retval | self.terminal_session.unshare_local_folder(_shared_folder)
- self.shared_folders = []
+ if update_exported_folders:
+ self._update_restore_exported_folders()
return retval
else:
raise x2go_exceptions.X2goSessionException('this X2goSession object does not have any associated terminal')
return False
__unshare_all_local_folders = unshare_all_local_folders
- def unshare_local_folder(self, local_path=None):
+ def unshare_local_folder(self, local_path=None, update_exported_folders=True):
"""\
Unshare a local folder that is mounted within this X2Go session.
@@ -2491,6 +2541,9 @@ class X2goSession(object):
file system that is mounted in this X2Go session and shall be
unmounted
@type local_path: C{str}
+ @param update_exported_folders: do an update of the session profile option ,,export'' after the operation
+ @type update_exported_folders: C{bool}
+
@return: returns C{True} if all local folders could be successfully unmounted
inside this X2Go session
@rtype: C{bool}
@@ -2500,10 +2553,18 @@ class X2goSession(object):
"""
if self.has_terminal_session():
if self.is_folder_sharing_available() and self.is_master_session() and local_path in self.shared_folders:
+ # update shared_folders before(!) unmounting...
self.shared_folders.remove(local_path)
- return self.terminal_session.unshare_local_folder(local_path=local_path)
+ _retval = self.terminal_session.unshare_local_folder(local_path=local_path)
+ # unmounting failed? Re-add local_patch to shared_folders for now...
+ if not _retval:
+ self.shared_folders.append(local_path)
+ if _retval and update_exported_folders:
+ self._update_restore_exported_folders()
else:
raise x2go_exceptions.X2goSessionException('this X2goSession object does not have any associated terminal')
+
+ return _retval
__unshare_local_folder = unshare_local_folder
def get_shared_folders(self, check_list_mounts=False, mounts=None):
@@ -2531,21 +2592,22 @@ class X2goSession(object):
_found = False
for mount in mounts:
+ if _found: continue
mount = mount.split('|')[1]
if _X2GOCLIENT_OS == 'Windows':
+
_driveletter, _path = os.path.splitdrive(shared_folder)
_mount_point = '_windrive_%s%s' % (_driveletter[0], _path.replace('\\', '_'))
_mount_point = _mount_point.replace(' ', '_')
if mount.lower().endswith(_mount_point.lower()):
_found = True
- break
+
else:
_mount_point = shared_folder.replace('/', '_')
_mount_point = _mount_point.replace(' ', '_')
if mount.endswith(_mount_point):
_found = True
- break
if not _found:
unshared_folders.append(shared_folder)
@@ -2553,11 +2615,12 @@ class X2goSession(object):
for unshared_folder in unshared_folders:
try:
self.shared_folders.remove(unshared_folder)
+ self._update_restore_exported_folders()
self.logger('Detected server-side unsharing of client-side folder for profile %s: %s:' % (self.get_profile_name(), shared_folder), loglevel=log.loglevel_INFO)
except IndexError:
pass
- return self.shared_folders
+ return [ unicode(s) for s in self.shared_folders ]
__get_shared_folders = get_shared_folders
def is_locked(self):
diff --git a/x2go/utils.py b/x2go/utils.py
index 0a25fd3..ab98120 100644
--- a/x2go/utils.py
+++ b/x2go/utils.py
@@ -186,6 +186,7 @@ def _convert_SessionProfileOptions_2_SessionParams(options):
'speed': 'link',
'sshport': 'port',
'useexports': 'allow_share_local_folders',
+ 'restoreexports': 'restore_shared_local_folders',
'usemimebox': 'allow_mimebox',
'mimeboxextensions': 'mimebox_extensions',
'mimeboxaction': 'mimebox_action',
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