Package x2go :: Package backends :: Package control :: Module _stdout
[frames] | no frames]

Source Code for Module x2go.backends.control._stdout

  1  # -*- coding: utf-8 -*- 
  2   
  3  # Copyright (C) 2010-2011 by Mike Gabriel <mike.gabriel@das-netzwerkteam.de> 
  4  # 
  5  # Python X2go is free software; you can redistribute it and/or modify 
  6  # it under the terms of the GNU General Public License as published by 
  7  # the Free Software Foundation; either version 3 of the License, or 
  8  # (at your option) any later version. 
  9  # 
 10  # Python X2go is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
 13  # GNU General Public License for more details. 
 14  # 
 15  # You should have received a copy of the GNU General Public License 
 16  # along with this program; if not, write to the 
 17  # Free Software Foundation, Inc., 
 18  # 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. 
 19   
 20  """\ 
 21  X2goControlSessionSTDOUT class - core functions for handling your individual X2go sessions. 
 22   
 23  This backend handles X2go server implementations that respond via server-side STDOUT. 
 24   
 25  """ 
 26  __NAME__ = 'x2gocontrolsession-pylib' 
 27   
 28  # modules 
 29  import os 
 30  import types 
 31  import paramiko 
 32  import gevent 
 33   
 34  import copy 
 35  import binascii 
 36   
 37  import string 
 38  import random 
 39   
 40  # Python X2go modules 
 41  import x2go.sshproxy as sshproxy 
 42  import x2go.log as log 
 43  import x2go.utils as utils 
 44  import x2go.x2go_exceptions as x2go_exceptions 
 45  import x2go.defaults as defaults 
 46  import x2go.checkhosts as checkhosts 
 47   
 48  from x2go.backends.terminal import X2goTerminalSession as _X2goTerminalSession 
 49  from x2go.backends.info import X2goServerSessionInfo as _X2goServerSessionInfo 
 50  from x2go.backends.info import X2goServerSessionList as _X2goServerSessionList 
 51  from x2go.backends.proxy import X2goProxy as _X2goProxy 
52 53 -def _rerewrite_blanks(cmd):
54 # X2go run command replace X2GO_SPACE_CHAR string with blanks 55 if cmd: 56 cmd = cmd.replace("X2GO_SPACE_CHAR", " ") 57 return cmd
58
59 -def _rewrite_password(cmd, user=None, password=None):
60 61 # if there is a ,,-u X2GO_USER'' parameter in RDP options then we will replace 62 # it by our X2go session password 63 if cmd and user: 64 cmd = cmd.replace('X2GO_USER', user) 65 # if there is a ,,-p X2GO_PASSWORD'' parameter in RDP options then we will replace 66 # it by our X2go session password 67 if cmd and password: 68 cmd = cmd.replace('X2GO_PASSWORD', password) 69 return cmd
70
71 72 -class X2goControlSessionSTDOUT(paramiko.SSHClient):
73 """\ 74 STILL UNDOCUMENTED 75 76 @param logger: you can pass an L{X2goLogger} object to the 77 L{X2goControlSessionSTDOUT} constructor 78 @type logger: L{X2goLogger} instance 79 @param loglevel: if no L{X2goLogger} object has been supplied a new one will be 80 constructed with the given loglevel 81 @type loglevel: int 82 83 """ 84 associated_terminals = None 85
86 - def __init__(self, 87 profile_name='UNKNOWN', 88 add_to_known_hosts=False, 89 known_hosts=None, 90 terminal_backend=_X2goTerminalSession, 91 info_backend=_X2goServerSessionInfo, 92 list_backend=_X2goServerSessionList, 93 proxy_backend=_X2goProxy, 94 client_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_CLIENT_ROOTDIR), 95 sessions_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_SESSIONS_ROOTDIR), 96 ssh_rootdir=os.path.join(defaults.LOCAL_HOME, defaults.X2GO_SSH_ROOTDIR), 97 logger=None, loglevel=log.loglevel_DEFAULT, 98 *args, **kwargs):
99 """\ 100 Initialize an X2go session. With the L{X2goControlSessionSTDOUT} class you can start 101 new X2go sessions, resume suspended sessions or suspend resp. terminate 102 currently running sessions on a connected X2go server. 103 104 """ 105 self.associated_terminals = {} 106 self.terminated_terminals = [] 107 108 self.profile_name = profile_name 109 self.add_to_known_hosts = add_to_known_hosts 110 self.known_hosts = known_hosts 111 112 self.hostname = None 113 self.port = None 114 115 self.sshproxy_session = None 116 117 self._session_auth_rsakey = None 118 self._remote_home = None 119 self._remote_group = {} 120 121 self.locked = False 122 123 if logger is None: 124 self.logger = log.X2goLogger(loglevel=loglevel) 125 else: 126 self.logger = copy.deepcopy(logger) 127 self.logger.tag = __NAME__ 128 129 self._terminal_backend = terminal_backend 130 self._info_backend = info_backend 131 self._list_backend = list_backend 132 self._proxy_backend = proxy_backend 133 134 self.client_rootdir = client_rootdir 135 self.sessions_rootdir = sessions_rootdir 136 self.ssh_rootdir = ssh_rootdir 137 138 paramiko.SSHClient.__init__(self, *args, **kwargs) 139 if self.add_to_known_hosts: 140 self.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 141 142 self.session_died = False
143
144 - def load_session_host_keys(self):
145 if self.known_hosts is not None: 146 utils.touch_file(self.known_hosts) 147 self.load_host_keys(self.known_hosts)
148
149 - def __del__(self):
150 151 self.disconnect()
152
153 - def _x2go_sftp_put(self, local_path, remote_path):
154 155 self.logger('sFTP-put: %s -> %s:%s' % (os.path.normpath(local_path), self.get_transport().getpeername(), remote_path), loglevel=log.loglevel_DEBUG) 156 self.sftp_client.put(os.path.normpath(local_path), remote_path)
157
158 - def _x2go_sftp_write(self, remote_path, content):
159 160 self.logger('sFTP-write: opening remote file %s on host %s for writing' % (remote_path, self.get_transport().getpeername()), loglevel=log.loglevel_DEBUG) 161 remote_fileobj = self.sftp_client.open(remote_path, 'w') 162 self.logger('sFTP-write: writing content: %s' % content, loglevel=log.loglevel_DEBUG_SFTPXFER) 163 remote_fileobj.write(content) 164 remote_fileobj.close()
165
166 - def _x2go_sftp_remove(self, remote_path):
167 168 self.logger('sFTP-write: removing remote file %s on host %s' % (remote_path, self.get_transport().getpeername()), loglevel=log.loglevel_DEBUG) 169 self.sftp_client.remove(remote_path)
170
171 - def _x2go_exec_command(self, cmd_line, loglevel=log.loglevel_INFO, **kwargs):
172 173 while self.locked: 174 gevent.sleep(.1) 175 176 self.locked = True 177 _retval = None 178 179 if type(cmd_line) == types.ListType: 180 cmd = " ".join(cmd_line) 181 else: 182 cmd = cmd_line 183 if self.get_transport() is not None: 184 185 timeout = gevent.Timeout(20) 186 timeout.start() 187 try: 188 self.logger("executing command on X2go server ,,%s'': %s" % (self.profile_name, _rerewrite_blanks(cmd)), loglevel) 189 _retval = self.exec_command(_rewrite_password(cmd, user=self.get_transport().get_username(), password=self._session_password), **kwargs) 190 except AttributeError: 191 self.session_died = True 192 if self.sshproxy_session: 193 self.sshproxy_session.stop_thread() 194 self.locked = False 195 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly') 196 except EOFError: 197 self.session_died = True 198 if self.sshproxy_session: 199 self.sshproxy_session.stop_thread() 200 self.locked = False 201 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly') 202 except x2go_exceptions.SSHException: 203 self.session_died = True 204 if self.sshproxy_session: 205 self.sshproxy_session.stop_thread() 206 self.locked = False 207 raise x2go_exceptions.X2goControlSessionException('the X2go control session has died unexpectedly') 208 except gevent.timeout.Timeout: 209 self.session_died = True 210 if self.sshproxy_session: 211 self.sshproxy_session.stop_thread() 212 self.locked = False 213 raise x2go_exceptions.X2goControlSessionException('the X2go control session command timed out') 214 finally: 215 self.locked = False 216 timeout.cancel() 217 218 else: 219 self.locked = False 220 raise x2go_exceptions.X2goControlSessionException('the X2go control session is not connected') 221 return _retval
222 223 @property
224 - def _x2go_remote_home(self):
225 226 if self._remote_home is None: 227 (stdin, stdout, stderr) = self._x2go_exec_command('echo $HOME') 228 self._remote_home = stdout.read().split()[0] 229 self.logger('remote user\' home directory: %s' % self._remote_home, loglevel=log.loglevel_DEBUG) 230 return self._remote_home 231 else: 232 return self._remote_home
233
234 - def _x2go_remote_group(self, group):
235 236 if not self._remote_group.has_key(group): 237 (stdin, stdout, stderr) = self._x2go_exec_command('getent group %s | cut -d":" -f4' % group) 238 self._remote_group[group] = stdout.read().split('\n')[0].split(',') 239 self.logger('remote %s group: %s' % (group, self._remote_group[group]), loglevel=log.loglevel_DEBUG) 240 return self._remote_group[group] 241 else: 242 return self._remote_group[group]
243
244 - def is_x2gouser(self, username):
245 ### 246 ### FIXME: 247 ### 248 # discussion about server-side access restriction based on posix group membership or similar currently 249 # in process (as of 20110517, mg) 250 #return username in self._x2go_remote_group('x2gousers') 251 return True
252
253 - def remote_username(self):
254 """\ 255 Returns the control session's remote username. 256 257 """ 258 if self.get_transport() is not None: 259 return self.get_transport().get_username() 260 else: 261 return None
262 263 @property
265 if self._session_auth_rsakey is None: 266 self._session_auth_rsakey = paramiko.RSAKey.generate(defaults.RSAKEY_STRENGTH) 267 return self._session_auth_rsakey
268
269 - def set_profile_name(self, profile_name):
270 self.profile_name = profile_name
271
272 - def check_host(self, hostname, port=22):
273 """\ 274 Wraps around a Paramiko/SSH host key check. 275 276 """ 277 return checkhosts.check_ssh_host_key(self, hostname, port=port)
278
279 - def connect(self, hostname, port=22, username='', password='', pkey=None, 280 use_sshproxy=False, sshproxy_host='', sshproxy_user='', sshproxy_password='', 281 sshproxy_key_filename='', sshproxy_tunnel='', 282 key_filename=None, timeout=None, allow_agent=False, look_for_keys=False, 283 session_instance=None, 284 add_to_known_hosts=False, force_password_auth=False):
285 """\ 286 Connect to an X2go server and authenticate to it. This method is directly 287 inherited from the paramiko.SSHClient module. The features of the Paramiko 288 SSH client connect method are recited here. The parameters C{add_to_known_hosts} 289 and C{force_password_auth} have been added as a parameter for X2go. 290 291 The server's host key 292 is checked against the system host keys (see C{load_system_host_keys}) 293 and any local host keys (C{load_host_keys}). If the server's hostname 294 is not found in either set of host keys, the missing host key policy 295 is used (see C{set_missing_host_key_policy}). The default policy is 296 to reject the key and raise an C{SSHException}. 297 298 Authentication is attempted in the following order of priority: 299 300 - The C{pkey} or C{key_filename} passed in (if any) 301 - Any key we can find through an SSH agent 302 - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/} 303 - Plain username/password auth, if a password was given 304 305 If a private key requires a password to unlock it, and a password is 306 passed in, that password will be used to attempt to unlock the key. 307 308 @param hostname: the server to connect to 309 @type hostname: str 310 @param port: the server port to connect to 311 @type port: int 312 @param username: the username to authenticate as (defaults to the 313 current local username) 314 @type username: str 315 @param password: a password to use for authentication or for unlocking 316 a private key 317 @type password: str 318 @param pkey: an optional private key to use for authentication 319 @type pkey: C{PKey} 320 @param key_filename: the filename, or list of filenames, of optional 321 private key(s) to try for authentication 322 @type key_filename: str or list(str) 323 @param timeout: an optional timeout (in seconds) for the TCP connect 324 @type timeout: float 325 @param allow_agent: set to False to disable connecting to the SSH agent 326 @type allow_agent: C{bool} 327 @param look_for_keys: set to False to disable searching for discoverable 328 private key files in C{~/.ssh/} 329 @type look_for_keys: C{bool} 330 @param add_to_known_hosts: non-paramiko option, if C{True} paramiko.AutoAddPolicy() 331 is used as missing-host-key-policy. If set to C{False} paramiko.RejectPolicy() 332 is used 333 @type add_to_known_hosts: C{bool} 334 @param force_password_auth: non-paramiko option, disable pub/priv key authentication 335 completely, even if the C{pkey} or the C{key_filename} parameter is given 336 @type force_password_auth: C{bool} 337 @param session_instance: an instance L{X2goSession} using this L{X2goControlSessionSTDOUT} 338 instance. 339 @type session_instance: C{instance} 340 341 @raise BadHostKeyException: if the server's host key could not be 342 verified 343 @raise AuthenticationException: if authentication failed 344 @raise SSHException: if there was any other error connecting or 345 establishing an SSH session 346 @raise socket.error: if a socket error occurred while connecting 347 348 """ 349 if use_sshproxy and sshproxy_host and sshproxy_user: 350 try: 351 self.sshproxy_session = sshproxy.X2goSSHProxy(known_hosts=self.known_hosts, 352 sshproxy_host=sshproxy_host, 353 sshproxy_user=sshproxy_user, 354 sshproxy_password=sshproxy_password, 355 sshproxy_key_filename=sshproxy_key_filename, 356 sshproxy_tunnel=sshproxy_tunnel, 357 session_instance=session_instance, 358 logger=self.logger, 359 ) 360 361 except: 362 if self.sshproxy_session: 363 self.sshproxy_session.stop_thread() 364 self.sshproxy_session = None 365 raise 366 367 if self.sshproxy_session is not None: 368 self.sshproxy_session.start() 369 370 # divert port to sshproxy_session's local forwarding port (it might have changed due to 371 # SSH connection errors 372 gevent.sleep(.1) 373 port = self.sshproxy_session.get_local_proxy_port() 374 375 if not add_to_known_hosts and session_instance: 376 self.set_missing_host_key_policy(checkhosts.X2goInteractiveAddPolicy(caller=self, session_instance=session_instance)) 377 378 if add_to_known_hosts: 379 self.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 380 381 # disable pub/priv key authentication if forced 382 if force_password_auth: 383 key_filename = None 384 pkey = None 385 386 self.logger('connecting to [%s]:%s' % (hostname, port), loglevel=log.loglevel_NOTICE) 387 388 self.load_session_host_keys() 389 390 _hostname = hostname 391 # enforce IPv4 for localhost address 392 if _hostname == 'localhost': 393 _hostname = '127.0.0.1' 394 395 if (key_filename and os.path.exists(os.path.normpath(key_filename))) or pkey: 396 try: 397 self.logger('trying SSH pub/priv key authentication with server', loglevel=log.loglevel_DEBUG) 398 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, pkey=pkey, 399 key_filename=key_filename, timeout=timeout, allow_agent=allow_agent, 400 look_for_keys=look_for_keys) 401 402 except paramiko.AuthenticationException, e: 403 self.close() 404 if password: 405 self.logger('next auth mechanism we\'ll try is keyboard-interactive authentication', loglevel=log.loglevel_DEBUG) 406 try: 407 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password, 408 timeout=timeout, allow_agent=allow_agent, 409 look_for_keys=look_for_keys) 410 except paramiko.AuthenticationException, e: 411 self.close() 412 if self.sshproxy_session: 413 self.sshproxy_session.stop_thread() 414 raise e 415 except: 416 self.close() 417 if self.sshproxy_session: 418 self.sshproxy_session.stop_thread() 419 raise 420 else: 421 self.close() 422 if self.sshproxy_session: 423 self.sshproxy_session.stop_thread() 424 raise(e) 425 426 except: 427 self.close() 428 if self.sshproxy_session: 429 self.sshproxy_session.stop_thread() 430 raise 431 432 # if there is not private key, we will use the given password, if any 433 else: 434 # create a random password if password is empty to trigger host key validity check 435 if not password: 436 password = "".join([random.choice(string.letters+string.digits) for x in range(1, 20)]) 437 self.logger('performing SSH keyboard-interactive authentication with server', loglevel=log.loglevel_DEBUG) 438 try: 439 paramiko.SSHClient.connect(self, _hostname, port=port, username=username, password=password, 440 timeout=timeout, allow_agent=allow_agent, look_for_keys=look_for_keys) 441 except paramiko.AuthenticationException, e: 442 self.close() 443 if self.sshproxy_session: 444 self.sshproxy_session.stop_thread() 445 raise e 446 except: 447 self.close() 448 if self.sshproxy_session: 449 self.sshproxy_session.stop_thread() 450 raise 451 452 self.set_missing_host_key_policy(paramiko.RejectPolicy()) 453 454 self.hostname = hostname 455 self.port = port 456 457 # if we succeed, we immediately grab us an sFTP client session 458 try: 459 self.sftp_client = self.open_sftp() 460 except: 461 raise x2go_exceptions.X2goControlSessionException('could not invoke server-side SFTP subsystem') 462 463 # preparing reverse tunnels 464 ssh_transport = self.get_transport() 465 ssh_transport.reverse_tunnels = {} 466 467 # mark Paramiko/SSH transport as X2goControlSession 468 ssh_transport._x2go_session_marker = True 469 self._session_password = password 470 471 if self.get_transport(): 472 self.session_died = False 473 return (self.get_transport() is not None)
474
475 - def dissociate(self, terminal_session):
476 """\ 477 STILL UNDOCUMENTED 478 479 """ 480 for t_name in self.associated_terminals.keys(): 481 if self.associated_terminals[t_name] == terminal_session: 482 del self.associated_terminals[t_name] 483 if self.terminated_terminals.has_key(t_name): 484 del self.terminated_terminals[t_name]
485
486 - def disconnect(self):
487 """\ 488 STILL UNDOCUMENTED 489 490 """ 491 if self.associated_terminals: 492 t_names = self.associated_terminals.keys() 493 for t_obj in self.associated_terminals.values(): 494 try: 495 if not self.session_died: 496 t_obj.suspend() 497 except x2go_exceptions.X2goTerminalSessionException: 498 pass 499 except x2go_exceptions.X2goControlSessionException: 500 pass 501 t_obj.__del__() 502 for t_name in t_names: 503 try: 504 del self.associated_terminals[t_name] 505 except KeyError: 506 pass 507 508 self._remote_home = None 509 self._remote_group = {} 510 511 self._session_auth_rsakey = None 512 513 try: 514 if self.get_transport() is not None: 515 still_active = self.get_transport().is_active() 516 self.close() 517 if self.sshproxy_session is not None: 518 self.sshproxy_session.stop_thread() 519 return still_active 520 return False 521 except AttributeError: 522 # if the Paramiko _transport object has not yet been initialized, ignore it 523 # but state that this method call did not close the SSH client, but was already closed 524 return False
525 526
527 - def is_alive(self):
528 if self._x2go_exec_command('echo', loglevel=log.loglevel_DEBUG): 529 return True 530 return False
531
532 - def start(self, **kwargs):
533 """\ 534 Start a new X2go session. 535 536 The L{X2goControlSessionSTDOUT.start()} method accepts any parameter 537 that can be passed to any of the C{X2goTerminalSession} backend class 538 constructors. 539 540 """ 541 return self.resume(**kwargs)
542
543 - def resume(self, session_name=None, session_instance=None, **kwargs):
544 """\ 545 Resume a running/suspended X2go session. 546 547 The L{X2goControlSessionSTDOUT.resume()} method accepts any parameter 548 that can be passed to any of the C{X2goTerminalSession} backend class constructors. 549 550 @return: True if the session could be successfully resumed 551 @rtype: C{bool} 552 553 """ 554 if not self.is_x2gouser(self.get_transport().get_username()): 555 raise x2go_exceptions.X2goUserException('remote user %s is not allowed to run X2go commands' % self.get_transport().get_username()) 556 557 if session_name is not None: 558 session_info = self.list_sessions()[session_name] 559 else: 560 session_info = None 561 562 _terminal = self._terminal_backend(self, 563 profile_name=self.profile_name, 564 session_info=session_info, 565 info_backend=self._info_backend, 566 list_backend=self._list_backend, 567 proxy_backend=self._proxy_backend, 568 client_rootdir=self.client_rootdir, 569 session_instance=session_instance, 570 sessions_rootdir=self.sessions_rootdir, 571 **kwargs) 572 573 _success = False 574 if session_name is not None: 575 try: 576 _success = _terminal.resume() 577 except x2go_exceptions.X2goFwTunnelException: 578 pass 579 580 else: 581 try: 582 _success = _terminal.start() 583 except x2go_exceptions.X2goFwTunnelException: 584 pass 585 586 if _success: 587 while not _terminal.ok(): 588 gevent.sleep(.2) 589 590 if _terminal.ok(): 591 self.associated_terminals[_terminal.get_session_name()] = _terminal 592 self.get_transport().reverse_tunnels[_terminal.get_session_name()] = { 593 'sshfs': (0, None), 594 'snd': (0, None), 595 } 596 597 return _terminal or None 598 599 return None
600
601 - def share_desktop(self, desktop=None, user=None, display=None, share_mode=0, **kwargs):
602 """\ 603 Share another already running desktop session. Desktop sharing can be run 604 in two different modes: view-only and full-access mode. 605 606 @param desktop: desktop ID of a sharable desktop in format <user>@<display> 607 @type desktop: C{str} 608 @param user: user name and display number can be given separately, here give the 609 name of the user who wants to share a session with you. 610 @type user: C{str} 611 @param display: user name and display number can be given separately, here give the 612 number of the display that a user allows you to be shared with. 613 @type display: C{str} 614 @param share_mode: desktop sharing mode, 0 is VIEW-ONLY, 1 is FULL-ACCESS. 615 @type share_mode: C{int} 616 617 @return: True if the session could be successfully shared. 618 @rtype: C{bool} 619 620 """ 621 if desktop: 622 user = desktop.split('@')[0] 623 display = desktop.split('@')[1] 624 if not (user and display): 625 raise x2go_exceptions.X2goDesktopSharingException('Need user name and display number of sharable desktop.') 626 627 cmd = '%sXSHAD%sXSHAD%s' % (share_mode, user, display) 628 629 kwargs['cmd'] = cmd 630 kwargs['session_type'] = 'shared' 631 632 return self.start(**kwargs)
633
634 - def list_desktops(self, raw=False, maxwait=20):
635 """\ 636 List all desktop-like sessions of current user (or of users that have 637 granted desktop sharing) on the connected server. 638 639 @param raw: if C{True}, the raw output of the server-side X2go command 640 C{x2godesktopsharing} is returned. 641 @type raw: C{bool} 642 643 @return: a list of X2go desktops available for sharing 644 @rtype: C{list} 645 646 """ 647 if raw: 648 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops") 649 return stdout.read(), stderr.read() 650 651 else: 652 653 # this _success loop will catch errors in case the x2golistsessions output is corrupt 654 # this should not be needed and is a workaround for the current X2go server implementation 655 656 timeout = gevent.Timeout(maxwait) 657 timeout.start() 658 try: 659 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistdesktops") 660 _stdout_read = stdout.read() 661 _listdesktops = _stdout_read.split('\n') 662 except gevent.timeout.Timeout: 663 # if we do not get a reply here after <maxwait> seconds we will raise a time out, we have to 664 # make sure that we catch this at places where we want to ignore timeouts (e.g. in the 665 # desktop list cache) 666 raise x2go_exceptions.X2goTimeOutException('x2golistdesktop command timed out') 667 finally: 668 timeout.cancel() 669 670 return _listdesktops
671
672 - def list_sessions(self, raw=False):
673 """\ 674 List all sessions of current user on the connected server. 675 676 @param raw: if C{True}, the raw output of the server-side X2go command 677 C{x2golistsessions} is returned. 678 @type raw: C{bool} 679 680 @return: normally an instance of a C{X2goServerSessionList} backend Bis returned. However, 681 if the raw argument is set, the plain text output of the x2golistsessions 682 command is returned 683 @rtype: C{X2goServerSessionList} instance or str 684 685 """ 686 if raw: 687 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions") 688 return stdout.read(), stderr.read() 689 690 else: 691 692 # this _success loop will catch errors in case the x2golistsessions output is corrupt 693 # this should not be needed and is a workaround for the current X2go server implementation 694 _listsessions = {} 695 _success = False 696 _count = 0 697 _maxwait = 20 698 699 # we will try this 20 times before giving up... we might simply catch the x2golistsessions 700 # output in the middle of creating a session in the database... 701 while not _success and _count < _maxwait: 702 _count += 1 703 try: 704 (stdin, stdout, stderr) = self._x2go_exec_command("export HOSTNAME && x2golistsessions") 705 _stdout_read = stdout.read() 706 _listsessions = self._list_backend(_stdout_read, info_backend=self._info_backend).sessions 707 _success = True 708 except KeyError: 709 gevent.sleep(1) 710 except IndexError: 711 gevent.sleep(1) 712 except ValueError: 713 gevent.sleep(1) 714 715 if _count >= _maxwait: 716 raise x2go_exceptions.X2goControlSessionException('x2golistsessions command failed after we have tried 20 times') 717 718 # update internal variables when list_sessions() is called 719 for _session_name, _session_info in self.associated_terminals.items(): 720 if _session_name not in _listsessions.keys(): 721 del self.associated_terminals[_session_name] 722 self.terminated_terminals.append(_session_name) 723 elif _session_info.is_suspended(): 724 del self.associated_terminals[_session_name] 725 726 return _listsessions
727
728 - def clean_sessions(self):
729 """\ 730 Find X2go terminals that have previously been started by the 731 connected user on the remote X2go server and terminate them. 732 733 """ 734 session_list = self.list_sessions() 735 for session_name in session_list.keys(): 736 self.terminate(session_name=session_name)
737
738 - def is_connected(self):
739 """\ 740 Returns C{True} if this X2go session is connected to the remote server (that 741 is if it has a valid Paramiko Transport object). 742 743 @return: X2go session connected? 744 @rtype: C{bool} 745 746 """ 747 return self.get_transport() is not None and self.get_transport().is_authenticated()
748
749 - def is_running(self, session_name):
750 """\ 751 Returns C{True} if the given X2go session is in running state, 752 C{False} else. 753 754 @param session_name: X2go name of the session to be queried 755 @type session_name: str 756 757 @return: X2go session running? 758 @rtype: C{bool} 759 760 """ 761 session_infos = self.list_sessions() 762 if session_name in session_infos.keys(): 763 return session_infos[session_name].is_running() 764 return None
765
766 - def is_suspended(self, session_name):
767 """\ 768 Returns C{True} if the given X2go session is in suspended state, 769 C{False} else. 770 771 @return: X2go session suspended? 772 @rtype: C{bool} 773 774 """ 775 session_infos = self.list_sessions() 776 if session_name in session_infos.keys(): 777 return session_infos[session_name].is_suspended() 778 return None
779
780 - def has_terminated(self, session_name):
781 """\ 782 Returns C{True} if this X2go session is not in the session list on the 783 connected server, C{False} else. 784 785 Of course, if this command is called before session startup, it will also 786 return C{True}. 787 788 @return: X2go session has terminate? 789 @rtype: C{bool} 790 791 """ 792 session_infos = self.list_sessions() 793 794 if session_name not in session_infos.keys(): 795 if session_name in self.terminated_terminals: 796 return True 797 else: 798 # do a post-mortem tidy up 799 if session_name in self.associated_terminals.keys(): 800 self.terminate(session_name) 801 return True 802 803 return False
804
805 - def suspend(self, session_name):
806 """\ 807 Suspend either this or another available X2go session on the connected 808 server. 809 810 If L{session_name} is given, L{X2goControlSessionSTDOUT.suspend()} tries to suspend the 811 corresponding session. 812 813 @param session_name: X2go name of the session to be suspended 814 @type session_name: str 815 816 @return: True if the session could be successfully suspended 817 @rtype: C{bool} 818 819 """ 820 _ret = False 821 _session_names = [ t.get_session_name() for t in self.associated_terminals.values() ] 822 if session_name in _session_names: 823 824 self.logger('suspending associated terminal session: %s' % session_name, loglevel=log.loglevel_DEBUG) 825 (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG) 826 dummy_stdout = stdout.read() 827 dummy_stderr = stderr.read() 828 if self.associated_terminals.has_key(session_name): 829 if self.associated_terminals[session_name] is not None: 830 self.associated_terminals[session_name].__del__() 831 try: del self.associated_terminals[session_name] 832 except KeyError: pass 833 _ret = True 834 835 else: 836 837 self.logger('suspending non-associated terminal session: %s' % session_name, loglevel=log.loglevel_DEBUG) 838 (stdin, stdout, stderr) = self._x2go_exec_command("x2gosuspend-session %s" % session_name, loglevel=log.loglevel_DEBUG) 839 dummy_stdout = stdout.read() 840 dummy_stderr = stderr.read() 841 _ret = True 842 843 return _ret
844
845 - def terminate(self, session_name):
846 """\ 847 Terminate either this or another available X2go session on the connected 848 server. 849 850 If L{session_name} is given, L{X2goControlSessionSTDOUT.terminate()} tries to terminate the 851 corresponding session. 852 853 @param session_name: X2go name of the session to be terminated 854 @type session_name: str 855 856 @return: True if the session could be successfully terminate 857 @rtype: C{bool} 858 859 """ 860 861 _ret = False 862 _session_names = [ t.get_session_name() for t in self.associated_terminals.values() ] 863 if session_name in _session_names: 864 865 self.logger('terminating associated session: %s' % session_name, loglevel=log.loglevel_DEBUG) 866 (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG) 867 dummy_stdout = stdout.read() 868 dummy_stderr = stderr.read() 869 if self.associated_terminals.has_key(session_name): 870 if self.associated_terminals[session_name] is not None: 871 self.associated_terminals[session_name].__del__() 872 try: del self.associated_terminals[session_name] 873 except KeyError: pass 874 self.terminated_terminals.append(session_name) 875 _ret = True 876 877 else: 878 879 self.logger('terminating non-associated session: %s' % session_name, loglevel=log.loglevel_DEBUG) 880 (stdin, stdout, stderr) = self._x2go_exec_command("x2goterminate-session %s" % session_name, loglevel=log.loglevel_DEBUG) 881 dummy_stdout = stdout.read() 882 dummy_stderr = stderr.read() 883 _ret = True 884 885 return _ret
886