rewrite creating new qube

This commit is contained in:
Wojtek Porczyk 2017-07-12 14:10:15 +02:00
parent eefa6c31c2
commit 43f2f6b881
4 changed files with 151 additions and 358 deletions

View File

@ -4,6 +4,7 @@
#
# Copyright (C) 2012 Agnieszka Kostrzewa <agnieszka.kostrzewa@gmail.com>
# Copyright (C) 2012 Marek Marczykowski <marmarek@mimuw.edu.pl>
# Copyright (C) 2017 Wojtek Porczyk <woju@invisiblethingslab.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@ -21,281 +22,142 @@
#
#
import sys
import os
import sys
import threading
import time
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qubes.qubes import QubesVmCollection
from qubes.qubes import QubesVmLabels
from qubes.qubes import QubesException
from qubes.qubes import QubesVm,QubesHVm
import qubesadmin
import qubesadmin.tools
import qubesmanager.resources_rc
import time
import threading
from . import utils
from .ui_newappvmdlg import *
from .thread_monitor import *
from .ui_newappvmdlg import Ui_NewVMDlg
from .thread_monitor import ThreadMonitor
class NewVmDlg(QDialog, Ui_NewVMDlg):
def __init__(self, app, qvm_collection, trayIcon, parent = None):
def __init__(self, qtapp, app, parent = None):
super(NewVmDlg, self).__init__(parent)
self.setupUi(self)
self.qtapp = qtapp
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.hvmtpl_radio.setEnabled(True)
self.label_list, self.label_idx = utils.prepare_label_choice(
self.label,
self.app, None,
None,
allow_default=False)
self.qvm_collection.lock_db_for_reading()
self.qvm_collection.load()
self.qvm_collection.unlock_db()
self.template_list, self.template_idx = utils.prepare_vm_choice(
self.template_vm,
self.app, None,
self.app.default_template,
(lambda vm: isinstance(vm, qubesadmin.vm.TemplateVM)),
allow_internal=False, allow_default=True, allow_none=False)
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.netvm_list, self.netvm_idx = utils.prepare_vm_choice(
self.netvm,
self.app, None,
self.app.default_netvm,
(lambda vm: vm.provides_network),
allow_internal=False, allow_default=True, allow_none=True)
self.fill_template_list()
self.fill_netvm_list()
self.name.setValidator(QRegExpValidator(
QRegExp("[a-zA-Z0-9-]*", Qt.CaseInsensitive), None))
self.name.selectAll()
self.name.setFocus()
self.vmname.setValidator(QRegExpValidator(QRegExp("[a-zA-Z0-9-]*", Qt.CaseInsensitive), None))
self.vmname.selectAll()
self.vmname.setFocus()
self.hvmtemplatewarningbox.hide()
def fill_template_list(self):
def filter_template(vm):
if vm.internal:
return False
if not vm.is_template():
return False
if self.hvm_radio.isChecked():
return QubesHVm.is_template_compatible(vm)
elif self.hvmtpl_radio.isChecked():
return False
else:
return QubesVm.is_template_compatible(vm)
self.template_vm_list = filter(filter_template, self.qvm_collection.values())
self.template_name.clear()
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 + self.tr(" (default)"))
else:
self.template_name.insertItem(i, vm.name)
self.template_name.setCurrentIndex(default_index)
def fill_netvm_list(self):
def filter_netvm(vm):
if vm.internal:
return False
if vm.qid == 0:
return False
if vm.is_netvm():
return True
if vm.is_proxyvm():
return True
else:
return False
self.netvm_list = filter(filter_netvm, self.qvm_collection.values())
self.netvm_name.clear()
default_index = 0
for (i, vm) in enumerate(self.netvm_list):
if vm is self.qvm_collection.get_default_netvm():
default_index = i
self.netvm_name.insertItem(i, vm.name + self.tr(" (default)"))
else:
self.netvm_name.insertItem(i, vm.name)
self.netvm_name.setCurrentIndex(default_index)
def on_allow_networking_toggled(self, checked):
if checked:
self.fill_netvm_list()
self.netvm_name.setEnabled(True)
else:
self.netvm_name.clear()
self.netvm_name.setEnabled(False)
def on_appvm_radio_toggled(self, checked):
if checked:
self.template_name.setEnabled(True)
self.allow_networking.setEnabled(True)
self.netvm_name.setEnabled(self.allow_networking.isChecked())
def on_netvm_radio_toggled(self, checked):
if checked:
self.template_name.setEnabled(True)
self.allow_networking.setChecked(True)
self.allow_networking.setEnabled(False)
self.netvm_name.setEnabled(False)
def on_proxyvm_radio_toggled(self, checked):
if checked:
self.template_name.setEnabled(True)
self.allow_networking.setEnabled(True)
self.netvm_name.setEnabled(self.allow_networking.isChecked())
def on_hvm_radio_toggled(self, checked):
if self.hvm_radio.isChecked() or self.hvmtpl_radio.isChecked():
self.standalone.setChecked(True)
self.allow_networking.setEnabled(True)
self.netvm_name.setEnabled(self.allow_networking.isChecked())
self.standalone.setEnabled(self.hvm_radio.isChecked())
else:
self.standalone.setChecked(False)
self.standalone.setEnabled(True)
self.fill_template_list()
def on_hvmtpl_radio_toggled(self, checked):
return self.on_hvm_radio_toggled(checked)
def on_standalone_toggled(self, checked):
if checked and (self.hvm_radio.isChecked() or
self.hvmtpl_radio.isChecked()):
self.template_name.setEnabled(False)
else:
self.template_name.setEnabled(True)
if not checked and self.hvm_radio.isChecked():
self.hvmtemplatewarningbox.show()
else:
self.hvmtemplatewarningbox.hide()
if len(self.template_list) == 0:
QMessageBox.warning(None,
self.tr('No template available!'),
self.tr('Cannot create a qube when no template exists.'))
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,
self.tr("Incorrect AppVM Name!"),
self.tr("A VM with the name <b>{0}</b> already exists in the "
"system!").format(vmname))
return
vmclass = ('StandaloneVM' if self.standalone.isChecked() else 'AppVM')
label = self.label_list[self.vmlabel.currentIndex()]
template_vm = None
if self.template_name.isEnabled():
if len(self.template_vm_list) == 0:
QMessageBox.warning(None,
self.tr("No template available!"),
self.tr("Cannot create non-standalone VM when no "
"compatible template exists. Create template VM "
"first or choose to create standalone VM."))
return
template_vm = self.template_vm_list[self.template_name.currentIndex()]
netvm = None
if self.netvm_name.isEnabled():
netvm = self.netvm_list[self.netvm_name.currentIndex()]
standalone = self.standalone.isChecked()
allow_networking = None
if self.allow_networking.isEnabled():
allow_networking = self.allow_networking.isChecked()
if self.appvm_radio.isChecked():
vmtype = "AppVM"
elif self.netvm_radio.isChecked():
vmtype = "NetVM"
elif self.proxyvm_radio.isChecked():
vmtype = "ProxyVM"
elif self.hvm_radio.isChecked():
vmtype = "HVM"
elif self.hvmtpl_radio.isChecked():
vmtype = "TemplateHVM"
name = str(self.name.text())
try:
self.app.domains[name]
except LookupError:
pass
else:
QErrorMessage.showMessage(None,
self.tr("Error creating AppVM!"),
self.tr("Unknown VM type, this is error in Qubes Manager"))
self.done(0)
QMessageBox.warning(None,
self.tr('Incorrect qube name!'),
self.tr('A qube with the name <b>{}</b> already exists in the '
'system!').format(name))
return
label = self.label_list[self.label.currentIndex()]
template = self.template_list[self.template_vm.currentIndex()]
properties = {}
properties['provides_network'] = self.provides_network.isChecked()
properties['hvm'] = self.hvm.isChecked()
properties['netvm'] = self.netvm_list[self.netvm.currentIndex()]
vmclass = "Qubes" + vmtype.replace("VM", "Vm")
thread_monitor = ThreadMonitor()
thread = threading.Thread (target=self.do_create_vm, args=(vmclass, vmname, label, template_vm, netvm, standalone, allow_networking, thread_monitor))
thread = threading.Thread(target=self.do_create_vm,
args=(self.app, vmclass, name, label, template, properties,
thread_monitor))
thread.daemon = True
thread.start()
progress = QProgressDialog(
self.tr("Creating new {0} <b>{1}</b>...").format(vmtype, vmname), "", 0, 0)
self.tr("Creating new qube <b>{}</b>...").format(name), "", 0, 0)
progress.setCancelButton(None)
progress.setModal(True)
progress.show()
while not thread_monitor.is_finished():
self.app.processEvents()
self.qtapp.processEvents()
time.sleep (0.1)
progress.hide()
if thread_monitor.success:
self.trayIcon.showMessage(
self.tr("VM '{0}' has been created.").format(vmname), msecs=3000)
else:
if not thread_monitor.success:
QMessageBox.warning(None,
self.tr("Error creating AppVM!"),
self.tr("ERROR: {0}").format(thread_monitor.error_msg))
self.tr("Error creating the qube!"),
self.tr("ERROR: {}").format(thread_monitor.error_msg))
self.done(0)
@staticmethod
def do_create_vm(vmclass, vmname, label, template_vm, netvm,
standalone, allow_networking, thread_monitor):
vm = None
qc = QubesVmCollection()
qc.lock_db_for_writing()
qc.load()
def do_create_vm(app, vmclass, name, label, template, properties,
thread_monitor):
try:
if not standalone:
vm = qc.add_new_vm(vmclass, name=vmname, template=template_vm,
label=label)
else:
vm = qc.add_new_vm(vmclass, name=vmname, template=None,
label=label)
vm.create_on_disk(verbose=False, source_template=template_vm)
vm = app.add_new_vm(vmclass,
name=name, label=label, template=template)
for k, v in properties.items():
setattr(vm, k, v)
if not allow_networking:
vm.uses_default_netvm = False
vm.netvm = None
else:
vm.netvm = netvm
if vm.netvm.qid == qc.get_default_netvm().qid:
vm.uses_default_netvm = True
else:
vm.uses_default_netvm = False
qc.save()
except Exception as ex:
thread_monitor.set_error_msg(str(ex))
if vm:
vm.remove_from_disk()
qc.pop(vm.qid)
finally:
qc.unlock_db()
thread_monitor.set_finished()
parser = qubesadmin.tools.QubesArgumentParser()
def main(args=None):
args = parser.parse_args(args)
qtapp = QApplication(sys.argv)
qtapp.setOrganizationName('Invisible Things Lab')
qtapp.setOrganizationDomain('https://www.qubes-os.org/')
qtapp.setApplicationName('Create qube')
dialog = NewVmDlg(qtapp, args.app)
dialog.exec_()

View File

@ -62,6 +62,7 @@ rm -rf $RPM_BUILD_ROOT
%files
%defattr(-,root,root,-)
/usr/bin/qubes-vm-settings
/usr/bin/qubes-vm-create
/usr/libexec/qubes-manager/mount_for_backup.sh
/usr/libexec/qubes-manager/qvm_about.sh

View File

@ -19,5 +19,6 @@ if __name__ == '__main__':
entry_points={
'console_scripts': [
'qubes-vm-settings = qubesmanager.settings:main',
'qubes-vm-create = qubesmanager.create_new_vm:main',
],
})

View File

@ -6,54 +6,18 @@
<rect>
<x>0</x>
<y>0</y>
<width>496</width>
<height>215</height>
<width>618</width>
<height>214</height>
</rect>
</property>
<property name="windowTitle">
<string>Create New VM</string>
<string>Create new qube</string>
</property>
<property name="windowIcon">
<iconset theme="qubes-manager"/>
<iconset theme="qubes-manager">
<normaloff/>
</iconset>
</property>
<widget class="QFrame" name="hvmtemplatewarningbox">
<property name="geometry">
<rect>
<x>9</x>
<y>117</y>
<width>481</width>
<height>56</height>
</rect>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<widget class="QLabel" name="hvmtemplatewarning">
<property name="enabled">
<bool>true</bool>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>471</width>
<height>61</height>
</rect>
</property>
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-weight:600; color:#ff0000;&quot;&gt;Make sure that the license of the OS installed in a template VM grants you permission to run multiple instances of an installed system.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</widget>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="geometry">
<rect>
@ -70,131 +34,96 @@
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<widget class="QWidget" name="">
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>14</x>
<y>14</y>
<width>471</width>
<height>103</height>
<x>0</x>
<y>10</y>
<width>596</width>
<height>175</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label">
<property name="text">
<string>Name and label:</string>
</property>
<property name="buddy">
<cstring>vmname</cstring>
</property>
</widget>
</item>
<item row="0" column="2" colspan="3">
<widget class="QLineEdit" name="vmname">
<property name="text">
<string>my-new-vm</string>
</property>
</widget>
</item>
<item row="0" column="5">
<widget class="QComboBox" name="vmlabel">
<item row="0" column="3">
<widget class="QComboBox" name="label">
<property name="frame">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<item row="0" column="1" colspan="2">
<widget class="QLineEdit" name="name">
<property name="text">
<string>my-new-vm</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="name_label">
<property name="text">
<string>Name and label:</string>
</property>
<property name="buddy">
<cstring>name</cstring>
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<widget class="QComboBox" name="netvm"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="template_label">
<property name="text">
<string>Use this template:</string>
</property>
<property name="buddy">
<cstring>template_name</cstring>
<cstring>template_vm</cstring>
</property>
</widget>
</item>
<item row="1" column="2" colspan="3">
<widget class="QComboBox" name="template_name"/>
<item row="1" column="1" colspan="3">
<widget class="QComboBox" name="template_vm"/>
</item>
<item row="1" column="5">
<item row="2" column="0">
<widget class="QLabel" name="netvm_label">
<property name="text">
<string>Network:</string>
</property>
<property name="buddy">
<cstring>template_vm</cstring>
</property>
</widget>
</item>
<item row="3" column="2">
<widget class="QCheckBox" name="hvm">
<property name="text">
<string>HVM</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QCheckBox" name="provides_network">
<property name="text">
<string>Provides network</string>
</property>
</widget>
</item>
<item row="3" column="3">
<widget class="QCheckBox" name="standalone">
<property name="text">
<string>Standalone</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QRadioButton" name="appvm_radio">
<property name="text">
<string>AppVM</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QRadioButton" name="netvm_radio">
<property name="text">
<string>NetVM</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QRadioButton" name="proxyvm_radio">
<property name="text">
<string>ProxyVM</string>
</property>
</widget>
</item>
<item row="2" column="4">
<widget class="QRadioButton" name="hvm_radio">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>HVM</string>
</property>
</widget>
</item>
<item row="2" column="5">
<widget class="QRadioButton" name="hvmtpl_radio">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>HVM template</string>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QCheckBox" name="allow_networking">
<property name="text">
<string>Allow networking:</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="3" column="2" colspan="3">
<widget class="QComboBox" name="netvm_name"/>
</item>
</layout>
</widget>
</widget>
<tabstops>
<tabstop>vmname</tabstop>
<tabstop>vmlabel</tabstop>
<tabstop>template_name</tabstop>
<tabstop>standalone</tabstop>
<tabstop>appvm_radio</tabstop>
<tabstop>netvm_radio</tabstop>
<tabstop>proxyvm_radio</tabstop>
<tabstop>hvm_radio</tabstop>
<tabstop>hvmtpl_radio</tabstop>
<tabstop>allow_networking</tabstop>
<tabstop>name</tabstop>
<tabstop>template_vm</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>