create_new_vm.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #!/usr/bin/python2
  2. #
  3. # The Qubes OS Project, http://www.qubes-os.org
  4. #
  5. # Copyright (C) 2012 Agnieszka Kostrzewa <agnieszka.kostrzewa@gmail.com>
  6. # Copyright (C) 2012 Marek Marczykowski <marmarek@mimuw.edu.pl>
  7. # Copyright (C) 2017 Wojtek Porczyk <woju@invisiblethingslab.com>
  8. #
  9. # This program is free software; you can redistribute it and/or
  10. # modify it under the terms of the GNU General Public License
  11. # as published by the Free Software Foundation; either version 2
  12. # of the License, or (at your option) any later version.
  13. #
  14. # This program is distributed in the hope that it will be useful,
  15. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. # GNU General Public License for more details.
  18. #
  19. # You should have received a copy of the GNU Lesser General Public License along
  20. # with this program; if not, see <http://www.gnu.org/licenses/>.
  21. #
  22. #
  23. import sys
  24. import subprocess
  25. from PyQt4 import QtCore, QtGui # pylint: disable=import-error
  26. import qubesadmin
  27. import qubesadmin.tools
  28. import qubesadmin.exc
  29. from . import utils
  30. from .ui_newappvmdlg import Ui_NewVMDlg # pylint: disable=import-error
  31. # pylint: disable=too-few-public-methods
  32. class CreateVMThread(QtCore.QThread):
  33. def __init__(self, app, vmclass, name, label, template, properties):
  34. QtCore.QThread.__init__(self)
  35. self.app = app
  36. self.vmclass = vmclass
  37. self.name = name
  38. self.label = label
  39. self.template = template
  40. self.properties = properties
  41. self.msg = None
  42. def run(self):
  43. try:
  44. if self.vmclass == 'StandaloneVM' and self.template is not None:
  45. if self.template is qubesadmin.DEFAULT:
  46. src_vm = self.app.default_template
  47. else:
  48. src_vm = self.template
  49. vm = self.app.clone_vm(src_vm, self.name, self.vmclass)
  50. vm.label = self.label
  51. for k, v in self.properties.items():
  52. setattr(vm, k, v)
  53. else:
  54. vm = self.app.add_new_vm(self.vmclass,
  55. name=self.name, label=self.label, template=self.template)
  56. for k, v in self.properties.items():
  57. setattr(vm, k, v)
  58. except qubesadmin.exc.QubesException as qex:
  59. self.msg = str(qex)
  60. except Exception as ex: # pylint: disable=broad-except
  61. self.msg = repr(ex)
  62. class NewVmDlg(QtGui.QDialog, Ui_NewVMDlg):
  63. def __init__(self, qtapp, app, parent=None):
  64. super(NewVmDlg, self).__init__(parent)
  65. self.setupUi(self)
  66. self.qtapp = qtapp
  67. self.app = app
  68. self.thread = None
  69. self.progress = None
  70. # Theoretically we should be locking for writing here and unlock
  71. # only after the VM creation finished. But the code would be
  72. # more messy...
  73. # Instead we lock for writing in the actual worker thread
  74. self.label_list, self.label_idx = utils.prepare_label_choice(
  75. self.label,
  76. self.app, None,
  77. None,
  78. allow_default=False)
  79. self.template_list, self.template_idx = utils.prepare_vm_choice(
  80. self.template_vm,
  81. self.app, None,
  82. self.app.default_template,
  83. (lambda vm: vm.klass == 'TemplateVM'),
  84. allow_internal=False, allow_default=True, allow_none=False)
  85. self.netvm_list, self.netvm_idx = utils.prepare_vm_choice(
  86. self.netvm,
  87. self.app, None,
  88. self.app.default_netvm,
  89. (lambda vm: vm.provides_network),
  90. allow_internal=False, allow_default=True, allow_none=True)
  91. self.name.setValidator(QtGui.QRegExpValidator(
  92. QtCore.QRegExp("[a-zA-Z0-9_-]*", QtCore.Qt.CaseInsensitive), None))
  93. self.name.selectAll()
  94. self.name.setFocus()
  95. if not self.template_list:
  96. QtGui.QMessageBox.warning(None,
  97. self.tr('No template available!'),
  98. self.tr('Cannot create a qube when no template exists.'))
  99. # Order of types is important and used elsewhere; if it's changed
  100. # check for changes needed in self.type_change and TODO
  101. type_list = [self.tr("AppVM"),
  102. self.tr("Standalone qube based on a template"),
  103. self.tr("Standalone qube not based on a template")]
  104. self.vm_type.addItems(type_list)
  105. self.vm_type.currentIndexChanged.connect(self.type_change)
  106. self.launch_settings.stateChanged.connect(self.settings_change)
  107. self.install_system.stateChanged.connect(self.install_change)
  108. def reject(self):
  109. self.done(0)
  110. def accept(self):
  111. vmclass = ('AppVM' if self.vm_type.currentIndex() == 0
  112. else 'StandaloneVM')
  113. name = str(self.name.text())
  114. try:
  115. self.app.domains[name]
  116. except LookupError:
  117. pass
  118. else:
  119. QtGui.QMessageBox.warning(None,
  120. self.tr('Incorrect qube name!'),
  121. self.tr('A qube with the name <b>{}</b> already exists in the '
  122. 'system!').format(name))
  123. return
  124. label = self.label_list[self.label.currentIndex()]
  125. if self.template_vm.currentIndex() == -1:
  126. template = None
  127. else:
  128. template = self.template_list[self.template_vm.currentIndex()]
  129. properties = {}
  130. properties['provides_network'] = self.provides_network.isChecked()
  131. properties['netvm'] = self.netvm_list[self.netvm.currentIndex()]
  132. if self.install_system.isChecked():
  133. properties['virt_mode'] = 'hvm'
  134. properties['kernel'] = None
  135. self.thread = CreateVMThread(self.app, vmclass, name, label,
  136. template, properties)
  137. self.thread.finished.connect(self.create_finished)
  138. self.thread.start()
  139. self.progress = QtGui.QProgressDialog(
  140. self.tr("Creating new qube <b>{}</b>...").format(name), "", 0, 0)
  141. self.progress.setCancelButton(None)
  142. self.progress.setModal(True)
  143. self.progress.show()
  144. def create_finished(self):
  145. self.progress.hide()
  146. if self.thread.msg:
  147. QtGui.QMessageBox.warning(None,
  148. self.tr("Error creating the qube!"),
  149. self.tr("ERROR: {}").format(self.thread.msg))
  150. self.done(0)
  151. if not self.thread.msg:
  152. if self.launch_settings.isChecked():
  153. subprocess.check_call(['qubes-vm-settings',
  154. str(self.name.text())])
  155. if self.install_system.isChecked():
  156. subprocess.check_call(
  157. ['qubes-vm-boot-from-device', str(self.name.text())])
  158. def type_change(self):
  159. # AppVM
  160. if self.vm_type.currentIndex() == 0:
  161. self.template_vm.setEnabled(True)
  162. self.template_vm.setCurrentIndex(0)
  163. self.install_system.setEnabled(False)
  164. self.install_system.setChecked(False)
  165. # Standalone - based on a template
  166. if self.vm_type.currentIndex() == 1:
  167. self.template_vm.setEnabled(True)
  168. self.template_vm.setCurrentIndex(0)
  169. self.install_system.setEnabled(False)
  170. self.install_system.setChecked(False)
  171. # Standalone - not based on a template
  172. if self.vm_type.currentIndex() == 2:
  173. self.template_vm.setEnabled(False)
  174. self.template_vm.setCurrentIndex(-1)
  175. self.install_system.setEnabled(True)
  176. self.install_system.setChecked(True)
  177. def install_change(self):
  178. if self.install_system.isChecked():
  179. self.launch_settings.setChecked(False)
  180. def settings_change(self):
  181. if self.launch_settings.isChecked() and self.install_system.isEnabled():
  182. self.install_system.setChecked(False)
  183. parser = qubesadmin.tools.QubesArgumentParser()
  184. def main(args=None):
  185. args = parser.parse_args(args)
  186. qtapp = QtGui.QApplication(sys.argv)
  187. qtapp.setOrganizationName('Invisible Things Lab')
  188. qtapp.setOrganizationDomain('https://www.qubes-os.org/')
  189. qtapp.setApplicationName('Create qube')
  190. dialog = NewVmDlg(qtapp, args.app)
  191. dialog.exec_()