mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-01-03 01:18:00 +03:00
virtManager: add support to create external snapshots
Add new 2 rows into the snapshotsnew windows, one to select if the snapshot mode is `internal` or `external` and memory state location when making external snapshot for running VM. We will check if libvirt fully supports external snapshots by consulting host capabilities and add `external` to snapshot mode only if it is supported. For external snapshots the memory state is stored in separate file but libvirt doesn't have any default location so virtManager will get path to all usable disks and create a dropdown menu for it, but user is still allowed to modify that path to use whatever path they prefer. Signed-off-by: Pavel Hrdina <phrdina@redhat.com>
This commit is contained in:
parent
f1ba012e8f
commit
5488ff4773
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.38.2 -->
|
<!-- Generated with glade 3.40.0 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.22"/>
|
<requires lib="gtk+" version="3.22"/>
|
||||||
<object class="GtkWindow" id="snapshot-new">
|
<object class="GtkWindow" id="snapshot-new">
|
||||||
@ -89,7 +89,7 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<child>
|
<child>
|
||||||
<!-- n-columns=2 n-rows=4 -->
|
<!-- n-columns=2 n-rows=6 -->
|
||||||
<object class="GtkGrid" id="snapshot-new-box">
|
<object class="GtkGrid" id="snapshot-new-box">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
@ -134,7 +134,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left-attach">0</property>
|
<property name="left-attach">0</property>
|
||||||
<property name="top-attach">2</property>
|
<property name="top-attach">4</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@ -153,7 +153,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left-attach">1</property>
|
<property name="left-attach">1</property>
|
||||||
<property name="top-attach">2</property>
|
<property name="top-attach">4</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@ -213,7 +213,7 @@
|
|||||||
</object>
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left-attach">0</property>
|
<property name="left-attach">0</property>
|
||||||
<property name="top-attach">3</property>
|
<property name="top-attach">5</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@ -223,6 +223,121 @@
|
|||||||
<property name="halign">start</property>
|
<property name="halign">start</property>
|
||||||
<property name="icon-name">image-missing</property>
|
<property name="icon-name">image-missing</property>
|
||||||
</object>
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">1</property>
|
||||||
|
<property name="top-attach">5</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="snapshot-new-mode-label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">Snapshot _Mode:</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">0</property>
|
||||||
|
<property name="top-attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkButtonBox" id="snapshot-new-mode-box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="layout-style">start</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="snapshot-new-mode-external">
|
||||||
|
<property name="label" translatable="yes">_external</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">False</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw-indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_snapshot_new_mode_toggled" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkRadioButton" id="snapshot-new-mode-internal">
|
||||||
|
<property name="label" translatable="yes">_internal</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">False</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
<property name="active">True</property>
|
||||||
|
<property name="draw-indicator">True</property>
|
||||||
|
<property name="group">snapshot-new-mode-external</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">True</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">1</property>
|
||||||
|
<property name="top-attach">2</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="snapshot-new-memory-label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="label" translatable="yes">Memory _State:</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left-attach">0</property>
|
||||||
|
<property name="top-attach">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="snapshot-new-memory-box">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="snapshot-new-memory-auto">
|
||||||
|
<property name="label" translatable="yes">_auto</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="receives-default">False</property>
|
||||||
|
<property name="use-underline">True</property>
|
||||||
|
<property name="draw-indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_snapshot_new_memory_toggled" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBox" id="snapshot-new-memory-path">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="hexpand">True</property>
|
||||||
|
<property name="has-entry">True</property>
|
||||||
|
<child internal-child="entry">
|
||||||
|
<object class="GtkEntry">
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
<packing>
|
<packing>
|
||||||
<property name="left-attach">1</property>
|
<property name="left-attach">1</property>
|
||||||
<property name="top-attach">3</property>
|
<property name="top-attach">3</property>
|
||||||
|
@ -81,6 +81,8 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
"on_snapshot_new_name_changed": self._name_changed_cb,
|
"on_snapshot_new_name_changed": self._name_changed_cb,
|
||||||
"on_snapshot_new_name_activate": self._ok_clicked_cb,
|
"on_snapshot_new_name_activate": self._ok_clicked_cb,
|
||||||
"on_snapshot_new_ok_clicked": self._ok_clicked_cb,
|
"on_snapshot_new_ok_clicked": self._ok_clicked_cb,
|
||||||
|
"on_snapshot_new_mode_toggled": self._mode_toggled_cb,
|
||||||
|
"on_snapshot_new_memory_toggled": self._memory_toggled_cb,
|
||||||
})
|
})
|
||||||
self.bind_escape_key_close()
|
self.bind_escape_key_close()
|
||||||
|
|
||||||
@ -92,6 +94,7 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
def show(self, parent):
|
def show(self, parent):
|
||||||
log.debug("Showing new snapshot wizard")
|
log.debug("Showing new snapshot wizard")
|
||||||
self._reset_state()
|
self._reset_state()
|
||||||
|
self.topwin.resize(1, 1)
|
||||||
self.topwin.set_transient_for(parent)
|
self.topwin.set_transient_for(parent)
|
||||||
self.topwin.present()
|
self.topwin.present()
|
||||||
|
|
||||||
@ -108,9 +111,43 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
# UI init #
|
# UI init #
|
||||||
###########
|
###########
|
||||||
|
|
||||||
|
def _init_snapshot_mode(self):
|
||||||
|
mode_external = self.widget("snapshot-new-mode-external")
|
||||||
|
|
||||||
|
capsinfo = self.vm.xmlobj.lookup_capsinfo()
|
||||||
|
|
||||||
|
if not capsinfo.guest.supports_externalSnapshot():
|
||||||
|
mode_external.set_sensitive(False)
|
||||||
|
mode_external.set_tooltip_text(
|
||||||
|
_("external snapshots not supported with this libvirt connection"))
|
||||||
|
|
||||||
|
def _init_memory_path(self):
|
||||||
|
mempaths = self.widget("snapshot-new-memory-path")
|
||||||
|
|
||||||
|
model = Gtk.ListStore(str)
|
||||||
|
mempaths.set_model(model)
|
||||||
|
uiutil.init_combo_text_column(mempaths, 0)
|
||||||
|
|
||||||
def _init_ui(self):
|
def _init_ui(self):
|
||||||
buf = Gtk.TextBuffer()
|
buf = Gtk.TextBuffer()
|
||||||
self.widget("snapshot-new-description").set_buffer(buf)
|
self.widget("snapshot-new-description").set_buffer(buf)
|
||||||
|
self._init_snapshot_mode()
|
||||||
|
self._init_memory_path()
|
||||||
|
|
||||||
|
def _reset_snapshot_mode(self):
|
||||||
|
mode_external = self.widget("snapshot-new-mode-external")
|
||||||
|
mode_internal = self.widget("snapshot-new-mode-internal")
|
||||||
|
|
||||||
|
if mode_external.is_sensitive():
|
||||||
|
mode_external.set_active(True)
|
||||||
|
else:
|
||||||
|
mode_internal.set_active(True)
|
||||||
|
|
||||||
|
def _reset_snapshot_memory_path(self):
|
||||||
|
mode_box = self.widget("snapshot-new-mode-external")
|
||||||
|
self._set_memory_path_visibility(mode_box)
|
||||||
|
|
||||||
|
self.widget("snapshot-new-memory-auto").set_active(True)
|
||||||
|
|
||||||
def _reset_state(self):
|
def _reset_state(self):
|
||||||
basename = "snapshot"
|
basename = "snapshot"
|
||||||
@ -134,8 +171,49 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
if sn:
|
if sn:
|
||||||
self.widget("snapshot-new-screenshot").set_from_pixbuf(sn)
|
self.widget("snapshot-new-screenshot").set_from_pixbuf(sn)
|
||||||
|
|
||||||
|
self._reset_snapshot_mode()
|
||||||
|
|
||||||
|
self._reset_snapshot_memory_path()
|
||||||
|
|
||||||
self.widget("snapshot-new-name").grab_focus()
|
self.widget("snapshot-new-name").grab_focus()
|
||||||
|
|
||||||
|
############
|
||||||
|
# UI utils #
|
||||||
|
############
|
||||||
|
|
||||||
|
def _set_memory_path_visibility(self, mode_box):
|
||||||
|
uiutil.set_grid_row_visible(self.widget("snapshot-new-memory-label"),
|
||||||
|
mode_box.get_active() and self.vm.is_active())
|
||||||
|
|
||||||
|
def _populate_memory_path(self):
|
||||||
|
mempaths = self.widget("snapshot-new-memory-path")
|
||||||
|
|
||||||
|
if mempaths.is_sensitive():
|
||||||
|
return
|
||||||
|
|
||||||
|
model = mempaths.get_model()
|
||||||
|
paths = []
|
||||||
|
|
||||||
|
model.clear()
|
||||||
|
|
||||||
|
name = self.widget("snapshot-new-name").get_text()
|
||||||
|
memname = f"{self.vm.get_name()}-mem.{name}"
|
||||||
|
|
||||||
|
for disk in self.vm.get_xmlobj().devices.disk:
|
||||||
|
diskpath = disk.get_source_path()
|
||||||
|
|
||||||
|
if not diskpath:
|
||||||
|
continue
|
||||||
|
|
||||||
|
newpath = os.path.join(os.path.dirname(diskpath), memname)
|
||||||
|
|
||||||
|
if newpath not in paths:
|
||||||
|
paths.append(newpath)
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
model.append([path])
|
||||||
|
|
||||||
|
mempaths.set_active(0)
|
||||||
|
|
||||||
###################
|
###################
|
||||||
# Create handling #
|
# Create handling #
|
||||||
@ -192,6 +270,13 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
setattr(newpix, "vmm_sndata", sdata)
|
setattr(newpix, "vmm_sndata", sdata)
|
||||||
return newpix
|
return newpix
|
||||||
|
|
||||||
|
def _get_mode(self):
|
||||||
|
mode_external = self.widget("snapshot-new-mode-external")
|
||||||
|
if mode_external.get_active():
|
||||||
|
return "external"
|
||||||
|
|
||||||
|
return "internal"
|
||||||
|
|
||||||
def _new_finish_cb(self, error, details, newname):
|
def _new_finish_cb(self, error, details, newname):
|
||||||
self.reset_finish_cursor()
|
self.reset_finish_cursor()
|
||||||
|
|
||||||
@ -207,11 +292,16 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
name = self.widget("snapshot-new-name").get_text()
|
name = self.widget("snapshot-new-name").get_text()
|
||||||
desc = self.widget("snapshot-new-description"
|
desc = self.widget("snapshot-new-description"
|
||||||
).get_buffer().get_property("text")
|
).get_buffer().get_property("text")
|
||||||
|
mode = self._get_mode()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
newsnap = DomainSnapshot(self.vm.conn.get_backend())
|
newsnap = DomainSnapshot(self.vm.conn.get_backend())
|
||||||
newsnap.name = name
|
newsnap.name = name
|
||||||
newsnap.description = desc or None
|
newsnap.description = desc or None
|
||||||
|
if mode == "external" and self.vm.is_active():
|
||||||
|
mempath = uiutil.get_list_selection(self.widget("snapshot-new-memory-path"))
|
||||||
|
newsnap.memory_type = mode
|
||||||
|
newsnap.memory_file = mempath
|
||||||
newsnap.get_xml()
|
newsnap.get_xml()
|
||||||
newsnap.validate_generic_name(_("Snapshot"), newsnap.name)
|
newsnap.validate_generic_name(_("Snapshot"), newsnap.name)
|
||||||
return newsnap
|
return newsnap
|
||||||
@ -231,10 +321,10 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
sndata = getattr(sn, "vmm_sndata", None)
|
sndata = getattr(sn, "vmm_sndata", None)
|
||||||
return mime, sndata
|
return mime, sndata
|
||||||
|
|
||||||
def _do_create_snapshot(self, asyncjob, xml, name, mime, sndata):
|
def _do_create_snapshot(self, asyncjob, xml, name, mime, sndata, diskOnly):
|
||||||
ignore = asyncjob
|
ignore = asyncjob
|
||||||
|
|
||||||
self.vm.create_snapshot(xml, diskOnly=False)
|
self.vm.create_snapshot(xml, diskOnly=diskOnly)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
cachedir = self.vm.get_cache_dir()
|
cachedir = self.vm.get_cache_dir()
|
||||||
@ -263,10 +353,11 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
xml = snap.get_xml()
|
xml = snap.get_xml()
|
||||||
name = snap.name
|
name = snap.name
|
||||||
mime, sndata = self._get_screenshot_data_for_save()
|
mime, sndata = self._get_screenshot_data_for_save()
|
||||||
|
diskOnly = not self.vm.is_active() and self._get_mode() == "external"
|
||||||
|
|
||||||
self.set_finish_cursor()
|
self.set_finish_cursor()
|
||||||
progWin = vmmAsyncJob(
|
progWin = vmmAsyncJob(
|
||||||
self._do_create_snapshot, [xml, name, mime, sndata],
|
self._do_create_snapshot, [xml, name, mime, sndata, diskOnly],
|
||||||
self._new_finish_cb, [name],
|
self._new_finish_cb, [name],
|
||||||
_("Creating snapshot"),
|
_("Creating snapshot"),
|
||||||
_("Creating virtual machine snapshot"),
|
_("Creating virtual machine snapshot"),
|
||||||
@ -280,10 +371,24 @@ class vmmSnapshotNew(vmmGObjectUI):
|
|||||||
|
|
||||||
def _name_changed_cb(self, src):
|
def _name_changed_cb(self, src):
|
||||||
self.widget("snapshot-new-ok").set_sensitive(bool(src.get_text()))
|
self.widget("snapshot-new-ok").set_sensitive(bool(src.get_text()))
|
||||||
|
self._populate_memory_path()
|
||||||
|
|
||||||
def _ok_clicked_cb(self, src):
|
def _ok_clicked_cb(self, src):
|
||||||
return self._create_new_snapshot()
|
return self._create_new_snapshot()
|
||||||
|
|
||||||
|
def _mode_toggled_cb(self, src):
|
||||||
|
self._set_memory_path_visibility(src)
|
||||||
|
|
||||||
|
def _memory_toggled_cb(self, src):
|
||||||
|
mempaths = self.widget("snapshot-new-memory-path")
|
||||||
|
|
||||||
|
if src.get_active():
|
||||||
|
mempaths.set_sensitive(False)
|
||||||
|
else:
|
||||||
|
mempaths.set_sensitive(True)
|
||||||
|
|
||||||
|
self._populate_memory_path()
|
||||||
|
|
||||||
|
|
||||||
class vmmSnapshotPage(vmmGObjectUI):
|
class vmmSnapshotPage(vmmGObjectUI):
|
||||||
def __init__(self, vm, builder, topwin):
|
def __init__(self, vm, builder, topwin):
|
||||||
|
Loading…
Reference in New Issue
Block a user