From 35733452ce14eb164f853a1156b1c26be1b14e12 Mon Sep 17 00:00:00 2001 From: Agnieszka Kostrzewa Date: Tue, 8 May 2012 18:57:30 +0200 Subject: [PATCH 1/6] Updates pending icon for VMs (NOT dom0) displayed correctly (#475) --- qubesmanager/main.py | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/qubesmanager/main.py b/qubesmanager/main.py index bc29047..5b1fac2 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -51,8 +51,8 @@ from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, Pro import subprocess import time from datetime import datetime,timedelta +from qubes.qubes import updates_stat_file -updates_stat_file = 'last_update.stat' qubes_guid_path = '/usr/bin/qubes_guid' update_suggestion_interval = 14 # 14 days @@ -458,21 +458,38 @@ class VmUpdateInfoWidget(QWidget): self.previous_outdated = outdated - if vm.is_updateable(): + if vm.qid == 0: 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: + #slot for dom0 special treatment + # + # + elif vm.is_updateable(): + update_recommended = self.previous_update_recommended + stat_file_path = vm.dir_path + '/' + updates_stat_file + if not os.path.exists(stat_file_path): update_recommended = False - if not self.show_text and self.previous_update_recommended != False: - self.update_status_widget(None) + else: + if (not hasattr(vm, "updates_stat_file_read_time")) or vm.updates_stat_file_read_time <= os.path.getmtime(stat_file_path): + 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() + 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) + self.previous_update_recommended = update_recommended @@ -482,7 +499,7 @@ class VmUpdateInfoWidget(QWidget): if state == "update": label_text = "Check updates" icon_path = ":/update-recommended.png" - tooltip_text = "Update recommended." + tooltip_text = "Updates pending!" elif state == "outdated": label_text = "VM outdated" icon_path = ":/outdated.png" From 7890aac8e8050aebcf7b59de4080268df2e88419 Mon Sep 17 00:00:00 2001 From: Agnieszka Kostrzewa Date: Thu, 10 May 2012 13:17:24 +0200 Subject: [PATCH 2/6] Log dialog with 'copy-clipboard-to-qubes-clipboard'(#543) --- Makefile | 1 + logdlg.ui | 122 +++++++++++++++++++++++++++++++++++++ qubesmanager/log_dialog.py | 89 +++++++++++++++++++++++++++ qubesmanager/main.py | 30 ++------- rpm_spec/qmgr.spec | 5 ++ 5 files changed, 221 insertions(+), 26 deletions(-) create mode 100644 logdlg.ui create mode 100644 qubesmanager/log_dialog.py diff --git a/Makefile b/Makefile index cab057c..05993cd 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,7 @@ res: pyuic4 -o qubesmanager/ui_restoredlg.py restoredlg.ui pyuic4 -o qubesmanager/ui_backupdlg.py backupdlg.ui pyuic4 -o qubesmanager/ui_globalsettingsdlg.py globalsettingsdlg.ui + pyuic4 -o qubesmanager/ui_logdlg.py logdlg.ui update-repo-current: ln -f $(RPMS_DIR)/x86_64/qubes-manager-*$(VERSION)*.rpm ../yum/current-release/current/dom0/rpm/ diff --git a/logdlg.ui b/logdlg.ui new file mode 100644 index 0000000..6e8bbe2 --- /dev/null +++ b/logdlg.ui @@ -0,0 +1,122 @@ + + + LogDialog + + + + 0 + 0 + 750 + 450 + + + + Dialog + + + + + + Copy Dom0 clipboard to Qubes clipboard + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Qt::TabFocus + + + Copy clipboard to Qubes clipboard + + + + :/copy.png:/copy.png + + + + 20 + 20 + + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + + + + + buttonBox + rejected() + LogDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + buttonBox + accepted() + LogDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + diff --git a/qubesmanager/log_dialog.py b/qubesmanager/log_dialog.py new file mode 100644 index 0000000..a694fda --- /dev/null +++ b/qubesmanager/log_dialog.py @@ -0,0 +1,89 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2012 Agnieszka Kostrzewa +# Copyright (C) 2012 Marek Marczykowski +# +# 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 +import os +import fcntl +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from qubes.qubes import QubesException + +import qubesmanager.resources_rc + +from ui_logdlg import * + + +class LogDialog(Ui_LogDialog, QDialog): + + def __init__(self, app, log_path, parent=None): + super(LogDialog, self).__init__(parent) + + self.app = app + self.log_path = log_path + + self.setupUi(self) + self.setWindowTitle(log_path) + + self.connect(self.copy_to_qubes_clipboard, SIGNAL("clicked()"), self.copy_to_qubes_clipboard_triggered) + + self.__init_log_text__() + + def __init_log_text__(self): + log = open(self.log_path) + text = log.read() + log.close() + self.log_text.setText(text) + + + def copy_to_qubes_clipboard_triggered(self): + clipboard = self.app.clipboard().text() + copy_text_to_qubes_clipboard(clipboard) + +########################COPY TEXT TO QUBES CLIPBOARD + +def copy_text_to_qubes_clipboard(text): + #inter-appviewer lock + + try: + fd = os.open("/var/run/qubes/appviewer.lock", os.O_RDWR|os.O_CREAT, 0600); + fcntl.flock(fd, fcntl.LOCK_EX); + except IOError: + QMessageBox.warning (None, "Warning!", "Error while accessing Qubes clipboard!") + return + + qubes_clipboard = open("/var/run/qubes/qubes_clipboard.bin", 'w') + qubes_clipboard.write(text) + qubes_clipboard.close() + + qubes_clip_source = open("/var/run/qubes/qubes_clipboard.bin.source", 'w') + qubes_clip_source.write("dom0") + qubes_clip_source.close() + + try: + fcntl.flock(fd, fcntl.LOCK_UN) + os.close(fd) + except IOError: + QMessageBox.warning (None, "Warning!", "Error while writing to Qubes clipboard!") + return + diff --git a/qubesmanager/main.py b/qubesmanager/main.py index 5b1fac2..9df1a82 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -44,6 +44,7 @@ from settings import VMSettingsWindow from restore import RestoreVMsWindow from backup import BackupVMsWindow from global_settings import GlobalSettingsWindow +from log_dialog import LogDialog, copy_text_to_qubes_clipboard from thread_monitor import * from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent @@ -1514,29 +1515,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): @pyqtSlot(name='on_action_copy_clipboard_triggered') def action_copy_clipboard_triggered(self): clipboard = app.clipboard().text() - - #inter-appviewer lock - try: - fd = os.open("/var/run/qubes/appviewer.lock", os.O_RDWR|os.O_CREAT, 0600); - fcntl.flock(fd, fcntl.LOCK_EX); - except IOError: - QMessageBox.warning (None, "Warning!", "Error while accessing Qubes clipboard!") - return - - qubes_clipboard = open("/var/run/qubes/qubes_clipboard.bin", 'w') - qubes_clipboard.write(clipboard) - qubes_clipboard.close() - - qubes_clip_source = open("/var/run/qubes/qubes_clipboard.bin.source", 'w') - qubes_clip_source.write("dom0") - qubes_clip_source.close() - - try: - fcntl.flock(fd, fcntl.LOCK_UN) - os.close(fd) - except IOError: - QMessageBox.warning (None, "Warning!", "Error while writing to Qubes clipboard!") - return + copy_text_to_qubes_clipboard(clipboard) def createPopupMenu(self): @@ -1620,9 +1599,8 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): @pyqtSlot('QAction *') def show_log(self, action): log = str(action.data().toString()) - - cmd = ['kdialog', '--textbox', log, '700', '450', '--title', log] - subprocess.Popen(cmd) + log_dialog = LogDialog(app, log) + log_dialog.exec_() @pyqtSlot('QAction *') diff --git a/rpm_spec/qmgr.spec b/rpm_spec/qmgr.spec index aac95da..e14fd7a 100644 --- a/rpm_spec/qmgr.spec +++ b/rpm_spec/qmgr.spec @@ -44,6 +44,7 @@ cp qubesmanager/global_settings.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubes cp qubesmanager/multiselectwidget.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager cp qubesmanager/restore.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager cp qubesmanager/settings.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/log_dialog.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager cp qubesmanager/thread_monitor.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager cp qubesmanager/resources_rc.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager cp qubesmanager/__init__.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager @@ -56,6 +57,7 @@ cp qubesmanager/ui_newappvmdlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesm cp qubesmanager/ui_newfwruledlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager cp qubesmanager/ui_restoredlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager cp qubesmanager/ui_settingsdlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager +cp qubesmanager/ui_logdlg.py{,c,o} $RPM_BUILD_ROOT%{python_sitearch}/qubesmanager mkdir -p $RPM_BUILD_ROOT/usr/share/applications cp qubes-manager.desktop $RPM_BUILD_ROOT/usr/share/applications @@ -139,6 +141,9 @@ rm -rf $RPM_BUILD_ROOT %{python_sitearch}/qubesmanager/ui_settingsdlg.py %{python_sitearch}/qubesmanager/ui_settingsdlg.pyc %{python_sitearch}/qubesmanager/ui_settingsdlg.pyo +%{python_sitearch}/qubesmanager/ui_logdlg.py +%{python_sitearch}/qubesmanager/ui_logdlg.pyc +%{python_sitearch}/qubesmanager/ui_logdlg.pyo /usr/share/applications/qubes-manager.desktop From c78d382f264c4c3530ec1409813ab5797c0d4cf4 Mon Sep 17 00:00:00 2001 From: Agnieszka Kostrzewa Date: Thu, 10 May 2012 13:40:27 +0200 Subject: [PATCH 3/6] One click copy logs to Qubes clipboard (#543) --- logdlg.ui | 2 +- qubesmanager/log_dialog.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/logdlg.ui b/logdlg.ui index 6e8bbe2..4662d1e 100644 --- a/logdlg.ui +++ b/logdlg.ui @@ -48,7 +48,7 @@ Qt::TabFocus - Copy clipboard to Qubes clipboard + Copy to Qubes clipboard diff --git a/qubesmanager/log_dialog.py b/qubesmanager/log_dialog.py index a694fda..aed86d2 100644 --- a/qubesmanager/log_dialog.py +++ b/qubesmanager/log_dialog.py @@ -51,14 +51,13 @@ class LogDialog(Ui_LogDialog, QDialog): def __init_log_text__(self): log = open(self.log_path) - text = log.read() + self.displayed_text = log.read() log.close() - self.log_text.setText(text) + self.log_text.setText(self.displayed_text) def copy_to_qubes_clipboard_triggered(self): - clipboard = self.app.clipboard().text() - copy_text_to_qubes_clipboard(clipboard) + copy_text_to_qubes_clipboard(self.displayed_text) ########################COPY TEXT TO QUBES CLIPBOARD From fb3ac12aa68436f56d6cff8ca1861970480f2e76 Mon Sep 17 00:00:00 2001 From: Agnieszka Kostrzewa Date: Thu, 10 May 2012 13:46:55 +0200 Subject: [PATCH 4/6] No more 'copy-clipboard-to-qubes-clipboard' button in main window. --- mainwindow.ui | 14 -------------- qubesmanager/main.py | 7 +------ 2 files changed, 1 insertion(+), 20 deletions(-) diff --git a/mainwindow.ui b/mainwindow.ui index 2f716e0..30cfa25 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -239,7 +239,6 @@ - @@ -346,7 +345,6 @@ - @@ -727,18 +725,6 @@ Size on Disk - - - - :/copy.png:/copy.png - - - Copy clipboard to Qubes clipboard - - - Copy Dom0 clipboard to Qubes clipboard - - diff --git a/qubesmanager/main.py b/qubesmanager/main.py index 9df1a82..91a80c3 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -44,7 +44,7 @@ from settings import VMSettingsWindow from restore import RestoreVMsWindow from backup import BackupVMsWindow from global_settings import GlobalSettingsWindow -from log_dialog import LogDialog, copy_text_to_qubes_clipboard +from log_dialog import LogDialog from thread_monitor import * from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent @@ -1512,11 +1512,6 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): def action_about_qubes_triggered(self): QMessageBox.about(self, "About...", "Qubes OS

Release 1.0") - @pyqtSlot(name='on_action_copy_clipboard_triggered') - def action_copy_clipboard_triggered(self): - clipboard = app.clipboard().text() - copy_text_to_qubes_clipboard(clipboard) - def createPopupMenu(self): menu = QMenu() From c809896b523ac0afc90c40a629793688b6d895c9 Mon Sep 17 00:00:00 2001 From: Agnieszka Kostrzewa Date: Fri, 11 May 2012 12:50:45 +0200 Subject: [PATCH 5/6] Run command in VM (#544) --- icons/run-command.png | Bin 0 -> 2182 bytes mainwindow.ui | 13 ++++++++++++ qubesmanager/main.py | 45 ++++++++++++++++++++++++++++++++++++++++++ resources.qrc | 1 + 4 files changed, 59 insertions(+) create mode 100644 icons/run-command.png diff --git a/icons/run-command.png b/icons/run-command.png new file mode 100644 index 0000000000000000000000000000000000000000..9dcc87ad90ac84d54c84aca78f6f55fc67f16c14 GIT binary patch literal 2182 zcmV;12zmF3P)LCeZsCj6F z3cEq};6qv2ZRjn=r@}#E9=h1YOR0PQZ?)HE?X~wl7bnkx#s2p``|^MP|NZ~^|8+Dr zL1WIGIq@Gqe$4Rse9Ju^k5Bzw)3lK97#SHEr1{s>sZ)Diy?Qm|bWiBFjANvxrY5JP zq#WP7ckhX`w6r8rR)@kgH3Fm4WHfQA8#?HcJ~|D4@7UOw-rL*TfByXWGbFe4_4R$8 zZ~*b~@g7njucoHvPXgkdGG&UkV8H_2>-BP_45ek+R9eVnbwetY@y<*y$;e6UdIhXWw1CNEpItm(jk1I6pstz$_^NhU=|F^jeJ??W7! zDwD;{drL}60uLWP{N*!^i3X68m6g?4T3TAXbLURZbtZ9KXe24?3V^Jy$HvC;=ZcDo z0uLTMh#r7_`}URW*|W!%ZNj0Cn4k}+gi%;n$N^AXOgw<4OP4lM1}ZKnC>XZ~DC=lM zKYjA#$voim^Ya5;U0u-ukeQj;SWJW5&L&NoWXm#&vNiyuyh)*igai&?_wL<+`}glh z4*>LQQ#jc|`y~S;T4avUmVBk4z!i1IR*n4p}HF0LbD1(7bu` zX8+NnM@;~R^_?<~MQIrf0HB6~o)lBjNu%U)1aV-)h7JB>$Bvm4FaWq_9GOBwroxaJ zD=}Zlq5^=dmO{l`V9lB}g;YU82}ch|8HZcl$piGPZlqx?YAZ23Kv^h|o15!DdGaI= zMAiDL<<$TdM|h8_ThjwksfEbpDGL`aY^F-oUsY9wg~*cog@q-{+hRb-1b{a`(9`et zj~@WFhlzuOgQ+BX#Wi6;A!Ce=j}KgLaQu8%3ZyB zwdJ~;08max41ivcPBneojvYI)zkU0L+FYA9Z5r=yp(ss_CU?0k!+Owp%?vT0i!|Zr81gy zA*08Y8+ORr_l7{FO+)~oCl$Am0U)J))Qg@71OjJpzGB4+P7y;XW+>_?%SX}B1k3lN zI5Pm)C&2ZI)KOA+}ONWuYxwwpctM zXCWAk89i~t0A|mgUD?voQj1hLfBt;_F^MLQN{j0<0JRHKDo+!@rcIkHzXJfpo&~^{ zFJB@9(AwHshb28dJ)Ic=05bqsD1`4C062vnKYkoJfQuI|){>~r zojcb?p?_~gL3{YRI4-XGBD)vkO+qZAm4<9~kivd9)5d-4_(B9r&hmsh@7AO>o zHf|Z{{rmTC6%>L8o;`aOIe_ceuk(eBR%S;E zxp@Ev7eRdE#tj{%HL}^MQ>S$7HcNEU=r1s;&BZ0G=;){umO%P*%0f;aaPorMf`;+{x;fzi0?E4&78{$M-SjR{KZ7QxbQ$^08k5230=|E)m4X962VLE)Tn6}idm?l zrcPBhEbg>ZZ36`XL5?<7=vh!uzyZ+r!vjER)0>=}Ttw-wmAv5r7eOIp1<@tRbO<#f zxlc^&31Mpya&B&JO$(cSjY?vATx4-B-|zHpl`5)NR9QZkpSO(v0MoE7xRfK`A^-pY07*qo IM6N<$g7uH_@Bjb+ literal 0 HcmV?d00001 diff --git a/mainwindow.ui b/mainwindow.ui index 30cfa25..7f17ac8 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -295,6 +295,7 @@ + @@ -725,6 +726,18 @@ Size on Disk
+ + + + :/run-command.png:/run-command.png + + + Run command in VM + + + Run command in the specified VM + + diff --git a/qubesmanager/main.py b/qubesmanager/main.py index 91a80c3..f99340b 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -23,6 +23,7 @@ import sys import os import fcntl +import errno import dbus from PyQt4.QtCore import * from PyQt4.QtGui import * @@ -735,6 +736,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): self.context_menu.addAction(self.action_appmenus) self.context_menu.addAction(self.action_editfwrules) self.context_menu.addAction(self.action_updatevm) + self.context_menu.addAction(self.action_run_command_in_vm) self.context_menu.addAction(self.action_set_keyboard_layout) self.context_menu.addMenu(self.logs_menu) self.context_menu.addMenu(self.blk_menu) @@ -1040,6 +1042,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): 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) + self.action_run_command_in_vm.setEnabled(vm.qid != 0) self.action_set_keyboard_layout.setEnabled(vm.qid != 0 and vm.last_running) else: self.action_settings.setEnabled(False) @@ -1051,6 +1054,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): self.action_appmenus.setEnabled(False) self.action_editfwrules.setEnabled(False) self.action_updatevm.setEnabled(False) + self.action_run_command_in_vm.setEnabled(False) self.action_set_keyboard_layout.setEnabled(False) @@ -1401,6 +1405,47 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): thread_monitor.set_finished() + @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:
{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 '+vm.name+':'] + 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() + + + + + @pyqtSlot(name='on_action_set_keyboard_layout_triggered') def action_set_keyboard_layout_triggered(self): vm = self.get_selected_vm() diff --git a/resources.qrc b/resources.qrc index 0ca3594..e418cf4 100644 --- a/resources.qrc +++ b/resources.qrc @@ -12,6 +12,7 @@ icons/show-all-running.png icons/mount.png icons/log.png + icons/run-command.png icons/kbd-layout.png icons/copy.png icons/pencil.png From d4673d540e58fec42ac9d672870f40470045ec2a Mon Sep 17 00:00:00 2001 From: Agnieszka Kostrzewa Date: Fri, 11 May 2012 22:52:27 +0200 Subject: [PATCH 6/6] Allow to specify type of the VM being created (#559) --- newappvmdlg.ui | 309 +++++++++++----------------------- qubesmanager/create_new_vm.py | 194 +++++++++++++++++++++ qubesmanager/main.py | 95 +---------- 3 files changed, 298 insertions(+), 300 deletions(-) create mode 100644 qubesmanager/create_new_vm.py diff --git a/newappvmdlg.ui b/newappvmdlg.ui index 4ba6904..c8a86be 100644 --- a/newappvmdlg.ui +++ b/newappvmdlg.ui @@ -1,229 +1,116 @@ - NewAppVMDlg - + NewVMDlg + 0 0 - 507 - 209 + 500 + 200 - Create New AppVM + Create New VM :/qubes.png:/qubes.png - - - - - 0 + + + + + + + true + + + + + + + + + + Use this template: + + + + + + + ProxyVM + + + + + + + NetVM + + + + + + + my-new-vm + + + + + + + AppVM + + + true + + + + + + + false + + + HVM + + + + + + + Name & label: + + + + + + + + + Allow networking + + + true - - - Basic - - - - - - myappvm - - - - - - - true - - - - - - - Name & label: - - - - - - - - - - Use this template: - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Allow networking - - - true - - - - - - - - Advanced - - - - - - Disk storage - - - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 10000 - - - 2 - - - - - - - false - - - Allow to grow - - - - - - - GB - - - - - - - Private storage max. size - - - - - - - false - - - Include in backups - - - true - - - - - - - - - - Memory/CPU - - - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 10000 - - - 100 - - - 400 - - - - - - - MB - - - - - - - false - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - 1 - - - - - - - VCPUs - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - + + + + Qt::Vertical + + + + 20 + 40 + + + + + Qt::Horizontal @@ -240,7 +127,7 @@ buttonBox accepted() - NewAppVMDlg + NewVMDlg accept() @@ -256,7 +143,7 @@ buttonBox rejected() - NewAppVMDlg + NewVMDlg reject() diff --git a/qubesmanager/create_new_vm.py b/qubesmanager/create_new_vm.py new file mode 100644 index 0000000..7ec8b2b --- /dev/null +++ b/qubesmanager/create_new_vm.py @@ -0,0 +1,194 @@ +#!/usr/bin/python2.6 +# +# The Qubes OS Project, http://www.qubes-os.org +# +# Copyright (C) 2012 Agnieszka Kostrzewa +# Copyright (C) 2012 Marek Marczykowski +# +# 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 +import os +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from qubes.qubes import QubesVmCollection +from qubes.qubes import QubesVmLabels +from qubes.qubes import QubesException + +import qubesmanager.resources_rc + +from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent + +import subprocess +import time +import threading + +from ui_newappvmdlg import * +from thread_monitor import * + + +class NewVmDlg (QDialog, Ui_NewVMDlg): + def __init__(self, app, qvm_collection, trayIcon, parent = None): + super (NewVmDlg, self).__init__(parent) + self.setupUi(self) + + self.app = app + self.trayIcon = trayIcon + self.qvm_collection = qvm_collection + + # 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 + + try: + from qubes.qubes import QubesHVM + except ImportError: + pass + else: + self.hvm_radio.setEnabled(True) + + self.qvm_collection.lock_db_for_reading() + self.qvm_collection.load() + self.qvm_collection.unlock_db() + + self.label_list = QubesVmLabels.values() + self.label_list.sort(key=lambda l: l.index) + for (i, label) in enumerate(self.label_list): + self.vmlabel.insertItem(i, label.name) + self.vmlabel.setItemIcon (i, QIcon(label.icon_path)) + + self.template_vm_list = [vm for vm in self.qvm_collection.values() if not vm.internal and vm.is_template()] + + default_index = 0 + for (i, vm) in enumerate(self.template_vm_list): + if vm is self.qvm_collection.get_default_template(): + default_index = i + self.template_name.insertItem(i, vm.name + " (default)") + else: + self.template_name.insertItem(i, vm.name) + self.template_name.setCurrentIndex(default_index) + + self.vmname.selectAll() + self.vmname.setFocus() + + def on_appvm_radio_toggled(self, checked): + if checked: + self.template_name.setEnabled(True) + self.allow_networking.setEnabled(True) + def on_netvm_radio_toggled(self, checked): + if checked: + self.template_name.setEnabled(True) + self.allow_networking.setEnabled(False) + def on_proxyvm_radio_toggled(self, checked): + if checked: + self.template_name.setEnabled(True) + self.allow_networking.setEnabled(True) + def on_hvm_radio_toggled(self, checked): + if checked: + self.template_name.setEnabled(False) + self.allow_networking.setEnabled(True) + + + def reject(self): + self.done(0) + + def accept(self): + vmname = str(self.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 {0} already exists in the system!".format(vmname)) + return + + label = self.label_list[self.vmlabel.currentIndex()] + + template_vm = None + if self.template_name.isEnabled(): + template_vm = self.template_vm_list[self.template_name.currentIndex()] + + allow_networking = None + if self.allow_networking.isEnabled(): + allow_networking = self.allow_networking.isChecked() + + if self.appvm_radio.isChecked(): + createvm_method = self.qvm_collection.add_new_appvm + vmtype = "AppVM" + elif self.netvm_radio.isChecked(): + createvm_method = self.qvm_collection.add_new_netvm + vmtype = "NetVM" + elif self.proxyvm_radio.isChecked(): + createvm_method = self.qvm_collection.add_new_proxyvm + vmtype = "ProxyVM" + else: #hvm_radio.isChecked() + createvm_method = self.qvm_collection.add_new_hvm + vmtype = "HVM" + + + thread_monitor = ThreadMonitor() + thread = threading.Thread (target=self.do_create_vm, args=(createvm_method, vmname, label, template_vm, allow_networking, thread_monitor)) + thread.daemon = True + thread.start() + + progress = QProgressDialog ("Creating new {0} {1}...".format(vmtype, vmname), "", 0, 0) + progress.setCancelButton(None) + progress.setModal(True) + progress.show() + + while not thread_monitor.is_finished(): + self.app.processEvents() + time.sleep (0.1) + + progress.hide() + + if thread_monitor.success: + self.trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' has been created.".format(vmname), msecs=3000) + else: + QMessageBox.warning (None, "Error creating AppVM!", "ERROR: {0}".format(thread_monitor.error_msg)) + + self.done(0) + + + + def do_create_vm (self, createvm_method, vmname, label, template_vm, allow_networking, thread_monitor): + vm = None + try: + self.qvm_collection.lock_db_for_writing() + self.qvm_collection.load() + + if template_vm is not None: + vm = createvm_method(vmname, template_vm, label = label) + vm.create_on_disk(verbose=False, source_template = template_vm) + else: + vm = createvm_method(vmname, label = label) + vm.create_on_disk(verbose=False) + + if allow_networking is not None: + firewall = vm.get_firewall_conf() + firewall["allow"] = allow_networking + firewall["allowDns"] = allow_networking + vm.write_firewall_conf(firewall) + self.qvm_collection.save() + + except Exception as ex: + thread_monitor.set_error_msg (str(ex)) + if vm: + vm.remove_from_disk() + finally: + self.qvm_collection.unlock_db() + + thread_monitor.set_finished() + + diff --git a/qubesmanager/main.py b/qubesmanager/main.py index f99340b..3977b2b 100755 --- a/qubesmanager/main.py +++ b/qubesmanager/main.py @@ -41,6 +41,7 @@ from qubes import qubesutils import qubesmanager.resources_rc import ui_newappvmdlg from ui_mainwindow import * +from create_new_vm import NewVmDlg from settings import VMSettingsWindow from restore import RestoreVMsWindow from backup import BackupVMsWindow @@ -607,10 +608,8 @@ class VmRowInTable(object): if update_size_on_disk == True: self.size_widget.update() -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 @@ -1067,92 +1066,10 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow): @pyqtSlot(name='on_action_createvm_triggered') def action_createvm_triggered(self): - dialog = NewAppVmDlg() - - # 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)) - - template_vm_list = [vm for vm in self.qvm_collection.values() if not vm.internal and vm.is_template()] - - default_index = 0 - for (i, vm) in enumerate(template_vm_list): - if vm is self.qvm_collection.get_default_template(): - 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 {0} already exists in the system!".format(vmname)) - return - - label = label_list[dialog.vmlabel.currentIndex()] - template_vm = template_vm_list[dialog.template_name.currentIndex()] - - allow_networking = dialog.allow_networking.isChecked() - - thread_monitor = ThreadMonitor() - thread = threading.Thread (target=self.do_create_appvm, args=(vmname, label, template_vm, allow_networking, thread_monitor)) - thread.daemon = True - thread.start() - - progress = QProgressDialog ("Creating new AppVM {0}...".format(vmname), "", 0, 0) - progress.setCancelButton(None) - progress.setModal(True) - progress.show() - - while not thread_monitor.is_finished(): - app.processEvents() - time.sleep (0.1) - - progress.hide() - - if thread_monitor.success: - trayIcon.showMessage ("Qubes VM Manager", "VM '{0}' has been created.".format(vmname), msecs=3000) - else: - QMessageBox.warning (None, "Error creating AppVM!", "ERROR: {0}".format(thread_monitor.error_msg)) - - - def do_create_appvm (self, vmname, label, template_vm, allow_networking, thread_monitor): - vm = None - 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) - firewall = vm.get_firewall_conf() - firewall["allow"] = allow_networking - firewall["allowDns"] = allow_networking - vm.write_firewall_conf(firewall) - self.qvm_collection.save() - except Exception as ex: - thread_monitor.set_error_msg (str(ex)) - if vm: - vm.remove_from_disk() - finally: - self.qvm_collection.unlock_db() - - thread_monitor.set_finished() - + dialog = NewVmDlg(app, self.qvm_collection, trayIcon) + dialog.exec_() + def get_selected_vm(self): #vm selection relies on the VmInfo widget's value used for sorting by VM name row_index = self.table.currentRow()