2010-12-22 00:34:36 +03:00
# -*- coding: utf-8 -*-
2009-10-30 20:25:27 +03:00
#
2013-10-28 00:59:46 +04:00
# Copyright (C) 2006-2008, 2013 Red Hat, Inc.
2009-10-30 20:25:27 +03:00
# Copyright (C) 2006 Daniel P. Berrange <berrange@redhat.com>
2010-12-22 00:34:36 +03:00
# Copyright (C) 2010 Marc-André Lureau <marcandre.lureau@redhat.com>
2009-10-30 20:25:27 +03:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301 USA.
#
2012-05-14 17:24:56 +04:00
from gi . repository import GObject
from gi . repository import Gtk
from gi . repository import Gdk
from gi . repository import GtkVnc
from gi . repository import SpiceClientGtk
from gi . repository import SpiceClientGLib
2010-02-11 17:32:05 +03:00
2009-10-30 20:25:27 +03:00
import libvirt
2011-01-07 22:59:31 +03:00
2013-09-07 03:36:09 +04:00
import logging
2009-10-30 20:25:27 +03:00
import os
2013-09-07 03:36:09 +04:00
import Queue
2010-02-11 17:32:05 +03:00
import signal
2009-10-30 20:25:27 +03:00
import socket
2013-09-07 03:36:09 +04:00
import threading
2009-10-30 20:25:27 +03:00
2014-09-13 00:10:45 +04:00
from . autodrawer import AutoDrawer
from . baseclass import vmmGObjectUI , vmmGObject
from . serialcon import vmmSerialConsole
from . details import DETAILS_PAGE_CONSOLE
2009-10-30 20:25:27 +03:00
# Console pages
2013-09-02 01:40:38 +04:00
( CONSOLE_PAGE_UNAVAILABLE ,
CONSOLE_PAGE_AUTHENTICATE ,
CONSOLE_PAGE_VIEWER ,
CONSOLE_PAGE_OFFSET ) = range ( 4 )
2009-10-30 20:25:27 +03:00
2013-01-16 06:22:32 +04:00
2009-10-30 21:36:17 +03:00
def has_property ( obj , setting ) :
try :
obj . get_property ( setting )
except TypeError :
return False
return True
2013-01-16 06:22:32 +04:00
2014-01-27 02:09:07 +04:00
2012-03-13 23:27:23 +04:00
class ConnectionInfo ( object ) :
"""
Holds all the bits needed to make a connection to a graphical console
"""
def __init__ ( self , conn , gdev ) :
self . gtype = gdev . type
self . gport = gdev . port and str ( gdev . port ) or None
self . gsocket = gdev . socket
self . gaddr = gdev . listen or " 127.0.0.1 "
2013-09-02 00:08:14 +04:00
self . gtlsport = gdev . tlsPort or None
2012-03-13 23:27:23 +04:00
self . transport , self . connuser = conn . get_transport ( )
2014-01-29 20:02:54 +04:00
( self . _connhost ,
self . _connport ) = conn . get_backend ( ) . get_uri_host_port ( )
if self . _connhost == " localhost " :
self . _connhost = " 127.0.0.1 "
def _is_listen_localhost ( self , host = None ) :
return ( host or self . gaddr ) in [ " 127.0.0.1 " , " ::1 " ]
def _is_listen_any ( self ) :
return self . gaddr in [ " 0.0.0.0 " , " :: " ]
2012-03-13 23:27:23 +04:00
def need_tunnel ( self ) :
2014-01-29 20:02:54 +04:00
if not self . _is_listen_localhost ( ) :
2012-03-13 23:27:23 +04:00
return False
return self . transport in [ " ssh " , " ext " ]
2013-06-30 19:44:58 +04:00
def is_bad_localhost ( self ) :
2014-01-29 20:02:54 +04:00
"""
Return True if the guest is listening on localhost , but the libvirt
URI doesn ' t give us any way to tunnel the connection
"""
2013-09-02 00:08:14 +04:00
host = self . get_conn_host ( ) [ 0 ]
2013-06-30 19:44:58 +04:00
if self . need_tunnel ( ) :
return False
2014-01-29 20:02:54 +04:00
return self . transport and self . _is_listen_localhost ( host )
2013-06-30 19:44:58 +04:00
2012-03-13 23:27:23 +04:00
def get_conn_host ( self ) :
host = self . _connhost
port = self . _connport
2013-09-02 00:08:14 +04:00
tlsport = None
2012-03-13 23:27:23 +04:00
if not self . need_tunnel ( ) :
port = self . gport
2013-09-02 00:08:14 +04:00
tlsport = self . gtlsport
2014-01-29 20:02:54 +04:00
if not self . _is_listen_any ( ) :
2012-03-13 23:27:23 +04:00
host = self . gaddr
2013-09-02 00:08:14 +04:00
return host , port , tlsport
2012-03-13 23:27:23 +04:00
def logstring ( self ) :
return ( " proto= %s trans= %s connhost= %s connuser= %s "
2013-09-02 00:08:14 +04:00
" connport= %s gaddr= %s gport= %s gtlsport= %s gsocket= %s " %
2012-03-13 23:27:23 +04:00
( self . gtype , self . transport , self . _connhost , self . connuser ,
2013-09-02 00:08:14 +04:00
self . _connport , self . gaddr , self . gport , self . gtlsport ,
self . gsocket ) )
2012-03-13 23:27:23 +04:00
def console_active ( self ) :
if self . gsocket :
return True
2013-10-06 23:30:01 +04:00
if ( self . gport in [ None , - 1 ] and self . gtlsport in [ None , - 1 ] ) :
2012-03-13 23:27:23 +04:00
return False
2013-10-06 23:30:01 +04:00
return True
2010-12-22 00:34:36 +03:00
2013-01-16 06:22:32 +04:00
2013-09-07 03:36:09 +04:00
class _TunnelScheduler ( object ) :
"""
If the user is using Spice + SSH URI + no SSH keys , we need to
serialize connection opening otherwise ssh - askpass gets all angry .
This handles the locking and scheduling .
It ' s only instantiated once for the whole app, because we serialize
independent of connection , vm , etc .
"""
def __init__ ( self ) :
self . _thread = threading . Thread ( name = " Tunnel thread " ,
target = self . _handle_queue ,
args = ( ) )
self . _thread . daemon = True
self . _queue = Queue . Queue ( )
self . _lock = threading . Lock ( )
def _handle_queue ( self ) :
while True :
cb , args , = self . _queue . get ( )
self . lock ( )
vmmGObject . idle_add ( cb , * args )
def schedule ( self , cb , * args ) :
if not self . _thread . is_alive ( ) :
self . _thread . start ( )
self . _queue . put ( ( cb , args ) )
def lock ( self ) :
self . _lock . acquire ( )
def unlock ( self ) :
self . _lock . release ( )
_tunnel_sched = _TunnelScheduler ( )
class _Tunnel ( object ) :
2010-12-22 00:34:36 +03:00
def __init__ ( self ) :
self . outfd = None
self . errfd = None
self . pid = None
2013-09-07 03:36:09 +04:00
self . _outfds = None
self . _errfds = None
self . closed = False
2010-12-22 00:34:36 +03:00
2012-03-13 23:27:23 +04:00
def open ( self , ginfo ) :
2013-09-07 03:36:09 +04:00
self . _outfds = socket . socketpair ( )
self . _errfds = socket . socketpair ( )
return self . _outfds [ 0 ] . fileno ( ) , self . _launch_tunnel , ginfo
def close ( self ) :
if self . closed :
return
self . closed = True
logging . debug ( " Close tunnel PID= %s OUTFD= %s ERRFD= %s " ,
self . pid ,
self . outfd and self . outfd . fileno ( ) or self . _outfds ,
self . errfd and self . errfd . fileno ( ) or self . _errfds )
if self . outfd :
self . outfd . close ( )
elif self . _outfds :
self . _outfds [ 0 ] . close ( )
self . _outfds [ 1 ] . close ( )
self . outfd = None
self . _outfds = None
if self . errfd :
self . errfd . close ( )
elif self . _errfds :
self . _errfds [ 0 ] . close ( )
self . _errfds [ 1 ] . close ( )
self . errfd = None
self . _errfds = None
if self . pid :
os . kill ( self . pid , signal . SIGKILL )
os . waitpid ( self . pid , 0 )
self . pid = None
def get_err_output ( self ) :
errout = " "
while True :
try :
new = self . errfd . recv ( 1024 )
except :
break
if not new :
break
errout + = new
return errout
def _launch_tunnel ( self , ginfo ) :
if self . closed :
2010-12-22 00:34:36 +03:00
return - 1
2013-09-02 00:08:14 +04:00
host , port , ignore = ginfo . get_conn_host ( )
2012-03-13 23:27:23 +04:00
2010-12-22 00:34:36 +03:00
# Build SSH cmd
argv = [ " ssh " , " ssh " ]
2012-03-13 23:27:23 +04:00
if port :
argv + = [ " -p " , str ( port ) ]
2010-12-22 00:34:36 +03:00
2012-03-13 23:27:23 +04:00
if ginfo . connuser :
argv + = [ ' -l ' , ginfo . connuser ]
2010-12-22 00:34:36 +03:00
2012-03-13 23:27:23 +04:00
argv + = [ host ]
2010-12-22 00:34:36 +03:00
# Build 'nc' command run on the remote host
#
# This ugly thing is a shell script to detect availability of
# the -q option for 'nc': debian and suse based distros need this
# flag to ensure the remote nc will exit on EOF, so it will go away
# when we close the VNC tunnel. If it doesn't go away, subsequent
# VNC connection attempts will hang.
#
# Fedora's 'nc' doesn't have this option, and apparently defaults
# to the desired behavior.
#
2012-03-13 23:27:23 +04:00
if ginfo . gsocket :
nc_params = " -U %s " % ginfo . gsocket
2011-01-14 18:20:20 +03:00
else :
2012-03-13 23:27:23 +04:00
nc_params = " %s %s " % ( ginfo . gaddr , ginfo . gport )
2011-01-13 19:26:38 +03:00
2010-12-22 00:34:36 +03:00
nc_cmd = (
2011-08-09 00:42:18 +04:00
""" nc -q 2>&1 | grep " requires an argument " >/dev/null; """
2010-12-22 00:34:36 +03:00
""" if [ $? -eq 0 ] ; then """
""" CMD= " nc -q 0 %(nc_params)s " ; """
""" else """
""" CMD= " nc %(nc_params)s " ; """
""" fi; """
""" eval " $CMD " ; """ %
{ ' nc_params ' : nc_params } )
argv . append ( " sh -c " )
argv . append ( " ' %s ' " % nc_cmd )
argv_str = reduce ( lambda x , y : x + " " + y , argv [ 1 : ] )
2012-01-17 07:04:40 +04:00
logging . debug ( " Creating SSH tunnel: %s " , argv_str )
2010-12-22 00:34:36 +03:00
pid = os . fork ( )
if pid == 0 :
2013-09-07 03:36:09 +04:00
self . _outfds [ 0 ] . close ( )
self . _errfds [ 0 ] . close ( )
2010-12-22 00:34:36 +03:00
os . close ( 0 )
os . close ( 1 )
os . close ( 2 )
2013-09-07 03:36:09 +04:00
os . dup ( self . _outfds [ 1 ] . fileno ( ) )
os . dup ( self . _outfds [ 1 ] . fileno ( ) )
os . dup ( self . _errfds [ 1 ] . fileno ( ) )
2010-12-22 00:34:36 +03:00
os . execlp ( * argv )
2014-04-03 02:39:43 +04:00
os . _exit ( 1 ) # pylint: disable=protected-access
2010-12-22 00:34:36 +03:00
else :
2013-09-07 03:36:09 +04:00
self . _outfds [ 1 ] . close ( )
self . _errfds [ 1 ] . close ( )
2010-12-22 00:34:36 +03:00
2013-09-07 03:36:09 +04:00
logging . debug ( " Open tunnel PID= %d OUTFD= %d ERRFD= %d " ,
pid , self . _outfds [ 0 ] . fileno ( ) , self . _errfds [ 0 ] . fileno ( ) )
self . _errfds [ 0 ] . setblocking ( 0 )
2010-12-22 00:34:36 +03:00
2013-09-07 03:36:09 +04:00
self . outfd = self . _outfds [ 0 ]
self . errfd = self . _errfds [ 0 ]
self . _outfds = None
self . _errfds = None
2010-12-22 00:34:36 +03:00
self . pid = pid
2013-01-16 06:22:32 +04:00
2010-12-22 00:34:36 +03:00
class Tunnels ( object ) :
2012-03-13 23:27:23 +04:00
def __init__ ( self , ginfo ) :
self . ginfo = ginfo
2010-12-22 00:34:36 +03:00
self . _tunnels = [ ]
def open_new ( self ) :
2013-09-07 03:36:09 +04:00
t = _Tunnel ( )
fd , cb , args = t . open ( self . ginfo )
2010-12-22 00:34:36 +03:00
self . _tunnels . append ( t )
2013-09-07 03:36:09 +04:00
_tunnel_sched . schedule ( cb , args )
2010-12-22 00:34:36 +03:00
return fd
def close_all ( self ) :
for l in self . _tunnels :
l . close ( )
def get_err_output ( self ) :
errout = " "
for l in self . _tunnels :
errout + = l . get_err_output ( )
return errout
2013-09-07 03:36:09 +04:00
lock = _tunnel_sched . lock
unlock = _tunnel_sched . unlock
2010-12-22 00:34:36 +03:00
2011-05-19 01:36:08 +04:00
class Viewer ( vmmGObject ) :
def __init__ ( self , console ) :
vmmGObject . __init__ ( self )
2010-12-22 04:13:11 +03:00
self . console = console
2014-09-21 21:25:43 +04:00
self . _display = None
2011-05-19 01:36:08 +04:00
2011-06-01 18:38:37 +04:00
def close ( self ) :
raise NotImplementedError ( )
2011-07-24 05:16:54 +04:00
def _cleanup ( self ) :
2011-06-01 18:28:59 +04:00
self . close ( )
2011-05-19 01:36:08 +04:00
2014-09-21 21:25:43 +04:00
if self . _display :
self . _display . destroy ( )
self . _display = None
2011-06-01 18:28:59 +04:00
self . console = None
2010-12-22 04:13:11 +03:00
2014-09-21 21:25:43 +04:00
def grab_focus ( self ) :
if self . _display :
self . _display . grab_focus ( )
def has_focus ( self ) :
return self . _display and self . _display . get_property ( " has-focus " )
def set_size_request ( self , * args , * * kwargs ) :
return self . _display . set_size_request ( * args , * * kwargs )
def size_allocate ( self , * args , * * kwargs ) :
return self . _display . size_allocate ( * args , * * kwargs )
def get_visible ( self ) :
return self . _display and self . _display . get_visible ( )
2010-12-22 04:13:11 +03:00
def get_pixbuf ( self ) :
2014-09-21 21:25:43 +04:00
return self . _display . get_pixbuf ( )
2010-12-22 04:13:11 +03:00
2013-09-07 03:36:09 +04:00
def open_ginfo ( self , ginfo ) :
if ginfo . need_tunnel ( ) :
self . open_fd ( self . console . tunnels . open_new ( ) )
else :
self . open_host ( ginfo )
2010-12-22 04:13:11 +03:00
def get_grab_keys ( self ) :
2012-05-14 17:24:56 +04:00
raise NotImplementedError ( )
2010-12-22 04:13:11 +03:00
2012-05-14 17:24:56 +04:00
def set_grab_keys ( self ) :
raise NotImplementedError ( )
2010-12-22 04:13:11 +03:00
def send_keys ( self , keys ) :
2013-01-16 06:22:32 +04:00
raise NotImplementedError ( )
2010-12-22 04:13:11 +03:00
2014-09-21 21:05:44 +04:00
def set_keyboard_grab_default ( self ) :
2014-03-10 21:07:15 +04:00
raise NotImplementedError ( )
2013-09-07 03:36:09 +04:00
def open_host ( self , ginfo ) :
2012-03-13 23:27:23 +04:00
raise NotImplementedError ( )
2013-09-07 03:36:09 +04:00
def open_fd ( self , fd ) :
2011-01-13 19:26:38 +03:00
raise NotImplementedError ( )
2011-04-26 18:51:19 +04:00
def get_desktop_resolution ( self ) :
raise NotImplementedError ( )
2013-07-01 22:33:59 +04:00
def has_usb_redirection ( self ) :
return False
2014-01-31 18:13:53 +04:00
def has_agent ( self ) :
return False
def set_resizeguest ( self , val ) :
ignore = val
2014-02-05 16:09:53 +04:00
def get_resizeguest ( self ) :
return False
2013-07-01 22:33:59 +04:00
2013-01-16 06:22:32 +04:00
2010-12-22 04:13:11 +03:00
class VNCViewer ( Viewer ) :
2014-01-31 18:13:53 +04:00
viewer_type = " vnc "
2011-05-19 01:36:08 +04:00
def __init__ ( self , console ) :
Viewer . __init__ ( self , console )
2014-09-21 21:25:43 +04:00
self . _display = GtkVnc . Display . new ( )
2011-01-13 19:26:38 +03:00
self . sockfd = None
2011-05-19 01:36:08 +04:00
2011-04-26 18:51:19 +04:00
# Last noticed desktop resolution
self . desktop_resolution = None
2010-12-22 04:13:11 +03:00
2013-09-07 03:36:09 +04:00
self . _tunnel_unlocked = False
2014-09-21 21:25:43 +04:00
self . _init_widget ( )
def _init_widget ( self ) :
self . console . widget ( " console-gfx-viewport " ) . add ( self . _display )
2012-01-30 06:51:20 +04:00
self . set_grab_keys ( )
2014-09-21 21:05:44 +04:00
self . set_keyboard_grab_default ( )
2010-12-22 04:13:11 +03:00
2014-09-21 21:25:43 +04:00
self . _display . realize ( )
2010-12-22 04:13:11 +03:00
# Make sure viewer doesn't force resize itself
2014-09-21 21:25:43 +04:00
self . _display . set_force_size ( False )
2010-12-22 04:13:11 +03:00
2014-01-31 04:15:02 +04:00
self . console . sync_scaling_with_display ( )
2014-01-31 18:13:53 +04:00
self . console . refresh_resizeguest_from_settings ( )
2010-12-22 04:13:11 +03:00
2014-09-21 21:25:43 +04:00
self . _display . set_pointer_grab ( True )
2010-12-22 04:13:11 +03:00
2014-09-21 21:25:43 +04:00
self . _display . connect ( " size-allocate " ,
2014-01-30 01:58:34 +04:00
self . console . viewer_allocate_cb )
2014-09-21 21:25:43 +04:00
self . _display . connect ( " vnc-pointer-grab " , self . console . pointer_grabbed )
self . _display . connect ( " vnc-pointer-ungrab " ,
2013-09-07 03:36:09 +04:00
self . console . pointer_ungrabbed )
2014-09-21 21:25:43 +04:00
self . _display . connect ( " vnc-auth-credential " , self . _auth_credential )
self . _display . connect ( " vnc-initialized " , self . _connected_cb )
self . _display . connect ( " vnc-disconnected " , self . _disconnected_cb )
self . _display . connect ( " vnc-desktop-resize " , self . _desktop_resize )
self . _display . connect ( " focus-in-event " ,
2013-09-07 03:36:09 +04:00
self . console . viewer_focus_changed )
2014-09-21 21:25:43 +04:00
self . _display . connect ( " focus-out-event " ,
2013-09-07 03:36:09 +04:00
self . console . viewer_focus_changed )
2010-12-22 04:13:11 +03:00
2014-09-21 21:25:43 +04:00
self . _display . show ( )
2010-12-22 04:13:11 +03:00
2013-09-07 03:36:09 +04:00
def _unlock_tunnel ( self ) :
if self . console . tunnels and not self . _tunnel_unlocked :
self . console . tunnels . unlock ( )
self . _tunnel_unlocked = True
def _connected_cb ( self , ignore ) :
self . _unlock_tunnel ( )
self . console . connected ( )
def _disconnected_cb ( self , ignore ) :
self . _unlock_tunnel ( )
self . console . disconnected ( )
2012-05-14 17:24:56 +04:00
def get_grab_keys ( self ) :
2014-09-21 21:25:43 +04:00
return self . _display . get_grab_keys ( ) . as_string ( )
2012-05-14 17:24:56 +04:00
def set_grab_keys ( self ) :
try :
keys = self . config . get_keys_combination ( )
if not keys :
return
try :
2013-04-12 00:32:00 +04:00
keys = [ int ( k ) for k in keys . split ( ' , ' ) ]
2012-05-14 17:24:56 +04:00
except :
logging . debug ( " Error in grab_keys configuration in GConf " ,
exc_info = True )
return
seq = GtkVnc . GrabSequence . new ( keys )
2014-09-21 21:25:43 +04:00
self . _display . set_grab_keys ( seq )
2012-05-14 17:24:56 +04:00
except Exception , e :
logging . debug ( " Error when getting the grab keys combination: %s " ,
str ( e ) )
2013-01-16 06:22:32 +04:00
def send_keys ( self , keys ) :
2014-09-21 21:25:43 +04:00
return self . _display . send_keys ( [ Gdk . keyval_from_name ( k ) for k in keys ] )
2013-01-16 06:22:32 +04:00
2014-09-21 21:05:44 +04:00
def set_keyboard_grab_default ( self ) :
2014-09-21 21:25:43 +04:00
self . _display . set_keyboard_grab ( self . config . get_keyboard_grab_default ( ) )
2014-03-10 21:07:15 +04:00
2011-04-26 18:51:19 +04:00
def _desktop_resize ( self , src_ignore , w , h ) :
self . desktop_resolution = ( w , h )
2013-04-21 20:10:14 +04:00
self . console . widget ( " console-gfx-scroll " ) . queue_resize ( )
2011-04-26 18:51:19 +04:00
def get_desktop_resolution ( self ) :
return self . desktop_resolution
2010-12-22 04:13:11 +03:00
def _auth_credential ( self , src_ignore , credList ) :
2012-11-09 19:08:15 +04:00
values = [ ]
for idx in range ( int ( credList . n_values ) ) :
values . append ( credList . get_nth ( idx ) )
for cred in values :
2012-05-14 17:24:56 +04:00
if cred in [ GtkVnc . DisplayCredential . PASSWORD ,
GtkVnc . DisplayCredential . USERNAME ,
GtkVnc . DisplayCredential . CLIENTNAME ] :
2011-04-15 22:12:50 +04:00
continue
self . console . err . show_err (
summary = _ ( " Unable to provide requested credentials to "
" the VNC server " ) ,
details = ( _ ( " The credential type %s is not supported " ) %
( str ( cred ) ) ) ,
2013-09-07 04:16:37 +04:00
title = _ ( " Unable to authenticate " ) )
2011-04-15 22:12:50 +04:00
# schedule_retry will error out
self . console . viewerRetriesScheduled = 10
self . close ( )
self . console . activate_unavailable_page (
_ ( " Unsupported console authentication type " ) )
return
2010-12-22 04:13:11 +03:00
withUsername = False
withPassword = False
2012-11-09 19:08:15 +04:00
for cred in values :
2012-01-17 07:04:40 +04:00
logging . debug ( " Got credential request %s " , cred )
2012-05-14 17:24:56 +04:00
if cred == GtkVnc . DisplayCredential . PASSWORD :
2010-12-22 04:13:11 +03:00
withPassword = True
2012-05-14 17:24:56 +04:00
elif cred == GtkVnc . DisplayCredential . USERNAME :
2010-12-22 04:13:11 +03:00
withUsername = True
2012-05-14 17:24:56 +04:00
elif cred == GtkVnc . DisplayCredential . CLIENTNAME :
2014-09-21 21:25:43 +04:00
self . _display . set_credential ( cred , " libvirt-vnc " )
2010-12-22 04:13:11 +03:00
if withUsername or withPassword :
self . console . activate_auth_page ( withPassword , withUsername )
def get_scaling ( self ) :
2014-09-21 21:25:43 +04:00
return self . _display . get_scaling ( )
2010-12-22 04:13:11 +03:00
def set_scaling ( self , scaling ) :
2014-09-21 21:25:43 +04:00
return self . _display . set_scaling ( scaling )
2010-12-22 04:13:11 +03:00
def close ( self ) :
2014-09-21 21:25:43 +04:00
self . _display . close ( )
2011-01-13 19:26:38 +03:00
if not self . sockfd :
return
self . sockfd . close ( )
self . sockfd = None
2010-12-22 04:13:11 +03:00
def is_open ( self ) :
2014-09-21 21:25:43 +04:00
return self . _display . is_open ( )
2010-12-22 04:13:11 +03:00
2013-09-07 03:36:09 +04:00
def open_host ( self , ginfo ) :
2013-09-02 00:08:14 +04:00
host , port , ignore = ginfo . get_conn_host ( )
2011-01-14 18:20:20 +03:00
2012-03-13 23:27:23 +04:00
if not ginfo . gsocket :
logging . debug ( " VNC connection to %s : %s " , host , port )
2014-09-21 21:25:43 +04:00
self . _display . open_host ( host , port )
2011-01-14 18:20:20 +03:00
return
2012-03-13 23:27:23 +04:00
logging . debug ( " VNC connecting to socket= %s " , ginfo . gsocket )
2011-01-14 18:20:20 +03:00
try :
sock = socket . socket ( socket . AF_UNIX , socket . SOCK_STREAM )
2012-03-13 23:27:23 +04:00
sock . connect ( ginfo . gsocket )
2011-01-14 18:20:20 +03:00
self . sockfd = sock
except Exception , e :
raise RuntimeError ( _ ( " Error opening socket path ' %s ' : %s " ) %
2012-03-13 23:27:23 +04:00
( ginfo . gsocket , e ) )
2011-01-14 18:20:20 +03:00
fd = self . sockfd . fileno ( )
if fd < 0 :
raise RuntimeError ( ( _ ( " Error opening socket path ' %s ' " ) %
2012-03-13 23:27:23 +04:00
ginfo . gsocket ) + " fd= %s " % fd )
2011-01-14 18:20:20 +03:00
self . open_fd ( fd )
2010-12-22 04:13:11 +03:00
2013-09-07 03:36:09 +04:00
def open_fd ( self , fd ) :
2014-09-21 21:25:43 +04:00
self . _display . open_fd ( fd )
2010-12-22 04:13:11 +03:00
def set_credential_username ( self , cred ) :
2014-09-21 21:25:43 +04:00
self . _display . set_credential ( GtkVnc . DisplayCredential . USERNAME , cred )
2010-12-22 04:13:11 +03:00
def set_credential_password ( self , cred ) :
2014-09-21 21:25:43 +04:00
self . _display . set_credential ( GtkVnc . DisplayCredential . PASSWORD , cred )
2010-12-22 04:13:11 +03:00
2010-12-22 04:13:25 +03:00
class SpiceViewer ( Viewer ) :
2014-01-31 18:13:53 +04:00
viewer_type = " spice "
2011-05-19 01:36:08 +04:00
def __init__ ( self , console ) :
Viewer . __init__ ( self , console )
2010-12-22 04:13:25 +03:00
self . spice_session = None
2014-09-21 21:25:43 +04:00
self . _display = None
2010-12-22 04:13:25 +03:00
self . audio = None
2014-01-31 18:13:53 +04:00
self . main_channel = None
2014-07-01 17:01:58 +04:00
self . _main_channel_hids = [ ]
2014-09-21 21:25:43 +04:00
self . _display_channel = None
2013-07-01 22:33:59 +04:00
self . usbdev_manager = None
2010-12-22 04:13:25 +03:00
def _init_widget ( self ) :
2011-01-07 23:29:33 +03:00
self . set_grab_keys ( )
2014-09-21 21:05:44 +04:00
self . set_keyboard_grab_default ( )
2014-01-31 04:15:02 +04:00
self . console . sync_scaling_with_display ( )
2014-01-31 18:13:53 +04:00
self . console . refresh_resizeguest_from_settings ( )
2010-12-22 04:13:25 +03:00
2014-09-21 21:25:43 +04:00
self . _display . realize ( )
2014-01-30 01:58:34 +04:00
2014-09-21 21:25:43 +04:00
self . _display . connect ( " size-allocate " ,
2014-01-30 01:58:34 +04:00
self . console . viewer_allocate_cb )
2014-09-21 21:25:43 +04:00
self . _display . connect ( " mouse-grab " ,
2013-09-07 03:36:09 +04:00
lambda src , g : g and self . console . pointer_grabbed ( src ) )
2014-09-21 21:25:43 +04:00
self . _display . connect ( " mouse-grab " ,
2013-09-07 03:36:09 +04:00
lambda src , g : g or self . console . pointer_ungrabbed ( src ) )
2011-09-27 00:32:37 +04:00
2014-09-21 21:25:43 +04:00
self . _display . connect ( " focus-in-event " ,
2011-09-27 00:32:37 +04:00
self . console . viewer_focus_changed )
2014-09-21 21:25:43 +04:00
self . _display . connect ( " focus-out-event " ,
2011-09-27 00:32:37 +04:00
self . console . viewer_focus_changed )
2014-09-21 21:25:43 +04:00
self . _display . show ( )
2010-12-22 04:13:25 +03:00
2012-05-14 17:24:56 +04:00
def get_grab_keys ( self ) :
2014-09-21 21:25:43 +04:00
return self . _display . get_grab_keys ( ) . as_string ( )
2012-05-14 17:24:56 +04:00
def set_grab_keys ( self ) :
try :
keys = self . config . get_keys_combination ( )
if not keys :
return
try :
2013-04-12 00:32:00 +04:00
keys = [ int ( k ) for k in keys . split ( ' , ' ) ]
2012-05-14 17:24:56 +04:00
except :
logging . debug ( " Error in grab_keys configuration in GConf " ,
exc_info = True )
return
seq = SpiceClientGtk . GrabSequence . new ( keys )
2014-09-21 21:25:43 +04:00
self . _display . set_grab_keys ( seq )
2012-05-14 17:24:56 +04:00
except Exception , e :
logging . debug ( " Error when getting the grab keys combination: %s " ,
str ( e ) )
2013-01-16 06:22:32 +04:00
def send_keys ( self , keys ) :
2014-09-21 21:25:43 +04:00
return self . _display . send_keys ( [ Gdk . keyval_from_name ( k ) for k in keys ] ,
2013-01-16 06:22:32 +04:00
SpiceClientGtk . DisplayKeyEvent . CLICK )
2014-09-21 21:05:44 +04:00
def set_keyboard_grab_default ( self ) :
2014-09-21 21:25:43 +04:00
self . _display . set_property ( " grab-keyboard " ,
2014-09-21 21:05:44 +04:00
self . config . get_keyboard_grab_default ( ) )
2014-03-10 21:07:15 +04:00
2010-12-22 04:13:25 +03:00
def close ( self ) :
if self . spice_session is not None :
self . spice_session . disconnect ( )
2011-01-14 19:34:51 +03:00
self . spice_session = None
self . audio = None
2014-09-21 21:25:43 +04:00
if self . _display :
self . _display . destroy ( )
self . _display = None
self . _display_channel = None
2014-07-01 17:01:58 +04:00
for i in self . _main_channel_hids :
self . main_channel . handler_disconnect ( i )
self . _main_channel_hids = [ ]
2014-01-31 18:13:53 +04:00
self . main_channel = None
2013-07-01 22:33:59 +04:00
self . usbdev_manager = None
2010-12-22 04:13:25 +03:00
def is_open ( self ) :
2012-11-08 17:15:02 +04:00
return self . spice_session is not None
2010-12-22 04:13:25 +03:00
def _main_channel_event_cb ( self , channel , event ) :
2013-09-01 21:25:37 +04:00
if not self . console :
return
2012-05-14 17:24:56 +04:00
if event == SpiceClientGLib . ChannelEvent . CLOSED :
2013-09-01 21:25:37 +04:00
self . console . disconnected ( )
2012-05-14 17:24:56 +04:00
elif event == SpiceClientGLib . ChannelEvent . ERROR_AUTH :
2013-09-01 21:25:37 +04:00
self . console . activate_auth_page ( )
elif event in [ SpiceClientGLib . ChannelEvent . ERROR_CONNECT ,
SpiceClientGLib . ChannelEvent . ERROR_IO ,
SpiceClientGLib . ChannelEvent . ERROR_LINK ,
SpiceClientGLib . ChannelEvent . ERROR_TLS ] :
logging . debug ( " Spice channel event error: %s " , event )
self . console . disconnected ( )
2010-12-22 04:13:25 +03:00
2013-09-07 03:36:09 +04:00
def _fd_channel_event_cb ( self , channel , event ) :
# When we see any event from the channel, release the
# associated tunnel lock
channel . disconnect_by_func ( self . _fd_channel_event_cb )
self . console . tunnels . unlock ( )
2010-12-22 04:13:25 +03:00
def _channel_open_fd_request ( self , channel , tls_ignore ) :
if not self . console . tunnels :
raise SystemError ( " Got fd request with no configured tunnel! " )
2012-04-05 19:03:16 +04:00
logging . debug ( " Opening tunnel for channel: %s " , channel )
2013-09-07 03:36:09 +04:00
channel . connect_after ( " channel-event " , self . _fd_channel_event_cb )
2010-12-22 04:13:25 +03:00
fd = self . console . tunnels . open_new ( )
channel . open_fd ( fd )
def _channel_new_cb ( self , session , channel ) :
2012-05-14 17:24:56 +04:00
GObject . GObject . connect ( channel , " open-fd " ,
2010-12-22 04:13:25 +03:00
self . _channel_open_fd_request )
2014-01-31 18:13:53 +04:00
if ( type ( channel ) == SpiceClientGLib . MainChannel and
not self . main_channel ) :
2013-09-07 03:36:09 +04:00
if self . console . tunnels :
self . console . tunnels . unlock ( )
2014-01-31 18:13:53 +04:00
self . main_channel = channel
2014-07-01 17:01:58 +04:00
hid = self . main_channel . connect_after ( " channel-event " ,
2014-01-31 18:13:53 +04:00
self . _main_channel_event_cb )
2014-07-01 17:01:58 +04:00
self . _main_channel_hids . append ( hid )
hid = self . main_channel . connect_after ( " notify::agent-connected " ,
2014-01-31 18:13:53 +04:00
self . _agent_connected_cb )
2014-07-01 17:01:58 +04:00
self . _main_channel_hids . append ( hid )
2010-12-22 04:13:25 +03:00
2014-01-31 18:13:53 +04:00
elif ( type ( channel ) == SpiceClientGLib . DisplayChannel and
2014-09-21 21:25:43 +04:00
not self . _display ) :
2010-12-22 04:13:25 +03:00
channel_id = channel . get_property ( " channel-id " )
2011-04-26 18:51:20 +04:00
if channel_id != 0 :
logging . debug ( " Spice multi-head unsupported " )
return
2014-09-21 21:25:43 +04:00
self . _display_channel = channel
self . _display = SpiceClientGtk . Display . new ( self . spice_session ,
2013-01-16 03:23:17 +04:00
channel_id )
2014-09-21 21:25:43 +04:00
self . console . widget ( " console-gfx-viewport " ) . add ( self . _display )
2010-12-22 04:13:25 +03:00
self . _init_widget ( )
2011-01-07 23:15:10 +03:00
self . console . connected ( )
2010-12-22 04:13:25 +03:00
2014-01-31 18:13:53 +04:00
elif ( type ( channel ) in [ SpiceClientGLib . PlaybackChannel ,
SpiceClientGLib . RecordChannel ] and
2011-01-07 23:15:10 +03:00
not self . audio ) :
2013-01-16 03:23:17 +04:00
self . audio = SpiceClientGLib . Audio . get ( self . spice_session , None )
2010-12-22 04:13:25 +03:00
2011-04-26 18:51:19 +04:00
def get_desktop_resolution ( self ) :
2014-09-21 21:25:43 +04:00
if ( not self . _display_channel or
not has_property ( self . _display_channel , " width " ) ) :
2011-04-26 18:51:19 +04:00
return None
2014-09-21 21:25:43 +04:00
return self . _display_channel . get_properties ( " width " , " height " )
2011-04-26 18:51:19 +04:00
2014-01-31 18:13:53 +04:00
def has_agent ( self ) :
if ( not self . main_channel or
not has_property ( self . main_channel , " agent-connected " ) ) :
return False
ret = self . main_channel . get_property ( " agent-connected " )
return ret
def _agent_connected_cb ( self , src , val ) :
self . console . refresh_resizeguest_from_settings ( )
2013-07-01 22:33:59 +04:00
def _create_spice_session ( self ) :
self . spice_session = SpiceClientGLib . Session ( )
2013-09-02 00:08:14 +04:00
SpiceClientGLib . set_session_option ( self . spice_session )
2013-07-01 22:33:59 +04:00
gtk_session = SpiceClientGtk . GtkSession . get ( self . spice_session )
gtk_session . set_property ( " auto-clipboard " , True )
2013-09-07 03:36:09 +04:00
GObject . GObject . connect ( self . spice_session , " channel-new " ,
self . _channel_new_cb )
2013-07-09 03:13:52 +04:00
self . usbdev_manager = SpiceClientGLib . UsbDeviceManager . get (
self . spice_session )
self . usbdev_manager . connect ( " auto-connect-failed " ,
self . _usbdev_redirect_error )
self . usbdev_manager . connect ( " device-error " ,
self . _usbdev_redirect_error )
2013-07-01 22:33:59 +04:00
autoredir = self . config . get_auto_redirection ( )
if autoredir :
gtk_session . set_property ( " auto-usbredir " , True )
2013-09-07 03:36:09 +04:00
def open_host ( self , ginfo ) :
2013-09-02 00:08:14 +04:00
host , port , tlsport = ginfo . get_conn_host ( )
2013-07-01 22:33:59 +04:00
self . _create_spice_session ( )
2013-09-07 03:36:09 +04:00
2013-09-02 00:08:14 +04:00
self . spice_session . set_property ( " host " , str ( host ) )
2013-10-03 23:54:15 +04:00
if port :
self . spice_session . set_property ( " port " , str ( port ) )
2013-09-02 00:08:14 +04:00
if tlsport :
self . spice_session . set_property ( " tls-port " , str ( tlsport ) )
2013-09-07 03:36:09 +04:00
2010-12-22 04:13:25 +03:00
self . spice_session . connect ( )
2013-09-07 03:36:09 +04:00
def open_fd ( self , fd ) :
2013-07-01 22:33:59 +04:00
self . _create_spice_session ( )
2010-12-22 04:13:25 +03:00
self . spice_session . open_fd ( fd )
def set_credential_password ( self , cred ) :
self . spice_session . set_property ( " password " , cred )
2011-01-07 17:14:33 +03:00
if self . console . tunnels :
fd = self . console . tunnels . open_new ( )
self . spice_session . open_fd ( fd )
else :
self . spice_session . connect ( )
2010-12-22 04:13:25 +03:00
def get_scaling ( self ) :
2014-09-21 21:25:43 +04:00
if not has_property ( self . _display , " scaling " ) :
2012-01-29 01:38:01 +04:00
return False
2014-09-21 21:25:43 +04:00
return self . _display . get_property ( " scaling " )
2010-12-22 04:13:25 +03:00
def set_scaling ( self , scaling ) :
2014-09-21 21:25:43 +04:00
if not has_property ( self . _display , " scaling " ) :
2012-01-29 01:38:01 +04:00
logging . debug ( " Spice version doesn ' t support scaling. " )
return
2014-09-21 21:25:43 +04:00
self . _display . set_property ( " scaling " , scaling )
2010-12-22 04:13:25 +03:00
2014-01-31 18:13:53 +04:00
def set_resizeguest ( self , val ) :
2014-09-21 21:25:43 +04:00
if self . _display :
self . _display . set_property ( " resize-guest " , val )
2014-01-31 18:13:53 +04:00
2014-02-05 16:09:53 +04:00
def get_resizeguest ( self ) :
2014-09-21 21:25:43 +04:00
if self . _display :
return self . _display . get_property ( " resize-guest " )
2014-02-05 16:09:53 +04:00
return False
2013-07-01 22:33:59 +04:00
def _usbdev_redirect_error ( self ,
spice_usbdev_widget , spice_usb_device ,
errstr ) :
ignore_widget = spice_usbdev_widget
ignore_device = spice_usb_device
error = self . console . err
error . show_err ( _ ( " USB redirection error " ) ,
text2 = str ( errstr ) ,
2013-09-07 04:16:37 +04:00
modal = True )
2013-07-01 22:33:59 +04:00
def get_usb_widget ( self ) :
# The @format positional parameters are the following:
# 1 '%s' manufacturer
# 2 '%s' product
# 3 '%s' descriptor (a [vendor_id:product_id] string)
# 4 '%d' bus
# 5 '%d' address
usb_device_description_fmt = _ ( " %s %s %s at %d - %d " )
2013-07-09 03:13:52 +04:00
if not self . spice_session :
return
2013-07-01 22:33:59 +04:00
2013-07-09 03:13:52 +04:00
usbwidget = SpiceClientGtk . UsbDeviceWidget . new (
self . spice_session ,
usb_device_description_fmt )
usbwidget . connect ( " connect-failed " , self . _usbdev_redirect_error )
return usbwidget
2013-07-01 22:33:59 +04:00
def has_usb_redirection ( self ) :
2013-07-09 03:13:52 +04:00
if not self . spice_session or not self . usbdev_manager :
return False
2013-07-01 22:33:59 +04:00
2013-07-09 03:13:52 +04:00
for c in self . spice_session . get_channels ( ) :
if c . __class__ is SpiceClientGLib . UsbredirChannel :
2013-07-01 22:33:59 +04:00
return True
return False
2010-12-22 04:13:25 +03:00
2010-12-09 01:26:19 +03:00
class vmmConsolePages ( vmmGObjectUI ) :
2013-02-16 22:31:46 +04:00
def __init__ ( self , vm , builder , topwin ) :
2013-06-09 03:25:36 +04:00
vmmGObjectUI . __init__ ( self , None , None , builder = builder , topwin = topwin )
2009-10-30 20:25:27 +03:00
self . vm = vm
2011-03-23 23:56:12 +03:00
self . pointer_is_grabbed = False
self . change_title ( )
self . vm . connect ( " config-changed " , self . change_title )
2014-01-30 01:58:34 +04:00
self . force_resize = False
2009-10-30 20:25:27 +03:00
# State for disabling modifiers when keyboard is grabbed
2013-04-17 17:30:16 +04:00
self . accel_groups = Gtk . accel_groups_from_object ( self . topwin )
2009-10-30 20:25:27 +03:00
self . gtk_settings_accel = None
2009-10-30 21:36:17 +03:00
self . gtk_settings_mnemonic = None
2009-10-30 20:25:27 +03:00
# Initialize display widget
2011-04-13 18:45:10 +04:00
self . viewer = None
2010-12-22 00:34:36 +03:00
self . tunnels = None
2010-12-22 04:13:11 +03:00
self . viewerRetriesScheduled = 0
self . viewerRetryDelay = 125
2011-06-01 20:22:05 +04:00
self . _viewer_connected = False
2011-01-14 22:17:35 +03:00
self . viewer_connecting = False
2009-10-30 20:25:27 +03:00
2011-05-19 01:22:07 +04:00
# Fullscreen toolbar
2012-01-30 06:56:18 +04:00
self . send_key_button = None
2011-05-19 01:22:07 +04:00
self . fs_toolbar = None
self . fs_drawer = None
2014-01-27 02:09:07 +04:00
self . keycombo_menu = self . build_keycombo_menu ( self . send_key )
2011-05-19 01:22:07 +04:00
self . init_fs_toolbar ( )
2010-12-22 04:13:11 +03:00
# Make viewer widget background always be black
2012-05-14 17:24:56 +04:00
black = Gdk . Color ( 0 , 0 , 0 )
2013-04-21 20:10:14 +04:00
self . widget ( " console-gfx-viewport " ) . modify_bg ( Gtk . StateType . NORMAL ,
2011-07-14 21:13:13 +04:00
black )
2009-11-29 02:48:56 +03:00
2013-09-02 01:40:38 +04:00
self . serial_tabs = [ ]
self . last_gfx_page = 0
self . _init_menus ( )
2012-02-02 02:26:46 +04:00
# Signals are added by vmmDetails. Don't use connect_signals here
2009-10-30 20:25:27 +03:00
# or it changes will be overwritten
2013-04-18 01:39:25 +04:00
2014-01-31 04:15:02 +04:00
self . refresh_scaling_from_settings ( )
2011-04-13 18:45:10 +04:00
self . add_gconf_handle (
2014-01-31 04:15:02 +04:00
self . vm . on_console_scaling_changed (
self . refresh_scaling_from_settings ) )
2014-01-31 18:13:53 +04:00
self . refresh_resizeguest_from_settings ( )
self . add_gconf_handle (
self . vm . on_console_resizeguest_changed (
self . refresh_resizeguest_from_settings ) )
2009-10-30 20:25:27 +03:00
2013-04-21 20:10:14 +04:00
scroll = self . widget ( " console-gfx-scroll " )
2010-12-22 04:13:11 +03:00
scroll . connect ( " size-allocate " , self . scroll_size_allocate )
2011-04-13 18:45:10 +04:00
self . add_gconf_handle (
self . config . on_console_accels_changed ( self . set_enable_accel ) )
2012-01-30 06:51:20 +04:00
self . add_gconf_handle (
self . config . on_keys_combination_changed ( self . grab_keys_changed ) )
2014-03-10 21:07:15 +04:00
self . add_gconf_handle (
2014-09-21 21:05:44 +04:00
self . config . on_keyboard_grab_default_changed (
self . keyboard_grab_default_changed ) )
2009-10-30 20:25:27 +03:00
2011-06-01 20:22:05 +04:00
self . page_changed ( )
2014-01-27 02:09:07 +04:00
2009-10-30 20:25:27 +03:00
def is_visible ( self ) :
2013-06-18 07:29:20 +04:00
if self . topwin :
return self . topwin . get_visible ( )
else :
return False
2009-10-30 20:25:27 +03:00
2011-07-24 05:16:54 +04:00
def _cleanup ( self ) :
2011-04-12 02:35:21 +04:00
self . vm = None
2011-05-19 01:36:08 +04:00
if self . viewer :
self . viewer . cleanup ( )
2011-04-13 18:45:10 +04:00
self . viewer = None
2011-04-12 02:35:21 +04:00
2011-05-19 02:54:27 +04:00
self . keycombo_menu . destroy ( )
self . keycombo_menu = None
2011-05-19 01:22:07 +04:00
self . fs_drawer . destroy ( )
self . fs_drawer = None
2011-10-28 20:20:11 +04:00
self . fs_toolbar . destroy ( )
self . fs_toolbar = None
2011-05-19 01:22:07 +04:00
2013-09-02 01:40:38 +04:00
for serial in self . serial_tabs :
serial . cleanup ( )
self . serial_tabs = [ ]
2009-10-30 20:25:27 +03:00
##########################
# Initialization helpers #
##########################
2014-01-27 02:09:07 +04:00
@staticmethod
def build_keycombo_menu ( cb ) :
# Shared with vmmDetails
menu = Gtk . Menu ( )
def make_item ( name , combo ) :
item = Gtk . MenuItem . new_with_mnemonic ( name )
item . connect ( " activate " , cb , combo )
menu . add ( item )
make_item ( " Ctrl+Alt+_Backspace " , [ " Control_L " , " Alt_L " , " BackSpace " ] )
make_item ( " Ctrl+Alt+_Delete " , [ " Control_L " , " Alt_L " , " Delete " ] )
menu . add ( Gtk . SeparatorMenuItem ( ) )
for i in range ( 1 , 13 ) :
make_item ( " Ctrl+Alt+F_ %d " % i , [ " Control_L " , " Alt_L " , " F %d " % i ] )
menu . add ( Gtk . SeparatorMenuItem ( ) )
make_item ( " _Printscreen " , [ " Print " ] )
menu . show_all ( )
return menu
2011-05-19 01:22:07 +04:00
def init_fs_toolbar ( self ) :
2013-04-21 20:10:14 +04:00
scroll = self . widget ( " console-gfx-scroll " )
2011-07-14 21:13:13 +04:00
pages = self . widget ( " console-pages " )
2011-05-19 01:22:07 +04:00
pages . remove ( scroll )
2012-05-14 17:24:56 +04:00
self . fs_toolbar = Gtk . Toolbar ( )
2011-05-19 01:22:07 +04:00
self . fs_toolbar . set_show_arrow ( False )
self . fs_toolbar . set_no_show_all ( True )
2012-05-14 17:24:56 +04:00
self . fs_toolbar . set_style ( Gtk . ToolbarStyle . BOTH_HORIZ )
2011-05-19 01:22:07 +04:00
# Exit fullscreen button
2012-05-14 17:24:56 +04:00
button = Gtk . ToolButton . new_from_stock ( Gtk . STOCK_LEAVE_FULLSCREEN )
button . set_tooltip_text ( _ ( " Leave fullscreen " ) )
2011-05-19 01:22:07 +04:00
button . show ( )
self . fs_toolbar . add ( button )
button . connect ( " clicked " , self . leave_fullscreen )
2011-05-19 02:54:27 +04:00
def keycombo_menu_clicked ( src ) :
ignore = src
def menu_location ( menu , toolbar ) :
ignore = menu
2013-04-15 20:35:45 +04:00
ignore , x , y = toolbar . get_window ( ) . get_origin ( )
height = toolbar . get_window ( ) . get_height ( )
2011-05-19 02:54:27 +04:00
return x , y + height , True
2013-04-14 21:16:02 +04:00
self . keycombo_menu . popup ( None , None , menu_location ,
self . fs_toolbar , 0 ,
Gtk . get_current_event_time ( ) )
2011-05-19 02:54:27 +04:00
2012-05-14 17:24:56 +04:00
self . send_key_button = Gtk . ToolButton ( )
2012-01-30 06:56:18 +04:00
self . send_key_button . set_icon_name (
" preferences-desktop-keyboard-shortcuts " )
2012-05-14 17:24:56 +04:00
self . send_key_button . set_tooltip_text ( _ ( " Send key combination " ) )
2012-01-30 06:56:18 +04:00
self . send_key_button . show_all ( )
self . send_key_button . connect ( " clicked " , keycombo_menu_clicked )
self . fs_toolbar . add ( self . send_key_button )
2011-05-19 02:54:27 +04:00
2011-05-19 01:22:07 +04:00
self . fs_drawer = AutoDrawer ( )
self . fs_drawer . set_active ( False )
self . fs_drawer . set_over ( self . fs_toolbar )
self . fs_drawer . set_under ( scroll )
self . fs_drawer . set_offset ( - 1 )
self . fs_drawer . set_fill ( False )
self . fs_drawer . set_overlap_pixels ( 1 )
self . fs_drawer . set_nooverlap_pixels ( 0 )
2013-09-25 21:03:03 +04:00
self . fs_drawer . period = 20
self . fs_drawer . step = .1
2011-05-19 01:22:07 +04:00
self . fs_drawer . show_all ( )
pages . add ( self . fs_drawer )
2013-09-02 01:40:38 +04:00
def _init_menus ( self ) :
# Serial list menu
smenu = Gtk . Menu ( )
smenu . connect ( " show " , self . populate_serial_menu )
self . widget ( " details-menu-view-serial-list " ) . set_submenu ( smenu )
2011-03-23 23:56:12 +03:00
def change_title ( self , ignore1 = None ) :
title = self . vm . get_name ( ) + " " + _ ( " Virtual Machine " )
if self . pointer_is_grabbed and self . viewer :
keystr = self . viewer . get_grab_keys ( )
keymsg = _ ( " Press %s to release pointer. " ) % keystr
title = keymsg + " " + title
self . topwin . set_title ( title )
2013-12-19 01:27:33 +04:00
def someone_has_focus ( self ) :
if ( self . viewer and
2014-09-21 21:25:43 +04:00
self . viewer . has_focus ( ) and
2013-12-19 01:27:33 +04:00
self . viewer_connected ) :
return True
for serial in self . serial_tabs :
if ( serial . terminal and
serial . terminal . get_property ( " has-focus " ) ) :
return True
2010-12-22 04:13:11 +03:00
def viewer_focus_changed ( self , ignore1 = None , ignore2 = None ) :
2010-12-02 21:41:22 +03:00
force_accel = self . config . get_console_accels ( )
if force_accel :
self . _enable_modifiers ( )
2013-12-19 01:27:33 +04:00
elif self . someone_has_focus ( ) :
2010-12-02 21:41:22 +03:00
self . _disable_modifiers ( )
else :
self . _enable_modifiers ( )
2010-04-21 20:59:25 +04:00
2010-12-22 04:13:11 +03:00
def pointer_grabbed ( self , src_ignore ) :
2011-03-23 23:56:12 +03:00
self . pointer_is_grabbed = True
self . change_title ( )
2009-10-30 20:25:27 +03:00
2010-12-09 19:22:35 +03:00
def pointer_ungrabbed ( self , src_ignore ) :
2011-03-23 23:56:12 +03:00
self . pointer_is_grabbed = False
self . change_title ( )
2009-10-30 20:25:27 +03:00
2010-04-21 20:59:25 +04:00
def _disable_modifiers ( self ) :
2009-11-02 00:36:44 +03:00
if self . gtk_settings_accel is not None :
return
2009-10-30 20:25:27 +03:00
for g in self . accel_groups :
self . topwin . remove_accel_group ( g )
2009-10-30 21:36:17 +03:00
2012-05-14 17:24:56 +04:00
settings = Gtk . Settings . get_default ( )
2009-10-30 20:25:27 +03:00
self . gtk_settings_accel = settings . get_property ( ' gtk-menu-bar-accel ' )
settings . set_property ( ' gtk-menu-bar-accel ' , None )
2009-10-30 21:36:17 +03:00
if has_property ( settings , " gtk-enable-mnemonics " ) :
2010-12-02 21:41:22 +03:00
self . gtk_settings_mnemonic = settings . get_property (
" gtk-enable-mnemonics " )
2009-10-30 21:36:17 +03:00
settings . set_property ( " gtk-enable-mnemonics " , False )
2010-04-21 20:59:25 +04:00
def _enable_modifiers ( self ) :
2009-10-30 20:25:27 +03:00
if self . gtk_settings_accel is None :
return
2009-11-02 00:36:44 +03:00
2012-05-14 17:24:56 +04:00
settings = Gtk . Settings . get_default ( )
2009-10-30 20:25:27 +03:00
settings . set_property ( ' gtk-menu-bar-accel ' , self . gtk_settings_accel )
self . gtk_settings_accel = None
2009-10-30 21:36:17 +03:00
if self . gtk_settings_mnemonic is not None :
settings . set_property ( " gtk-enable-mnemonics " ,
self . gtk_settings_mnemonic )
2009-10-30 20:25:27 +03:00
for g in self . accel_groups :
self . topwin . add_accel_group ( g )
2013-04-18 01:39:25 +04:00
def grab_keys_changed ( self ) :
2014-01-29 22:07:43 +04:00
if self . viewer :
self . viewer . set_grab_keys ( )
2012-01-30 06:51:20 +04:00
2014-09-21 21:05:44 +04:00
def keyboard_grab_default_changed ( self ) :
2014-03-10 21:07:15 +04:00
if self . viewer :
2014-09-21 21:05:44 +04:00
self . viewer . set_keyboard_grab_default ( )
2014-03-10 21:07:15 +04:00
2013-04-18 01:39:25 +04:00
def set_enable_accel ( self ) :
2010-12-02 21:41:22 +03:00
# Make sure modifiers are up to date
2010-12-22 04:13:11 +03:00
self . viewer_focus_changed ( )
2009-10-30 20:25:27 +03:00
2014-01-31 18:13:53 +04:00
def refresh_resizeguest_from_settings ( self ) :
tooltip = " "
if self . viewer :
if self . viewer . viewer_type != " spice " :
tooltip = (
_ ( " Graphics type ' %s ' does not support auto resize. " ) %
self . viewer . viewer_type )
elif not self . viewer . has_agent ( ) :
tooltip = _ ( " Guest agent is not available. " )
val = self . vm . get_console_resizeguest ( )
widget = self . widget ( " details-menu-view-resizeguest " )
widget . set_tooltip_text ( tooltip )
widget . set_sensitive ( not bool ( tooltip ) )
if not tooltip :
self . widget ( " details-menu-view-resizeguest " ) . set_active ( bool ( val ) )
self . sync_resizeguest_with_display ( )
def resizeguest_ui_changed_cb ( self , src ) :
# Called from details.py
2014-02-28 20:46:05 +04:00
if not src . get_sensitive ( ) :
2014-01-31 18:13:53 +04:00
return
val = int ( self . widget ( " details-menu-view-resizeguest " ) . get_active ( ) )
self . vm . set_console_resizeguest ( val )
self . sync_resizeguest_with_display ( )
def sync_resizeguest_with_display ( self ) :
if not self . viewer :
return
val = bool ( self . vm . get_console_resizeguest ( ) )
self . viewer . set_resizeguest ( val )
self . widget ( " console-gfx-scroll " ) . queue_resize ( )
2014-01-31 04:15:02 +04:00
def refresh_scaling_from_settings ( self ) :
scale_type = self . vm . get_console_scaling ( )
2011-07-14 21:13:13 +04:00
self . widget ( " details-menu-view-scale-always " ) . set_active (
2014-01-31 04:15:02 +04:00
scale_type == self . config . CONSOLE_SCALE_ALWAYS )
2011-07-14 21:13:13 +04:00
self . widget ( " details-menu-view-scale-never " ) . set_active (
2014-01-31 04:15:02 +04:00
scale_type == self . config . CONSOLE_SCALE_NEVER )
2011-07-14 21:13:13 +04:00
self . widget ( " details-menu-view-scale-fullscreen " ) . set_active (
2014-01-31 04:15:02 +04:00
scale_type == self . config . CONSOLE_SCALE_FULLSCREEN )
2009-10-30 20:25:27 +03:00
2014-01-31 04:15:02 +04:00
self . sync_scaling_with_display ( )
2009-10-30 20:25:27 +03:00
2014-01-31 04:15:02 +04:00
def scaling_ui_changed_cb ( self , src ) :
# Called from details.py
2009-10-30 20:25:27 +03:00
if not src . get_active ( ) :
return
2014-01-31 04:15:02 +04:00
scale_type = 0
2011-07-14 21:13:13 +04:00
if src == self . widget ( " details-menu-view-scale-always " ) :
2014-01-31 04:15:02 +04:00
scale_type = self . config . CONSOLE_SCALE_ALWAYS
2011-07-14 21:13:13 +04:00
elif src == self . widget ( " details-menu-view-scale-fullscreen " ) :
2014-01-31 04:15:02 +04:00
scale_type = self . config . CONSOLE_SCALE_FULLSCREEN
2011-07-14 21:13:13 +04:00
elif src == self . widget ( " details-menu-view-scale-never " ) :
2014-01-31 04:15:02 +04:00
scale_type = self . config . CONSOLE_SCALE_NEVER
2009-10-30 20:25:27 +03:00
2014-01-31 04:15:02 +04:00
self . vm . set_console_scaling ( scale_type )
self . sync_scaling_with_display ( )
2009-10-30 20:25:27 +03:00
2014-01-31 04:15:02 +04:00
def sync_scaling_with_display ( self ) :
2010-12-22 04:13:11 +03:00
if not self . viewer :
return
curscale = self . viewer . get_scaling ( )
2011-07-14 21:13:13 +04:00
fs = self . widget ( " control-fullscreen " ) . get_active ( )
2014-01-31 04:15:02 +04:00
scale_type = self . vm . get_console_scaling ( )
2009-10-30 20:25:27 +03:00
2014-01-31 04:15:02 +04:00
if ( scale_type == self . config . CONSOLE_SCALE_NEVER
2012-11-08 17:15:02 +04:00
and curscale is True ) :
2010-12-22 04:13:11 +03:00
self . viewer . set_scaling ( False )
2014-01-31 04:15:02 +04:00
elif ( scale_type == self . config . CONSOLE_SCALE_ALWAYS
2012-11-08 17:15:02 +04:00
and curscale is False ) :
2010-12-22 04:13:11 +03:00
self . viewer . set_scaling ( True )
2014-01-31 04:15:02 +04:00
elif ( scale_type == self . config . CONSOLE_SCALE_FULLSCREEN
2009-10-30 20:25:27 +03:00
and curscale != fs ) :
2010-12-22 04:13:11 +03:00
self . viewer . set_scaling ( fs )
2009-10-30 20:25:27 +03:00
2009-11-29 02:48:56 +03:00
# Refresh viewer size
2014-01-30 01:58:34 +04:00
self . widget ( " console-gfx-scroll " ) . queue_resize ( )
2009-11-29 02:48:56 +03:00
2009-10-30 20:25:27 +03:00
def auth_login ( self , ignore ) :
self . set_credentials ( )
self . activate_viewer_page ( )
def toggle_fullscreen ( self , src ) :
2009-11-23 00:39:38 +03:00
do_fullscreen = src . get_active ( )
2011-05-19 01:22:07 +04:00
self . _change_fullscreen ( do_fullscreen )
2011-06-01 18:57:57 +04:00
def leave_fullscreen ( self , ignore = None ) :
2011-05-19 01:22:07 +04:00
self . _change_fullscreen ( False )
2009-11-23 00:39:38 +03:00
2011-05-19 01:22:07 +04:00
def _change_fullscreen ( self , do_fullscreen ) :
2011-07-14 21:13:13 +04:00
self . widget ( " control-fullscreen " ) . set_active ( do_fullscreen )
2009-11-23 00:39:38 +03:00
if do_fullscreen :
2009-10-30 20:25:27 +03:00
self . topwin . fullscreen ( )
2011-05-19 01:22:07 +04:00
self . fs_toolbar . show ( )
self . fs_drawer . set_active ( True )
2011-07-14 21:13:13 +04:00
self . widget ( " toolbar-box " ) . hide ( )
self . widget ( " details-menubar " ) . hide ( )
2009-10-30 20:25:27 +03:00
else :
2011-05-19 01:22:07 +04:00
self . fs_toolbar . hide ( )
self . fs_drawer . set_active ( False )
2009-10-30 20:25:27 +03:00
self . topwin . unfullscreen ( )
2011-07-14 21:13:13 +04:00
if self . widget ( " details-menu-view-toolbar " ) . get_active ( ) :
self . widget ( " toolbar-box " ) . show ( )
self . widget ( " details-menubar " ) . show ( )
2009-10-30 20:25:27 +03:00
2014-01-31 04:15:02 +04:00
self . sync_scaling_with_display ( )
2009-10-30 20:25:27 +03:00
2014-01-30 01:58:34 +04:00
def viewer_allocate_cb ( self , src , req ) :
self . widget ( " console-gfx-scroll " ) . queue_resize ( )
2010-12-09 19:22:35 +03:00
def size_to_vm ( self , src_ignore ) :
2009-11-29 04:07:01 +03:00
# Resize the console to best fit the VM resolution
2011-04-26 18:51:19 +04:00
if not self . viewer :
return
if not self . viewer . get_desktop_resolution ( ) :
2009-11-29 04:07:01 +03:00
return
self . topwin . unmaximize ( )
self . topwin . resize ( 1 , 1 )
2014-01-30 01:58:34 +04:00
self . force_resize = True
self . widget ( " console-gfx-scroll " ) . queue_resize ( )
2009-11-29 04:07:01 +03:00
2011-05-19 02:54:27 +04:00
def send_key ( self , src , keys ) :
ignore = src
2009-10-30 20:25:27 +03:00
2012-11-08 17:15:02 +04:00
if keys is not None :
2010-12-22 04:13:11 +03:00
self . viewer . send_keys ( keys )
2009-10-30 20:25:27 +03:00
##########################
# State tracking methods #
##########################
def view_vm_status ( self ) :
2013-06-18 07:29:20 +04:00
if not self . vm :
# window has been closed and no pages to update are available.
return
2009-10-30 20:25:27 +03:00
status = self . vm . status ( )
if status == libvirt . VIR_DOMAIN_SHUTOFF :
self . activate_unavailable_page ( _ ( " Guest not running " ) )
else :
if status == libvirt . VIR_DOMAIN_CRASHED :
self . activate_unavailable_page ( _ ( " Guest has crashed " ) )
2010-12-22 04:13:11 +03:00
def close_viewer ( self ) :
2011-01-14 19:34:51 +03:00
if self . viewer is None :
return
2014-09-21 21:25:43 +04:00
viewer = self . viewer
display = getattr ( viewer , " _display " )
2013-09-07 03:55:46 +04:00
self . viewer = None
2011-01-14 19:34:51 +03:00
2013-06-18 07:29:20 +04:00
viewport = self . widget ( " console-gfx-viewport " )
2014-09-21 21:25:43 +04:00
if display and display in viewport . get_children ( ) :
viewport . remove ( display )
2011-01-14 19:34:51 +03:00
2014-09-21 21:25:43 +04:00
viewer . close ( )
2011-01-14 19:34:51 +03:00
self . viewer_connected = False
2011-06-01 18:57:57 +04:00
self . leave_fullscreen ( )
2010-12-22 04:13:11 +03:00
2013-11-10 02:54:47 +04:00
for serial in self . serial_tabs :
serial . close ( )
2010-12-09 19:22:35 +03:00
def update_widget_states ( self , vm , status_ignore ) :
2009-10-30 20:25:27 +03:00
runable = vm . is_runable ( )
2011-07-14 21:13:13 +04:00
pages = self . widget ( " console-pages " )
2009-10-30 20:25:27 +03:00
page = pages . get_current_page ( )
if runable :
2013-09-02 01:40:38 +04:00
if page != CONSOLE_PAGE_UNAVAILABLE :
pages . set_current_page ( CONSOLE_PAGE_UNAVAILABLE )
2009-10-30 20:25:27 +03:00
self . view_vm_status ( )
2013-09-02 01:40:38 +04:00
elif page in [ CONSOLE_PAGE_UNAVAILABLE , CONSOLE_PAGE_VIEWER ] :
2010-12-22 04:13:11 +03:00
if self . viewer and self . viewer . is_open ( ) :
2009-10-30 20:25:27 +03:00
self . activate_viewer_page ( )
else :
2010-12-22 04:13:11 +03:00
self . viewerRetriesScheduled = 0
self . viewerRetryDelay = 125
2009-10-30 20:25:27 +03:00
self . try_login ( )
return
2013-09-02 01:40:38 +04:00
2009-10-30 20:25:27 +03:00
###################
# Page Navigation #
###################
def activate_unavailable_page ( self , msg ) :
2011-06-21 03:09:31 +04:00
"""
This function is passed to serialcon . py at least , so change
with care
"""
2011-01-14 22:17:35 +03:00
self . close_viewer ( )
2013-09-02 01:40:38 +04:00
self . widget ( " console-pages " ) . set_current_page ( CONSOLE_PAGE_UNAVAILABLE )
2011-07-14 21:13:13 +04:00
self . widget ( " details-menu-vm-screenshot " ) . set_sensitive ( False )
2013-07-01 22:33:59 +04:00
self . widget ( " details-menu-usb-redirection " ) . set_sensitive ( False )
2011-07-14 21:13:13 +04:00
self . widget ( " console-unavailable " ) . set_label ( " <b> " + msg + " </b> " )
2009-10-30 20:25:27 +03:00
def activate_auth_page ( self , withPassword = True , withUsername = False ) :
( pw , username ) = self . config . get_console_password ( self . vm )
2011-07-14 21:13:13 +04:00
self . widget ( " details-menu-vm-screenshot " ) . set_sensitive ( False )
2013-07-01 22:33:59 +04:00
self . widget ( " details-menu-usb-redirection " ) . set_sensitive ( False )
2009-10-30 20:25:27 +03:00
2013-09-02 01:40:38 +04:00
self . widget ( " console-auth-password " ) . set_visible ( withPassword )
self . widget ( " label-auth-password " ) . set_visible ( withPassword )
self . widget ( " console-auth-username " ) . set_visible ( withUsername )
self . widget ( " label-auth-username " ) . set_visible ( withUsername )
2009-10-30 20:25:27 +03:00
if withUsername :
2013-09-02 01:40:38 +04:00
self . widget ( " console-auth-username " ) . grab_focus ( )
2009-10-30 20:25:27 +03:00
else :
2013-09-02 01:40:38 +04:00
self . widget ( " console-auth-password " ) . grab_focus ( )
2009-10-30 20:25:27 +03:00
2011-07-14 21:13:13 +04:00
self . widget ( " console-auth-username " ) . set_text ( username )
self . widget ( " console-auth-password " ) . set_text ( pw )
2009-10-30 20:25:27 +03:00
2013-09-02 01:40:38 +04:00
self . widget ( " console-auth-remember " ) . set_sensitive (
bool ( self . config . has_keyring ( ) ) )
2009-10-30 20:25:27 +03:00
if self . config . has_keyring ( ) :
2013-09-02 01:40:38 +04:00
self . widget ( " console-auth-remember " ) . set_active ( bool ( pw and
username ) )
self . widget ( " console-pages " ) . set_current_page (
CONSOLE_PAGE_AUTHENTICATE )
2009-10-30 20:25:27 +03:00
def activate_viewer_page ( self ) :
2013-09-02 01:40:38 +04:00
self . widget ( " console-pages " ) . set_current_page ( CONSOLE_PAGE_VIEWER )
2011-07-14 21:13:13 +04:00
self . widget ( " details-menu-vm-screenshot " ) . set_sensitive ( True )
2014-09-21 21:25:43 +04:00
if self . viewer :
self . viewer . grab_focus ( )
2009-10-30 20:25:27 +03:00
2013-09-02 01:40:38 +04:00
if ( self . viewer . has_usb_redirection ( ) and
self . vm . has_spicevmc_type_redirdev ( ) ) :
2013-07-01 22:33:59 +04:00
self . widget ( " details-menu-usb-redirection " ) . set_sensitive ( True )
return
2014-02-28 22:35:48 +04:00
def page_changed ( self , ignore1 = None , ignore2 = None , newpage = None ) :
2013-09-02 01:40:38 +04:00
pagenum = self . widget ( " console-pages " ) . get_current_page ( )
2014-02-28 22:35:48 +04:00
for i in range ( self . widget ( " console-pages " ) . get_n_pages ( ) ) :
2014-03-01 07:23:42 +04:00
w = self . widget ( " console-pages " ) . get_nth_page ( i )
w . set_visible ( i == newpage )
2014-02-28 22:35:48 +04:00
2013-09-02 01:40:38 +04:00
if pagenum < CONSOLE_PAGE_OFFSET :
self . last_gfx_page = pagenum
2011-06-01 20:22:05 +04:00
self . set_allow_fullscreen ( )
def set_allow_fullscreen ( self ) :
2011-07-14 21:13:13 +04:00
cpage = self . widget ( " console-pages " ) . get_current_page ( )
dpage = self . widget ( " details-pages " ) . get_current_page ( )
2011-06-01 20:22:05 +04:00
2013-09-02 01:40:38 +04:00
allow_fullscreen = ( dpage == DETAILS_PAGE_CONSOLE and
cpage == CONSOLE_PAGE_VIEWER and
2011-06-01 20:22:05 +04:00
self . viewer_connected )
2011-07-14 21:13:13 +04:00
self . widget ( " control-fullscreen " ) . set_sensitive ( allow_fullscreen )
self . widget ( " details-menu-view-fullscreen " ) . set_sensitive ( allow_fullscreen )
2011-06-01 20:22:05 +04:00
2010-12-22 04:13:11 +03:00
def disconnected ( self ) :
2010-02-11 18:43:44 +03:00
errout = " "
2010-12-22 00:34:36 +03:00
if self . tunnels is not None :
errout = self . tunnels . get_err_output ( )
self . tunnels . close_all ( )
self . tunnels = None
2010-02-11 18:43:44 +03:00
2013-09-07 03:55:46 +04:00
self . widget ( " console-pages " ) . set_current_page ( CONSOLE_PAGE_UNAVAILABLE )
2010-12-22 04:13:11 +03:00
self . close_viewer ( )
logging . debug ( " Viewer disconnected " )
2010-12-02 21:41:22 +03:00
# Make sure modifiers are set correctly
2010-12-22 04:13:11 +03:00
self . viewer_focus_changed ( )
2010-02-11 18:43:44 +03:00
2013-09-07 03:55:46 +04:00
if self . guest_not_avail ( ) :
2010-02-11 18:43:44 +03:00
# Exit was probably for legitimate reasons
2009-10-30 20:25:27 +03:00
self . view_vm_status ( )
return
2010-12-22 04:13:11 +03:00
error = _ ( " Error: viewer connection to hypervisor host got refused "
2010-02-11 18:43:44 +03:00
" or disconnected! " )
if errout :
2012-01-17 07:04:40 +04:00
logging . debug ( " Error output from closed console: %s " , errout )
2010-02-11 18:43:44 +03:00
error + = " \n \n Error: %s " % errout
2009-10-30 20:25:27 +03:00
2010-02-11 18:43:44 +03:00
self . activate_unavailable_page ( error )
2014-01-31 18:13:53 +04:00
self . refresh_resizeguest_from_settings ( )
2009-10-30 20:25:27 +03:00
2011-06-01 20:22:05 +04:00
def _set_viewer_connected ( self , val ) :
self . _viewer_connected = val
self . set_allow_fullscreen ( )
def _get_viewer_connected ( self ) :
return self . _viewer_connected
viewer_connected = property ( _get_viewer_connected , _set_viewer_connected )
2010-12-22 04:13:11 +03:00
def connected ( self ) :
self . viewer_connected = True
logging . debug ( " Viewer connected " )
2009-10-30 20:25:27 +03:00
self . activate_viewer_page ( )
2014-04-22 19:41:04 +04:00
# Had a successful connect, so reset counters now
2010-12-22 04:13:11 +03:00
self . viewerRetriesScheduled = 0
self . viewerRetryDelay = 125
2009-10-30 20:25:27 +03:00
2010-12-02 21:41:22 +03:00
# Make sure modifiers are set correctly
2010-12-22 04:13:11 +03:00
self . viewer_focus_changed ( )
2010-12-02 21:41:22 +03:00
2009-10-30 20:25:27 +03:00
def schedule_retry ( self ) :
2010-12-22 04:13:11 +03:00
if self . viewerRetriesScheduled > = 10 :
2009-10-30 20:25:27 +03:00
logging . error ( " Too many connection failures, not retrying again " )
return
2012-02-10 23:07:51 +04:00
self . timeout_add ( self . viewerRetryDelay , self . try_login )
2009-10-30 20:25:27 +03:00
2010-12-22 04:13:11 +03:00
if self . viewerRetryDelay < 2000 :
self . viewerRetryDelay = self . viewerRetryDelay * 2
2009-10-30 20:25:27 +03:00
2010-02-11 18:43:44 +03:00
def skip_connect_attempt ( self ) :
2011-01-14 22:17:35 +03:00
return ( self . viewer or
2010-02-11 18:43:44 +03:00
not self . is_visible ( ) )
def guest_not_avail ( self ) :
2011-04-11 01:41:56 +04:00
return ( self . vm . is_shutoff ( ) or self . vm . is_crashed ( ) )
2010-02-11 18:43:44 +03:00
2010-12-09 19:22:35 +03:00
def try_login ( self , src_ignore = None ) :
2011-01-14 22:17:35 +03:00
if self . viewer_connecting :
return
try :
self . viewer_connecting = True
self . _try_login ( )
finally :
self . viewer_connecting = False
def _try_login ( self ) :
2010-02-11 18:43:44 +03:00
if self . skip_connect_attempt ( ) :
# Don't try and login for these cases
2009-10-30 20:25:27 +03:00
return
2010-02-11 18:43:44 +03:00
if self . guest_not_avail ( ) :
# Guest isn't running, schedule another try
2009-10-30 20:25:27 +03:00
self . activate_unavailable_page ( _ ( " Guest not running " ) )
self . schedule_retry ( )
return
2012-03-13 23:27:23 +04:00
ginfo = None
2010-01-11 18:30:40 +03:00
try :
2012-03-13 23:27:23 +04:00
gdevs = self . vm . get_graphics_devices ( )
gdev = gdevs and gdevs [ 0 ] or None
if gdev :
ginfo = ConnectionInfo ( self . vm . conn , gdev )
2010-01-11 18:30:40 +03:00
except Exception , e :
# We can fail here if VM is destroyed: xen is a bit racy
# and can't handle domain lookups that soon after
2012-01-17 07:04:40 +04:00
logging . exception ( " Getting graphics console failed: %s " , str ( e ) )
2010-01-11 18:30:40 +03:00
return
2012-03-13 23:27:23 +04:00
if ginfo is None :
2011-01-07 22:59:31 +03:00
logging . debug ( " No graphics configured for guest " )
2010-02-11 18:43:44 +03:00
self . activate_unavailable_page (
_ ( " Graphical console not configured for guest " ) )
2009-10-30 20:25:27 +03:00
return
2012-03-13 23:27:23 +04:00
if ginfo . gtype not in self . config . embeddable_graphics ( ) :
2011-04-10 07:03:32 +04:00
logging . debug ( " Don ' t know how to show graphics type ' %s ' "
2012-03-13 23:27:23 +04:00
" disabling console page " , ginfo . gtype )
2011-01-07 22:59:31 +03:00
msg = ( _ ( " Cannot display graphical console type ' %s ' " )
2012-03-13 23:27:23 +04:00
% ginfo . gtype )
2011-01-07 22:59:31 +03:00
self . activate_unavailable_page ( msg )
2009-10-30 20:25:27 +03:00
return
2013-06-30 19:44:58 +04:00
if ginfo . is_bad_localhost ( ) :
self . activate_unavailable_page (
_ ( " Guest is on a remote host with transport ' %s ' \n "
2014-01-29 20:02:54 +04:00
" but is only configured to listen on locally. \n "
2013-06-30 19:44:58 +04:00
" Connect using ' ssh ' transport or change the \n "
" guest ' s listen address. " % ginfo . transport ) )
return
2013-10-06 23:30:01 +04:00
if not ginfo . console_active ( ) :
2010-02-11 18:43:44 +03:00
self . activate_unavailable_page (
_ ( " Graphical console is not yet active for guest " ) )
2009-10-30 20:25:27 +03:00
self . schedule_retry ( )
return
2010-02-11 18:43:44 +03:00
self . activate_unavailable_page (
_ ( " Connecting to graphical console for guest " ) )
2012-03-13 23:27:23 +04:00
logging . debug ( " Starting connect process for %s " , ginfo . logstring ( ) )
2009-10-30 20:25:27 +03:00
try :
2012-03-13 23:27:23 +04:00
if ginfo . gtype == " vnc " :
2011-05-19 01:36:08 +04:00
self . viewer = VNCViewer ( self )
2012-03-13 23:27:23 +04:00
elif ginfo . gtype == " spice " :
2011-05-19 01:36:08 +04:00
self . viewer = SpiceViewer ( self )
2011-01-14 22:17:35 +03:00
self . set_enable_accel ( )
2012-03-13 23:27:23 +04:00
if ginfo . need_tunnel ( ) :
self . tunnels = Tunnels ( ginfo )
2013-09-07 03:36:09 +04:00
self . viewer . open_ginfo ( ginfo )
2011-01-13 19:26:38 +03:00
except Exception , e :
logging . exception ( " Error connection to graphical console " )
self . activate_unavailable_page (
_ ( " Error connecting to graphical console " ) + " : \n %s " % e )
2009-10-30 20:25:27 +03:00
2010-12-09 19:22:35 +03:00
def set_credentials ( self , src_ignore = None ) :
2011-07-14 21:13:13 +04:00
passwd = self . widget ( " console-auth-password " )
2012-05-14 17:24:56 +04:00
if passwd . get_visible ( ) :
2010-12-22 04:13:11 +03:00
self . viewer . set_credential_password ( passwd . get_text ( ) )
2011-07-14 21:13:13 +04:00
username = self . widget ( " console-auth-username " )
2012-05-14 17:24:56 +04:00
if username . get_visible ( ) :
2010-12-22 04:13:11 +03:00
self . viewer . set_credential_username ( username . get_text ( ) )
2009-10-30 20:25:27 +03:00
2011-07-14 21:13:13 +04:00
if self . widget ( " console-auth-remember " ) . get_active ( ) :
2009-10-30 20:25:27 +03:00
self . config . set_console_password ( self . vm , passwd . get_text ( ) ,
username . get_text ( ) )
2010-12-09 19:22:35 +03:00
def scroll_size_allocate ( self , src_ignore , req ) :
2011-04-26 18:51:19 +04:00
if not self . viewer or not self . viewer . get_desktop_resolution ( ) :
2009-11-29 02:48:56 +03:00
return
2013-04-21 20:10:14 +04:00
scroll = self . widget ( " console-gfx-scroll " )
2010-12-22 04:13:11 +03:00
is_scale = self . viewer . get_scaling ( )
2014-02-05 16:09:53 +04:00
is_resizeguest = self . viewer . get_resizeguest ( )
2009-11-29 02:48:56 +03:00
dx = 0
dy = 0
align_ratio = float ( req . width ) / float ( req . height )
2014-03-22 19:21:19 +04:00
# pylint: disable=unpacking-non-sequence
2011-04-26 18:51:19 +04:00
desktop_w , desktop_h = self . viewer . get_desktop_resolution ( )
if desktop_h == 0 :
return
2010-12-22 04:13:11 +03:00
desktop_ratio = float ( desktop_w ) / float ( desktop_h )
2009-11-29 02:48:56 +03:00
2014-01-30 01:58:34 +04:00
if is_scale or self . force_resize :
# Make sure we never show scrollbars when scaling
scroll . set_policy ( Gtk . PolicyType . NEVER , Gtk . PolicyType . NEVER )
else :
scroll . set_policy ( Gtk . PolicyType . AUTOMATIC ,
Gtk . PolicyType . AUTOMATIC )
2014-02-05 16:09:53 +04:00
if not self . force_resize and is_resizeguest :
# With resize guest, we don't want to maintain aspect ratio,
2014-04-26 19:01:08 +04:00
# since the guest can resize to arbitrary resolutions.
2014-09-21 21:25:43 +04:00
self . viewer . set_size_request ( req . width , req . height )
2014-02-05 16:09:53 +04:00
return
2014-01-30 01:58:34 +04:00
if not is_scale or self . force_resize :
2009-11-29 02:48:56 +03:00
# Scaling disabled is easy, just force the VNC widget size. Since
# we are inside a scrollwindow, it shouldn't cause issues.
2014-01-30 01:58:34 +04:00
self . force_resize = False
2014-09-21 21:25:43 +04:00
self . viewer . set_size_request ( desktop_w , desktop_h )
2009-11-29 02:48:56 +03:00
return
# Make sure there is no hard size requirement so we can scale down
2014-09-21 21:25:43 +04:00
self . viewer . set_size_request ( - 1 , - 1 )
2009-11-29 02:48:56 +03:00
# Make sure desktop aspect ratio is maintained
2010-12-22 04:13:11 +03:00
if align_ratio > desktop_ratio :
desktop_w = int ( req . height * desktop_ratio )
desktop_h = req . height
dx = ( req . width - desktop_w ) / 2
2009-11-29 02:48:56 +03:00
else :
2010-12-22 04:13:11 +03:00
desktop_w = req . width
desktop_h = int ( req . width / desktop_ratio )
dy = ( req . height - desktop_h ) / 2
2009-11-29 02:48:56 +03:00
2013-04-15 00:39:02 +04:00
viewer_alloc = Gdk . Rectangle ( )
viewer_alloc . x = dx
viewer_alloc . y = dy
viewer_alloc . width = desktop_w
viewer_alloc . height = desktop_h
2014-09-21 21:25:43 +04:00
self . viewer . size_allocate ( viewer_alloc )
2013-09-02 01:40:38 +04:00
###########################
# Serial console handling #
###########################
def activate_default_console_page ( self ) :
if self . vm . get_graphics_devices ( ) or not self . vm . get_serial_devs ( ) :
return
# Show serial console
devs = self . build_serial_list ( )
for name , ignore , sensitive , ignore , cb , serialidx in devs :
if not sensitive or not cb :
continue
self . _show_serial_tab ( name , serialidx )
break
def build_serial_list ( self ) :
ret = [ ]
def add_row ( text , err , sensitive , do_radio , cb , serialidx ) :
ret . append ( [ text , err , sensitive , do_radio , cb , serialidx ] )
devs = self . vm . get_serial_devs ( )
if len ( devs ) == 0 :
add_row ( _ ( " No text console available " ) ,
None , False , False , None , None )
def build_desc ( dev ) :
if dev . virtual_device_type == " console " :
return " Text Console %d " % ( dev . vmmindex + 1 )
return " Serial %d " % ( dev . vmmindex + 1 )
for dev in devs :
desc = build_desc ( dev )
idx = dev . vmmindex
err = vmmSerialConsole . can_connect ( self . vm , dev )
sensitive = not bool ( err )
def cb ( src ) :
return self . control_serial_tab ( src , desc , idx )
add_row ( desc , err , sensitive , True , cb , idx )
return ret
def current_serial_dev ( self ) :
current_page = self . widget ( " console-pages " ) . get_current_page ( )
if not current_page > = CONSOLE_PAGE_OFFSET :
return
serial_idx = current_page - CONSOLE_PAGE_OFFSET
if len ( self . serial_tabs ) < serial_idx :
return
return self . serial_tabs [ serial_idx ]
def control_serial_tab ( self , src_ignore , name , target_port ) :
self . widget ( " details-pages " ) . set_current_page ( DETAILS_PAGE_CONSOLE )
if name == " graphics " :
self . widget ( " console-pages " ) . set_current_page ( self . last_gfx_page )
else :
self . _show_serial_tab ( name , target_port )
def _show_serial_tab ( self , name , target_port ) :
serial = None
for s in self . serial_tabs :
if s . name == name :
serial = s
break
if not serial :
serial = vmmSerialConsole ( self . vm , target_port , name )
2013-12-19 01:27:33 +04:00
serial . terminal . connect ( " focus-in-event " ,
self . viewer_focus_changed )
serial . terminal . connect ( " focus-out-event " ,
self . viewer_focus_changed )
2013-09-02 01:40:38 +04:00
title = Gtk . Label ( label = name )
self . widget ( " console-pages " ) . append_page ( serial . box , title )
self . serial_tabs . append ( serial )
2013-11-10 02:54:47 +04:00
serial . open_console ( )
2013-09-02 01:40:38 +04:00
page_idx = self . serial_tabs . index ( serial ) + CONSOLE_PAGE_OFFSET
self . widget ( " console-pages " ) . set_current_page ( page_idx )
def populate_serial_menu ( self , src ) :
for ent in src :
src . remove ( ent )
serial_page_dev = self . current_serial_dev ( )
showing_graphics = (
self . widget ( " console-pages " ) . get_current_page ( ) ==
CONSOLE_PAGE_VIEWER )
# Populate serial devices
group = None
itemlist = self . build_serial_list ( )
for msg , err , sensitive , do_radio , cb , ignore in itemlist :
if do_radio :
item = Gtk . RadioMenuItem ( group )
item . set_label ( msg )
if group is None :
group = item
else :
2014-02-26 00:57:38 +04:00
item = Gtk . MenuItem . new_with_label ( msg )
2013-09-02 01:40:38 +04:00
item . set_sensitive ( sensitive )
if err and not sensitive :
item . set_tooltip_text ( err )
if cb :
item . connect ( " toggled " , cb )
# Tab is already open, make sure marked as such
if ( sensitive and
serial_page_dev and
serial_page_dev . name == msg ) :
item . set_active ( True )
src . add ( item )
src . add ( Gtk . SeparatorMenuItem ( ) )
# Populate graphical devices
devs = self . vm . get_graphics_devices ( )
if len ( devs ) == 0 :
2014-02-26 00:57:38 +04:00
item = Gtk . MenuItem . new_with_label (
_ ( " No graphical console available " ) )
2013-09-02 01:40:38 +04:00
item . set_sensitive ( False )
src . add ( item )
else :
dev = devs [ 0 ]
item = Gtk . RadioMenuItem ( group )
item . set_label ( _ ( " Graphical Console %s " ) %
dev . pretty_type_simple ( dev . type ) )
if group is None :
group = item
if showing_graphics :
item . set_active ( True )
item . connect ( " toggled " , self . control_serial_tab ,
dev . virtual_device_type , dev . type )
src . add ( item )
src . show_all ( )