[gnome-boxes] properties: Allow changing of RAM & storage size



commit e75c506ab73506751bfd2cdedb943aff17e2f137
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Thu Feb 23 16:04:33 2012 +0200

    properties: Allow changing of RAM & storage size
    
    https://bugzilla.gnome.org/show_bug.cgi?id=664774

 src/i-properties-provider.vala |   31 ++++++++++
 src/libvirt-machine.vala       |  119 +++++++++++++++++++++++++++++++++++++++-
 src/properties.vala            |   44 ++++++++------
 src/spice-display.vala         |    3 +
 src/util.vala                  |    6 +-
 src/vm-creator.vala            |    2 +-
 6 files changed, 181 insertions(+), 24 deletions(-)
---
diff --git a/src/i-properties-provider.vala b/src/i-properties-provider.vala
index 84eb7d4..068b780 100644
--- a/src/i-properties-provider.vala
+++ b/src/i-properties-provider.vala
@@ -2,6 +2,7 @@
 using Gtk;
 
 public delegate void PropertyStringChanged (string value) throws Boxes.Error;
+public delegate void PropertySizeChanged (uint64 value) throws Boxes.Error;
 
 private interface Boxes.IPropertiesProvider: GLib.Object {
     public abstract List<Pair<string, Widget>> get_properties (Boxes.PropertiesPage page);
@@ -30,5 +31,35 @@ private interface Boxes.IPropertiesProvider: GLib.Object {
 
         add_property (ref list, name, entry);
     }
+
+    protected void add_size_property (ref List<Pair<string, Widget>> list,
+                                      string                         name,
+                                      uint64                         size,
+                                      uint64                         min,
+                                      uint64                         max,
+                                      uint64                         step,
+                                      PropertySizeChanged?           changed = null) {
+        var scale = new Gtk.HScale.with_range (min, max, step);
+
+        scale.format_value.connect ((scale, value) => {
+            return format_size (((uint64) value) * Osinfo.KIBIBYTES, FormatSizeFlags.IEC_UNITS);
+        });
+
+        scale.set_value (size);
+        scale.hexpand = true;
+        scale.vexpand = true;
+        scale.margin_bottom = 20;
+
+        if (changed != null)
+            scale.value_changed.connect (() => {
+                try {
+                    changed ((uint64) scale.get_value ());
+                } catch (Boxes.Error error) {
+                    warning (error.message);
+                }
+            });
+
+        add_property (ref list, name, scale);
+    }
 }
 
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index a932844..f2f0bb6 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -82,6 +82,9 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
         double net_write;
     }
 
+    private uint ram_update_timeout = 0;
+    private uint storage_update_timeout = 0;
+
     static const int STATS_SIZE = 20;
     private MachineStat[] stats;
     construct {
@@ -241,6 +244,11 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
             add_string_property (ref list, _("URI"), display.uri);
             break;
 
+        case PropertiesPage.SYSTEM:
+            add_ram_property (ref list);
+            add_storage_property (ref list);
+            break;
+
         case PropertiesPage.DISPLAY:
             add_string_property (ref list, _("Protocol"), display.protocol);
             break;
@@ -331,7 +339,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
 
             try {
                 if (connection == app.default_connection) {
-                    var volume = get_storage_volume (connection, domain);
+                    var volume = get_storage_volume (connection, domain, null);
                     if (volume != null)
                         volume.delete (0);
                 }
@@ -348,7 +356,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
 
     private GVir.DomainDisk? get_domain_disk () throws GLib.Error {
         var disk = null as GVir.DomainDisk;
-        var volume = get_storage_volume (connection, domain);
+        var volume = get_storage_volume (connection, domain, null);
 
         foreach (var device in domain.get_devices ()) {
             if (device is GVir.DomainDisk) {
@@ -383,4 +391,111 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
 
         return net;
     }
+
+    private void add_ram_property (ref List list) {
+        try {
+            var max_ram = connection.get_node_info ().memory;
+
+            add_size_property (ref list,
+                               _("RAM"),
+                               domain_config.memory,
+                               Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
+                               max_ram,
+                               Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
+                               on_ram_changed);
+        } catch (GLib.Error error) {}
+    }
+
+    private void on_ram_changed (uint64 value) {
+        // Ensure that we don't end-up changing RAM like a 1000 times a second while user moves the slider..
+        if (ram_update_timeout != 0)
+            Source.remove (ram_update_timeout);
+
+        ram_update_timeout = Timeout.add_seconds (1, () => {
+            domain_config.memory = value;
+            try {
+                domain.set_config (domain_config);
+                debug ("RAM changed to %llu", value);
+                notify_reboot_required ();
+            } catch (GLib.Error error) {
+                warning ("Failed to change RAM of box '%s' to %llu: %s",
+                         domain.get_name (),
+                         value,
+                         error.message);
+            }
+            ram_update_timeout = 0;
+
+            return false;
+        });
+    }
+
+    private void notify_reboot_required () {
+        Notificationbar.OKFunc reboot = () => {
+            debug ("Rebooting '%s'..", name);
+            try {
+                domain.reboot (0);
+            } catch (GLib.Error error) {
+                warning ("Failed to reboot '%s': %s", domain.get_name (), error.message);
+            }
+        };
+        var message = _("Changes require restart of '%s'. Attempt restart?").printf (name);
+        app.notificationbar.display_for_action (message, Gtk.Stock.YES, (owned) reboot);
+    }
+
+    private void add_storage_property (ref List list) {
+        StoragePool pool;
+
+        var volume = get_storage_volume (connection, domain, out pool);
+        if (volume == null)
+            return;
+
+        try {
+            var volume_info = volume.get_info ();
+            var pool_info = pool.get_info ();
+            var max_storage = (volume_info.capacity + pool_info.available)  / Osinfo.KIBIBYTES;
+
+            add_size_property (ref list,
+                               _("Storage"),
+                               volume_info.capacity / Osinfo.KIBIBYTES,
+                               volume_info.capacity / Osinfo.KIBIBYTES,
+                               max_storage,
+                               Osinfo.GIBIBYTES / Osinfo.KIBIBYTES,
+                               on_storage_changed);
+        } catch (GLib.Error error) {
+            warning ("Failed to get information on volume '%s' or it's parent pool: %s",
+                     volume.get_name (),
+                     error.message);
+        }
+    }
+
+    private void on_storage_changed (uint64 value) {
+        // Ensure that we don't end-up changing storage like a 1000 times a second while user moves the slider..
+        if (storage_update_timeout != 0)
+            Source.remove (storage_update_timeout);
+
+        storage_update_timeout = Timeout.add_seconds (1, () => {
+            var volume = get_storage_volume (connection, domain, null);
+            if (volume == null)
+                return false;
+
+            try {
+                if (is_running ()) {
+                    var disk = get_domain_disk ();
+                    if (disk != null)
+                        disk.resize (value, 0);
+                } else
+                    // Currently this never happens as properties page cant be reached without starting the machine
+                    volume.resize (value * Osinfo.KIBIBYTES, StorageVolResizeFlags.NONE);
+                debug ("Storage changed to %llu", value);
+            } catch (GLib.Error error) {
+                warning ("Failed to change storage capacity of volume '%s' to %llu: %s",
+                         volume.get_name (),
+                         value,
+                         error.message);
+            }
+            storage_update_timeout = 0;
+
+            return false;
+        });
+    }
 }
diff --git a/src/properties.vala b/src/properties.vala
index 75aa148..cc4a23a 100644
--- a/src/properties.vala
+++ b/src/properties.vala
@@ -3,6 +3,7 @@ using Gtk;
 
 private enum Boxes.PropertiesPage {
     LOGIN,
+    SYSTEM,
     DISPLAY,
     DEVICES,
 
@@ -28,6 +29,7 @@ private class Boxes.Properties: Boxes.UI {
     private class PageWidget {
         public Gtk.Widget widget;
         public string name;
+        public bool empty;
 
         private Gtk.Grid grid;
 
@@ -37,6 +39,10 @@ private class Boxes.Properties: Boxes.UI {
                 name = _("Login");
                 break;
 
+            case PropertiesPage.SYSTEM:
+                name = _("System");
+                break;
+
             case PropertiesPage.DISPLAY:
                 name = _("Display");
                 break;
@@ -59,28 +65,27 @@ private class Boxes.Properties: Boxes.UI {
             label.hexpand = false;
             grid.attach (label, 0, 0, 2, 1);
 
-            int current_row = 1;
-            foreach (var p in machine.get_properties (page)) {
-                var label_name = new Gtk.Label (p.first);
-                label_name.modify_fg (Gtk.StateType.NORMAL, get_color ("grey"));
-                label_name.margin_left = 25;
-                label_name.halign = Gtk.Align.START;
-                label_name.hexpand = false;
-                grid.attach (label_name, 0, current_row, 1, 1);
-                var widget = p.second;
-                widget.halign = Gtk.Align.START;
-                grid.attach (widget, 1, current_row, 1, 1);
-
-                current_row += 1;
+            var properties = machine.get_properties (page);
+            empty = properties.length () == 0;
+            if (!empty) {
+                int current_row = 1;
+                foreach (var property in properties) {
+                    var label_name = new Gtk.Label (property.first);
+                    label_name.modify_fg (Gtk.StateType.NORMAL, get_color ("grey"));
+                    label_name.margin_left = 25;
+                    label_name.halign = Gtk.Align.START;
+                    label_name.hexpand = false;
+                    grid.attach (label_name, 0, current_row, 1, 1);
+                    var widget = property.second;
+                    grid.attach (widget, 1, current_row, 1, 1);
+
+                    current_row += 1;
+                }
             }
 
             grid.show_all ();
             widget = grid;
         }
-
-        public bool is_empty () {
-            return false;
-        }
     }
 
     public Properties (App app) {
@@ -105,10 +110,11 @@ private class Boxes.Properties: Boxes.UI {
             return;
 
         for (var i = 0; i < PropertiesPage.LAST; i++) {
-            var page = new PageWidget (i, app.current_item as Machine);
+            var machine = app.current_item as Machine;
+            var page = new PageWidget (i, machine);
             notebook.append_page (page.widget, null);
 
-            if (!page.is_empty ())
+            if (!page.empty)
                 list_append (listmodel, page.name);
         }
 
diff --git a/src/spice-display.vala b/src/spice-display.vala
index e36f6ca..7a64e74 100644
--- a/src/spice-display.vala
+++ b/src/spice-display.vala
@@ -129,6 +129,7 @@ private class Boxes.SpiceDisplay: Boxes.Display, Boxes.IPropertiesProvider {
             var toggle = new Gtk.Switch ();
             gtk_session.bind_property ("auto-clipboard", toggle, "active",
                                        BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE);
+            toggle.halign = Gtk.Align.START;
             add_property (ref list, _("Share clipboard"), toggle);
 
             try {
@@ -136,6 +137,7 @@ private class Boxes.SpiceDisplay: Boxes.Display, Boxes.IPropertiesProvider {
                 var display = get_display (0);
                 display.bind_property ("resize-guest", toggle, "active",
                                        BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE);
+                toggle.halign = Gtk.Align.START;
                 add_property (ref list, _("Resize guest"), toggle);
             } catch (Boxes.Error error) {
                 warning (error.message);
@@ -146,6 +148,7 @@ private class Boxes.SpiceDisplay: Boxes.Display, Boxes.IPropertiesProvider {
             var toggle = new Gtk.Switch ();
             gtk_session.bind_property ("auto-usbredir", toggle, "active",
                                        BindingFlags.BIDIRECTIONAL | BindingFlags.SYNC_CREATE);
+            toggle.halign = Gtk.Align.START;
             add_property (ref list, _("USB redirection"), toggle);
             break;
         }
diff --git a/src/util.vala b/src/util.vala
index 2388b2b..dd9e436 100644
--- a/src/util.vala
+++ b/src/util.vala
@@ -310,8 +310,10 @@ namespace Boxes {
         return val.value;
     }
 
-    public GVir.StorageVol? get_storage_volume (GVir.Connection connection, GVir.Domain domain) {
-        var pool = connection.find_storage_pool_by_name (Config.PACKAGE_TARNAME);
+    public GVir.StorageVol? get_storage_volume (GVir.Connection connection,
+                                                GVir.Domain domain,
+                                                out GVir.StoragePool pool) {
+        pool = connection.find_storage_pool_by_name (Config.PACKAGE_TARNAME);
         if (pool == null)
             // Absence of our pool just means that disk was not created by us.
             return null;
diff --git a/src/vm-creator.vala b/src/vm-creator.vala
index d2674c1..6881db0 100644
--- a/src/vm-creator.vala
+++ b/src/vm-creator.vala
@@ -86,7 +86,7 @@ private class Boxes.VMCreator {
             // steps for this domain.
             return;
 
-        var volume = get_storage_volume (connection, domain);
+        var volume = get_storage_volume (connection, domain, null);
 
         if (guest_installed_os (volume)) {
             post_install_setup (domain);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]