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
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 *
2011-05-25 02:28:24 +02:00
from appmenu_select import AppmenuSelectWindow
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
2010-05-11 16:51:31 +02:00
2011-02-21 18:15:35 +01:00
from firewall import EditFwRulesDlg , QubesFirewallRulesModel
2010-05-11 16:51:31 +02:00
from pyinotify import WatchManager , Notifier , ThreadedNotifier , EventsCodes , ProcessEvent
import subprocess
import time
import threading
2011-05-12 19:08:15 +02:00
from datetime import datetime , timedelta
2010-05-11 16:51:31 +02:00
2011-10-31 21:46:35 +01:00
updates_stat_file = ' last_update.stat '
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
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 ( )
class VmStatusIcon ( QLabel ) :
def __init__ ( self , vm , parent = None ) :
super ( VmStatusIcon , self ) . __init__ ( parent )
self . vm = vm
( icon_pixmap , icon_sz ) = self . set_vm_icon ( self . vm )
self . setPixmap ( icon_pixmap )
self . setFixedSize ( icon_sz )
2011-04-02 15:27:40 +02:00
self . previous_power_state = vm . last_power_state
2010-05-11 16:51:31 +02:00
def update ( self ) :
2011-04-02 15:27:40 +02:00
if self . previous_power_state != self . vm . last_power_state :
2010-05-11 16:51:31 +02:00
( icon_pixmap , icon_sz ) = self . set_vm_icon ( self . vm )
self . setPixmap ( icon_pixmap )
self . setFixedSize ( icon_sz )
2011-04-02 15:27:40 +02:00
self . previous_power_state = self . vm . last_power_state
2010-05-11 16:51:31 +02:00
def set_vm_icon ( self , vm ) :
if vm . qid == 0 :
icon = QIcon ( " :/dom0.png " )
elif vm . is_appvm ( ) :
icon = QIcon ( vm . label . icon_path )
2011-03-05 15:12:34 +01:00
elif vm . is_template ( ) :
2010-05-11 16:51:31 +02:00
icon = QIcon ( " :/templatevm.png " )
elif vm . is_netvm ( ) :
icon = QIcon ( " :/netvm.png " )
else :
icon = QIcon ( )
icon_sz = QSize ( VmManagerWindow . row_height * 0.8 , VmManagerWindow . row_height * 0.8 )
2011-04-02 15:27:40 +02:00
if vm . last_power_state :
2010-05-11 16:51:31 +02:00
icon_pixmap = icon . pixmap ( icon_sz )
else :
icon_pixmap = icon . pixmap ( icon_sz , QIcon . Disabled )
return ( icon_pixmap , icon_sz )
class VmInfoWidget ( QWidget ) :
def __init__ ( self , vm , parent = None ) :
super ( VmInfoWidget , self ) . __init__ ( parent )
2012-01-22 18:45:41 +01:00
layout = QHBoxLayout ( )
2010-05-11 16:51:31 +02:00
2011-04-03 01:21:02 +02:00
self . label_name = QLabel ( vm . name )
2012-01-22 18:45:41 +01:00
self . vm_icon = VmStatusIcon ( vm )
layout . addWidget ( self . vm_icon )
layout . addSpacing ( 10 )
layout . addWidget ( self . label_name , alignment = Qt . AlignLeft )
self . setLayout ( layout )
def update_vm_state ( self , vm ) :
self . vm_icon . update ( )
2010-05-11 16:51:31 +02:00
2012-01-22 18:45:41 +01:00
2010-05-11 16:51:31 +02:00
2012-01-22 18:45:41 +01:00
class VmTemplateWidget ( QWidget ) :
def __init__ ( self , vm , parent = None ) :
super ( VmTemplateWidget , self ) . __init__ ( parent )
layout = QVBoxLayout ( )
2011-04-03 01:27:42 +02:00
if vm . template_vm is not None :
2012-01-22 18:45:41 +01:00
self . label_tmpl = QLabel ( " <font color= \" black \" > " + ( vm . template_vm . name ) + " </font> " )
2010-05-11 16:51:31 +02:00
else :
2012-01-22 18:45:41 +01:00
if vm . is_appvm ( ) : # and vm.template_vm is None
2012-01-24 20:59:44 +01:00
self . label_tmpl = QLabel ( " <i><font color= \" gray \" >StandaloneVM</i></font> " )
2012-01-22 18:45:41 +01:00
elif vm . is_template ( ) :
2012-01-24 20:59:44 +01:00
self . label_tmpl = QLabel ( " <i><font color= \" gray \" >TemplateVM</i></font> " )
2012-01-22 18:45:41 +01:00
elif vm . qid == 0 :
2012-01-24 20:59:44 +01:00
self . label_tmpl = QLabel ( " <i><font color= \" gray \" >AdminVM</i></font> " )
2012-01-22 18:45:41 +01:00
elif vm . is_netvm ( ) :
2012-01-24 20:59:44 +01:00
self . label_tmpl = QLabel ( " <i><font color= \" gray \" >NetVM</i></font> " )
2012-01-22 18:45:41 +01:00
else :
2012-01-24 20:59:44 +01:00
self . label_tmpl = QLabel ( " <i><font color= \" gray \" >---</i></font> " )
2010-05-11 16:51:31 +02:00
2012-01-22 18:45:41 +01:00
layout . addWidget ( self . label_tmpl , alignment = Qt . AlignHCenter )
2010-05-11 16:51:31 +02:00
2012-01-22 18:45:41 +01:00
self . setLayout ( layout )
2010-05-11 16:51:31 +02:00
2012-01-22 18:45:41 +01:00
class VmIconWidget ( QWidget ) :
def __init__ ( self , icon_path , enabled = True , parent = None ) :
super ( VmIconWidget , self ) . __init__ ( parent )
2010-05-11 16:51:31 +02:00
label_icon = QLabel ( )
icon = QIcon ( icon_path )
2012-02-05 18:41:41 +01:00
icon_sz = QSize ( VmManagerWindow . row_height * 0.8 , VmManagerWindow . row_height * 0.8 )
2010-05-11 16:51:31 +02:00
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-01-22 18:45:41 +01:00
layout = QVBoxLayout ( )
layout . addWidget ( label_icon )
self . setLayout ( layout )
2010-05-11 16:51:31 +02:00
2012-01-22 18:45:41 +01:00
class VmNetvmWidget ( QWidget ) :
def __init__ ( self , vm , parent = None ) :
super ( VmNetvmWidget , self ) . __init__ ( parent )
layout = QHBoxLayout ( )
self . icon = VmIconWidget ( " :/networking.png " , vm . is_networked ( ) )
if vm . is_netvm ( ) :
self . label_nvm = QLabel ( " <font color= \" black \" >self</font> " )
elif vm . netvm_vm is not None :
self . label_nvm = QLabel ( " <font color= \" black \" > " + ( vm . netvm_vm . name ) + " </font> " )
else :
self . label_nvm = QLabel ( " <font color= \" black \" >None</font> " )
2011-04-03 01:23:28 +02:00
2012-01-22 18:45:41 +01:00
layout . addWidget ( self . icon , alignment = Qt . AlignLeft )
layout . addWidget ( self . label_nvm , alignment = Qt . AlignHCenter )
self . setLayout ( layout )
2011-03-07 16:48:19 +01:00
2012-01-22 18:45:41 +01:00
class VmUsageBarWidget ( QWidget ) :
2012-02-05 18:41:41 +01:00
def __init__ ( self , min , max , format , update_func , vm , load , parent = None ) :
2012-01-22 18:45:41 +01:00
super ( VmUsageBarWidget , self ) . __init__ ( parent )
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
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 (
" QProgressBar:horizontal { \
border : 1 px solid lightblue ; \
border - radius : 4 px ; \
background : white ; \
text - align : center ; \
} \
QProgressBar : : chunk : horizontal { \
background : qlineargradient ( x1 : 0 , y1 : 0.5 , x2 : 1 , y2 : 0.5 , stop : 0 hsv ( 210 , 170 , 207 ) , stop : 1 white ) ; \
} "
)
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 )
self . update_load ( vm , load )
def update_load ( self , vm , load ) :
self . widget . setValue ( self . update_func ( vm , load ) )
2011-03-07 16:48:19 +01:00
2010-05-11 16:51:31 +02:00
class LoadChartWidget ( QWidget ) :
2011-06-02 01:15:24 +02:00
def __init__ ( self , vm , cpu_load = 0 , parent = None ) :
2010-05-11 16:51:31 +02:00
super ( LoadChartWidget , self ) . __init__ ( parent )
2011-06-02 01:15:24 +02:00
self . load = cpu_load if vm . last_power_state else 0
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 ]
2011-06-02 01:15:24 +02:00
def update_load ( self , vm , cpu_load ) :
self . load = cpu_load if vm . last_power_state else 0
2011-09-14 20:04:33 +02: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 :/
self . load = 100
2010-05-11 16:51:31 +02:00
self . load_history . append ( self . load )
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 ]
hue = 200
sat = 70 + val * ( 255 - 70 ) / 100
color = QColor . fromHsv ( hue , sat , 255 )
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
class MemChartWidget ( QWidget ) :
def __init__ ( self , vm , parent = None ) :
super ( MemChartWidget , self ) . __init__ ( parent )
2011-04-02 15:27:40 +02:00
self . load = vm . get_mem ( ) * 100 / qubes_host . memory_total if vm . last_power_state else 0
2010-09-16 18:49:36 +02:00
assert self . load > = 0 and self . load < = 100 , " mem = {0} " . format ( self . load )
self . load_history = [ self . load ]
def update_load ( self , vm ) :
2011-04-02 15:27:40 +02:00
self . load = vm . get_mem ( ) * 100 / qubes_host . memory_total if vm . last_power_state else 0
2010-09-16 18:49:36 +02:00
assert self . load > = 0 and self . load < = 100 , " load = {0} " . format ( self . load )
self . load_history . append ( self . load )
self . repaint ( )
def paintEvent ( self , Event = None ) :
p = QPainter ( self )
dx = 4
2011-03-07 16:48:53 +01:00
W = self . width ( )
2010-09-16 18:49:36 +02:00
H = self . height ( ) - 5
N = len ( self . load_history )
if N > W / dx :
tail = N - W / dx
N = W / dx
self . load_history = self . load_history [ tail : ]
assert len ( self . load_history ) == N
for i in range ( 0 , N - 1 ) :
val = self . load_history [ N - i - 1 ]
hue = 120
sat = 70 + val * ( 255 - 70 ) / 100
color = QColor . fromHsv ( hue , sat , 255 )
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 )
2012-01-22 18:45:41 +01:00
class VmUpdateInfoWidget ( QWidget ) :
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 ( " " )
layout . addWidget ( self . icon , alignment = Qt . AlignHCenter )
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-01-22 18:45:41 +01:00
def update_outdated ( self , vm ) :
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-01-22 18:45:41 +01:00
self . previous_outdated = outdated
if vm . is_updateable ( ) :
update_recommended = self . previous_update_recommended
stat_file = vm . dir_path + ' / ' + updates_stat_file
if not os . path . exists ( stat_file ) or \
time . time ( ) - os . path . getmtime ( stat_file ) > \
update_suggestion_interval * 24 * 3600 :
update_recommended = True
else :
update_recommended = False
2012-02-05 18:41:41 +01:00
if not self . show_text and self . previous_update_recommended != False :
self . update_status_widget ( " ok " )
2012-01-22 18:45:41 +01:00
if update_recommended and not self . previous_update_recommended :
2012-02-05 18:41:41 +01:00
self . update_status_widget ( " update " )
2012-01-24 20:59:44 +01:00
self . previous_update_recommended = update_recommended
2012-01-22 18:45:41 +01:00
2012-02-05 18:41:41 +01:00
def update_status_widget ( self , state ) :
if state == " ok " :
label_text = " "
icon_path = " :/flag-green.png "
tooltip_text = " VM up to date "
elif state == " update " :
label_text = " <font color= \" #CCCC00 \" >Check updates</font> "
icon_path = " :/flag-yellow.png "
tooltip_text = " Update recommended "
elif state == " outdated " :
label_text = " <font color= \" red \" >VM outdated</font> "
icon_path = " :/flag-red.png "
tooltip_text = " VM outdated "
if self . show_text :
self . label . setText ( label_text )
else :
self . layout ( ) . removeWidget ( self . icon )
self . icon . deleteLater ( )
self . icon = VmIconWidget ( icon_path , True )
self . icon . setToolTip ( tooltip_text )
self . layout ( ) . addWidget ( self . icon , alignment = Qt . AlignCenter )
2012-01-22 18:45:41 +01:00
class VmBlockDevicesWidget ( QWidget ) :
def __init__ ( self , vm , parent = None ) :
super ( VmBlockDevicesWidget , self ) . __init__ ( parent )
combo = QComboBox ( )
combo . addItem ( " USB dummy1 " )
combo . addItem ( " USB dummy2 " )
combo . addItem ( " USB dummy3 " )
layout = QVBoxLayout ( )
layout . addWidget ( combo )
self . setLayout ( layout )
2010-09-16 18:49:36 +02:00
2010-05-11 16:51:31 +02:00
class VmRowInTable ( object ) :
def __init__ ( self , vm , row_no , table ) :
self . vm = vm
self . row_no = row_no
table . setRowHeight ( row_no , VmManagerWindow . row_height )
self . info_widget = VmInfoWidget ( vm )
table . setCellWidget ( row_no , 0 , self . info_widget )
2012-02-05 18:41:41 +01:00
self . upd_widget = VmUpdateInfoWidget ( vm , False )
table . setCellWidget ( row_no , 1 , self . upd_widget )
2012-01-22 18:45:41 +01:00
self . template_widget = VmTemplateWidget ( vm )
2012-02-05 18:41:41 +01:00
table . setCellWidget ( row_no , 2 , self . template_widget )
2012-01-22 18:45:41 +01:00
self . netvm_widget = VmNetvmWidget ( vm )
2012-02-05 18:41:41 +01:00
table . setCellWidget ( row_no , 3 , self . netvm_widget )
2012-01-22 18:45:41 +01:00
2012-02-05 18:41:41 +01:00
self . cpu_usage_widget = VmUsageBarWidget ( 0 , 100 , " " ,
2012-01-22 18:45:41 +01:00
lambda vm , val : val if vm . last_power_state else 0 , vm , 0 )
2012-02-05 18:41:41 +01:00
table . setCellWidget ( row_no , 4 , self . cpu_usage_widget )
2011-03-07 16:48:19 +01:00
2010-05-11 16:51:31 +02:00
self . load_widget = LoadChartWidget ( vm )
2012-02-05 18:41:41 +01:00
table . setCellWidget ( row_no , 5 , self . load_widget )
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-01-22 18:45:41 +01:00
lambda vm , val : vm . get_mem ( ) / 1024 if vm . last_power_state else 0 , vm , 0 )
2012-02-05 18:41:41 +01:00
table . setCellWidget ( row_no , 6 , self . mem_usage_widget )
2010-05-11 16:51:31 +02:00
2010-09-16 18:49:36 +02:00
self . mem_widget = MemChartWidget ( vm )
2012-02-05 18:41:41 +01:00
table . setCellWidget ( row_no , 7 , self . mem_widget )
2012-01-22 18:45:41 +01:00
2012-02-05 18:41:41 +01:00
self . updateinfo_widget = VmUpdateInfoWidget ( vm , True )
table . setCellWidget ( row_no , 8 , self . updateinfo_widget )
2012-01-22 18:45:41 +01:00
self . blockdevices_widget = VmBlockDevicesWidget ( vm )
2012-02-05 18:41:41 +01:00
table . setCellWidget ( row_no , 9 , self . blockdevices_widget )
2010-09-16 18:49:36 +02:00
2011-06-02 01:15:24 +02:00
def update ( self , counter , cpu_load = None ) :
2010-05-11 16:51:31 +02:00
self . info_widget . update_vm_state ( self . vm )
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 )
2010-09-16 18:49:36 +02:00
self . mem_widget . update_load ( self . vm )
2012-01-22 18:45:41 +01:00
self . updateinfo_widget . update_outdated ( self . vm )
2012-02-05 18:41:41 +01:00
self . upd_widget . update_outdated ( self . vm )
2010-05-11 16:51:31 +02:00
class NewAppVmDlg ( QDialog , ui_newappvmdlg . Ui_NewAppVMDlg ) :
def __init__ ( self , parent = None ) :
super ( NewAppVmDlg , self ) . __init__ ( parent )
self . setupUi ( self )
vm_shutdown_timeout = 15000 # in msec
class VmShutdownMonitor ( QObject ) :
def __init__ ( self , vm ) :
self . vm = vm
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 ( )
if not vm . is_running ( ) or ( vm_start_time and vm_start_time > = datetime . utcnow ( ) - timedelta ( 0 , vm_shutdown_timeout / 1000 ) ) :
2011-04-03 01:24:16 +02:00
if vm . is_template ( ) :
2011-04-07 12:23:01 +02:00
trayIcon . showMessage ( " Qubes 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 " ,
2010-06-04 11:22:02 +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 , vm_shutdown_timeout / 1000 ) ,
2010-05-11 16:51:31 +02:00
" Kill it! " , " Wait another {0} seconds... " . format ( vm_shutdown_timeout / 1000 ) )
if reply == 0 :
vm . force_shutdown ( )
else :
QTimer . singleShot ( vm_shutdown_timeout , self . check_if_vm_has_shutdown )
class ThreadMonitor ( QObject ) :
def __init__ ( self ) :
self . success = True
self . error_msg = None
self . event_finished = threading . Event ( )
def set_error_msg ( self , error_msg ) :
self . success = False
self . error_msg = error_msg
self . set_finished ( )
def is_finished ( self ) :
return self . event_finished . is_set ( )
def set_finished ( self ) :
self . event_finished . set ( )
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-01-24 20:59:44 +01:00
max_visible_rows = 7
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-01-22 18:45:41 +01:00
columns_indices = { " Name " : 0 ,
2012-02-05 18:41:41 +01:00
" Upd " : 1 ,
" Template " : 2 ,
" NetVM " : 3 ,
" CPU " : 4 ,
" CPU Graph " : 5 ,
" MEM " : 6 ,
" MEM Graph " : 7 ,
" Update Info " : 8 ,
" Block Device " : 9 }
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
2010-05-11 16:51:31 +02:00
self . qvm_collection = QubesVmCollection ( )
2012-01-22 18:45:41 +01:00
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
2011-04-06 23:45:23 +02:00
cur_pos = self . pos ( )
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 ) )
2010-05-11 16:51:31 +02:00
self . fill_table ( )
2011-04-06 23:45:23 +02:00
self . move ( cur_pos )
2012-01-24 20:59:44 +01:00
self . table . setColumnHidden ( self . columns_indices [ " NetVM " ] , True )
self . actionNetVM . setChecked ( False )
2012-02-05 18:41:41 +01:00
self . table . setColumnHidden ( self . columns_indices [ " Update Info " ] , True )
self . actionUpdate_Info . setChecked ( False )
2012-01-24 20:59:44 +01:00
self . table . setColumnHidden ( self . columns_indices [ " CPU Graph " ] , True )
self . actionCPU_Graph . setChecked ( False )
self . table . setColumnHidden ( self . columns_indices [ " MEM Graph " ] , True )
self . actionMEM_Graph . setChecked ( False )
self . table . setColumnHidden ( self . columns_indices [ " Block Device " ] , True )
self . actionBlock_Devices . setChecked ( False )
2012-02-05 18:41:41 +01:00
self . table . setColumnWidth ( self . columns_indices [ " Upd " ] , 50 )
#self.table.setFrameShape(QFrame.NoFrame)
self . table . setContentsMargins ( 0 , 0 , 0 , 0 )
self . centralwidget . layout ( ) . setContentsMargins ( 0 , 0 , 0 , 0 )
self . layout ( ) . setContentsMargins ( 0 , 0 , 0 , 0 )
2012-01-24 20:59:44 +01:00
2011-04-02 15:48:48 +02:00
self . counter = 0
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-02-05 18:41:41 +01:00
def show ( self ) :
super ( VmManagerWindow , self ) . show ( )
self . set_table_geom_height ( )
self . update_table_columns ( )
2011-04-02 15:48:48 +02:00
def set_table_geom_height ( self ) :
2012-02-05 18:41:41 +01:00
minH = self . table . horizontalHeader ( ) . height ( ) + \
2 * self . table . frameWidth ( )
2010-05-11 16:51:31 +02:00
2012-01-24 20:59:44 +01:00
#All this sizing is kind of magic, so change it only if you have to
#or if you know what you're doing :)
2010-05-11 16:51:31 +02:00
n = self . table . rowCount ( ) ;
2012-02-05 18:41:41 +01:00
maxH = minH
if n > = self . max_visible_rows :
minH + = self . max_visible_rows * self . row_height
maxH + = n * self . row_height
2012-01-24 20:59:44 +01:00
else :
2012-02-05 18:41:41 +01:00
minH + = n * self . row_height
2012-01-24 20:59:44 +01:00
maxH = minH
2012-02-05 18:41:41 +01:00
2012-01-24 20:59:44 +01:00
self . centralwidget . setMinimumHeight ( minH )
2012-02-05 18:41:41 +01:00
self . centralwidget . setMaximumHeight ( maxH )
2010-05-11 16:51:31 +02:00
2012-02-05 18:41:41 +01:00
mainwindow_to_add = self . menubar . height ( ) + \
self . toolbar . height ( ) + \
self . menubar . contentsMargins ( ) . top ( ) + self . menubar . contentsMargins ( ) . bottom ( ) + \
self . toolbar . contentsMargins ( ) . top ( ) + self . toolbar . contentsMargins ( ) . bottom ( )
maxH + = mainwindow_to_add
minH + = mainwindow_to_add
self . setMaximumHeight ( maxH )
self . setMinimumHeight ( minH )
2010-05-11 16:51:31 +02:00
2012-02-05 18:41:41 +01: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 ( )
2011-04-02 15:27:40 +02:00
vms_list = [ vm for vm in self . qvm_collection . values ( ) ]
for vm in vms_list :
vm . last_power_state = vm . is_running ( )
2010-05-11 16:51:31 +02:00
no_vms = len ( vms_list )
vms_to_display = [ ]
# First, the NetVMs...
for netvm in vms_list :
if netvm . is_netvm ( ) :
vms_to_display . append ( netvm )
# Now, the templates...
for tvm in vms_list :
2011-03-05 15:12:34 +01:00
if tvm . is_template ( ) :
2010-05-11 16:51:31 +02:00
vms_to_display . append ( tvm )
label_list = QubesVmLabels . values ( )
label_list . sort ( key = lambda l : l . index )
for label in [ label . name for label in label_list ] :
2010-09-23 01:18:26 +02:00
for appvm in [ vm for vm in vms_list if ( ( vm . is_appvm ( ) or vm . is_disposablevm ( ) ) and vm . label . name == label ) ] :
2010-05-11 16:51:31 +02:00
vms_to_display . append ( appvm )
assert len ( vms_to_display ) == no_vms
return vms_to_display
def fill_table ( self ) :
2012-01-22 18:45:41 +01:00
#self.table.clear()
2010-05-11 16:51:31 +02:00
vms_list = self . get_vms_list ( )
self . table . setRowCount ( len ( vms_list ) )
vms_in_table = [ ]
2011-04-02 15:27:40 +02:00
row_no = 0
for vm in vms_list :
if ( not self . show_inactive_vms ) and ( not vm . last_power_state ) :
continue
2011-04-04 12:02:12 +02:00
if vm . internal :
continue
2010-05-11 16:51:31 +02:00
vm_row = VmRowInTable ( vm , row_no , self . table )
vms_in_table . append ( vm_row )
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
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 ) :
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 :
state = vm . is_running ( ) ;
if vm . last_power_state != state :
vm . last_power_state = state
some_vms_have_changed_power_state = True
2010-05-11 16:51:31 +02:00
2011-04-02 16:27:18 +02:00
if self . reload_table or ( ( not self . show_inactive_vms ) and some_vms_have_changed_power_state ) :
self . fill_table ( )
2010-05-11 16:51:31 +02:00
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 )
for vm_row in self . vms_in_table :
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
vm_row . update ( self . counter , cpu_load = cur_cpu_load )
else :
for vm_row in self . vms_in_table :
vm_row . update ( self . counter )
2010-05-11 16:51:31 +02:00
2012-01-31 14:29:13 +01:00
#self.table_selection_changed()
2010-05-11 16:51:31 +02:00
if not out_of_schedule :
self . counter + = 1
QTimer . singleShot ( self . update_interval , self . update_table )
2011-03-07 16:48:19 +01:00
def update_table_columns ( self ) :
2012-02-05 18:41:41 +01:00
table_width = self . table . horizontalHeader ( ) . length ( ) + \
self . table . verticalScrollBar ( ) . width ( ) + \
2 * self . table . frameWidth ( ) + 1
self . table . setFixedWidth ( table_width )
self . centralwidget . setFixedWidth ( table_width )
self . setFixedWidth ( table_width )
2011-03-07 16:48:19 +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 ( )
# Update available actions:
2012-01-31 14:29:13 +01:00
self . action_settings . setEnabled ( True )
2011-04-02 15:27:40 +02:00
self . action_removevm . setEnabled ( not vm . installed_by_rpm and not vm . last_power_state )
self . action_resumevm . setEnabled ( not vm . last_power_state )
self . action_pausevm . setEnabled ( vm . last_power_state and vm . qid != 0 )
2011-04-07 10:46:00 +02:00
self . action_shutdownvm . setEnabled ( not vm . is_netvm ( ) and vm . last_power_state and vm . qid != 0 )
2011-05-25 02:28:24 +02:00
self . action_appmenus . setEnabled ( not vm . is_netvm ( ) )
2011-03-31 23:59:37 +02:00
self . action_editfwrules . setEnabled ( vm . is_networked ( ) and not ( vm . is_netvm ( ) and not vm . is_proxyvm ( ) ) )
2011-12-18 14:03:16 +01:00
self . action_updatevm . setEnabled ( vm . is_updateable ( ) or vm . qid == 0 )
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-01-23 19:04:58 +01:00
@pyqtSlot ( name = ' on_action_createvm_triggered ' )
def action_createvm_triggered ( self ) :
2010-05-11 16:51:31 +02:00
dialog = NewAppVmDlg ( )
2012-01-23 19:04:58 +01:00
print " Create VM triggered! \n "
2010-05-11 16:51:31 +02:00
# Theoretically we should be locking for writing here and unlock
# only after the VM creation finished. But the code would be more messy...
# Instead we lock for writing in the actual worker thread
self . qvm_collection . lock_db_for_reading ( )
self . qvm_collection . load ( )
self . qvm_collection . unlock_db ( )
label_list = QubesVmLabels . values ( )
label_list . sort ( key = lambda l : l . index )
for ( i , label ) in enumerate ( label_list ) :
dialog . vmlabel . insertItem ( i , label . name )
dialog . vmlabel . setItemIcon ( i , QIcon ( label . icon_path ) )
2011-04-03 17:50:32 +02:00
template_vm_list = [ vm for vm in self . qvm_collection . values ( ) if not vm . internal and vm . is_template ( ) ]
2010-05-11 16:51:31 +02:00
default_index = 0
for ( i , vm ) in enumerate ( template_vm_list ) :
if vm is self . qvm_collection . get_default_template_vm ( ) :
default_index = i
dialog . template_name . insertItem ( i , vm . name + " (default) " )
else :
dialog . template_name . insertItem ( i , vm . name )
dialog . template_name . setCurrentIndex ( default_index )
dialog . vmname . selectAll ( )
dialog . vmname . setFocus ( )
if dialog . exec_ ( ) :
vmname = str ( dialog . vmname . text ( ) )
if self . qvm_collection . get_vm_by_name ( vmname ) is not None :
QMessageBox . warning ( None , " Incorrect AppVM Name! " , " A VM with the name <b> {0} </b> already exists in the system! " . format ( vmname ) )
return
label = label_list [ dialog . vmlabel . currentIndex ( ) ]
template_vm = template_vm_list [ dialog . template_name . currentIndex ( ) ]
2011-03-14 21:37:08 +01:00
allow_networking = dialog . allow_networking . isChecked ( )
2010-05-11 16:51:31 +02:00
thread_monitor = ThreadMonitor ( )
2011-03-14 21:37:08 +01:00
thread = threading . Thread ( target = self . do_create_appvm , args = ( vmname , label , template_vm , allow_networking , thread_monitor ) )
2010-05-11 16:51:31 +02:00
thread . daemon = True
thread . start ( )
progress = QProgressDialog ( " Creating new AppVM <b> {0} </b>... " . format ( vmname ) , " " , 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 :
trayIcon . showMessage ( " Qubes Manager " , " VM ' {0} ' has been created. " . format ( vmname ) , msecs = 3000 )
else :
QMessageBox . warning ( None , " Error creating AppVM! " , " ERROR: {0} " . format ( thread_monitor . error_msg ) )
2011-03-14 21:37:08 +01:00
def do_create_appvm ( self , vmname , label , template_vm , allow_networking , thread_monitor ) :
2011-03-09 15:26:27 +01:00
vm = None
2010-05-11 16:51:31 +02:00
try :
self . qvm_collection . lock_db_for_writing ( )
self . qvm_collection . load ( )
vm = self . qvm_collection . add_new_appvm ( vmname , template_vm , label = label )
vm . create_on_disk ( verbose = False )
2011-03-14 21:37:08 +01:00
firewall = vm . get_firewall_conf ( )
firewall [ " allow " ] = allow_networking
firewall [ " allowDns " ] = allow_networking
vm . write_firewall_conf ( firewall )
2010-05-11 16:51:31 +02:00
self . qvm_collection . save ( )
except Exception as ex :
thread_monitor . set_error_msg ( str ( ex ) )
2011-03-09 15:26:27 +01:00
if vm :
vm . remove_from_disk ( )
2010-05-11 16:51:31 +02:00
finally :
self . qvm_collection . unlock_db ( )
thread_monitor . set_finished ( )
def get_selected_vm ( self ) :
row_index = self . table . currentRow ( )
assert self . vms_in_table [ row_index ] is not None
vm = self . vms_in_table [ row_index ] . vm
return vm
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 :
trayIcon . showMessage ( " Qubes Manager " , " VM ' {0} ' has been removed. " . format ( vm . name ) , msecs = 3000 )
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...
2011-03-05 15:12:34 +01:00
if vm . is_template ( ) and qvm_collection . default_template_qid == vm . qid :
2010-05-11 16:51:31 +02:00
qvm_collection . default_template_qid = None
if vm . is_netvm ( ) and qvm_collection . default_netvm_qid == vm . qid :
qvm_collection . default_netvm_qid = None
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 ( )
trayIcon . showMessage ( " Qubes Manager " , " Starting ' {0} ' ... " . format ( vm . name ) , msecs = 3000 )
while not thread_monitor . is_finished ( ) :
app . processEvents ( )
time . sleep ( 0.1 )
if thread_monitor . success :
trayIcon . showMessage ( " Qubes Manager " , " VM ' {0} ' has been started. " . format ( vm . name ) , msecs = 3000 )
else :
QMessageBox . warning ( None , " Error starting VM! " , " ERROR: {0} " . format ( thread_monitor . error_msg ) )
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 ] )
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 ( )
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 :
try :
2011-06-06 01:20:49 +02:00
subprocess . check_call ( [ " /usr/sbin/xl " , " shutdown " , vm . name ] )
2010-05-11 16:51:31 +02:00
except Exception as ex :
QMessageBox . warning ( None , " Error shutting down VM! " , " ERROR: {0} " . format ( ex ) )
return
trayIcon . showMessage ( " Qubes Manager " , " VM ' {0} ' is shutting down... " . format ( vm . name ) , msecs = 3000 )
self . shutdown_monitor [ vm . qid ] = VmShutdownMonitor ( vm )
QTimer . singleShot ( vm_shutdown_timeout , self . shutdown_monitor [ vm . qid ] . check_if_vm_has_shutdown )
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-05 18:41:41 +01:00
settings_window = VMSettingsWindow ( vm , 1 )
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 ( )
select_window = AppmenuSelectWindow ( vm )
select_window . exec_ ( )
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 " ,
" VM need to be running for update. Do you want to start this VM?<br> " ,
QMessageBox . Yes | QMessageBox . Cancel )
if reply != QMessageBox . Yes :
return
trayIcon . showMessage ( " Qubes 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 ( )
while not thread_monitor . is_finished ( ) :
app . processEvents ( )
time . sleep ( 0.2 )
2011-12-18 14:03:16 +01:00
if vm . qid != 0 :
if thread_monitor . success :
# gpk-update-viewer was started, don't know if user installs updates, but touch stat file anyway
open ( vm . dir_path + ' / ' + updates_stat_file , ' w ' ) . close ( )
else :
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 :
subprocess . check_call ( [ " /usr/bin/qvm-dom0-update " , " --gui " ] )
else :
qubesutils . run_in_vm ( vm , " 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-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 ( )
self . mark_table_for_update ( )
self . update_table ( out_of_schedule = True )
2012-01-31 14:29:13 +01:00
self . set_table_geom_height ( )
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 ( )
dialog = EditFwRulesDlg ( )
model = QubesFirewallRulesModel ( )
model . set_vm ( vm )
dialog . set_model ( model )
2011-03-21 22:36:00 +01:00
if vm . netvm_vm is not None and not vm . netvm_vm . is_proxyvm ( ) :
QMessageBox . warning ( None , " VM configuration problem! " , " The ' {0} ' AppVM is not network connected to a FirewallVM!<p> " . format ( vm . name ) + \
" You may edit the ' {0} ' VM firewall rules, but these will not take any effect until you connect it to a working Firewall VM. " . format ( vm . name ) )
2011-02-21 18:15:35 +01:00
if dialog . exec_ ( ) :
model . apply_rules ( )
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 ) :
global_settings_window = GlobalSettingsWindow ( )
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 ) :
restore_window = RestoreVMsWindow ( )
restore_window . exec_ ( )
@pyqtSlot ( name = ' on_action_backup_triggered ' )
def action_backup_triggered ( self ) :
backup_window = BackupVMsWindow ( )
backup_window . exec_ ( )
2012-01-22 18:45:41 +01:00
def showhide_collumn ( self , col_num , show ) :
self . table . setColumnHidden ( col_num , not show )
self . update_table_columns ( )
2012-02-05 18:41:41 +01:00
def on_actionUpd_toggled ( self , checked ) :
self . showhide_collumn ( self . columns_indices [ ' Upd ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionTemplate_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' Template ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionNetVM_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' NetVM ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionCPU_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' CPU ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionCPU_Graph_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' CPU Graph ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionMEM_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' MEM ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionMEM_Graph_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' MEM Graph ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionUpdate_Info_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' Update Info ' ] , checked )
2012-01-22 18:45:41 +01:00
def on_actionBlock_Devices_toggled ( self , checked ) :
2012-02-05 18:41:41 +01:00
self . showhide_collumn ( self . columns_indices [ ' Block Device ' ] , checked )
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 )
def icon_clicked ( self , reason ) :
if reason == QSystemTrayIcon . Context :
# Handle the right click normally, i.e. display the context menu
return
else :
2011-03-10 18:09:52 +01:00
toggle_manager ( )
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 )
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
def show_manager ( ) :
manager_window . show ( )
2011-03-10 18:09:52 +01:00
def toggle_manager ( ) :
if manager_window . isVisible ( ) :
manager_window . hide ( )
else :
manager_window . show ( )
2011-06-02 01:14:13 +02:00
manager_window . update_table ( True )
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 )
QMessageBox . critical ( None , " Houston, we have a problem... " ,
" Whoops. A critical error has occured. This is most likely a bug "
" in Qubes Manager.<br><br> "
" <b><i> %s </i></b> " % error +
" at <b>line %d </b> of file <b> %s </b>.<br/><br/> "
% ( line , filename ) )
#sys.exit(1)
def main ( ) :
# Avoid starting more than one instance of the app
lock = QubesDaemonPidfile ( " qubes-manager " )
if lock . pidfile_exists ( ) :
if lock . pidfile_is_stale ( ) :
lock . remove_pidfile ( )
print " Removed stale pidfile (has the previous daemon instance crashed?). "
else :
exit ( 0 )
lock . create_pidfile ( )
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