2010-05-11 16:51:31 +02:00
#!/usr/bin/python2.6
#
# The Qubes OS Project, http://www.qubes-os.org
#
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
#
# 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.
#
#
import sys
2011-10-31 21:47:51 +01:00
import os
2012-05-15 09:11:25 +02:00
import signal
2012-05-06 20:55:52 +02:00
import fcntl
2012-05-11 12:50:45 +02:00
import errno
2012-04-27 12:55:54 +02:00
import dbus
2010-05-11 16:51:31 +02:00
from PyQt4 . QtCore import *
from PyQt4 . QtGui import *
from qubes . qubes import QubesVmCollection
from qubes . qubes import QubesException
from qubes . qubes import qubes_store_filename
from qubes . qubes import QubesVmLabels
from qubes . qubes import dry_run
from qubes . qubes import qubes_guid_path
from qubes . qubes import QubesDaemonPidfile
2010-09-16 18:49:36 +02:00
from qubes . qubes import QubesHost
2011-10-31 21:46:35 +01:00
from qubes import qubesutils
2010-05-11 16:51:31 +02:00
2012-01-24 16:49:14 +01:00
import qubesmanager . resources_rc
2010-05-11 16:51:31 +02:00
import ui_newappvmdlg
2012-01-22 18:45:41 +01:00
from ui_mainwindow import *
2012-05-11 22:52:27 +02:00
from create_new_vm import NewVmDlg
2012-01-31 14:29:13 +01:00
from settings import VMSettingsWindow
from restore import RestoreVMsWindow
from backup import BackupVMsWindow
2012-01-31 17:29:00 +01:00
from global_settings import GlobalSettingsWindow
2012-05-10 13:46:55 +02:00
from log_dialog import LogDialog
2012-02-20 07:56:38 +01:00
from thread_monitor import *
2010-05-11 16:51:31 +02:00
from pyinotify import WatchManager , Notifier , ThreadedNotifier , EventsCodes , ProcessEvent
import subprocess
import time
2011-05-12 19:08:15 +02:00
from datetime import datetime , timedelta
2012-05-08 18:57:30 +02:00
from qubes . qubes import updates_stat_file
2012-05-15 12:26:10 +02:00
qubes_dom0_updates_stat_file = ' /var/lib/qubes/updates/dom0-updates-available '
2010-05-11 16:51:31 +02:00
2011-03-02 14:42:11 +01:00
qubes_guid_path = ' /usr/bin/qubes_guid '
2011-10-31 21:47:51 +01:00
update_suggestion_interval = 14 # 14 days
2012-03-27 10:36:58 +02:00
power_order = Qt . DescendingOrder
update_order = Qt . AscendingOrder
2010-05-11 16:51:31 +02:00
class QubesConfigFileWatcher ( ProcessEvent ) :
def __init__ ( self , update_func ) :
self . update_func = update_func
2011-03-07 16:48:53 +01:00
2011-03-14 22:17:28 +01:00
def process_IN_MODIFY ( self , event ) :
2010-05-11 16:51:31 +02:00
self . update_func ( )
2012-03-21 13:27:10 +01:00
class VmIconWidget ( QWidget ) :
2012-04-23 10:58:26 +02:00
def __init__ ( self , icon_path , enabled = True , size_multiplier = 0.7 , tooltip = None , parent = None ) :
2012-03-21 13:27:10 +01:00
super ( VmIconWidget , self ) . __init__ ( parent )
label_icon = QLabel ( )
icon = QIcon ( icon_path )
icon_sz = QSize ( VmManagerWindow . row_height * size_multiplier , VmManagerWindow . row_height * size_multiplier )
icon_pixmap = icon . pixmap ( icon_sz , QIcon . Disabled if not enabled else QIcon . Normal )
label_icon . setPixmap ( icon_pixmap )
label_icon . setFixedSize ( icon_sz )
2012-04-23 10:58:26 +02:00
if tooltip != None :
label_icon . setToolTip ( tooltip )
2012-03-21 13:27:10 +01:00
layout = QHBoxLayout ( )
layout . addWidget ( label_icon )
layout . setContentsMargins ( 0 , 0 , 0 , 0 )
self . setLayout ( layout )
2012-04-23 10:58:26 +02:00
class VmTypeWidget ( VmIconWidget ) :
class VmTypeItem ( QTableWidgetItem ) :
def __init__ ( self , value ) :
super ( VmTypeWidget . VmTypeItem , self ) . __init__ ( )
self . value = value
def set_value ( self , value ) :
self . value = value
def __lt__ ( self , other ) :
return self . value < other . value
def __init__ ( self , vm , parent = None ) :
( icon_path , tooltip ) = self . get_vm_icon ( vm )
2012-04-24 18:30:19 +02:00
super ( VmTypeWidget , self ) . __init__ ( icon_path , True , 0.8 , tooltip , parent )
2012-04-23 10:58:26 +02:00
self . vm = vm
self . tableItem = self . VmTypeItem ( self . value )
def get_vm_icon ( self , vm ) :
if vm . qid == 0 :
self . value = 0
return ( " :/dom0.png " , " Dom0 " )
elif vm . is_netvm ( ) and not vm . is_proxyvm ( ) :
self . value = 1
return ( " :/netvm.png " , " NetVM " )
elif vm . is_proxyvm ( ) :
self . value = 2
return ( " :/proxyvm.png " , " ProxyVM " )
elif vm . is_template ( ) :
self . value = 3
return ( " :/templatevm.png " , " TemplateVM " )
elif vm . is_appvm ( ) and vm . template is None :
self . value = 4
return ( " :/standalonevm.png " , " StandaloneVM " )
elif vm . type == " HVM " :
self . value = 5
return ( " :/hvm.png " , " HVM " )
elif vm . is_appvm ( ) or vm . is_disposablevm ( ) :
self . value = 5 + vm . label . index
2012-04-24 18:30:19 +02:00
return ( " :/appvm.png " , " AppVM " )
2012-04-23 10:58:26 +02:00
2012-03-21 13:27:10 +01:00
class VmLabelWidget ( VmIconWidget ) :
class VmLabelItem ( QTableWidgetItem ) :
def __init__ ( self , value ) :
super ( VmLabelWidget . VmLabelItem , self ) . __init__ ( )
self . value = value
def set_value ( self , value ) :
self . value = value
def __lt__ ( self , other ) :
return self . value < other . value
2010-05-11 16:51:31 +02:00
def __init__ ( self , vm , parent = None ) :
2012-03-21 13:27:10 +01:00
icon_path = self . get_vm_icon_path ( vm )
2012-04-23 10:58:26 +02:00
super ( VmLabelWidget , self ) . __init__ ( icon_path , True , 0.8 , None , parent )
2010-05-11 16:51:31 +02:00
self . vm = vm
2012-03-21 13:27:10 +01:00
self . tableItem = self . VmLabelItem ( self . value )
2010-05-11 16:51:31 +02:00
2012-03-21 13:27:10 +01:00
def get_vm_icon_path ( self , vm ) :
2012-04-27 10:58:10 +02:00
if vm . qid == 0 :
self . value = 100
return " :/off.png "
else :
self . value = vm . label . index
return vm . label . icon_path
2012-04-23 10:58:26 +02:00
2012-03-21 13:27:10 +01:00
2010-05-11 16:51:31 +02:00
2012-03-21 13:27:10 +01:00
class VmNameItem ( QTableWidgetItem ) :
def __init__ ( self , vm ) :
super ( VmNameItem , self ) . __init__ ( )
2012-05-30 14:24:46 +02:00
self . setFlags ( Qt . ItemIsSelectable | Qt . ItemIsEnabled )
2012-03-21 13:27:10 +01:00
self . setText ( vm . name )
self . setTextAlignment ( Qt . AlignVCenter )
self . qid = vm . qid
2010-05-11 16:51:31 +02:00
2012-03-23 09:13:40 +01:00
class VmStatusIcon ( QLabel ) :
2012-03-20 22:59:48 +01:00
def __init__ ( self , vm , parent = None ) :
2012-03-23 09:13:40 +01:00
super ( VmStatusIcon , self ) . __init__ ( parent )
2012-03-20 22:59:48 +01:00
self . vm = vm
2012-03-23 09:13:40 +01:00
self . set_on_icon ( )
2012-03-20 22:59:48 +01:00
self . previous_power_state = vm . last_power_state
def update ( self ) :
if self . previous_power_state != self . vm . last_power_state :
2012-03-23 09:13:40 +01:00
self . set_on_icon ( )
2012-03-20 22:59:48 +01:00
self . previous_power_state = self . vm . last_power_state
2010-05-11 16:51:31 +02:00
2012-03-23 09:13:40 +01:00
def set_on_icon ( self ) :
2012-04-20 16:56:51 +02:00
if self . vm . last_power_state == " Running " :
2012-03-23 09:13:40 +01:00
icon = QIcon ( " :/on.png " )
2012-04-23 19:55:16 +02:00
elif self . vm . last_power_state in [ " Transient " , " Halting " , " Dying " ] :
2012-04-20 16:56:51 +02:00
icon = QIcon ( " :/transient.png " )
2012-03-23 09:13:40 +01:00
else :
icon = QIcon ( " :/off.png " )
2012-04-20 16:56:51 +02:00
2012-03-23 09:13:40 +01:00
icon_sz = QSize ( VmManagerWindow . row_height * 0.5 , VmManagerWindow . row_height * 0.5 )
icon_pixmap = icon . pixmap ( icon_sz )
self . setPixmap ( icon_pixmap )
self . setFixedSize ( icon_sz )
2012-03-20 22:59:48 +01:00
class VmInfoWidget ( QWidget ) :
2012-03-21 14:30:08 +01:00
class VmInfoItem ( QTableWidgetItem ) :
def __init__ ( self , upd_info_item , vm ) :
super ( VmInfoWidget . VmInfoItem , self ) . __init__ ( )
self . upd_info_item = upd_info_item
self . vm = vm
def __lt__ ( self , other ) :
2012-03-23 09:12:30 +01:00
self_val = self . upd_info_item . value
other_val = other . upd_info_item . value
2012-03-27 10:36:58 +02:00
if self . tableWidget ( ) . horizontalHeader ( ) . sortIndicatorOrder ( ) == update_order :
# the result will be sorted by upd, sorting order: Ascending
2012-03-23 09:12:30 +01:00
self_val + = 1 if self . vm . is_running ( ) else 0
other_val + = 1 if other . vm . is_running ( ) else 0
2012-03-27 10:36:58 +02:00
return ( self_val ) > ( other_val )
elif self . tableWidget ( ) . horizontalHeader ( ) . sortIndicatorOrder ( ) == power_order :
#the result will be sorted by power state, sorting order: Descending
self_val = - ( self_val / 10 + 10 * ( 1 if self . vm . is_running ( ) else 0 ) )
other_val = - ( other_val / 10 + 10 * ( 1 if other . vm . is_running ( ) else 0 ) )
return ( self_val ) > ( other_val )
2012-03-23 09:12:30 +01:00
else :
2012-03-27 10:36:58 +02:00
#it would be strange if this happened
return
2012-03-21 14:30:08 +01:00
2012-03-20 22:59:48 +01:00
def __init__ ( self , vm , parent = None ) :
super ( VmInfoWidget , self ) . __init__ ( parent )
2012-03-21 14:30:08 +01:00
self . vm = vm
2012-03-20 22:59:48 +01:00
layout = QHBoxLayout ( )
self . on_icon = VmStatusIcon ( vm )
self . upd_info = VmUpdateInfoWidget ( vm , show_text = False )
self . blk_icon = VmIconWidget ( " :/mount.png " )
layout . addWidget ( self . on_icon )
layout . addWidget ( self . upd_info )
layout . addItem ( QSpacerItem ( 0 , 10 , QSizePolicy . Expanding , QtGui . QSizePolicy . Expanding ) )
layout . addWidget ( self . blk_icon )
layout . setContentsMargins ( 5 , 0 , 5 , 0 )
self . setLayout ( layout )
self . blk_icon . setVisible ( False )
2012-03-21 14:30:08 +01:00
self . tableItem = self . VmInfoItem ( self . upd_info . tableItem , vm )
2012-03-20 22:59:48 +01:00
def update_vm_state ( self , vm , blk_visible ) :
self . on_icon . update ( )
self . upd_info . update_outdated ( vm )
2012-02-14 19:00:50 +01:00
if blk_visible != None :
self . blk_icon . setVisible ( blk_visible )
2010-05-11 16:51:31 +02:00
2012-02-06 20:22:11 +01:00
class VmTemplateItem ( QTableWidgetItem ) :
def __init__ ( self , vm ) :
super ( VmTemplateItem , self ) . __init__ ( )
2012-05-30 14:24:46 +02:00
self . setFlags ( Qt . ItemIsSelectable | Qt . ItemIsEnabled )
2012-03-12 12:53:07 +01:00
if vm . template is not None :
self . setText ( vm . template . name )
2010-05-11 16:51:31 +02:00
else :
2012-02-06 20:22:11 +01:00
font = QFont ( )
font . setStyle ( QFont . StyleItalic )
self . setFont ( font )
self . setTextColor ( QColor ( " gray " ) )
2012-03-12 12:53:07 +01:00
if vm . is_appvm ( ) : # and vm.template is None
2012-02-06 20:22:11 +01:00
self . setText ( " StandaloneVM " )
2012-01-22 18:45:41 +01:00
elif vm . is_template ( ) :
2012-02-06 20:22:11 +01:00
self . setText ( " TemplateVM " )
2012-01-22 18:45:41 +01:00
elif vm . qid == 0 :
2012-02-06 20:22:11 +01:00
self . setText ( " AdminVM " )
2012-01-22 18:45:41 +01:00
elif vm . is_netvm ( ) :
2012-02-06 20:22:11 +01:00
self . setText ( " NetVM " )
2012-01-22 18:45:41 +01:00
else :
2012-02-06 20:22:11 +01:00
self . setText ( " --- " )
2010-05-11 16:51:31 +02:00
2012-04-30 17:40:19 +02:00
self . setTextAlignment ( Qt . AlignVCenter )
2010-05-11 16:51:31 +02:00
2012-02-06 20:22:11 +01:00
class VmNetvmItem ( QTableWidgetItem ) :
def __init__ ( self , vm ) :
super ( VmNetvmItem , self ) . __init__ ( )
2012-05-30 14:24:46 +02:00
self . setFlags ( Qt . ItemIsSelectable | Qt . ItemIsEnabled )
2012-01-22 18:45:41 +01:00
2012-02-22 06:15:11 +01:00
if vm . is_netvm ( ) and not vm . is_proxyvm ( ) :
self . setText ( " n/a " )
2012-03-04 22:02:27 +01:00
elif vm . netvm is not None :
self . setText ( vm . netvm . name )
2012-01-22 18:45:41 +01:00
else :
2012-02-06 20:22:11 +01:00
self . setText ( " --- " )
2012-04-30 17:40:19 +02:00
self . setTextAlignment ( Qt . AlignVCenter )
2011-04-03 01:23:28 +02:00
2011-03-07 16:48:19 +01:00
2012-01-22 18:45:41 +01:00
class VmUsageBarWidget ( QWidget ) :
2012-02-06 20:22:11 +01:00
class VmUsageBarItem ( QTableWidgetItem ) :
def __init__ ( self , value ) :
super ( VmUsageBarWidget . VmUsageBarItem , self ) . __init__ ( )
self . value = value
def set_value ( self , value ) :
self . value = value
def __lt__ ( self , other ) :
2012-03-06 20:05:35 +01:00
return int ( self . value ) < int ( other . value )
2012-02-06 20:22:11 +01:00
2012-02-13 18:19:54 +01:00
def __init__ ( self , min , max , format , update_func , vm , load , hue = 210 , parent = None ) :
2012-01-22 18:45:41 +01:00
super ( VmUsageBarWidget , self ) . __init__ ( parent )
2012-02-06 20:22:11 +01:00
2011-03-07 16:48:19 +01:00
2012-01-22 18:45:41 +01:00
self . min = min
self . max = max
self . update_func = update_func
2012-02-06 20:22:11 +01:00
self . value = min
2011-03-07 16:48:19 +01:00
2012-01-22 18:45:41 +01:00
self . widget = QProgressBar ( )
self . widget . setMinimum ( min )
self . widget . setMaximum ( max )
self . widget . setFormat ( format ) ;
2012-02-05 18:41:41 +01:00
self . widget . setStyleSheet (
2012-02-13 18:19:54 +01:00
" QProgressBar:horizontal { " + \
" border: 1px solid hsv( {0} , 100, 250); " . format ( hue ) + \
" border-radius: 4px; \
2012-02-05 18:41:41 +01:00
background : white ; \
text - align : center ; \
} \
QProgressBar : : chunk : horizontal { \
2012-02-13 18:19:54 +01:00
background : qlineargradient ( x1 : 0 , y1 : 0.5 , x2 : 1 , y2 : 0.5 , " + \
" stop: 0 hsv( {0} , 170, 207), " . format ( hue ) +
" stop: 1 white); \
2012-02-05 18:41:41 +01:00
} "
)
2011-03-07 16:48:19 +01:00
2012-01-22 18:45:41 +01:00
layout = QHBoxLayout ( )
layout . addWidget ( self . widget )
self . setLayout ( layout )
2012-02-06 20:22:11 +01:00
self . tableItem = self . VmUsageBarItem ( min )
2012-01-22 18:45:41 +01:00
self . update_load ( vm , load )
2012-02-06 20:22:11 +01:00
2012-01-22 18:45:41 +01:00
def update_load ( self , vm , load ) :
2012-02-06 20:22:11 +01:00
self . value = self . update_func ( vm , load )
self . widget . setValue ( self . value )
self . tableItem . set_value ( self . value )
class ChartWidget ( QWidget ) :
class ChartItem ( QTableWidgetItem ) :
def __init__ ( self , value ) :
super ( ChartWidget . ChartItem , self ) . __init__ ( )
self . value = value
def set_value ( self , value ) :
self . value = value
def __lt__ ( self , other ) :
return self . value < other . value
2010-05-11 16:51:31 +02:00
2012-02-06 20:22:11 +01:00
def __init__ ( self , vm , update_func , hue , load = 0 , parent = None ) :
super ( ChartWidget , self ) . __init__ ( parent )
self . update_func = update_func
self . hue = hue
2012-02-06 22:32:36 +01:00
if hue < 0 or hue > 255 :
self . hue = 255
2012-02-06 20:22:11 +01:00
self . load = load
2010-05-11 16:51:31 +02:00
assert self . load > = 0 and self . load < = 100 , " load = {0} " . format ( self . load )
self . load_history = [ self . load ]
2012-02-06 20:22:11 +01:00
self . tableItem = ChartWidget . ChartItem ( self . load )
2010-05-11 16:51:31 +02:00
2012-02-06 20:22:11 +01:00
def update_load ( self , vm , load ) :
self . load = self . update_func ( vm , load )
2012-02-06 22:32:36 +01:00
assert self . load > = 0 , " load = {0} " . format ( self . load )
# assert self.load >= 0 and self.load <= 100, "load = {0}".format(self.load)
if self . load > 100 :
# FIXME: This is an ugly workaround for cpu_load:/
self . load = 100
2011-09-14 20:04:33 +02:00
2010-05-11 16:51:31 +02:00
self . load_history . append ( self . load )
2012-02-06 20:22:11 +01:00
self . tableItem . set_value ( self . load )
2010-05-11 16:51:31 +02:00
self . repaint ( )
def paintEvent ( self , Event = None ) :
p = QPainter ( self )
dx = 4
2011-03-07 16:48:53 +01:00
W = self . width ( )
2010-05-11 16:51:31 +02:00
H = self . height ( ) - 5
N = len ( self . load_history )
if N > W / dx :
2010-05-12 16:18:38 +02:00
tail = N - W / dx
2010-05-11 16:51:31 +02:00
N = W / dx
2010-05-12 16:18:38 +02:00
self . load_history = self . load_history [ tail : ]
assert len ( self . load_history ) == N
2010-05-11 16:51:31 +02:00
for i in range ( 0 , N - 1 ) :
val = self . load_history [ N - i - 1 ]
sat = 70 + val * ( 255 - 70 ) / 100
2012-02-06 20:22:11 +01:00
color = QColor . fromHsv ( self . hue , sat , 255 )
2010-05-11 16:51:31 +02:00
pen = QPen ( color )
pen . setWidth ( dx - 1 )
p . setPen ( pen )
if val > 0 :
p . drawLine ( W - i * dx - dx , H , W - i * dx - dx , H - ( H - 5 ) * val / 100 )
2010-09-16 18:49:36 +02:00
2012-02-06 20:22:11 +01:00
class VmUpdateInfoWidget ( QWidget ) :
2010-09-16 18:49:36 +02:00
2012-02-06 20:22:11 +01:00
class VmUpdateInfoItem ( QTableWidgetItem ) :
def __init__ ( self , value ) :
super ( VmUpdateInfoWidget . VmUpdateInfoItem , self ) . __init__ ( )
2012-03-21 14:30:08 +01:00
self . value = 0
self . set_value ( value )
2010-09-16 18:49:36 +02:00
2012-02-06 20:22:11 +01:00
def set_value ( self , value ) :
2012-03-21 14:30:08 +01:00
if value == " outdated " :
self . value = 30
elif value == " update " :
self . value = 20
2012-02-06 20:22:11 +01:00
else :
2012-03-21 14:30:08 +01:00
self . value = 0
def __lt__ ( self , other ) :
return self . value < other . value
2012-01-22 18:45:41 +01:00
2012-02-05 18:41:41 +01:00
def __init__ ( self , vm , show_text = True , parent = None ) :
2012-01-22 18:45:41 +01:00
super ( VmUpdateInfoWidget , self ) . __init__ ( parent )
2012-01-24 20:59:44 +01:00
layout = QHBoxLayout ( )
2012-02-05 18:41:41 +01:00
self . show_text = show_text
if self . show_text :
self . label = QLabel ( " " )
layout . addWidget ( self . label , alignment = Qt . AlignCenter )
else :
self . icon = QLabel ( " " )
2012-03-02 02:57:54 +01:00
layout . addWidget ( self . icon , alignment = Qt . AlignCenter )
2012-01-22 18:45:41 +01:00
self . setLayout ( layout )
self . previous_outdated = False
2012-02-05 18:41:41 +01:00
self . previous_update_recommended = None
2012-02-06 20:22:11 +01:00
self . value = None
self . tableItem = VmUpdateInfoWidget . VmUpdateInfoItem ( self . value )
2012-01-22 18:45:41 +01:00
def update_outdated ( self , vm ) :
2012-04-23 11:24:48 +02:00
if vm . type == " HVM " :
return
2012-01-22 18:45:41 +01:00
outdated = vm . is_outdated ( )
if outdated and not self . previous_outdated :
2012-02-05 18:41:41 +01:00
self . update_status_widget ( " outdated " )
2012-04-25 17:13:54 +02:00
elif not outdated and self . previous_outdated :
self . update_status_widget ( None )
2012-02-05 18:41:41 +01:00
2012-01-22 18:45:41 +01:00
self . previous_outdated = outdated
2012-04-23 10:58:26 +02:00
2012-05-15 12:26:10 +02:00
if not vm . is_updateable ( ) :
return
2012-05-08 18:57:30 +02:00
if vm . qid == 0 :
2012-01-22 18:45:41 +01:00
update_recommended = self . previous_update_recommended
2012-05-15 12:26:10 +02:00
if os . path . exists ( qubes_dom0_updates_stat_file ) :
update_recommended = True
else :
update_recommended = False
else :
2012-05-08 18:57:30 +02:00
update_recommended = self . previous_update_recommended
stat_file_path = vm . dir_path + ' / ' + updates_stat_file
if not os . path . exists ( stat_file_path ) :
2012-01-22 18:45:41 +01:00
update_recommended = False
2012-05-08 18:57:30 +02:00
else :
if ( not hasattr ( vm , " updates_stat_file_read_time " ) ) or vm . updates_stat_file_read_time < = os . path . getmtime ( stat_file_path ) :
2012-02-05 18:41:41 +01:00
2012-05-08 18:57:30 +02:00
stat_file = open ( stat_file_path , " r " )
updates = stat_file . read ( ) . strip ( )
stat_file . close ( )
if updates . isdigit ( ) :
updates = int ( updates )
else :
updates = 0
if updates == 0 :
update_recommended = False
else :
update_recommended = True
vm . updates_stat_file_read_time = time . time ( )
2012-05-15 12:26:10 +02:00
if update_recommended and not self . previous_update_recommended :
self . update_status_widget ( " update " )
elif self . previous_update_recommended and not update_recommended :
self . update_status_widget ( None )
2012-05-08 18:57:30 +02:00
2012-05-15 12:26:10 +02:00
self . previous_update_recommended = update_recommended
2012-01-22 18:45:41 +01:00
2012-04-25 17:13:54 +02:00
2012-02-05 18:41:41 +01:00
def update_status_widget ( self , state ) :
2012-02-06 20:22:11 +01:00
self . value = state
self . tableItem . set_value ( state )
2012-04-30 17:56:07 +02:00
if state == " update " :
2012-02-05 18:41:41 +01:00
label_text = " <font color= \" #CCCC00 \" >Check updates</font> "
2012-04-24 20:52:55 +02:00
icon_path = " :/update-recommended.png "
2012-05-08 18:57:30 +02:00
tooltip_text = " Updates pending! "
2012-02-05 18:41:41 +01:00
elif state == " outdated " :
label_text = " <font color= \" red \" >VM outdated</font> "
2012-04-24 20:52:55 +02:00
icon_path = " :/outdated.png "
2012-04-25 17:13:54 +02:00
tooltip_text = " The VM must be restarted for its filesystem to reflect the template ' s recent changes. "
elif state == None :
label_text = " "
icon_path = None
tooltip_text = None
2012-02-05 18:41:41 +01:00
if self . show_text :
self . label . setText ( label_text )
else :
self . layout ( ) . removeWidget ( self . icon )
self . icon . deleteLater ( )
2012-04-25 17:13:54 +02:00
if icon_path != None :
self . icon = VmIconWidget ( icon_path , True , 0.7 )
self . icon . setToolTip ( tooltip_text )
else :
self . icon = QLabel ( label_text )
2012-02-05 18:41:41 +01:00
self . layout ( ) . addWidget ( self . icon , alignment = Qt . AlignCenter )
2012-01-22 18:45:41 +01:00
2012-04-27 17:04:35 +02:00
class VmSizeOnDiskItem ( QTableWidgetItem ) :
def __init__ ( self , vm ) :
super ( VmSizeOnDiskItem , self ) . __init__ ( )
2012-05-30 14:24:46 +02:00
self . setFlags ( Qt . ItemIsSelectable | Qt . ItemIsEnabled )
2012-04-27 17:04:35 +02:00
self . vm = vm
self . value = 0
self . update ( )
2012-04-30 17:40:19 +02:00
self . setTextAlignment ( Qt . AlignVCenter )
2012-04-27 17:04:35 +02:00
def update ( self ) :
if self . vm . qid == 0 :
self . setText ( " n/a " )
else :
self . value = self . vm . get_disk_utilization ( ) / ( 1024 * 1024 )
self . setText ( str ( self . value ) + " MiB " )
def __lt__ ( self , other ) :
return self . value < other . value
2010-09-16 18:49:36 +02:00
2010-05-11 16:51:31 +02:00
class VmRowInTable ( object ) :
2012-02-13 18:19:54 +01:00
cpu_graph_hue = 210
mem_graph_hue = 120
2012-02-08 18:21:15 +01:00
def __init__ ( self , vm , row_no , table , block_manager ) :
2010-05-11 16:51:31 +02:00
self . vm = vm
self . row_no = row_no
table . setRowHeight ( row_no , VmManagerWindow . row_height )
2012-04-23 10:58:26 +02:00
self . type_widget = VmTypeWidget ( vm )
table . setCellWidget ( row_no , VmManagerWindow . columns_indices [ ' Type ' ] , self . type_widget )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' Type ' ] , self . type_widget . tableItem )
2012-03-21 13:27:10 +01:00
self . label_widget = VmLabelWidget ( vm )
table . setCellWidget ( row_no , VmManagerWindow . columns_indices [ ' Label ' ] , self . label_widget )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' Label ' ] , self . label_widget . tableItem )
2012-03-20 22:59:48 +01:00
2012-03-21 13:27:10 +01:00
self . name_widget = VmNameItem ( vm )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' Name ' ] , self . name_widget )
2010-05-11 16:51:31 +02:00
2012-03-21 13:27:10 +01:00
self . info_widget = VmInfoWidget ( vm )
table . setCellWidget ( row_no , VmManagerWindow . columns_indices [ ' State ' ] , self . info_widget )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' State ' ] , self . info_widget . tableItem )
2012-02-05 18:41:41 +01:00
2012-02-06 20:22:11 +01:00
self . template_widget = VmTemplateItem ( vm )
2012-03-21 13:27:10 +01:00
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' Template ' ] , self . template_widget )
2012-02-06 20:22:11 +01:00
self . netvm_widget = VmNetvmItem ( vm )
2012-03-21 13:27:10 +01:00
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' NetVM ' ] , self . netvm_widget )
2012-01-22 18:45:41 +01:00
2012-02-13 18:19:54 +01:00
self . cpu_usage_widget = VmUsageBarWidget ( 0 , 100 , " % v % " ,
2012-04-20 16:56:51 +02:00
lambda vm , val : val if vm . last_running else 0 , vm , 0 , self . cpu_graph_hue )
2012-03-21 13:27:10 +01:00
table . setCellWidget ( row_no , VmManagerWindow . columns_indices [ ' CPU ' ] , self . cpu_usage_widget )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' CPU ' ] , self . cpu_usage_widget . tableItem )
2011-03-07 16:48:19 +01:00
2012-04-20 16:56:51 +02:00
self . load_widget = ChartWidget ( vm , lambda vm , val : val if vm . last_running else 0 , self . cpu_graph_hue , 0 )
2012-03-21 13:27:10 +01:00
table . setCellWidget ( row_no , VmManagerWindow . columns_indices [ ' CPU Graph ' ] , self . load_widget )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' CPU Graph ' ] , self . load_widget . tableItem )
2012-01-22 18:45:41 +01:00
2012-02-05 18:41:41 +01:00
self . mem_usage_widget = VmUsageBarWidget ( 0 , qubes_host . memory_total / 1024 , " % v MB " ,
2012-04-20 16:56:51 +02:00
lambda vm , val : vm . get_mem ( ) / 1024 if vm . last_running else 0 , vm , 0 , self . mem_graph_hue )
2012-03-21 13:27:10 +01:00
table . setCellWidget ( row_no , VmManagerWindow . columns_indices [ ' MEM ' ] , self . mem_usage_widget )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' MEM ' ] , self . mem_usage_widget . tableItem )
2012-02-06 20:22:11 +01:00
2012-04-20 16:56:51 +02:00
self . mem_widget = ChartWidget ( vm , lambda vm , val : vm . get_mem ( ) * 100 / qubes_host . memory_total if vm . last_running else 0 , self . mem_graph_hue , 0 )
2012-03-21 13:27:10 +01:00
table . setCellWidget ( row_no , VmManagerWindow . columns_indices [ ' MEM Graph ' ] , self . mem_widget )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' MEM Graph ' ] , self . mem_widget . tableItem )
2010-09-16 18:49:36 +02:00
2012-04-27 17:04:35 +02:00
self . size_widget = VmSizeOnDiskItem ( vm )
table . setItem ( row_no , VmManagerWindow . columns_indices [ ' Size ' ] , self . size_widget )
2012-05-30 13:54:19 +02:00
def update ( self , blk_visible = None , cpu_load = None , update_size_on_disk = False ) :
2012-02-14 19:00:50 +01:00
self . info_widget . update_vm_state ( self . vm , blk_visible )
2011-06-02 01:15:24 +02:00
if cpu_load is not None :
2012-01-22 18:45:41 +01:00
self . cpu_usage_widget . update_load ( self . vm , cpu_load )
self . mem_usage_widget . update_load ( self . vm , None )
2011-06-02 01:15:24 +02:00
self . load_widget . update_load ( self . vm , cpu_load )
2012-02-06 20:22:11 +01:00
self . mem_widget . update_load ( self . vm , None )
2012-04-27 17:04:35 +02:00
if update_size_on_disk == True :
self . size_widget . update ( )
2010-05-11 16:51:31 +02:00
2012-05-11 22:52:27 +02:00
2010-05-11 16:51:31 +02:00
2012-05-13 16:56:27 +02:00
vm_shutdown_timeout = 20000 # in msec
2010-05-11 16:51:31 +02:00
class VmShutdownMonitor ( QObject ) :
2012-05-13 16:56:27 +02:00
def __init__ ( self , vm , shutdown_time = vm_shutdown_timeout ) :
2010-05-11 16:51:31 +02:00
self . vm = vm
2012-05-13 16:56:27 +02:00
self . shutdown_time = shutdown_time
2010-05-11 16:51:31 +02:00
def check_if_vm_has_shutdown ( self ) :
vm = self . vm
2011-09-30 20:39:39 +02:00
vm_start_time = vm . get_start_time ( )
2012-05-13 16:56:27 +02:00
if not vm . is_running ( ) or ( vm_start_time and vm_start_time > = datetime . utcnow ( ) - timedelta ( 0 , self . shutdown_time / 1000 ) ) :
2011-04-03 01:24:16 +02:00
if vm . is_template ( ) :
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " You have just modified template ' {0} ' . You should now restart all the VMs based on it, so they could see the changes. " . format ( vm . name ) , msecs = 8000 )
2010-05-11 16:51:31 +02:00
return
2011-03-07 16:48:53 +01:00
reply = QMessageBox . question ( None , " VM Shutdown " ,
2012-05-13 16:56:27 +02:00
" The VM <b> ' {0} ' </b> hasn ' t shutdown within the last {1} seconds, do you want to kill it?<br> " . format ( vm . name , self . shutdown_time / 1000 ) ,
" Kill it! " , " Wait another {0} seconds... " . format ( self . shutdown_time / 1000 ) )
2010-05-11 16:51:31 +02:00
if reply == 0 :
vm . force_shutdown ( )
else :
2012-05-13 16:56:27 +02:00
QTimer . singleShot ( self . shutdown_time , self . check_if_vm_has_shutdown )
2010-05-11 16:51:31 +02:00
2012-01-22 18:45:41 +01:00
class VmManagerWindow ( Ui_VmManagerWindow , QMainWindow ) :
2012-01-24 20:59:44 +01:00
row_height = 30
2012-02-05 18:41:41 +01:00
column_width = 200
2012-03-26 11:48:49 +02:00
min_visible_rows = 10
2010-05-11 16:51:31 +02:00
update_interval = 1000 # in msec
2011-04-08 20:11:31 +02:00
show_inactive_vms = True
2012-04-23 10:58:26 +02:00
columns_indices = { " Type " : 0 ,
" Label " : 1 ,
" Name " : 2 ,
" State " : 3 ,
" Template " : 4 ,
" NetVM " : 5 ,
" CPU " : 6 ,
" CPU Graph " : 7 ,
" MEM " : 8 ,
2012-04-27 17:04:35 +02:00
" MEM Graph " : 9 ,
" Size " : 10 , }
2010-05-11 16:51:31 +02:00
2011-03-07 16:48:19 +01:00
2012-01-22 18:45:41 +01:00
def __init__ ( self , parent = None ) :
super ( VmManagerWindow , self ) . __init__ ( )
self . setupUi ( self )
self . toolbar = self . toolBar
2012-04-13 01:08:36 +02:00
self . qubes_watch = qubesutils . QubesWatch ( )
2010-05-11 16:51:31 +02:00
self . qvm_collection = QubesVmCollection ( )
2012-02-14 19:00:50 +01:00
self . blk_manager = QubesBlockDevicesManager ( self . qvm_collection )
2012-04-13 01:08:36 +02:00
self . qubes_watch . setup_block_watch ( self . blk_manager . block_devs_event )
self . blk_watch_thread = threading . Thread ( target = self . qubes_watch . watch_loop )
self . blk_watch_thread . daemon = True
self . blk_watch_thread . start ( )
2010-05-11 16:51:31 +02:00
self . connect ( self . table , SIGNAL ( " itemSelectionChanged() " ) , self . table_selection_changed )
2012-01-22 18:45:41 +01:00
2012-02-05 18:41:41 +01:00
self . table . setColumnWidth ( 0 , self . column_width )
2012-01-24 20:59:44 +01:00
self . setSizeIncrement ( QtCore . QSize ( 200 , 30 ) )
self . centralwidget . setSizeIncrement ( QtCore . QSize ( 200 , 30 ) )
self . table . setSizeIncrement ( QtCore . QSize ( 200 , 30 ) )
2012-03-27 10:36:58 +02:00
self . sort_by_mem = None
self . sort_by_cpu = None
self . sort_by_state = None
2012-04-27 17:04:35 +02:00
self . sort_by_size_on_disk = None
2012-03-27 10:36:58 +02:00
2012-03-28 12:45:31 +02:00
self . screen_number = - 1
self . screen_changed = False
2012-05-29 17:05:34 +02:00
self . running_vms_count = 0
2012-03-28 12:45:31 +02:00
self . frame_width = 0
self . frame_height = 0
2010-05-11 16:51:31 +02:00
self . fill_table ( )
2012-03-28 12:45:31 +02:00
self . move ( self . x ( ) , 0 )
2012-04-26 16:32:40 +02:00
self . columns_actions = { }
self . columns_actions [ self . columns_indices [ " Type " ] ] = self . action_vm_type
self . columns_actions [ self . columns_indices [ " Label " ] ] = self . action_label
self . columns_actions [ self . columns_indices [ " Name " ] ] = self . action_name
self . columns_actions [ self . columns_indices [ " State " ] ] = self . action_state
self . columns_actions [ self . columns_indices [ " Template " ] ] = self . action_template
self . columns_actions [ self . columns_indices [ " NetVM " ] ] = self . action_netvm
self . columns_actions [ self . columns_indices [ " CPU " ] ] = self . action_cpu
self . columns_actions [ self . columns_indices [ " CPU Graph " ] ] = self . action_cpu_graph
self . columns_actions [ self . columns_indices [ " MEM " ] ] = self . action_mem
self . columns_actions [ self . columns_indices [ " MEM Graph " ] ] = self . action_mem_graph
2012-04-27 17:04:35 +02:00
self . columns_actions [ self . columns_indices [ " Size " ] ] = self . action_size_on_disk
2012-04-26 16:32:40 +02:00
2012-03-28 12:45:31 +02:00
2012-04-26 16:32:40 +02:00
self . visible_columns_count = len ( self . columns_indices ) ;
2012-01-24 20:59:44 +01:00
self . table . setColumnHidden ( self . columns_indices [ " NetVM " ] , True )
2012-04-26 16:32:40 +02:00
self . action_netvm . setChecked ( False )
2012-01-24 20:59:44 +01:00
self . table . setColumnHidden ( self . columns_indices [ " CPU Graph " ] , True )
2012-04-26 16:32:40 +02:00
self . action_cpu_graph . setChecked ( False )
2012-01-24 20:59:44 +01:00
self . table . setColumnHidden ( self . columns_indices [ " MEM Graph " ] , True )
2012-04-26 16:32:40 +02:00
self . action_mem_graph . setChecked ( False )
2012-04-27 17:04:35 +02:00
self . table . setColumnHidden ( self . columns_indices [ " Size " ] , True )
self . action_size_on_disk . setChecked ( False )
2012-04-26 16:32:40 +02:00
2012-03-20 22:59:48 +01:00
self . table . setColumnWidth ( self . columns_indices [ " State " ] , 80 )
2012-03-21 13:27:10 +01:00
self . table . setColumnWidth ( self . columns_indices [ " Name " ] , 150 )
2012-04-23 10:58:26 +02:00
self . table . setColumnWidth ( self . columns_indices [ " Label " ] , 40 )
self . table . setColumnWidth ( self . columns_indices [ " Type " ] , 40 )
2012-04-27 17:04:35 +02:00
self . table . setColumnWidth ( self . columns_indices [ " Size " ] , 100 )
2012-04-25 13:11:33 +02:00
self . action_showallvms . setChecked ( True )
2012-03-06 10:45:59 +01:00
self . table . horizontalHeader ( ) . setResizeMode ( QHeaderView . Fixed )
2012-03-23 09:12:30 +01:00
2012-04-23 10:58:26 +02:00
self . table . sortItems ( self . columns_indices [ " Type " ] , Qt . AscendingOrder )
2012-02-06 20:22:11 +01:00
2012-02-14 19:00:50 +01:00
self . context_menu = QMenu ( self )
self . context_menu . addAction ( self . action_removevm )
self . context_menu . addAction ( self . action_resumevm )
self . context_menu . addAction ( self . action_pausevm )
self . context_menu . addAction ( self . action_shutdownvm )
2012-04-22 18:32:50 +02:00
self . context_menu . addAction ( self . action_killvm )
2012-04-26 17:46:41 +02:00
self . context_menu . addAction ( self . action_settings )
2012-02-14 19:00:50 +01:00
self . context_menu . addAction ( self . action_appmenus )
self . context_menu . addAction ( self . action_editfwrules )
self . context_menu . addAction ( self . action_updatevm )
2012-05-11 12:50:45 +02:00
self . context_menu . addAction ( self . action_run_command_in_vm )
2012-04-24 17:48:10 +02:00
self . context_menu . addAction ( self . action_set_keyboard_layout )
2012-04-23 19:01:22 +02:00
self . context_menu . addMenu ( self . logs_menu )
2012-02-14 19:00:50 +01:00
self . context_menu . addMenu ( self . blk_menu )
2012-03-20 18:36:48 +01:00
self . context_menu . addSeparator ( )
2012-02-14 19:00:50 +01:00
2012-04-26 17:46:41 +02:00
self . table_selection_changed ( )
2012-03-06 20:05:35 +01:00
self . connect ( self . table . horizontalHeader ( ) , SIGNAL ( " sortIndicatorChanged(int, Qt::SortOrder) " ) , self . sortIndicatorChanged )
2012-02-14 19:00:50 +01:00
self . connect ( self . table , SIGNAL ( " customContextMenuRequested(const QPoint&) " ) , self . open_context_menu )
self . connect ( self . blk_menu , SIGNAL ( " triggered(QAction *) " ) , self . attach_dettach_device_triggered )
2012-04-23 19:01:22 +02:00
self . connect ( self . logs_menu , SIGNAL ( " triggered(QAction *) " ) , self . show_log )
2012-02-08 19:02:35 +01:00
2012-02-05 18:41:41 +01:00
self . table . setContentsMargins ( 0 , 0 , 0 , 0 )
self . centralwidget . layout ( ) . setContentsMargins ( 0 , 0 , 0 , 0 )
self . layout ( ) . setContentsMargins ( 0 , 0 , 0 , 0 )
2012-05-29 17:05:34 +02:00
2012-03-06 18:45:47 +01:00
self . connect ( self . action_menubar , SIGNAL ( " toggled(bool) " ) , self . showhide_menubar )
self . connect ( self . action_toolbar , SIGNAL ( " toggled(bool) " ) , self . showhide_toolbar )
2011-04-02 15:48:48 +02:00
self . counter = 0
2012-04-27 17:04:35 +02:00
self . update_size_on_disk = False
2011-04-02 15:48:48 +02:00
self . shutdown_monitor = { }
2011-06-02 01:15:24 +02:00
self . last_measure_results = { }
self . last_measure_time = time . time ( )
2011-04-02 15:48:48 +02:00
QTimer . singleShot ( self . update_interval , self . update_table )
2010-05-11 16:51:31 +02:00
2012-03-06 18:45:47 +01:00
2012-02-05 18:41:41 +01:00
def show ( self ) :
super ( VmManagerWindow , self ) . show ( )
2012-03-28 12:45:31 +02:00
self . screen_number = app . desktop ( ) . screenNumber ( self )
2012-02-05 18:41:41 +01:00
2012-03-28 12:45:31 +02:00
def set_table_geom_size ( self ) :
2010-05-11 16:51:31 +02:00
2012-03-28 12:45:31 +02:00
desktop_width = app . desktop ( ) . availableGeometry ( self ) . width ( ) - self . frame_width # might be wrong...
desktop_height = app . desktop ( ) . availableGeometry ( self ) . height ( ) - self . frame_height # might be wrong...
desktop_height - = self . row_height #UGLY! to somehow ommit taskbar...
W = self . table . horizontalHeader ( ) . length ( ) + \
self . table . verticalScrollBar ( ) . width ( ) + \
2 * self . table . frameWidth ( ) + 1
H = self . table . horizontalHeader ( ) . height ( ) + \
2 * self . table . frameWidth ( )
2010-05-11 16:51:31 +02:00
2012-03-06 18:45:47 +01:00
mainwindow_to_add = 0
2012-03-28 12:45:31 +02:00
available_space = desktop_height
2012-03-06 18:45:47 +01:00
if self . menubar . isVisible ( ) :
2012-04-26 16:32:40 +02:00
menubar_height = self . menubar . sizeHint ( ) . height ( ) + self . menubar . contentsMargins ( ) . top ( ) + self . menubar . contentsMargins ( ) . bottom ( )
2012-03-28 12:45:31 +02:00
available_space - = menubar_height
mainwindow_to_add + = menubar_height
2012-03-06 18:45:47 +01:00
if self . toolbar . isVisible ( ) :
2012-04-26 16:32:40 +02:00
toolbar_height = self . toolbar . sizeHint ( ) . height ( ) + self . toolbar . contentsMargins ( ) . top ( ) + self . toolbar . contentsMargins ( ) . bottom ( )
2012-03-28 12:45:31 +02:00
available_space - = toolbar_height
mainwindow_to_add + = toolbar_height
if W > = desktop_width :
available_space - = self . table . horizontalScrollBar ( ) . height ( )
H + = self . table . horizontalScrollBar ( ) . height ( )
default_rows = int ( available_space / self . row_height )
2012-02-05 18:41:41 +01:00
2012-05-29 17:05:34 +02:00
if self . show_inactive_vms :
n = self . table . rowCount ( )
else :
n = self . running_vms_count
2012-04-26 16:32:40 +02:00
2012-03-28 12:45:31 +02:00
if n > default_rows :
H + = default_rows * self . row_height
self . table . verticalScrollBar ( ) . show ( )
else :
H + = n * self . row_height
self . table . verticalScrollBar ( ) . hide ( )
W - = self . table . verticalScrollBar ( ) . width ( )
2012-03-26 11:48:49 +02:00
2012-03-28 12:45:31 +02:00
W = min ( desktop_width , W )
self . centralwidget . setFixedHeight ( H )
H + = mainwindow_to_add
self . setMaximumHeight ( H )
self . setMinimumHeight ( H )
self . table . setFixedWidth ( W )
self . centralwidget . setFixedWidth ( W )
# don't change the following two lines to setFixedWidth!
self . setMaximumWidth ( W )
self . setMinimumWidth ( W )
def moveEvent ( self , event ) :
super ( VmManagerWindow , self ) . moveEvent ( event )
screen_number = app . desktop ( ) . screenNumber ( self )
if self . screen_number != screen_number :
self . screen_changed = True
self . screen_number = screen_number
2010-05-11 16:51:31 +02:00
2012-03-26 11:48:49 +02:00
2010-05-11 16:51:31 +02:00
def get_vms_list ( self ) :
self . qvm_collection . lock_db_for_reading ( )
self . qvm_collection . load ( )
self . qvm_collection . unlock_db ( )
2012-05-29 17:05:34 +02:00
running_count = 0
2011-04-02 15:27:40 +02:00
vms_list = [ vm for vm in self . qvm_collection . values ( ) ]
for vm in vms_list :
2012-04-20 16:56:51 +02:00
vm . last_power_state = vm . get_power_state ( )
2012-04-23 19:55:16 +02:00
vm . last_running = vm . last_power_state in [ " Running " , " Transient " ]
2012-05-29 17:05:34 +02:00
if vm . last_running :
running_count + = 1
2010-05-11 16:51:31 +02:00
2012-05-29 17:05:34 +02:00
self . running_vms_count = running_count
2010-05-11 16:51:31 +02:00
2012-05-29 17:05:34 +02:00
return vms_list
2010-05-11 16:51:31 +02:00
def fill_table ( self ) :
2012-02-06 20:22:11 +01:00
self . table . setSortingEnabled ( False )
self . table . clearContents ( )
2010-05-11 16:51:31 +02:00
vms_list = self . get_vms_list ( )
self . table . setRowCount ( len ( vms_list ) )
2012-05-29 17:05:34 +02:00
2012-02-07 12:48:53 +01:00
vms_in_table = { }
2010-05-11 16:51:31 +02:00
2011-04-02 15:27:40 +02:00
row_no = 0
for vm in vms_list :
2011-04-04 12:02:12 +02:00
if vm . internal :
continue
2012-02-14 19:00:50 +01:00
vm_row = VmRowInTable ( vm , row_no , self . table , self . blk_manager )
2012-02-13 13:50:30 +01:00
vms_in_table [ vm . qid ] = vm_row
2012-05-29 17:05:34 +02:00
2011-04-02 15:27:40 +02:00
row_no + = 1
2010-05-11 16:51:31 +02:00
2011-04-02 15:27:40 +02:00
self . table . setRowCount ( row_no )
2010-05-11 16:51:31 +02:00
self . vms_list = vms_list
self . vms_in_table = vms_in_table
self . reload_table = False
2012-02-06 20:22:11 +01:00
self . table . setSortingEnabled ( True )
2012-05-29 17:05:34 +02:00
2012-06-19 00:17:22 +02:00
self . showhide_inactive_vms ( True )
self . set_table_geom_size ( )
2012-05-29 17:05:34 +02:00
if not self . show_inactive_vms :
self . showhide_inactive_vms ( self . show_inactive_vms )
self . set_table_geom_size ( )
def showhide_inactive_vms ( self , show_inactive ) :
if show_inactive :
row_no = 0
while row_no < self . table . rowCount ( ) :
self . table . setRowHidden ( row_no , False )
row_no + = 1
else :
row_no = 0
while row_no < self . table . rowCount ( ) :
widget = self . table . cellWidget ( row_no , self . columns_indices [ " State " ] )
running = widget . vm . last_running
if not running :
self . table . setRowHidden ( row_no , True )
row_no + = 1
2012-03-27 10:36:58 +02:00
2010-05-11 16:51:31 +02:00
def mark_table_for_update ( self ) :
self . reload_table = True
# When calling update_table() directly, always use out_of_schedule=True!
def update_table ( self , out_of_schedule = False ) :
2012-03-28 12:45:31 +02:00
2012-05-29 19:00:21 +02:00
update_devs = self . update_block_devices ( ) or out_of_schedule
2012-05-30 13:54:19 +02:00
reload_table = self . reload_table
2011-04-02 16:27:18 +02:00
if manager_window . isVisible ( ) :
some_vms_have_changed_power_state = False
for vm in self . vms_list :
2012-04-20 16:56:51 +02:00
state = vm . get_power_state ( )
2011-04-02 16:27:18 +02:00
if vm . last_power_state != state :
2012-05-29 17:05:34 +02:00
prev_running = vm . last_running
2011-04-02 16:27:18 +02:00
vm . last_power_state = state
2012-04-23 19:55:16 +02:00
vm . last_running = ( state in [ " Running " , " Transient " ] )
2012-05-29 17:05:34 +02:00
if not prev_running and vm . last_running :
self . running_vms_count + = 1
some_vms_have_changed_power_state = True
elif prev_running and not vm . last_running :
self . running_vms_count - = 1
some_vms_have_changed_power_state = True
2010-05-11 16:51:31 +02:00
2012-03-28 12:45:31 +02:00
if self . screen_changed == True :
reload_table = True
self . screen_changed = False
2012-05-29 17:05:34 +02:00
if reload_table :
2011-04-02 16:27:18 +02:00
self . fill_table ( )
2012-02-08 18:21:15 +01:00
update_devs = True
2010-05-11 16:51:31 +02:00
2012-05-29 17:05:34 +02:00
if ( not self . show_inactive_vms ) and some_vms_have_changed_power_state :
self . showhide_inactive_vms ( True )
self . showhide_inactive_vms ( False )
self . set_table_geom_size ( )
2012-03-28 12:45:31 +02:00
if self . sort_by_state != None and some_vms_have_changed_power_state :
2012-03-23 09:12:30 +01:00
self . table . sortItems ( self . columns_indices [ " State " ] , self . sort_by_state )
2012-02-14 19:00:50 +01:00
blk_visible = None
rows_with_blk = None
if update_devs == True :
rows_with_blk = [ ]
2012-04-13 01:08:36 +02:00
self . blk_manager . blk_lock . acquire ( )
2012-02-14 19:00:50 +01:00
for d in self . blk_manager . attached_devs :
rows_with_blk . append ( self . blk_manager . attached_devs [ d ] [ ' attached_to ' ] [ ' vm ' ] )
2012-04-13 01:08:36 +02:00
self . blk_manager . blk_lock . release ( )
2012-02-14 19:00:50 +01:00
2012-05-30 13:54:19 +02:00
if ( not self . table . isColumnHidden ( self . columns_indices [ ' Size ' ] ) ) and self . counter % 60 == 0 or out_of_schedule :
2012-04-27 17:04:35 +02:00
self . update_size_on_disk = True
2011-06-02 01:15:24 +02:00
if self . counter % 3 == 0 or out_of_schedule :
( self . last_measure_time , self . last_measure_results ) = \
qubes_host . measure_cpu_usage ( self . last_measure_results ,
self . last_measure_time )
2012-02-07 12:48:53 +01:00
for vm_row in self . vms_in_table . values ( ) :
2011-06-02 01:15:24 +02:00
cur_cpu_load = None
if vm_row . vm . get_xid ( ) in self . last_measure_results :
cur_cpu_load = self . last_measure_results [ vm_row . vm . xid ] [ ' cpu_usage ' ]
else :
cur_cpu_load = 0
2012-02-14 19:00:50 +01:00
if rows_with_blk != None :
if vm_row . vm . name in rows_with_blk :
blk_visible = True
else :
blk_visible = False
2012-05-30 13:54:19 +02:00
vm_row . update ( blk_visible = blk_visible , cpu_load = cur_cpu_load , update_size_on_disk = self . update_size_on_disk )
2012-03-06 20:05:35 +01:00
2011-06-02 01:15:24 +02:00
else :
2012-02-07 12:48:53 +01:00
for vm_row in self . vms_in_table . values ( ) :
2012-02-14 19:00:50 +01:00
if rows_with_blk != None :
if vm_row . vm . name in rows_with_blk :
blk_visible = True
else :
blk_visible = False
2012-05-30 13:54:19 +02:00
vm_row . update ( blk_visible = blk_visible , update_size_on_disk = self . update_size_on_disk )
2012-03-23 09:12:30 +01:00
if self . sort_by_cpu != None :
self . table . sortItems ( self . columns_indices [ " CPU " ] , self . sort_by_cpu )
elif self . sort_by_mem != None :
self . table . sortItems ( self . columns_indices [ " MEM " ] , self . sort_by_mem )
2012-03-27 10:36:58 +02:00
elif self . sort_by_state != None and reload_table :
#needed to sort after reload (fill_table sorts items with setSortingEnabled, but by that time the widgets values are not correct yet).
self . table . sortItems ( self . columns_indices [ " State " ] , self . sort_by_state )
2012-04-27 17:04:35 +02:00
elif self . sort_by_size_on_disk != None and self . update_size_on_disk == True :
self . table . sortItems ( self . columns_indices [ " Size " ] , self . sort_by_size_on_disk )
2012-03-23 09:12:30 +01:00
2012-02-28 07:45:08 +01:00
self . table_selection_changed ( )
2010-05-11 16:51:31 +02:00
2012-04-27 17:04:35 +02:00
self . update_size_on_disk = False
2010-05-11 16:51:31 +02:00
if not out_of_schedule :
self . counter + = 1
QTimer . singleShot ( self . update_interval , self . update_table )
2012-03-28 12:45:31 +02:00
2012-02-08 18:21:15 +01:00
def update_block_devices ( self ) :
2012-04-13 01:08:36 +02:00
res , msg = self . blk_manager . check_for_updates ( )
2012-02-14 19:00:50 +01:00
if msg != None and len ( msg ) > 0 :
str = " \n " . join ( msg )
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , str , msecs = 5000 )
2012-02-14 19:00:50 +01:00
return res
2010-05-11 16:51:31 +02:00
2012-03-27 10:36:58 +02:00
2012-03-06 20:05:35 +01:00
def sortIndicatorChanged ( self , column , order ) :
if column == self . columns_indices [ " CPU " ] or column == self . columns_indices [ " CPU Graph " ] :
self . sort_by_mem = None
2012-03-23 09:12:30 +01:00
self . sort_by_state = None
2012-04-27 17:04:35 +02:00
self . sort_by_size_on_disk = None
2012-03-06 20:05:35 +01:00
self . sort_by_cpu = order
2012-03-23 09:12:30 +01:00
return
2012-03-06 20:05:35 +01:00
elif column == self . columns_indices [ " MEM " ] or column == self . columns_indices [ " MEM Graph " ] :
self . sort_by_cpu = None
2012-03-23 09:12:30 +01:00
self . sort_by_state = None
2012-04-27 17:04:35 +02:00
self . sort_by_size_on_disk = None
2012-03-06 20:05:35 +01:00
self . sort_by_mem = order
2012-03-23 09:12:30 +01:00
return
2012-04-27 17:04:35 +02:00
elif column == self . columns_indices [ " State " ] :
2012-03-23 09:12:30 +01:00
self . sort_by_cpu = None
self . sort_by_mem = None
2012-04-27 17:04:35 +02:00
self . sort_by_size_on_disk = None
2012-03-27 10:36:58 +02:00
self . sort_by_state = order
2012-03-23 09:12:30 +01:00
return
2012-04-27 17:04:35 +02:00
elif column == self . columns_indices [ " Size " ] :
self . sort_by_cpu = None
self . sort_by_mem = None
self . sort_by_state = None
self . sort_by_size_on_disk = order
return
2012-03-06 20:05:35 +01:00
else :
self . sort_by_cpu = None
self . sort_by_mem = None
2012-03-23 09:12:30 +01:00
self . sort_by_state = None
2012-03-06 20:05:35 +01:00
2012-03-23 09:12:30 +01:00
2010-05-11 16:51:31 +02:00
def table_selection_changed ( self ) :
2012-01-31 14:29:13 +01:00
2010-05-11 16:51:31 +02:00
vm = self . get_selected_vm ( )
2012-02-08 18:21:15 +01:00
if vm != None :
# Update available actions:
2012-06-19 00:24:13 +02:00
self . action_settings . setEnabled ( vm . qid != 0 )
2012-04-20 16:56:51 +02:00
self . action_removevm . setEnabled ( not vm . installed_by_rpm and not ( vm . last_running ) )
self . action_resumevm . setEnabled ( not vm . last_running )
self . action_pausevm . setEnabled ( vm . last_running and vm . qid != 0 )
self . action_shutdownvm . setEnabled ( vm . last_running and vm . qid != 0 )
2012-04-22 18:32:50 +02:00
self . action_killvm . setEnabled ( vm . last_running and vm . qid != 0 )
2012-02-08 18:21:15 +01:00
self . action_appmenus . setEnabled ( not vm . is_netvm ( ) )
self . action_editfwrules . setEnabled ( vm . is_networked ( ) and not ( vm . is_netvm ( ) and not vm . is_proxyvm ( ) ) )
self . action_updatevm . setEnabled ( vm . is_updateable ( ) or vm . qid == 0 )
2012-05-11 12:50:45 +02:00
self . action_run_command_in_vm . setEnabled ( vm . qid != 0 )
2012-04-24 17:48:10 +02:00
self . action_set_keyboard_layout . setEnabled ( vm . qid != 0 and vm . last_running )
2012-02-08 18:21:15 +01:00
else :
self . action_settings . setEnabled ( False )
self . action_removevm . setEnabled ( False )
self . action_resumevm . setEnabled ( False )
self . action_pausevm . setEnabled ( False )
self . action_shutdownvm . setEnabled ( False )
2012-04-22 18:32:50 +02:00
self . action_killvm . setEnabled ( False )
2012-02-08 18:21:15 +01:00
self . action_appmenus . setEnabled ( False )
self . action_editfwrules . setEnabled ( False )
self . action_updatevm . setEnabled ( False )
2012-05-11 12:50:45 +02:00
self . action_run_command_in_vm . setEnabled ( False )
2012-04-24 17:48:10 +02:00
self . action_set_keyboard_layout . setEnabled ( False )
2012-02-08 18:21:15 +01:00
2010-05-11 16:51:31 +02:00
2011-03-07 16:48:19 +01:00
2010-05-11 16:51:31 +02:00
def closeEvent ( self , event ) :
2011-04-10 22:44:38 +02:00
if event . spontaneous ( ) : # There is something borked in Qt, as the logic here is inverted on X11
self . hide ( )
event . ignore ( )
2010-05-11 16:51:31 +02:00
2012-02-14 19:00:50 +01:00
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_createvm_triggered ' )
def action_createvm_triggered ( self ) :
2012-05-11 22:52:27 +02:00
dialog = NewVmDlg ( app , self . qvm_collection , trayIcon )
dialog . exec_ ( )
2010-05-11 16:51:31 +02:00
2012-05-11 22:52:27 +02:00
2010-05-11 16:51:31 +02:00
def get_selected_vm ( self ) :
2012-02-07 12:48:53 +01:00
#vm selection relies on the VmInfo widget's value used for sorting by VM name
2010-05-11 16:51:31 +02:00
row_index = self . table . currentRow ( )
2012-02-28 07:45:08 +01:00
if row_index != - 1 :
2012-03-21 13:27:10 +01:00
qid = self . table . item ( row_index , self . columns_indices [ " Name " ] ) . qid
2012-02-13 13:50:30 +01:00
assert self . vms_in_table [ qid ] is not None
vm = self . vms_in_table [ qid ] . vm
2012-02-08 18:21:15 +01:00
return vm
else :
return None
2010-05-11 16:51:31 +02:00
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_removevm_triggered ' )
def action_removevm_triggered ( self ) :
2012-02-05 18:41:41 +01:00
2010-05-11 16:51:31 +02:00
vm = self . get_selected_vm ( )
assert not vm . is_running ( )
assert not vm . installed_by_rpm
self . qvm_collection . lock_db_for_reading ( )
self . qvm_collection . load ( )
self . qvm_collection . unlock_db ( )
2011-03-05 15:12:34 +01:00
if vm . is_template ( ) :
2010-05-11 16:51:31 +02:00
dependent_vms = self . qvm_collection . get_vms_based_on ( vm . qid )
if len ( dependent_vms ) > 0 :
2011-03-07 16:48:53 +01:00
QMessageBox . warning ( None , " Warning! " ,
2010-05-11 16:51:31 +02:00
" This Template VM cannot be removed, because there is at least one AppVM that is based on it.<br> "
" <small>If you want to remove this Template VM and all the AppVMs based on it, "
" you should first remove each individual AppVM that uses this template.</small> " )
return
2011-03-07 16:48:53 +01:00
reply = QMessageBox . question ( None , " VM Removal Confirmation " ,
2010-05-11 16:51:31 +02:00
" Are you sure you want to remove the VM <b> ' {0} ' </b>?<br> "
" <small>All data on this VM ' s private storage will be lost!</small> " . format ( vm . name ) ,
QMessageBox . Yes | QMessageBox . Cancel )
if reply == QMessageBox . Yes :
thread_monitor = ThreadMonitor ( )
thread = threading . Thread ( target = self . do_remove_vm , args = ( vm , thread_monitor ) )
thread . daemon = True
thread . start ( )
progress = QProgressDialog ( " Removing VM: <b> {0} </b>... " . format ( vm . name ) , " " , 0 , 0 )
progress . setCancelButton ( None )
progress . setModal ( True )
progress . show ( )
2011-03-07 16:48:53 +01:00
2010-05-11 16:51:31 +02:00
while not thread_monitor . is_finished ( ) :
app . processEvents ( )
time . sleep ( 0.1 )
progress . hide ( )
if thread_monitor . success :
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " VM ' {0} ' has been removed. " . format ( vm . name ) , msecs = 3000 )
2010-05-11 16:51:31 +02:00
else :
2011-03-09 17:52:04 +01:00
QMessageBox . warning ( None , " Error removing VM! " , " ERROR: {0} " . format ( thread_monitor . error_msg ) )
2010-05-11 16:51:31 +02:00
def do_remove_vm ( self , vm , thread_monitor ) :
try :
self . qvm_collection . lock_db_for_writing ( )
self . qvm_collection . load ( )
#TODO: the following two conditions should really be checked by qvm_collection.pop() overload...
2012-03-02 00:16:12 +01:00
if vm . is_template ( ) and self . qvm_collection . default_template_qid == vm . qid :
self . qvm_collection . default_template_qid = None
if vm . is_netvm ( ) and self . qvm_collection . default_netvm_qid == vm . qid :
self . qvm_collection . default_netvm_qid = None
2010-05-11 16:51:31 +02:00
vm . remove_from_disk ( )
self . qvm_collection . pop ( vm . qid )
self . qvm_collection . save ( )
except Exception as ex :
thread_monitor . set_error_msg ( str ( ex ) )
finally :
self . qvm_collection . unlock_db ( )
thread_monitor . set_finished ( )
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_resumevm_triggered ' )
def action_resumevm_triggered ( self ) :
2011-03-02 14:42:11 +01:00
vm = self . get_selected_vm ( )
assert not vm . is_running ( )
2011-03-27 17:41:03 +02:00
if vm . is_paused ( ) :
try :
2011-06-06 01:20:49 +02:00
subprocess . check_call ( [ " /usr/sbin/xl " , " unpause " , vm . name ] )
2011-03-27 17:41:03 +02:00
except Exception as ex :
QMessageBox . warning ( None , " Error unpausing VM! " , " ERROR: {0} " . format ( ex ) )
return
2011-04-01 00:46:51 +02:00
thread_monitor = ThreadMonitor ( )
thread = threading . Thread ( target = self . do_start_vm , args = ( vm , thread_monitor ) )
thread . daemon = True
thread . start ( )
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " Starting ' {0} ' ... " . format ( vm . name ) , msecs = 3000 )
2011-04-01 00:46:51 +02:00
while not thread_monitor . is_finished ( ) :
app . processEvents ( )
time . sleep ( 0.1 )
if thread_monitor . success :
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " VM ' {0} ' has been started. " . format ( vm . name ) , msecs = 3000 )
2011-04-01 00:46:51 +02:00
else :
2012-05-25 15:48:12 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " Error starting VM <b> ' {0} ' </b>: {1} " . format ( vm . name , thread_monitor . error_msg ) , msecs = 3000 )
2011-04-01 00:46:51 +02:00
def do_start_vm ( self , vm , thread_monitor ) :
2011-03-02 14:42:11 +01:00
try :
vm . verify_files ( )
xid = vm . start ( )
2011-04-01 00:46:51 +02:00
except Exception as ex :
thread_monitor . set_error_msg ( str ( ex ) )
thread_monitor . set_finished ( )
2011-03-02 14:42:11 +01:00
return
2011-03-07 16:48:53 +01:00
2011-03-02 14:42:11 +01:00
retcode = subprocess . call ( [ qubes_guid_path , " -d " , str ( xid ) , " -c " , vm . label . color , " -i " , vm . label . icon , " -l " , str ( vm . label . index ) ] )
if ( retcode != 0 ) :
2011-04-03 02:26:23 +02:00
thread_monitor . set_error_msg ( " Cannot start qubes_guid! " )
2011-04-01 00:46:51 +02:00
thread_monitor . set_finished ( )
2010-05-11 16:51:31 +02:00
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_pausevm_triggered ' )
def action_pausevm_triggered ( self ) :
2011-03-02 14:50:21 +01:00
vm = self . get_selected_vm ( )
assert vm . is_running ( )
try :
2011-06-06 01:20:49 +02:00
subprocess . check_call ( [ " /usr/sbin/xl " , " pause " , vm . name ] )
2012-03-23 09:12:30 +01:00
2011-03-02 14:50:21 +01:00
except Exception as ex :
QMessageBox . warning ( None , " Error pausing VM! " , " ERROR: {0} " . format ( ex ) )
return
2010-05-11 16:51:31 +02:00
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_shutdownvm_triggered ' )
def action_shutdownvm_triggered ( self ) :
2010-05-11 16:51:31 +02:00
vm = self . get_selected_vm ( )
assert vm . is_running ( )
2012-05-25 13:59:58 +02:00
self . blk_manager . check_if_serves_as_backend ( vm )
2011-03-07 16:48:53 +01:00
reply = QMessageBox . question ( None , " VM Shutdown Confirmation " ,
2010-05-11 16:51:31 +02:00
" Are you sure you want to power down the VM <b> ' {0} ' </b>?<br> "
" <small>This will shutdown all the running applications within this VM.</small> " . format ( vm . name ) ,
QMessageBox . Yes | QMessageBox . Cancel )
2010-05-12 16:44:48 +02:00
app . processEvents ( )
2010-05-11 16:51:31 +02:00
if reply == QMessageBox . Yes :
2012-03-29 23:26:16 +02:00
self . shutdown_vm ( vm )
2012-05-13 16:56:27 +02:00
def shutdown_vm ( self , vm , shutdown_time = vm_shutdown_timeout ) :
2012-03-29 23:26:16 +02:00
try :
subprocess . check_call ( [ " /usr/sbin/xl " , " shutdown " , vm . name ] )
except Exception as ex :
QMessageBox . warning ( None , " Error shutting down VM! " , " ERROR: {0} " . format ( ex ) )
return
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " VM ' {0} ' is shutting down... " . format ( vm . name ) , msecs = 3000 )
2012-03-29 23:26:16 +02:00
2012-05-13 16:56:27 +02:00
self . shutdown_monitor [ vm . qid ] = VmShutdownMonitor ( vm , shutdown_time )
QTimer . singleShot ( shutdown_time , self . shutdown_monitor [ vm . qid ] . check_if_vm_has_shutdown )
2010-05-11 16:51:31 +02:00
2012-03-23 09:12:30 +01:00
2012-04-22 18:32:50 +02:00
@pyqtSlot ( name = ' on_action_killvm_triggered ' )
def action_killvm_triggered ( self ) :
vm = self . get_selected_vm ( )
assert vm . is_running ( )
reply = QMessageBox . question ( None , " VM Kill Confirmation " ,
" Are you sure you want to kill the VM <b> ' {0} ' </b>?<br> "
" <small>This will end <b>(not shutdown!)</b> all the running applications within this VM.</small> " . format ( vm . name ) ,
QMessageBox . Yes | QMessageBox . Cancel , QMessageBox . Cancel )
app . processEvents ( )
if reply == QMessageBox . Yes :
try :
vm . force_shutdown ( )
except Exception as ex :
QMessageBox . critical ( None , " Error while killing VM! " , " <b>An exception ocurred while killing {0} .</b><br>ERROR: {1} " . format ( vm . name , ex ) )
return
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " VM ' {0} ' killed! " . format ( vm . name ) , msecs = 3000 )
2012-04-22 18:32:50 +02:00
2010-05-11 16:51:31 +02:00
2012-01-31 14:29:13 +01:00
@pyqtSlot ( name = ' on_action_settings_triggered ' )
def action_settings_triggered ( self ) :
vm = self . get_selected_vm ( )
2012-02-13 13:50:30 +01:00
settings_window = VMSettingsWindow ( vm , app , self . qvm_collection , " basic " )
2012-01-31 14:29:13 +01:00
settings_window . exec_ ( )
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_appmenus_triggered ' )
def action_appmenus_triggered ( self ) :
2011-05-25 02:28:24 +02:00
vm = self . get_selected_vm ( )
2012-02-13 13:50:30 +01:00
settings_window = VMSettingsWindow ( vm , app , self . qvm_collection , " applications " )
2012-02-09 19:47:21 +01:00
settings_window . exec_ ( )
2011-05-25 02:28:24 +02:00
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_updatevm_triggered ' )
def action_updatevm_triggered ( self ) :
2011-03-02 12:51:29 +01:00
vm = self . get_selected_vm ( )
2011-10-31 21:46:35 +01:00
if not vm . is_running ( ) :
reply = QMessageBox . question ( None , " VM Update Confirmation " ,
2012-04-26 00:16:54 +02:00
" <b> {0} </b><br>The VM has to be running to be updated.<br>Do you want to start it?<br> " . format ( vm . name ) ,
2011-10-31 21:46:35 +01:00
QMessageBox . Yes | QMessageBox . Cancel )
if reply != QMessageBox . Yes :
return
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " Starting ' {0} ' ... " . format ( vm . name ) , msecs = 3000 )
2011-03-13 18:43:55 +01:00
app . processEvents ( )
2011-10-31 21:46:35 +01:00
thread_monitor = ThreadMonitor ( )
thread = threading . Thread ( target = self . do_update_vm , args = ( vm , thread_monitor ) )
thread . daemon = True
thread . start ( )
2012-04-26 00:16:54 +02:00
progress = QProgressDialog ( " <b> {0} </b><br>Please wait for the updater to launch... " . format ( vm . name ) , " " , 0 , 0 )
progress . setCancelButton ( None )
progress . setModal ( True )
progress . show ( )
2011-10-31 21:46:35 +01:00
while not thread_monitor . is_finished ( ) :
app . processEvents ( )
time . sleep ( 0.2 )
2012-04-26 00:16:54 +02:00
progress . hide ( )
2011-10-31 21:46:35 +01:00
2011-12-18 14:03:16 +01:00
if vm . qid != 0 :
2012-05-15 12:26:10 +02:00
if not thread_monitor . success :
2011-12-18 14:03:16 +01:00
QMessageBox . warning ( None , " Error VM update! " , " ERROR: {0} " . format ( thread_monitor . error_msg ) )
2011-10-31 21:46:35 +01:00
def do_update_vm ( self , vm , thread_monitor ) :
try :
2011-12-18 14:03:16 +01:00
if vm . qid == 0 :
2012-04-24 19:35:26 +02:00
subprocess . check_call ( [ " /usr/bin/qubes-dom0-update " , " --gui " ] )
2011-12-18 14:03:16 +01:00
else :
2012-02-10 20:49:16 +01:00
vm . run ( " user:gpk-update-viewer " , verbose = False , autostart = True )
2011-10-31 21:46:35 +01:00
except Exception as ex :
thread_monitor . set_error_msg ( str ( ex ) )
thread_monitor . set_finished ( )
return
thread_monitor . set_finished ( )
2011-03-02 12:51:29 +01:00
2012-04-24 17:48:10 +02:00
2012-05-11 12:50:45 +02:00
@pyqtSlot ( name = ' on_action_run_command_in_vm_triggered ' )
def action_run_command_in_vm_triggered ( self ) :
vm = self . get_selected_vm ( )
thread_monitor = ThreadMonitor ( )
thread = threading . Thread ( target = self . do_run_command_in_vm , args = ( vm , thread_monitor ) )
thread . daemon = True
thread . start ( )
while not thread_monitor . is_finished ( ) :
app . processEvents ( )
time . sleep ( 0.2 )
if not thread_monitor . success :
QMessageBox . warning ( None , " Error while running command " , " Exception while running command:<br> {0} " . format ( thread_monitor . error_msg ) )
def do_run_command_in_vm ( self , vm , thread_monitor ) :
cmd = [ ' kdialog ' , ' --title ' , ' Qubes command entry ' , ' --inputbox ' , ' Run command in <b> ' + vm . name + ' </b>: ' ]
kdialog = subprocess . Popen ( cmd , stdout = subprocess . PIPE )
command_to_run = kdialog . stdout . read ( )
command_to_run = " ' " + command_to_run . strip ( ) + " ' "
if command_to_run != " " :
command_to_run = " qvm-run -a " + vm . name + " " + command_to_run
try :
subprocess . check_call ( command_to_run , shell = True )
except OSError as ex :
if ex . errno == errno . EINTR :
pass
else :
thread_monitor . set_error_msg ( str ( ex ) )
thread_monitor . set_finished ( )
except Exception as ex :
thread_monitor . set_error_msg ( str ( ex ) )
thread_monitor . set_finished ( )
thread_monitor . set_finished ( )
2012-04-24 17:48:10 +02:00
@pyqtSlot ( name = ' on_action_set_keyboard_layout_triggered ' )
def action_set_keyboard_layout_triggered ( self ) :
vm = self . get_selected_vm ( )
subprocess . Popen ( [ ' qvm-run ' , vm . name , ' qubes-change-keyboard-layout ' ] )
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_showallvms_triggered ' )
def action_showallvms_triggered ( self ) :
2011-04-02 15:27:40 +02:00
self . show_inactive_vms = self . action_showallvms . isChecked ( )
2012-05-29 17:05:34 +02:00
self . showhide_inactive_vms ( self . show_inactive_vms )
2012-03-28 12:45:31 +02:00
self . set_table_geom_size ( )
2011-04-01 12:02:27 +02:00
2012-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_editfwrules_triggered ' )
def action_editfwrules_triggered ( self ) :
2011-02-21 18:15:35 +01:00
vm = self . get_selected_vm ( )
2012-02-13 17:47:12 +01:00
settings_window = VMSettingsWindow ( vm , app , self . qvm_collection , " firewall " )
2012-02-10 00:30:45 +01:00
settings_window . exec_ ( )
2011-03-09 17:52:32 +01:00
2012-01-31 17:29:00 +01:00
@pyqtSlot ( name = ' on_action_global_settings_triggered ' )
def action_global_settings_triggered ( self ) :
2012-03-15 00:22:06 +01:00
global_settings_window = GlobalSettingsWindow ( app , self . qvm_collection )
2012-01-31 17:29:00 +01:00
global_settings_window . exec_ ( )
2012-01-24 20:59:44 +01:00
2012-01-31 14:29:13 +01:00
@pyqtSlot ( name = ' on_action_restore_triggered ' )
def action_restore_triggered ( self ) :
2012-02-22 06:09:25 +01:00
restore_window = RestoreVMsWindow ( app , self . qvm_collection , self . blk_manager )
2012-01-31 14:29:13 +01:00
restore_window . exec_ ( )
@pyqtSlot ( name = ' on_action_backup_triggered ' )
def action_backup_triggered ( self ) :
2012-03-29 23:26:16 +02:00
backup_window = BackupVMsWindow ( app , self . qvm_collection , self . blk_manager , self . shutdown_vm )
2012-01-31 14:29:13 +01:00
backup_window . exec_ ( )
2012-03-06 18:45:47 +01:00
def showhide_menubar ( self , checked ) :
self . menuWidget ( ) . setVisible ( checked )
2012-03-28 12:45:31 +02:00
self . set_table_geom_size ( )
2012-03-20 18:36:48 +01:00
if not checked :
self . context_menu . addAction ( self . action_menubar )
else :
self . context_menu . removeAction ( self . action_menubar )
2012-03-06 18:45:47 +01:00
def showhide_toolbar ( self , checked ) :
self . toolbar . setVisible ( checked )
2012-03-28 12:45:31 +02:00
self . set_table_geom_size ( )
2012-03-20 18:36:48 +01:00
if not checked :
self . context_menu . addAction ( self . action_toolbar )
else :
self . context_menu . removeAction ( self . action_toolbar )
2012-03-06 18:45:47 +01:00
2012-01-31 14:29:13 +01:00
2012-03-26 11:48:49 +02:00
def showhide_column ( self , col_num , show ) :
2012-01-22 18:45:41 +01:00
self . table . setColumnHidden ( col_num , not show )
2012-03-28 12:45:31 +02:00
self . set_table_geom_size ( )
2012-04-26 16:32:40 +02:00
val = 1 if show else - 1
self . visible_columns_count + = val
if self . visible_columns_count == 1 :
#disable hiding the last one
for c in self . columns_actions :
if self . columns_actions [ c ] . isChecked ( ) :
self . columns_actions [ c ] . setEnabled ( False )
break
elif self . visible_columns_count == 2 and val == 1 :
#enable hiding previously disabled column
for c in self . columns_actions :
if not self . columns_actions [ c ] . isEnabled ( ) :
self . columns_actions [ c ] . setEnabled ( True )
break ;
def on_action_vm_type_toggled ( self , checked ) :
self . showhide_column ( self . columns_indices [ ' Type ' ] , checked )
def on_action_label_toggled ( self , checked ) :
self . showhide_column ( self . columns_indices [ ' Label ' ] , checked )
def on_action_name_toggled ( self , checked ) :
self . showhide_column ( self . columns_indices [ ' Name ' ] , checked )
def on_action_state_toggled ( self , checked ) :
2012-03-26 11:48:49 +02:00
self . showhide_column ( self . columns_indices [ ' State ' ] , checked )
2012-02-05 18:41:41 +01:00
2012-04-26 16:32:40 +02:00
def on_action_template_toggled ( self , checked ) :
2012-03-26 11:48:49 +02:00
self . showhide_column ( self . columns_indices [ ' Template ' ] , checked )
2012-01-22 18:45:41 +01:00
2012-04-26 16:32:40 +02:00
def on_action_netvm_toggled ( self , checked ) :
2012-03-26 11:48:49 +02:00
self . showhide_column ( self . columns_indices [ ' NetVM ' ] , checked )
2012-01-22 18:45:41 +01:00
2012-04-26 16:32:40 +02:00
def on_action_cpu_toggled ( self , checked ) :
2012-03-26 11:48:49 +02:00
self . showhide_column ( self . columns_indices [ ' CPU ' ] , checked )
2012-01-22 18:45:41 +01:00
2012-04-26 16:32:40 +02:00
def on_action_cpu_graph_toggled ( self , checked ) :
2012-03-26 11:48:49 +02:00
self . showhide_column ( self . columns_indices [ ' CPU Graph ' ] , checked )
2012-01-22 18:45:41 +01:00
2012-04-26 16:32:40 +02:00
def on_action_mem_toggled ( self , checked ) :
2012-03-26 11:48:49 +02:00
self . showhide_column ( self . columns_indices [ ' MEM ' ] , checked )
2012-01-22 18:45:41 +01:00
2012-04-26 16:32:40 +02:00
def on_action_mem_graph_toggled ( self , checked ) :
2012-03-26 11:48:49 +02:00
self . showhide_column ( self . columns_indices [ ' MEM Graph ' ] , checked )
2012-01-22 18:45:41 +01:00
2012-04-27 17:04:35 +02:00
def on_action_size_on_disk_toggled ( self , checked ) :
self . showhide_column ( self . columns_indices [ ' Size ' ] , checked )
2012-02-14 19:00:50 +01:00
2012-04-26 17:46:41 +02:00
@pyqtSlot ( name = ' on_action_about_qubes_triggered ' )
def action_about_qubes_triggered ( self ) :
QMessageBox . about ( self , " About... " , " <b>Qubes OS</b><br><br>Release 1.0 " )
2012-05-06 20:55:52 +02:00
2012-03-06 18:45:47 +01:00
def createPopupMenu ( self ) :
menu = QMenu ( )
menu . addAction ( self . action_toolbar )
menu . addAction ( self . action_menubar )
return menu
2012-02-14 19:00:50 +01:00
@pyqtSlot ( ' const QPoint& ' )
def open_context_menu ( self , point ) :
vm = self . get_selected_vm ( )
2012-04-23 19:01:22 +02:00
running = vm . is_running ( )
#logs menu
2012-04-24 12:06:08 +02:00
self . logs_menu . clear ( )
if vm . qid == 0 :
text = " /var/log/xen/console/hypervisor.log "
action = self . logs_menu . addAction ( QIcon ( " :/log.png " ) , text )
action . setData ( QVariant ( text ) )
2012-04-23 19:01:22 +02:00
self . logs_menu . setEnabled ( True )
2012-04-24 12:06:08 +02:00
else :
menu_empty = True
text = " /var/log/xen/console/guest- " + vm . name + " .log "
if os . path . exists ( text ) :
action = self . logs_menu . addAction ( QIcon ( " :/log.png " ) , text )
action . setData ( QVariant ( text ) )
menu_empty = False
text = " /var/log/xen/console/guest- " + vm . name + " -dm.log "
if os . path . exists ( text ) :
action = self . logs_menu . addAction ( QIcon ( " :/log.png " ) , text )
action . setData ( QVariant ( text ) )
menu_empty = False
if running :
xid = vm . xid
if xid != None :
2012-04-23 19:01:22 +02:00
text = " /var/log/qubes/guid. " + str ( xid ) + " .log "
action = self . logs_menu . addAction ( QIcon ( " :/log.png " ) , text )
action . setData ( QVariant ( text ) )
text = " /var/log/qubes/qrexec. " + str ( xid ) + " .log "
action = self . logs_menu . addAction ( QIcon ( " :/log.png " ) , text )
action . setData ( QVariant ( text ) )
2012-04-24 12:06:08 +02:00
menu_empty = False
self . logs_menu . setEnabled ( not menu_empty )
2012-04-23 19:01:22 +02:00
# blk menu
if not running :
2012-02-14 19:00:50 +01:00
self . blk_menu . setEnabled ( False )
else :
self . blk_menu . clear ( )
self . blk_menu . setEnabled ( True )
2012-04-13 01:08:36 +02:00
self . blk_manager . blk_lock . acquire ( )
2012-02-14 19:00:50 +01:00
if len ( self . blk_manager . attached_devs ) > 0 :
for d in self . blk_manager . attached_devs :
if self . blk_manager . attached_devs [ d ] [ ' attached_to ' ] [ ' vm ' ] == vm . name :
2012-04-23 19:01:22 +02:00
text = " Detach " + d + " " + unicode ( self . blk_manager . attached_devs [ d ] [ ' size ' ] ) + " " + self . blk_manager . attached_devs [ d ] [ ' desc ' ]
action = self . blk_menu . addAction ( QIcon ( " :/remove.png " ) , text )
2012-02-14 19:00:50 +01:00
action . setData ( QVariant ( d ) )
2012-02-20 07:56:38 +01:00
if len ( self . blk_manager . free_devs ) > 0 :
2012-02-14 19:00:50 +01:00
for d in self . blk_manager . free_devs :
2012-02-20 07:56:38 +01:00
if d . startswith ( vm . name ) :
continue
2012-04-23 19:01:22 +02:00
text = " Attach " + d + " " + unicode ( self . blk_manager . free_devs [ d ] [ ' size ' ] ) + " " + self . blk_manager . free_devs [ d ] [ ' desc ' ]
action = self . blk_menu . addAction ( QIcon ( " :/add.png " ) , text )
2012-02-14 19:00:50 +01:00
action . setData ( QVariant ( d ) )
2012-04-13 01:08:36 +02:00
self . blk_manager . blk_lock . release ( )
2012-02-14 19:00:50 +01:00
if self . blk_menu . isEmpty ( ) :
self . blk_menu . setEnabled ( False )
2012-05-25 13:59:58 +02:00
2012-02-14 19:00:50 +01:00
self . context_menu . exec_ ( self . table . mapToGlobal ( point ) )
2012-04-23 19:01:22 +02:00
@pyqtSlot ( ' QAction * ' )
def show_log ( self , action ) :
log = str ( action . data ( ) . toString ( ) )
2012-05-10 13:17:24 +02:00
log_dialog = LogDialog ( app , log )
log_dialog . exec_ ( )
2012-04-23 19:01:22 +02:00
2012-02-14 19:00:50 +01:00
@pyqtSlot ( ' QAction * ' )
def attach_dettach_device_triggered ( self , action ) :
dev = str ( action . data ( ) . toString ( ) )
vm = self . get_selected_vm ( )
2012-04-13 01:08:36 +02:00
self . blk_manager . blk_lock . acquire ( )
2012-02-14 19:00:50 +01:00
if dev in self . blk_manager . attached_devs :
self . blk_manager . detach_device ( vm , dev )
else :
self . blk_manager . attach_device ( vm , dev )
2012-04-13 01:08:36 +02:00
self . blk_manager . blk_lock . release ( )
2012-02-14 19:00:50 +01:00
2012-05-25 13:59:58 +02:00
2012-02-14 19:00:50 +01:00
class QubesBlockDevicesManager ( ) :
def __init__ ( self , qvm_collection ) :
self . qvm_collection = qvm_collection
self . attached_devs = { }
self . free_devs = { }
self . current_blk = { }
self . current_attached = { }
self . devs_changed = False
2012-04-13 01:08:36 +02:00
self . last_update_time = time . time ( )
self . blk_state_changed = True
self . msg = [ ]
self . check_counter = 0
self . blk_lock = threading . Lock ( )
self . update ( )
def block_devs_event ( self , xid ) :
now = time . time ( )
#don't update more often than 1/10 s
if now - self . last_update_time > = 0.1 :
self . last_update_time = now
self . blk_lock . acquire ( )
self . blk_state_changed = True
self . blk_lock . release ( )
def check_for_updates ( self ) :
self . blk_lock . acquire ( )
ret = ( self . blk_state_changed , self . msg )
if self . blk_state_changed == True :
self . check_counter + = 1
self . update ( )
ret = ( self . blk_state_changed , self . msg )
#let the update last for 3 manager-update cycles
if self . check_counter == 3 :
self . check_counter = 0
self . blk_state_changed = False
self . msg = [ ]
self . blk_lock . release ( )
return ret
2012-02-14 19:00:50 +01:00
def update ( self ) :
blk = qubesutils . block_list ( )
for b in blk :
att = qubesutils . block_check_attached ( None , blk [ b ] [ ' device ' ] , backend_xid = blk [ b ] [ ' xid ' ] )
if b in self . current_blk :
if blk [ b ] == self . current_blk [ b ] :
if self . current_attached [ b ] != att : #devices the same, sth with attaching changed
self . current_attached [ b ] = att
else : #device changed ?!
self . current_blk [ b ] = blk [ b ]
self . current_attached [ b ] = att
else : #new device
self . current_blk [ b ] = blk [ b ]
self . current_attached [ b ] = att
2012-04-13 01:08:36 +02:00
self . msg . append ( " Attached new device: {0} " . format ( blk [ b ] [ ' device ' ] ) )
2012-02-14 19:00:50 +01:00
to_delete = [ ]
for b in self . current_blk : #remove devices that are not there anymore
if b not in blk :
to_delete . append ( b )
2012-04-13 01:08:36 +02:00
self . msg . append ( " Detached device: {0} " . format ( self . current_blk [ b ] [ ' device ' ] ) )
2012-02-14 19:00:50 +01:00
for d in to_delete :
del self . current_blk [ d ]
del self . current_attached [ d ]
2012-04-13 01:08:36 +02:00
self . __update_blk_entries__ ( )
2012-02-14 19:00:50 +01:00
def __update_blk_entries__ ( self ) :
self . free_devs . clear ( )
self . attached_devs . clear ( )
for b in self . current_attached :
if self . current_attached [ b ] :
self . attached_devs [ b ] = self . __make_entry__ ( b , self . current_blk [ b ] , self . current_attached [ b ] )
else :
self . free_devs [ b ] = self . __make_entry__ ( b , self . current_blk [ b ] , None )
def __make_entry__ ( self , k , dev , att ) :
size_str = qubesutils . bytes_to_kmg ( dev [ ' size ' ] )
entry = { ' dev ' : dev [ ' device ' ] ,
' backend_name ' : dev [ ' vm ' ] ,
' desc ' : dev [ ' desc ' ] ,
' size ' : size_str ,
' attached_to ' : att , }
return entry
def attach_device ( self , vm , dev ) :
backend_vm_name = self . free_devs [ dev ] [ ' backend_name ' ]
dev_id = self . free_devs [ dev ] [ ' dev ' ]
backend_vm = self . qvm_collection . get_vm_by_name ( backend_vm_name )
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " {0} - attaching {1} " . format ( vm . name , dev ) , msecs = 3000 )
2012-02-14 19:00:50 +01:00
qubesutils . block_attach ( vm , backend_vm , dev_id )
def detach_device ( self , vm , dev_name ) :
dev_id = self . attached_devs [ dev_name ] [ ' attached_to ' ] [ ' devid ' ]
vm_xid = self . attached_devs [ dev_name ] [ ' attached_to ' ] [ ' xid ' ]
2012-04-27 12:55:54 +02:00
trayIcon . showMessage ( " Qubes VM Manager " , " {0} - detaching {1} " . format ( vm . name , dev_name ) , msecs = 3000 )
2012-02-14 19:00:50 +01:00
qubesutils . block_detach ( None , dev_id , vm_xid )
2012-05-25 13:59:58 +02:00
def check_if_serves_as_backend ( self , vm ) :
serves_for = [ ]
for d in self . attached_devs :
if self . attached_devs [ d ] [ ' backend_name ' ] == vm . name :
serves_for . append ( ( self . attached_devs [ d ] [ ' dev ' ] , self . attached_devs [ d ] [ ' attached_to ' ] [ ' vm ' ] ) )
if len ( serves_for ) > 0 :
msg = " VM <b> " + vm . name + " </b> attaches block devices to other VMs: "
msg + = ' , ' . join ( [ " <b> " + v + " </b>( " + d + " ) " for ( d , v ) in serves_for ] )
msg + = " .<br><br> Shutting the VM down will dettach the devices from them. "
QMessageBox . warning ( None , " Warning! " , msg )
2012-01-22 18:45:41 +01:00
2010-05-11 16:51:31 +02:00
class QubesTrayIcon ( QSystemTrayIcon ) :
def __init__ ( self , icon ) :
QSystemTrayIcon . __init__ ( self , icon )
self . menu = QMenu ( )
action_showmanager = self . createAction ( " Open VM Manager " , slot = show_manager , icon = " qubes " )
action_backup = self . createAction ( " Make backup " )
action_preferences = self . createAction ( " Preferences " )
action_set_netvm = self . createAction ( " Set default NetVM " , icon = " networking " )
action_sys_info = self . createAction ( " System Info " , icon = " dom0 " )
action_exit = self . createAction ( " Exit " , slot = exit_app )
action_backup . setDisabled ( True )
action_preferences . setDisabled ( True )
action_set_netvm . setDisabled ( True )
action_sys_info . setDisabled ( True )
self . addActions ( self . menu , ( action_showmanager , action_backup , action_sys_info , None , action_preferences , action_set_netvm , None , action_exit ) )
self . setContextMenu ( self . menu )
self . connect ( self , SIGNAL ( " activated (QSystemTrayIcon::ActivationReason) " ) , self . icon_clicked )
2012-04-27 12:55:54 +02:00
self . tray_object = dbus . SessionBus ( ) . get_object ( " org.freedesktop.Notifications " , " /org/freedesktop/Notifications " )
self . tray_notifier = dbus . Interface ( self . tray_object , " org.freedesktop.Notifications " )
2010-05-11 16:51:31 +02:00
def icon_clicked ( self , reason ) :
if reason == QSystemTrayIcon . Context :
# Handle the right click normally, i.e. display the context menu
return
else :
2012-05-19 07:42:57 +02:00
bring_manager_to_front ( )
2010-05-11 16:51:31 +02:00
def addActions ( self , target , actions ) :
for action in actions :
if action is None :
target . addSeparator ( )
else :
target . addAction ( action )
2012-04-27 12:55:54 +02:00
def showMessage ( self , title , message , msecs ) :
self . tray_notifier . Notify ( " Qubes " , 0 , " /usr/share/qubes/icons/qubes.png " , title , message , [ ] , [ ] , msecs )
2010-05-11 16:51:31 +02:00
def createAction ( self , text , slot = None , shortcut = None , icon = None ,
tip = None , checkable = False , signal = " triggered() " ) :
action = QAction ( text , self )
if icon is not None :
action . setIcon ( QIcon ( " :/ %s .png " % icon ) )
if shortcut is not None :
action . setShortcut ( shortcut )
if tip is not None :
action . setToolTip ( tip )
action . setStatusTip ( tip )
if slot is not None :
self . connect ( action , SIGNAL ( signal ) , slot )
if checkable :
action . setCheckable ( True )
return action
2012-03-28 21:36:59 +02:00
def get_frame_size ( ) :
w = 0
h = 0
cmd = [ ' xprop ' , ' -name ' , ' Qubes VM Manager ' , ' | ' , ' grep ' , ' _NET_FRAME_EXTENTS ' ]
xprop = subprocess . Popen ( cmd , stdout = subprocess . PIPE )
for l in xprop . stdout :
line = l . split ( ' = ' )
if len ( line ) == 2 :
line = line [ 1 ] . strip ( ) . split ( ' , ' )
if len ( line ) == 4 :
w = int ( line [ 0 ] . strip ( ) ) + int ( line [ 1 ] . strip ( ) )
h = int ( line [ 2 ] . strip ( ) ) + int ( line [ 3 ] . strip ( ) )
break ;
#in case of some weird window managers we have to assume sth...
if w < = 0 :
w = 10
if h < = 0 :
h = 30
manager_window . frame_width = w
manager_window . frame_height = h
return
2010-05-11 16:51:31 +02:00
def show_manager ( ) :
manager_window . show ( )
2012-05-19 07:42:57 +02:00
manager_window . set_table_geom_size ( )
2012-06-10 01:55:01 +02:00
manager_window . repaint ( )
app . processEvents ( )
2012-05-19 07:42:57 +02:00
get_frame_size ( )
#print manager_window.frame_width, " x ", manager_window.frame_height
manager_window . set_table_geom_size ( )
2010-05-11 16:51:31 +02:00
2012-05-19 07:42:57 +02:00
def bring_manager_to_front ( ) :
2011-03-10 18:09:52 +01:00
if manager_window . isVisible ( ) :
2012-05-19 07:42:57 +02:00
subprocess . check_call ( [ ' wmctrl ' , ' -R ' , str ( manager_window . windowTitle ( ) ) ] )
2012-03-28 12:45:31 +02:00
2012-05-19 07:42:57 +02:00
else :
show_manager ( )
2012-03-28 12:45:31 +02:00
2010-05-11 16:51:31 +02:00
def exit_app ( ) :
notifier . stop ( )
app . exit ( )
# Bases on the original code by:
# Copyright (c) 2002-2007 Pascal Varet <p.varet@gmail.com>
def handle_exception ( exc_type , exc_value , exc_traceback ) :
import sys
import os . path
import traceback
filename , line , dummy , dummy = traceback . extract_tb ( exc_traceback ) . pop ( )
filename = os . path . basename ( filename )
error = " %s : %s " % ( exc_type . __name__ , exc_value )
2012-03-14 14:00:49 +01:00
strace = " "
stacktrace = traceback . extract_tb ( exc_traceback )
while len ( stacktrace ) > 0 :
( filename , line , func , txt ) = stacktrace . pop ( )
strace + = " ---- \n "
strace + = " line: %s \n " % txt
strace + = " func: %s \n " % func
strace + = " line no.: %d \n " % line
strace + = " file: %s \n " % filename
msg_box = QMessageBox ( )
msg_box . setDetailedText ( strace )
msg_box . setIcon ( QMessageBox . Critical )
msg_box . setWindowTitle ( " Houston, we have a problem... " )
msg_box . setText ( " Whoops. A critical error has occured. This is most likely a bug "
" in Qubes Manager.<br><br> "
" <b><i> %s </i></b> " % error +
" <br/>at line <b> %d </b><br/>of file %s .<br/><br/> "
% ( line , filename ) )
msg_box . exec_ ( )
2012-05-15 09:11:25 +02:00
def sighup_handler ( signum , frame ) :
os . execl ( " /usr/bin/qubes-manager " )
2010-05-11 16:51:31 +02:00
def main ( ) :
# Avoid starting more than one instance of the app
lock = QubesDaemonPidfile ( " qubes-manager " )
if lock . pidfile_exists ( ) :
2012-05-15 09:11:25 +02:00
if lock . read_pid ( ) == os . getpid ( ) :
pass
elif lock . pidfile_is_stale ( ) :
2010-05-11 16:51:31 +02:00
lock . remove_pidfile ( )
print " Removed stale pidfile (has the previous daemon instance crashed?). "
else :
exit ( 0 )
lock . create_pidfile ( )
2012-05-15 09:11:25 +02:00
signal . signal ( signal . SIGHUP , sighup_handler )
2010-09-16 18:49:36 +02:00
global qubes_host
qubes_host = QubesHost ( )
2010-05-11 16:51:31 +02:00
global app
app = QApplication ( sys . argv )
app . setOrganizationName ( " The Qubes Project " )
app . setOrganizationDomain ( " http://qubes-os.org " )
app . setApplicationName ( " Qubes VM Manager " )
app . setWindowIcon ( QIcon ( " :/qubes.png " ) )
sys . excepthook = handle_exception
global manager_window
manager_window = VmManagerWindow ( )
wm = WatchManager ( )
2011-03-14 22:17:28 +01:00
mask = EventsCodes . OP_FLAGS . get ( ' IN_MODIFY ' )
2010-05-11 16:51:31 +02:00
global notifier
notifier = ThreadedNotifier ( wm , QubesConfigFileWatcher ( manager_window . mark_table_for_update ) )
notifier . start ( )
wdd = wm . add_watch ( qubes_store_filename , mask )
global trayIcon
trayIcon = QubesTrayIcon ( QIcon ( " :/qubes.png " ) )
trayIcon . show ( )
app . exec_ ( )
trayIcon = None