Browse Source

Allow to specify type of the VM being created (#559)

Agnieszka Kostrzewa 12 years ago
parent
commit
d4673d540e
3 changed files with 298 additions and 300 deletions
  1. 98 211
      newappvmdlg.ui
  2. 194 0
      qubesmanager/create_new_vm.py
  3. 6 89
      qubesmanager/main.py

+ 98 - 211
newappvmdlg.ui

@@ -1,229 +1,116 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <ui version="4.0">
- <class>NewAppVMDlg</class>
- <widget class="QDialog" name="NewAppVMDlg">
+ <class>NewVMDlg</class>
+ <widget class="QDialog" name="NewVMDlg">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
-    <width>507</width>
-    <height>209</height>
+    <width>500</width>
+    <height>200</height>
    </rect>
   </property>
   <property name="windowTitle">
-   <string>Create New AppVM</string>
+   <string>Create New VM</string>
   </property>
   <property name="windowIcon">
    <iconset>
     <normaloff>:/qubes.png</normaloff>:/qubes.png</iconset>
   </property>
-  <layout class="QGridLayout" name="gridLayout_3">
-   <item row="2" column="0">
-    <widget class="QTabWidget" name="tabWidget">
-     <property name="currentIndex">
-      <number>0</number>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QGridLayout" name="gridLayout">
+     <item row="0" column="3">
+      <widget class="QComboBox" name="vmlabel">
+       <property name="frame">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1" colspan="2">
+      <widget class="QComboBox" name="template_name"/>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="label_2">
+       <property name="text">
+        <string>Use this template:</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="2">
+      <widget class="QRadioButton" name="proxyvm_radio">
+       <property name="text">
+        <string>ProxyVM</string>
+       </property>
+      </widget>
+     </item>
+     <item row="2" column="1">
+      <widget class="QRadioButton" name="netvm_radio">
+       <property name="text">
+        <string>NetVM</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="1" colspan="2">
+      <widget class="QLineEdit" name="vmname">
+       <property name="text">
+        <string>my-new-vm</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="3">
+      <widget class="QRadioButton" name="hvm_radio">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>HVM</string>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="0">
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Name &amp; label:</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="allow_networking">
+     <property name="text">
+      <string>Allow networking</string>
+     </property>
+     <property name="checked">
+      <bool>true</bool>
      </property>
-     <widget class="QWidget" name="tab">
-      <attribute name="title">
-       <string>Basic</string>
-      </attribute>
-      <layout class="QGridLayout" name="gridLayout_2">
-       <item row="0" column="1" colspan="2">
-        <widget class="QLineEdit" name="vmname">
-         <property name="text">
-          <string>myappvm</string>
-         </property>
-        </widget>
-       </item>
-       <item row="0" column="3">
-        <widget class="QComboBox" name="vmlabel">
-         <property name="frame">
-          <bool>true</bool>
-         </property>
-        </widget>
-       </item>
-       <item row="0" column="0">
-        <widget class="QLabel" name="label">
-         <property name="text">
-          <string>Name &amp; label:</string>
-         </property>
-        </widget>
-       </item>
-       <item row="1" column="1" colspan="2">
-        <widget class="QComboBox" name="template_name"/>
-       </item>
-       <item row="1" column="0">
-        <widget class="QLabel" name="label_2">
-         <property name="text">
-          <string>Use this template:</string>
-         </property>
-        </widget>
-       </item>
-       <item row="3" column="0">
-        <spacer name="verticalSpacer">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>40</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-       <item row="2" 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>
-      </layout>
-     </widget>
-     <widget class="QWidget" name="tab_2">
-      <attribute name="title">
-       <string>Advanced</string>
-      </attribute>
-      <layout class="QGridLayout" name="gridLayout_4">
-       <item row="0" column="0">
-        <widget class="QGroupBox" name="groupBox">
-         <property name="title">
-          <string>Disk storage</string>
-         </property>
-         <layout class="QGridLayout" name="gridLayout">
-          <item row="0" column="1">
-           <widget class="QSpinBox" name="priv_size">
-            <property name="enabled">
-             <bool>false</bool>
-            </property>
-            <property name="alignment">
-             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-            </property>
-            <property name="maximum">
-             <number>10000</number>
-            </property>
-            <property name="value">
-             <number>2</number>
-            </property>
-           </widget>
-          </item>
-          <item row="2" column="0">
-           <widget class="QCheckBox" name="priv_allow_to_grow">
-            <property name="enabled">
-             <bool>false</bool>
-            </property>
-            <property name="text">
-             <string>Allow to grow</string>
-            </property>
-           </widget>
-          </item>
-          <item row="0" column="2">
-           <widget class="QLabel" name="label_5">
-            <property name="text">
-             <string>GB</string>
-            </property>
-           </widget>
-          </item>
-          <item row="0" column="0">
-           <widget class="QLabel" name="label_3">
-            <property name="text">
-             <string>Private storage max. size</string>
-            </property>
-           </widget>
-          </item>
-          <item row="3" column="0">
-           <widget class="QCheckBox" name="checkBox">
-            <property name="enabled">
-             <bool>false</bool>
-            </property>
-            <property name="text">
-             <string>Include in backups</string>
-            </property>
-            <property name="checked">
-             <bool>true</bool>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </widget>
-       </item>
-       <item row="0" column="1">
-        <widget class="QGroupBox" name="groupBox_2">
-         <property name="title">
-          <string>Memory/CPU</string>
-         </property>
-         <layout class="QGridLayout" name="gridLayout_5">
-          <item row="0" column="1">
-           <widget class="QSpinBox" name="mem_size">
-            <property name="enabled">
-             <bool>false</bool>
-            </property>
-            <property name="alignment">
-             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-            </property>
-            <property name="maximum">
-             <number>10000</number>
-            </property>
-            <property name="singleStep">
-             <number>100</number>
-            </property>
-            <property name="value">
-             <number>400</number>
-            </property>
-           </widget>
-          </item>
-          <item row="0" column="2">
-           <widget class="QLabel" name="label_6">
-            <property name="text">
-             <string>MB</string>
-            </property>
-           </widget>
-          </item>
-          <item row="1" column="1">
-           <widget class="QSpinBox" name="spinBox">
-            <property name="enabled">
-             <bool>false</bool>
-            </property>
-            <property name="alignment">
-             <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-            </property>
-            <property name="value">
-             <number>1</number>
-            </property>
-           </widget>
-          </item>
-          <item row="1" column="2">
-           <widget class="QLabel" name="label_4">
-            <property name="text">
-             <string>VCPUs</string>
-            </property>
-           </widget>
-          </item>
-         </layout>
-        </widget>
-       </item>
-       <item row="1" column="0">
-        <spacer name="verticalSpacer_2">
-         <property name="orientation">
-          <enum>Qt::Vertical</enum>
-         </property>
-         <property name="sizeHint" stdset="0">
-          <size>
-           <width>20</width>
-           <height>40</height>
-          </size>
-         </property>
-        </spacer>
-       </item>
-      </layout>
-     </widget>
     </widget>
    </item>
-   <item row="3" column="0">
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item>
     <widget class="QDialogButtonBox" name="buttonBox">
      <property name="orientation">
       <enum>Qt::Horizontal</enum>
@@ -240,7 +127,7 @@
   <connection>
    <sender>buttonBox</sender>
    <signal>accepted()</signal>
-   <receiver>NewAppVMDlg</receiver>
+   <receiver>NewVMDlg</receiver>
    <slot>accept()</slot>
    <hints>
     <hint type="sourcelabel">
@@ -256,7 +143,7 @@
   <connection>
    <sender>buttonBox</sender>
    <signal>rejected()</signal>
-   <receiver>NewAppVMDlg</receiver>
+   <receiver>NewVMDlg</receiver>
    <slot>reject()</slot>
    <hints>
     <hint type="sourcelabel">

+ 194 - 0
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 <agnieszka.kostrzewa@gmail.com>
+# Copyright (C) 2012  Marek Marczykowski <marmarek@mimuw.edu.pl>
+#
+# 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 <b>{0}</b> 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} <b>{1}</b>...".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()
+
+

+ 6 - 89
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 <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()]
-
-            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 <b>{0}</b>...".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()