snapshots: Add some specific UI for external snapshots

We sort them separately in the snapshot list, explicitly mention that
they are 'external', and add a UI field listing the memory/disk
details.

In general mixing internal and external snapshots is a recipe for
confusion and disaster, so I think the best thing to do is at least
acknowledge their presence in the UI but not make any attempt to
predict what will or will not work.
This commit is contained in:
Cole Robinson 2013-09-30 17:53:55 -04:00
parent 6f469d7947
commit 6043a88a0c
7 changed files with 174 additions and 23 deletions

View File

@ -593,7 +593,7 @@ test-many-devices, like an alternate RNG.
<domain type='test' xmlns:test='http://libvirt.org/schemas/domain/test/1.0'>
<name>test-internal-snapshots</name>
<name>test-snapshots</name>
<memory>409600</memory>
<uuid>12345678-1234-FDDF-1234-12345678FFFF</uuid>
<bootloader>/tmp/bootfoo</bootloader>
@ -612,6 +612,9 @@ test-many-devices, like an alternate RNG.
<disks>
<disk name='hda' snapshot='internal'/>
</disks>
<description>FOO
desc line #2
ba</description>
<domain type='test'>
<name>test-internal-snapshots</name>
<uuid>12345678-1234-fddf-1234-12345678ffff</uuid>
@ -851,6 +854,77 @@ test-many-devices, like an alternate RNG.
<active>0</active>
</test:domainsnapshot>
<test:domainsnapshot>
<name>aaa-external-mem</name>
<state>running</state>
<creationTime>1365903080</creationTime>
<memory snapshot='external' file='/path/to/memory/state'/>
<disks>
<disk name='hda' snapshot='external'>
<source file='/path/to/new'/>
</disk>
</disks>
<domain type='test'>
<name>test-internal-snapshots</name>
<uuid>12345678-1234-fddf-1234-12345678ffff</uuid>
<memory unit='KiB'>409600</memory>
<currentMemory unit='KiB'>409600</currentMemory>
<vcpu placement='static'>1</vcpu>
<bootloader>/tmp/bootfoo</bootloader>
<os>
<type arch='i686'>xen</type>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<disk type='file' device='disk'>
<source file='/dev/default-pool/test-clone-simple.img'/>
<target dev='hda' bus='ide'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='ide' index='0'/>
</devices>
</domain>
<active>0</active>
</test:domainsnapshot>
<test:domainsnapshot>
<name>zzz-external-diskonly</name>
<state>shutoff</state>
<creationTime>1365903080</creationTime>
<disks>
<disk name='hda' snapshot='external'>
<source file='/path/to/diskonly'/>
</disk>
</disks>
<domain type='test'>
<name>test-internal-snapshots</name>
<uuid>12345678-1234-fddf-1234-12345678ffff</uuid>
<memory unit='KiB'>409600</memory>
<currentMemory unit='KiB'>409600</currentMemory>
<vcpu placement='static'>1</vcpu>
<bootloader>/tmp/bootfoo</bootloader>
<os>
<type arch='i686'>xen</type>
</os>
<clock offset='utc'/>
<on_poweroff>destroy</on_poweroff>
<on_reboot>restart</on_reboot>
<on_crash>destroy</on_crash>
<devices>
<disk type='file' device='disk'>
<source file='/dev/default-pool/test-clone-simple.img'/>
<target dev='hda' bus='ide'/>
<address type='drive' controller='0' bus='0' target='0' unit='0'/>
</disk>
<controller type='ide' index='0'/>
</devices>
</domain>
<active>0</active>
</test:domainsnapshot>
</domain>

View File

@ -8,9 +8,9 @@ newline
<name>newparent</name>
</parent>
<creationTime>1234</creationTime>
<memory snapshot="no"/>
<memory snapshot="internal"/>
<disks>
<disk name="hda" snapshot="internal"/>
<disk name="hdb" snapshot="no"/>
</disks>
<domain type="test">
<name>test-internal-snapshots</name>

View File

@ -833,6 +833,11 @@ class XMLParseTest(unittest.TestCase):
check("description", "offline desk", "foo\nnewline\n indent")
check("parent", "offline-root", "newparent")
check("creationTime", 1375905916, 1234)
check("memory_type", "no", "internal")
check = self._make_checker(snap.disks[0])
check("name", "hda", "hdb")
check("snapshot", "internal", "no")
utils.diff_compare(snap.get_xml_config(), outfile)

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.0 on Mon Sep 30 17:38:02 2013 -->
<!-- Generated with glade 3.16.0 on Mon Sep 30 18:13:03 2013 -->
<interface>
<!-- interface-requires gtk+ 3.6 -->
<object class="GtkImage" id="image3">
@ -343,7 +343,7 @@
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="top_attach">4</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
@ -364,7 +364,7 @@
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="top_attach">4</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
@ -464,6 +464,34 @@
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="label5">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Snapshot Mode:</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="snapshot-mode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">label</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">3</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
</object>
</child>
</object>

View File

@ -159,8 +159,6 @@ class vmmDomainSnapshot(vmmLibvirtObject):
def _XMLDesc(self, flags):
return self._backend.getXMLDesc(flags=flags)
def is_current(self):
return self._backend.isCurrent()
def delete(self, force=True):
ignore = force
self._backend.delete()
@ -175,6 +173,14 @@ class vmmDomainSnapshot(vmmLibvirtObject):
status = libvirt.VIR_DOMAIN_NOSTATE
return uihelpers.vm_status_icons[status]
def is_external(self):
if self.get_xmlobj().memory_type == "external":
return True
for disk in self.get_xmlobj().disks:
if disk.snapshot == "external":
return True
return False
class vmmDomain(vmmLibvirtObject):
"""

View File

@ -92,9 +92,9 @@ class vmmSnapshotPage(vmmGObjectUI):
buf = Gtk.TextBuffer()
self.widget("snapshot-new-description").set_buffer(buf)
# [snap object, row label, tooltip, icon name]
model = Gtk.ListStore(object, str, str, str)
model.set_sort_column_id(1, Gtk.SortType.ASCENDING)
# [snap object, row label, tooltip, icon name, sortname]
model = Gtk.ListStore(object, str, str, str, str)
model.set_sort_column_id(4, Gtk.SortType.ASCENDING)
col = Gtk.TreeViewColumn("")
col.set_min_width(150)
@ -108,10 +108,14 @@ class vmmSnapshotPage(vmmGObjectUI):
col.add_attribute(txt, 'markup', 1)
col.add_attribute(img, 'icon-name', 3)
def _sep_cb(_model, _iter, ignore):
return not bool(_model[_iter][0])
slist = self.widget("snapshot-list")
slist.set_model(model)
slist.set_tooltip_column(2)
slist.append_column(col)
slist.set_row_separator_func(_sep_cb, None)
###################
@ -156,6 +160,8 @@ class vmmSnapshotPage(vmmGObjectUI):
str(e))
return
has_external = False
has_internal = False
for snap in snapshots:
desc = snap.get_xmlobj().description
if not uihelpers.can_set_row_none:
@ -163,9 +169,22 @@ class vmmSnapshotPage(vmmGObjectUI):
name = snap.get_name()
state = util.xml_escape(snap.run_status())
label = "%s\n<span size='small'>%s: %s</span>" % (
(name, _("State"), state))
model.append([snap, label, desc, snap.run_status_icon_name()])
if snap.is_external():
has_external = True
sortname = "3%s" % name
external = " (%s)" % _("External")
else:
has_internal = True
external = ""
sortname = "1%s" % name
label = "%s\n<span size='small'>%s: %s%s</span>" % (
(name, _("State"), state, external))
model.append([snap, label, desc, snap.run_status_icon_name(),
sortname])
if has_internal and has_external:
model.append([None, None, None, None, "2"])
if len(model):
self.widget("snapshot-list").get_selection().select_iter(
@ -180,6 +199,8 @@ class vmmSnapshotPage(vmmGObjectUI):
desc = snap and xmlobj.description or ""
state = snap and snap.run_status() or ""
icon = snap and snap.run_status_icon_name() or None
is_external = snap and snap.is_external() or False
timestamp = ""
if snap:
timestamp = str(datetime.datetime.fromtimestamp(
@ -198,6 +219,19 @@ class vmmSnapshotPage(vmmGObjectUI):
self.widget("snapshot-status-icon").set_from_icon_name(
icon, Gtk.IconSize.MENU)
uihelpers.set_grid_row_visible(self.widget("snapshot-mode"),
is_external)
if is_external:
is_mem = xmlobj.memory_type == "external"
is_disk = [d.snapshot == "external" for d in xmlobj.disks]
if is_mem and is_disk:
mode = _("External disk and memory")
elif is_mem:
mode = _("External memory only")
else:
mode = _("External disk only")
self.widget("snapshot-mode").set_text(mode)
self.widget("snapshot-add").set_sensitive(True)
self.widget("snapshot-delete").set_sensitive(bool(snap))
self.widget("snapshot-start").set_sensitive(bool(snap))
@ -314,12 +348,12 @@ class vmmSnapshotPage(vmmGObjectUI):
snap = self._get_selected_snapshot()
result = self.err.yes_no(_("Are you sure you want to revert to "
"snapshot '%s'? All disk changes since "
"the last snapshot will be discarded.") %
snap.get_name())
"the last snapshot was created will be "
"discarded.") % snap.get_name())
if not result:
return
logging.debug("Revertin to snapshot '%s'", snap.get_name())
logging.debug("Reverting to snapshot '%s'", snap.get_name())
vmmAsyncJob.simple_async_noshow(self.vm.revert_to_snapshot,
[snap], self,
_("Error reverting to snapshot '%s'") %

View File

@ -20,7 +20,13 @@
import libvirt
from virtinst import util
from virtinst.xmlbuilder import XMLBuilder, XMLProperty
from virtinst.xmlbuilder import XMLBuilder, XMLChildProperty, XMLProperty
class _SnapshotDisk(XMLBuilder):
_XML_ROOT_NAME = "disk"
name = XMLProperty("./@name")
snapshot = XMLProperty("./@snapshot")
class DomainSnapshot(XMLBuilder):
@ -57,11 +63,9 @@ class DomainSnapshot(XMLBuilder):
creationTime = XMLProperty("./creationTime", is_int=True)
parent = XMLProperty("./parent/name")
# Missing bits:
# <memory> @type and @file
# <disks> block which has a psuedo VM disk device
# <domain> block which tracks the snapshot guest XML
# <active> which should list active status for an internal snapshot
memory_type = XMLProperty("./memory/@snapshot")
disks = XMLChildProperty(_SnapshotDisk, relative_xpath="./disks")
##################