Add "Clone VM" action (#613)
This commit is contained in:
parent
7472c3a98b
commit
e8a0461bac
@ -229,7 +229,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>769</width>
|
<width>769</width>
|
||||||
<height>25</height>
|
<height>22</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<widget class="QMenu" name="menu_system">
|
<widget class="QMenu" name="menu_system">
|
||||||
@ -285,6 +285,7 @@
|
|||||||
</widget>
|
</widget>
|
||||||
<addaction name="action_createvm"/>
|
<addaction name="action_createvm"/>
|
||||||
<addaction name="action_removevm"/>
|
<addaction name="action_removevm"/>
|
||||||
|
<addaction name="action_clonevm"/>
|
||||||
<addaction name="separator"/>
|
<addaction name="separator"/>
|
||||||
<addaction name="action_resumevm"/>
|
<addaction name="action_resumevm"/>
|
||||||
<addaction name="action_pausevm"/>
|
<addaction name="action_pausevm"/>
|
||||||
@ -738,6 +739,18 @@
|
|||||||
<string>Run command in the specified VM</string>
|
<string>Run command in the specified VM</string>
|
||||||
</property>
|
</property>
|
||||||
</action>
|
</action>
|
||||||
|
<action name="action_clonevm">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="icon">
|
||||||
|
<iconset resource="resources.qrc">
|
||||||
|
<normaloff>:/templatevm.png</normaloff>:/templatevm.png</iconset>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Clone VM</string>
|
||||||
|
</property>
|
||||||
|
</action>
|
||||||
</widget>
|
</widget>
|
||||||
<resources>
|
<resources>
|
||||||
<include location="resources.qrc"/>
|
<include location="resources.qrc"/>
|
||||||
|
@ -39,6 +39,7 @@ from qubes.qubes import dry_run
|
|||||||
from qubes.qubes import qubes_guid_path
|
from qubes.qubes import qubes_guid_path
|
||||||
from qubes.qubes import QubesDaemonPidfile
|
from qubes.qubes import QubesDaemonPidfile
|
||||||
from qubes.qubes import QubesHost
|
from qubes.qubes import QubesHost
|
||||||
|
from qubes import qubes
|
||||||
from qubes import qubesutils
|
from qubes import qubesutils
|
||||||
|
|
||||||
import qubesmanager.resources_rc
|
import qubesmanager.resources_rc
|
||||||
@ -754,6 +755,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
self.context_menu = QMenu(self)
|
self.context_menu = QMenu(self)
|
||||||
self.context_menu.addAction(self.action_removevm)
|
self.context_menu.addAction(self.action_removevm)
|
||||||
self.context_menu.addAction(self.action_resumevm)
|
self.context_menu.addAction(self.action_resumevm)
|
||||||
|
self.context_menu.addAction(self.action_clonevm)
|
||||||
self.context_menu.addAction(self.action_pausevm)
|
self.context_menu.addAction(self.action_pausevm)
|
||||||
self.context_menu.addAction(self.action_shutdownvm)
|
self.context_menu.addAction(self.action_shutdownvm)
|
||||||
self.context_menu.addAction(self.action_killvm)
|
self.context_menu.addAction(self.action_killvm)
|
||||||
@ -1073,6 +1075,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
# Update available actions:
|
# Update available actions:
|
||||||
self.action_settings.setEnabled(vm.qid != 0)
|
self.action_settings.setEnabled(vm.qid != 0)
|
||||||
self.action_removevm.setEnabled(not vm.installed_by_rpm and not (vm.last_running))
|
self.action_removevm.setEnabled(not vm.installed_by_rpm and not (vm.last_running))
|
||||||
|
self.action_clonevm.setEnabled(not vm.installed_by_rpm and not (vm.last_running) and not vm.is_netvm())
|
||||||
self.action_resumevm.setEnabled(not vm.last_running)
|
self.action_resumevm.setEnabled(not vm.last_running)
|
||||||
self.action_pausevm.setEnabled(vm.last_running and vm.qid != 0)
|
self.action_pausevm.setEnabled(vm.last_running and vm.qid != 0)
|
||||||
self.action_shutdownvm.setEnabled(vm.last_running and vm.qid != 0)
|
self.action_shutdownvm.setEnabled(vm.last_running and vm.qid != 0)
|
||||||
@ -1085,6 +1088,7 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
else:
|
else:
|
||||||
self.action_settings.setEnabled(False)
|
self.action_settings.setEnabled(False)
|
||||||
self.action_removevm.setEnabled(False)
|
self.action_removevm.setEnabled(False)
|
||||||
|
self.action_clonevm.setEnabled(False)
|
||||||
self.action_resumevm.setEnabled(False)
|
self.action_resumevm.setEnabled(False)
|
||||||
self.action_pausevm.setEnabled(False)
|
self.action_pausevm.setEnabled(False)
|
||||||
self.action_shutdownvm.setEnabled(False)
|
self.action_shutdownvm.setEnabled(False)
|
||||||
@ -1213,6 +1217,68 @@ class VmManagerWindow(Ui_VmManagerWindow, QMainWindow):
|
|||||||
|
|
||||||
thread_monitor.set_finished()
|
thread_monitor.set_finished()
|
||||||
|
|
||||||
|
@pyqtSlot(name='on_action_clonevm_triggered')
|
||||||
|
def action_clonevm_triggered(self):
|
||||||
|
vm = self.get_selected_vm()
|
||||||
|
|
||||||
|
name_number = 1
|
||||||
|
name_format = vm.name + '-clone-%d'
|
||||||
|
while self.qvm_collection.get_vm_by_name(name_format % name_number):
|
||||||
|
name_number += 1
|
||||||
|
|
||||||
|
cmd = ['kdialog', '--title', 'Qubes clone VM', '--inputbox', 'Enter name for VM <b>'+vm.name+'</b> clone:', name_format % name_number]
|
||||||
|
kdialog = subprocess.Popen(cmd, stdout = subprocess.PIPE)
|
||||||
|
clone_name = kdialog.stdout.read().strip()
|
||||||
|
if clone_name == "":
|
||||||
|
return
|
||||||
|
|
||||||
|
thread_monitor = ThreadMonitor()
|
||||||
|
thread = threading.Thread (target=self.do_clone_vm, args=(vm, clone_name, thread_monitor))
|
||||||
|
thread.daemon = True
|
||||||
|
thread.start()
|
||||||
|
|
||||||
|
progress = QProgressDialog ("Cloning VM <b>{0}</b> to <b>{1}</b>...".format(vm.name, clone_name), "", 0, 0)
|
||||||
|
progress.setCancelButton(None)
|
||||||
|
progress.setModal(True)
|
||||||
|
progress.show()
|
||||||
|
|
||||||
|
while not thread_monitor.is_finished():
|
||||||
|
app.processEvents()
|
||||||
|
time.sleep (0.2)
|
||||||
|
|
||||||
|
progress.hide()
|
||||||
|
|
||||||
|
if not thread_monitor.success:
|
||||||
|
QMessageBox.warning (None, "Error while cloning VM", "Exception while cloning:<br>{0}".format(thread_monitor.error_msg))
|
||||||
|
|
||||||
|
|
||||||
|
def do_clone_vm(self, vm, dst_name, thread_monitor):
|
||||||
|
try:
|
||||||
|
self.qvm_collection.lock_db_for_writing()
|
||||||
|
self.qvm_collection.load()
|
||||||
|
src_vm = self.qvm_collection[vm.qid]
|
||||||
|
|
||||||
|
dst_vm = None
|
||||||
|
if isinstance(src_vm, qubes.QubesTemplateVm):
|
||||||
|
dst_vm = self.qvm_collection.add_new_templatevm(name=dst_name,
|
||||||
|
installed_by_rpm=False)
|
||||||
|
elif isinstance(src_vm, qubes.QubesAppVm):
|
||||||
|
dst_vm = self.qvm_collection.add_new_appvm(name=dst_name, template=src_vm.template,
|
||||||
|
label=src_vm.label)
|
||||||
|
elif hasattr(qubes, 'QubesHVm') and isinstance(src_vm, qubes.QubesHVm):
|
||||||
|
dst_vm = self.qvm_collection.add_new_hvm(name=dst_name, label=src_vm.label)
|
||||||
|
|
||||||
|
dst_vm.clone_attrs(src_vm)
|
||||||
|
dst_vm.clone_disk_files (src_vm=src_vm, verbose=False)
|
||||||
|
self.qvm_collection.save()
|
||||||
|
self.qvm_collection.unlock_db()
|
||||||
|
except Exception as ex:
|
||||||
|
if dst_vm:
|
||||||
|
self.qvm_collection.pop(dst_vm.qid)
|
||||||
|
self.qvm_collection.unlock_db()
|
||||||
|
thread_monitor.set_error_msg(str(ex))
|
||||||
|
thread_monitor.set_finished()
|
||||||
|
|
||||||
@pyqtSlot(name='on_action_resumevm_triggered')
|
@pyqtSlot(name='on_action_resumevm_triggered')
|
||||||
def action_resumevm_triggered(self):
|
def action_resumevm_triggered(self):
|
||||||
vm = self.get_selected_vm()
|
vm = self.get_selected_vm()
|
||||||
|
Loading…
Reference in New Issue
Block a user