[gnome-boxes/show-recomended-resources: 1/8] Add LibvirtMachineProperties



commit 8ca0684d6d44ee9130a8883eddb9faced39e4266
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Tue Jan 29 21:36:17 2013 +0200

    Add LibvirtMachineProperties
    
    Refactor properties handling code of LibvirtMachine to a separate
    class/module. This change reduces LibvirtMachine LOC by 59%.

 src/Makefile.am                     |    1 +
 src/libvirt-machine-properties.vala |  533 ++++++++++++++++++++++++++++++++++
 src/libvirt-machine.vala            |  537 +----------------------------------
 3 files changed, 549 insertions(+), 522 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 42bc73c..14e07b9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -88,6 +88,7 @@ gnome_boxes_SOURCES =				\
 	installer-media.vala 			\
 	iso-extractor.vala			\
 	libvirt-machine.vala			\
+	libvirt-machine-properties.vala		\
 	machine.vala				\
 	main.vala				\
 	media-manager.vala 			\
diff --git a/src/libvirt-machine-properties.vala b/src/libvirt-machine-properties.vala
new file mode 100644
index 0000000..bc7c743
--- /dev/null
+++ b/src/libvirt-machine-properties.vala
@@ -0,0 +1,533 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using GVir;
+using Gtk;
+
+private abstract class Boxes.LibvirtMachineProperties: Boxes.Machine {
+    public abstract GVir.Domain domain { get; set; }
+    public abstract GVirConfig.Domain domain_config { get; set; }
+    public abstract GVir.Connection connection { get; set; }
+    public abstract GVir.StorageVol? storage_volume { get; set; }
+    public abstract VMCreator? vm_creator { get; set; } // Under installation if this is set to non-null
+
+    private uint shutdown_timeout;
+
+    public LibvirtMachineProperties (CollectionSource source, string name) {
+        base (source, name);
+    }
+
+    public void try_change_name (string name) throws Boxes.Error {
+        try {
+            var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+            // Te use libvirt "title" for free form user name
+            config.title = name;
+            // This will take effect only after next reboot, but we use pending/inactive config for name and title
+            domain.set_config (config);
+
+            this.name = name;
+        } catch (GLib.Error error) {
+            warning ("Failed to change title of box '%s' to '%s': %s",
+                     domain.get_name (), name, error.message);
+            throw new Boxes.Error.INVALID ("Invalid libvirt title");
+        }
+    }
+
+    public void try_enable_usb_redir () throws GLib.Error {
+        var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+
+        // Remove any old usb configuration from old config
+        VMConfigurator.remove_usb_controllers (config);
+
+        // Add usb redirection channel and usb2 controllers
+        VMConfigurator.add_usb_support (config);
+
+        // This will take effect only after next reboot
+        domain.set_config (config);
+        if (is_on ())
+            notify_reboot_required ();
+    }
+
+    public void try_enable_smartcard () throws GLib.Error {
+        var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+
+        VMConfigurator.add_smartcard_support (config);
+
+        // This will take effect only after next reboot
+        domain.set_config (config);
+        if (is_on ())
+            notify_reboot_required ();
+    }
+
+    private string collect_logs () {
+        var builder = new StringBuilder ();
+
+        builder.append_printf ("Domain: %s\n", domain.get_name ());
+        builder.append_printf ("UUID: %s\n", domain.get_uuid ());
+        builder.append_printf ("Persistent: %s\n", domain.get_persistent () ? "yes" : "no");
+        try {
+            var info = domain.get_info ();
+            builder.append_printf ("Cpu time: %"+uint64.FORMAT_MODIFIER+"d\n", info.cpuTime);
+            builder.append_printf ("Memory: %"+uint64.FORMAT_MODIFIER+"d\n", info.memory);
+            builder.append_printf ("Max memory: %"+uint64.FORMAT_MODIFIER+"d\n", info.maxMem);
+            builder.append_printf ("CPUs: %d\n", info.nrVirtCpu);
+            builder.append_printf ("State: %s\n", info.state.to_string ());
+        } catch (GLib.Error e) {
+        }
+
+        if (display != null)
+            display.collect_logs (builder);
+
+
+        try {
+            var conf = domain.get_config (GVir.DomainXMLFlags.NONE);
+            builder.append_printf ("\nDomain config:\n");
+            builder.append_printf ("------------------------------------------------------------\n");
+
+            builder.append (conf.to_xml ());
+            builder.append_printf ("\n" +
+                                   "------------------------------------------------------------\n");
+        } catch (GLib.Error error) {
+        }
+
+        try {
+            var logfile = Path.build_filename (Environment.get_user_cache_dir (), "libvirt/qemu/log", domain.get_name ()  + ".log");
+            string data;
+            FileUtils.get_contents (logfile, out data);
+            builder.append_printf ("\nQEMU log:\n");
+            builder.append_printf ("------------------------------------------------------------\n");
+
+            builder.append (data);
+            builder.append_printf ("------------------------------------------------------------\n");
+        } catch (GLib.Error e) {
+        }
+        return builder.str;
+    }
+
+    public override List<Boxes.Property> get_properties (Boxes.PropertiesPage page, PropertyCreationFlag flags) {
+        var list = new List<Boxes.Property> ();
+
+        // the wizard may want to modify display properties, before connect_display()
+        if (display == null)
+            try {
+                update_display ();
+            } catch (GLib.Error e) {
+                warning (e.message);
+            }
+
+        switch (page) {
+        case PropertiesPage.LOGIN:
+            add_string_property (ref list, _("Name"), name, (property, name) => {
+                try_change_name (name);
+            });
+            add_string_property (ref list, _("Virtualizer"), source.uri);
+            if (display != null)
+                add_string_property (ref list, _("URI"), display.uri);
+            break;
+
+        case PropertiesPage.SYSTEM:
+            add_ram_property (ref list);
+            add_storage_property (ref list);
+
+            var button = new Gtk.Button.with_label (_("Troubleshooting log"));
+            button.halign = Gtk.Align.START;
+            add_property (ref list, null, button);
+            button.clicked.connect (() => {
+                var log = collect_logs ();
+                var dialog = new Gtk.Dialog.with_buttons (_("Troubleshooting log"),
+                                                          App.app.window,
+                                                          DialogFlags.DESTROY_WITH_PARENT,
+                                                          Stock.SAVE, 100,
+                                                          _("Copy to clipboard"), 101,
+                                                          Stock.CLOSE, ResponseType.OK);
+                dialog.set_default_size (640, 480);
+                var text = new Gtk.TextView ();
+                text.editable = false;
+                var scroll = new Gtk.ScrolledWindow (null, null);
+                scroll.add (text);
+                scroll.vexpand = true;
+
+                dialog.get_content_area ().add (scroll);
+                text.buffer.set_text (log);
+                dialog.show_all ();
+
+                dialog.response.connect ( (response_id) => {
+                    if (response_id == 100) {
+                        var chooser = new Gtk.FileChooserDialog (_("Save log"), App.app.window,
+                                                                 Gtk.FileChooserAction.SAVE,
+                                                                 Stock.SAVE, ResponseType.OK);
+                        chooser.local_only = false;
+                        chooser.do_overwrite_confirmation = true;
+                        chooser.response.connect ((response_id) => {
+                            if (response_id == ResponseType.OK) {
+                                var file = chooser.get_file ();
+                                try {
+                                    file.replace_contents (log.data, null, false,
+                                                           FileCreateFlags.REPLACE_DESTINATION, null);
+                                } catch (GLib.Error e) {
+                                    var m = new Gtk.MessageDialog (chooser,
+                                                                   Gtk.DialogFlags.DESTROY_WITH_PARENT,
+                                                                   Gtk.MessageType.ERROR,
+                                                                   Gtk.ButtonsType.CLOSE,
+                                                                   _("Error saving: %s").printf (e.message));
+                                    m.show_all ();
+                                    m.response.connect ( () => { m.destroy (); });
+                                    return;
+                                }
+                                chooser.destroy ();
+                            } else {
+                                chooser.destroy ();
+                            }
+                        });
+                        chooser.show_all ();
+                    } else if (response_id == 101){
+                        Gtk.Clipboard.get_for_display (dialog.get_display (), Gdk.SELECTION_CLIPBOARD).set_text (log, -1);
+                    } else {
+                        dialog.destroy ();
+                    }
+                });
+            });
+            break;
+
+        case PropertiesPage.DISPLAY:
+            if (display != null)
+                add_string_property (ref list, _("Protocol"), display.protocol);
+            break;
+
+        case PropertiesPage.DEVICES:
+            foreach (var device_config in domain_config.get_devices ()) {
+                if (!(device_config is GVirConfig.DomainDisk))
+                    continue;
+                var disk_config = device_config as GVirConfig.DomainDisk;
+                var disk_type = disk_config.get_guest_device_type ();
+                if (disk_type == GVirConfig.DomainDiskGuestDeviceType.CDROM) {
+                    var grid = new Gtk.Grid ();
+                    grid.set_orientation (Gtk.Orientation.HORIZONTAL);
+                    grid.set_column_spacing (12);
+
+                    var label = new Gtk.Label ("");
+                    grid.add (label);
+
+                    var source = disk_config.get_source ();
+                    bool empty = (source == null || source == "");
+
+                    var button_label = new Gtk.Label ("");
+                    var button = new Gtk.Button ();
+                    button.add (button_label);
+
+                    grid.add (button);
+
+                    if (empty) {
+                        // Translators: This is the text on the button to select an iso for the cd
+                        button_label.set_text (_("Select"));
+                        // Translators: empty is listed as the filename for a non-mounted CD
+                        label.set_markup (Markup.printf_escaped ("<i>%s</i>", _("empty")));
+                    } else {
+                        // Translators: Remove is the label on the button to remove an iso from a cdrom drive
+                        button_label.set_text (_("Remove"));
+                        label.set_text (get_utf8_basename (source));
+                    }
+
+                    button.clicked.connect ( () => {
+                        if (empty) {
+                            var dialog = new Gtk.FileChooserDialog (_("Select a device or ISO file"),
+                                                                    App.app.window,
+                                                                    Gtk.FileChooserAction.OPEN,
+                                                                    Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
+                                                                    Gtk.Stock.OPEN, Gtk.ResponseType.ACCEPT);
+                            dialog.modal = true;
+                            dialog.show_hidden = false;
+                            dialog.local_only = true;
+                            dialog.filter = new Gtk.FileFilter ();
+                            dialog.filter.add_mime_type ("application/x-cd-image");
+                            dialog.response.connect ( (response) => {
+                                if (response == Gtk.ResponseType.ACCEPT) {
+                                    var path = dialog.get_filename ();
+                                    disk_config.set_source (path);
+                                    try {
+                                        domain.update_device (disk_config, DomainUpdateDeviceFlags.CURRENT);
+                                        button_label.set_text (_("Remove"));
+                                        label.set_text (get_utf8_basename (path));
+                                        empty = false;
+                                    } catch (GLib.Error e) {
+                                        got_error (e.message);
+                                    }
+                                }
+                                dialog.destroy ();
+                            });
+                            dialog.show_all ();
+                        } else {
+                            disk_config.set_source ("");
+                            try {
+                                domain.update_device (disk_config, DomainUpdateDeviceFlags.CURRENT);
+                                empty = true;
+                                button_label.set_text (_("Select"));
+                                label.set_markup (Markup.printf_escaped ("<i>%s</i>", _("empty")));
+                            } catch (GLib.Error e) {
+                                got_error (e.message);
+                            }
+                        }
+                    });
+
+                    add_property (ref list, _("CD/DVD"), grid);
+                }
+            }
+
+            bool has_usb_redir = false;
+            bool has_smartcard = false;
+            // We look at the INACTIVE config here, because we want to show the usb
+            // widgetry if usb has been added already but we have not rebooted
+            try {
+                var inactive_config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+                foreach (var device in inactive_config.get_devices ()) {
+                    if (device is GVirConfig.DomainRedirdev) {
+                        has_usb_redir = true;
+                    }
+                    if (device is GVirConfig.DomainSmartcard) {
+                        has_smartcard = true;
+                    }
+                }
+            } catch (GLib.Error error) {
+                warning ("Failed to fetch configuration for domain '%s': %s", domain.get_name (), error.message);
+            }
+
+            if (!has_usb_redir)
+                flags |= PropertyCreationFlag.NO_USB;
+
+            /* Only add usb support to guests if HAVE_USBREDIR, as older
+             * qemu versions break migration with it. */
+            if (!has_usb_redir && Config.HAVE_USBREDIR) {
+                var button = new Gtk.Button.with_label (_("Add support to guest"));
+                button.halign = Gtk.Align.START;
+                var property = add_property (ref list, _("USB device support"), button);
+                button.clicked.connect (() => {
+                    try {
+                        try_enable_usb_redir ();
+                        update_domain_config ();
+                        property.refresh_properties ();
+                    } catch (GLib.Error error) {
+                        warning ("Failed to enable usb");
+                    }
+                });
+            }
+
+            // Only add smartcart support to guests if HAVE_SMARTCARD, as qemu built
+            // without smartcard support will not start vms with it.
+            if (!has_smartcard && Config.HAVE_SMARTCARD) {
+                var button = new Gtk.Button.with_label (_("Add support to guest"));
+                button.halign = Gtk.Align.START;
+                var property = add_property (ref list, _("Smartcard support"), button);
+                button.clicked.connect (() => {
+                    try {
+                        try_enable_smartcard ();
+                        update_domain_config ();
+                        property.refresh_properties ();
+                    } catch (GLib.Error error) {
+                        warning ("Failed to enable smartcard");
+                    }
+                });
+            }
+
+            break;
+        }
+
+        if (display != null)
+            list.concat (display.get_properties (page,
+                                                 flags));
+
+        return list;
+    }
+
+    public abstract void force_shutdown (bool confirm = true);
+    protected abstract void update_domain_config ();
+    protected abstract bool update_display () throws GLib.Error;
+    protected abstract void try_shutdown ();
+    protected abstract async void start () throws GLib.Error;
+
+    protected GVir.DomainDisk? get_domain_disk () throws GLib.Error {
+        var disk = null as GVir.DomainDisk;
+
+        foreach (var device_config in domain_config.get_devices ()) {
+            if (device_config is GVirConfig.DomainDisk) {
+                var disk_config = device_config as GVirConfig.DomainDisk;
+                var disk_type = disk_config.get_guest_device_type ();
+                var storage_volume_path = (storage_volume != null)? storage_volume.get_path () : null;
+
+                // Prefer Boxes' managed volume over other disks
+                if (disk_type == GVirConfig.DomainDiskGuestDeviceType.DISK &&
+                    disk_config.get_source () == storage_volume_path) {
+                    disk = Object.new (typeof (GVir.DomainDisk),
+                                       "domain", domain,
+                                       "config", device_config) as GVir.DomainDisk;
+
+                    break;
+                }
+            }
+        }
+
+        return disk;
+    }
+
+    private void update_ram_property (Boxes.Property property) {
+        try {
+            var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+
+            // we use KiB unit, convert to MiB
+            var actual = domain_config.memory / 1024;
+            var pending = config.memory / 1024;
+
+            debug ("RAM actual: %llu, pending: %llu", actual, pending);
+            // somehow, there are rounded errors, so let's forget about 1Mb diff
+            property.reboot_required = (actual - pending) > 1; // no need for abs()
+
+        } catch (GLib.Error e) {}
+    }
+
+    private void add_ram_property (ref List list) {
+        try {
+            var max_ram = connection.get_node_info ().memory;
+
+            var property = add_size_property (ref list,
+                                              _("Memory"),
+                                              domain_config.memory,
+                                              Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
+                                              max_ram,
+                                              Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
+                                              on_ram_changed);
+
+            this.notify["state"].connect (() => {
+                if (!is_on ())
+                    property.reboot_required = false;
+            });
+
+            update_ram_property (property);
+        } catch (GLib.Error error) {}
+    }
+
+    private void on_ram_changed (Boxes.Property property, uint64 value) {
+        // Ensure that we don't end-up changing RAM like a 1000 times a second while user moves the slider..
+        property.deferred_change = () => {
+            try {
+                var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+                config.memory = value;
+                if (config.get_class ().find_property ("current-memory") != null)
+                    config.set ("current-memory", value);
+                domain.set_config (config);
+                debug ("RAM changed to %llu", value);
+                if (is_on ())
+                    notify_reboot_required ();
+            } catch (GLib.Error error) {
+                warning ("Failed to change RAM of box '%s' to %llu: %s",
+                         domain.get_name (),
+                         value,
+                         error.message);
+            }
+
+            if (is_on ())
+                update_ram_property (property);
+
+            return false;
+        };
+    }
+
+    private void notify_reboot_required () {
+        Notificationbar.OKFunc reboot = () => {
+            debug ("Rebooting '%s'..", name);
+            stay_on_display = true;
+            ulong state_id = 0;
+            Gd.Notification notification = null;
+
+            state_id = this.notify["state"].connect (() => {
+                if (state == MachineState.STOPPED || state == MachineState.FORCE_STOPPED) {
+                    debug ("'%s' stopped.", name);
+                    start.begin ((obj, res) => {
+                        try {
+                            start.end (res);
+                        } catch (GLib.Error error) {
+                            warning ("Failed to start '%s': %s", domain.get_name (), error.message);
+                        }
+                    });
+                    this.disconnect (state_id);
+                    if (shutdown_timeout != 0) {
+                        Source.remove (shutdown_timeout);
+                        shutdown_timeout = 0;
+                    }
+                    if (notification != null) {
+                        notification.dismiss ();
+                        notification = null;
+                    }
+                }
+            });
+
+            shutdown_timeout = Timeout.add_seconds (5, () => {
+                // Seems guest ignored ACPI shutdown, lets force shutdown with user's consent
+                Notificationbar.OKFunc really_force_shutdown = () => {
+                    notification = null;
+                    force_shutdown (false);
+                };
+
+                var message = _("Restart of '%s' is taking too long. Force it to shutdown?").printf (name);
+                notification = App.app.notificationbar.display_for_action (message,
+                                                                           Gtk.Stock.YES,
+                                                                           (owned) really_force_shutdown,
+                                                                           null,
+                                                                           -1);
+                shutdown_timeout = 0;
+
+                return false;
+            });
+
+            try_shutdown ();
+        };
+        var message = _("Changes require restart of '%s'. Attempt restart?").printf (name);
+        App.app.notificationbar.display_for_action (message, Gtk.Stock.YES, (owned) reboot);
+    }
+
+    private void add_storage_property (ref List list) {
+        if (storage_volume == null)
+            return;
+
+        try {
+            var volume_info = storage_volume.get_info ();
+            var pool = get_storage_pool (connection);
+            var pool_info = pool.get_info ();
+            var max_storage = (volume_info.capacity + pool_info.available)  / Osinfo.KIBIBYTES;
+
+            add_size_property (ref list,
+                               _("Maximum Disk Size"),
+                               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",
+                     storage_volume.get_name (),
+                     error.message);
+        }
+    }
+
+    private void on_storage_changed (Boxes.Property property, uint64 value) {
+        // Ensure that we don't end-up changing storage like a 1000 times a second while user moves the slider..
+        property.deferred_change = () => {
+            if (storage_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
+                    storage_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",
+                         storage_volume.get_name (),
+                         value,
+                         error.message);
+            }
+
+            return false;
+        };
+    }
+}
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index 2a9a527..b773bcc 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -2,12 +2,12 @@
 using GVir;
 using Gtk;
 
-private class Boxes.LibvirtMachine: Boxes.Machine {
-    public GVir.Domain domain;
-    public GVirConfig.Domain domain_config;
-    public GVir.Connection connection;
-    public GVir.StorageVol? storage_volume;
-    public VMCreator? vm_creator; // Under installation if this is set to non-null
+private class Boxes.LibvirtMachine: Boxes.LibvirtMachineProperties {
+    public override GVir.Domain domain { get; set; }
+    public override GVirConfig.Domain domain_config { get; set; }
+    public override GVir.Connection connection { get; set; }
+    public override GVir.StorageVol? storage_volume { get; set; }
+    public override VMCreator? vm_creator { get; set; } // Under installation if this is set to non-null
 
     public bool save_on_quit {
         get { return source.get_boolean ("source", "save-on-quit"); }
@@ -61,7 +61,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
         can_save = true;
     }
 
-    private void update_domain_config () {
+    protected override void update_domain_config () {
         try {
             domain_config = domain.get_config (GVir.DomainXMLFlags.NONE);
             storage_volume = get_storage_volume (connection, domain);
@@ -290,328 +290,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
         }
     }
 
-    public void try_change_name (string name) throws Boxes.Error {
-        try {
-            var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
-            // Te use libvirt "title" for free form user name
-            config.title = name;
-            // This will take effect only after next reboot, but we use pending/inactive config for name and title
-            domain.set_config (config);
-
-            this.name = name;
-        } catch (GLib.Error error) {
-            warning ("Failed to change title of box '%s' to '%s': %s",
-                     domain.get_name (), name, error.message);
-            throw new Boxes.Error.INVALID ("Invalid libvirt title");
-        }
-    }
-
-    public void try_enable_usb_redir () throws GLib.Error {
-        var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
-
-        // Remove any old usb configuration from old config
-        VMConfigurator.remove_usb_controllers (config);
-
-        // Add usb redirection channel and usb2 controllers
-        VMConfigurator.add_usb_support (config);
-
-        // This will take effect only after next reboot
-        domain.set_config (config);
-        if (is_on ())
-            notify_reboot_required ();
-    }
-
-    public void try_enable_smartcard () throws GLib.Error {
-        var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
-
-        VMConfigurator.add_smartcard_support (config);
-
-        // This will take effect only after next reboot
-        domain.set_config (config);
-        if (is_on ())
-            notify_reboot_required ();
-    }
-
-    private string collect_logs () {
-        var builder = new StringBuilder ();
-
-        builder.append_printf ("Domain: %s\n", domain.get_name ());
-        builder.append_printf ("UUID: %s\n", domain.get_uuid ());
-        builder.append_printf ("Persistent: %s\n", domain.get_persistent () ? "yes" : "no");
-        try {
-            var info = domain.get_info ();
-            builder.append_printf ("Cpu time: %"+uint64.FORMAT_MODIFIER+"d\n", info.cpuTime);
-            builder.append_printf ("Memory: %"+uint64.FORMAT_MODIFIER+"d\n", info.memory);
-            builder.append_printf ("Max memory: %"+uint64.FORMAT_MODIFIER+"d\n", info.maxMem);
-            builder.append_printf ("CPUs: %d\n", info.nrVirtCpu);
-            builder.append_printf ("State: %s\n", info.state.to_string ());
-        } catch (GLib.Error e) {
-        }
-
-        if (display != null)
-            display.collect_logs (builder);
-
-
-        try {
-            var conf = domain.get_config (GVir.DomainXMLFlags.NONE);
-            builder.append_printf ("\nDomain config:\n");
-            builder.append_printf ("------------------------------------------------------------\n");
-
-            builder.append (conf.to_xml ());
-            builder.append_printf ("\n" +
-                                   "------------------------------------------------------------\n");
-        } catch (GLib.Error error) {
-        }
-
-        try {
-            var logfile = Path.build_filename (Environment.get_user_cache_dir (), "libvirt/qemu/log", domain.get_name ()  + ".log");
-            string data;
-            FileUtils.get_contents (logfile, out data);
-            builder.append_printf ("\nQEMU log:\n");
-            builder.append_printf ("------------------------------------------------------------\n");
-
-            builder.append (data);
-            builder.append_printf ("------------------------------------------------------------\n");
-        } catch (GLib.Error e) {
-        }
-        return builder.str;
-    }
-
-    public override List<Boxes.Property> get_properties (Boxes.PropertiesPage page, PropertyCreationFlag flags) {
-        var list = new List<Boxes.Property> ();
-
-        // the wizard may want to modify display properties, before connect_display()
-        if (display == null)
-            try {
-                update_display ();
-            } catch (GLib.Error e) {
-                warning (e.message);
-            }
-
-        switch (page) {
-        case PropertiesPage.LOGIN:
-            add_string_property (ref list, _("Name"), name, (property, name) => {
-                try_change_name (name);
-            });
-            add_string_property (ref list, _("Virtualizer"), source.uri);
-            if (display != null)
-                add_string_property (ref list, _("URI"), display.uri);
-            break;
-
-        case PropertiesPage.SYSTEM:
-            add_ram_property (ref list);
-            add_storage_property (ref list);
-
-            var button = new Gtk.Button.with_label (_("Troubleshooting log"));
-            button.halign = Gtk.Align.START;
-            add_property (ref list, null, button);
-            button.clicked.connect (() => {
-                var log = collect_logs ();
-                var dialog = new Gtk.Dialog.with_buttons (_("Troubleshooting log"),
-                                                          App.app.window,
-                                                          DialogFlags.DESTROY_WITH_PARENT,
-                                                          Stock.SAVE, 100,
-                                                          _("Copy to clipboard"), 101,
-                                                          Stock.CLOSE, ResponseType.OK);
-                dialog.set_default_size (640, 480);
-                var text = new Gtk.TextView ();
-                text.editable = false;
-                var scroll = new Gtk.ScrolledWindow (null, null);
-                scroll.add (text);
-                scroll.vexpand = true;
-
-                dialog.get_content_area ().add (scroll);
-                text.buffer.set_text (log);
-                dialog.show_all ();
-
-                dialog.response.connect ( (response_id) => {
-                    if (response_id == 100) {
-                        var chooser = new Gtk.FileChooserDialog (_("Save log"), App.app.window,
-                                                                 Gtk.FileChooserAction.SAVE,
-                                                                 Stock.SAVE, ResponseType.OK);
-                        chooser.local_only = false;
-                        chooser.do_overwrite_confirmation = true;
-                        chooser.response.connect ((response_id) => {
-                            if (response_id == ResponseType.OK) {
-                                var file = chooser.get_file ();
-                                try {
-                                    file.replace_contents (log.data, null, false,
-                                                           FileCreateFlags.REPLACE_DESTINATION, null);
-                                } catch (GLib.Error e) {
-                                    var m = new Gtk.MessageDialog (chooser,
-                                                                   Gtk.DialogFlags.DESTROY_WITH_PARENT,
-                                                                   Gtk.MessageType.ERROR,
-                                                                   Gtk.ButtonsType.CLOSE,
-                                                                   _("Error saving: %s").printf (e.message));
-                                    m.show_all ();
-                                    m.response.connect ( () => { m.destroy (); });
-                                    return;
-                                }
-                                chooser.destroy ();
-                            } else {
-                                chooser.destroy ();
-                            }
-                        });
-                        chooser.show_all ();
-                    } else if (response_id == 101){
-                        Gtk.Clipboard.get_for_display (dialog.get_display (), Gdk.SELECTION_CLIPBOARD).set_text (log, -1);
-                    } else {
-                        dialog.destroy ();
-                    }
-                });
-            });
-            break;
-
-        case PropertiesPage.DISPLAY:
-            if (display != null)
-                add_string_property (ref list, _("Protocol"), display.protocol);
-            break;
-
-        case PropertiesPage.DEVICES:
-            foreach (var device_config in domain_config.get_devices ()) {
-                if (!(device_config is GVirConfig.DomainDisk))
-                    continue;
-                var disk_config = device_config as GVirConfig.DomainDisk;
-                var disk_type = disk_config.get_guest_device_type ();
-                if (disk_type == GVirConfig.DomainDiskGuestDeviceType.CDROM) {
-                    var grid = new Gtk.Grid ();
-                    grid.set_orientation (Gtk.Orientation.HORIZONTAL);
-                    grid.set_column_spacing (12);
-
-                    var label = new Gtk.Label ("");
-                    grid.add (label);
-
-                    var source = disk_config.get_source ();
-                    bool empty = (source == null || source == "");
-
-                    var button_label = new Gtk.Label ("");
-                    var button = new Gtk.Button ();
-                    button.add (button_label);
-
-                    grid.add (button);
-
-                    if (empty) {
-                        // Translators: This is the text on the button to select an iso for the cd
-                        button_label.set_text (_("Select"));
-                        // Translators: empty is listed as the filename for a non-mounted CD
-                        label.set_markup (Markup.printf_escaped ("<i>%s</i>", _("empty")));
-                    } else {
-                        // Translators: Remove is the label on the button to remove an iso from a cdrom drive
-                        button_label.set_text (_("Remove"));
-                        label.set_text (get_utf8_basename (source));
-                    }
-
-                    button.clicked.connect ( () => {
-                        if (empty) {
-                            var dialog = new Gtk.FileChooserDialog (_("Select a device or ISO file"),
-                                                                    App.app.window,
-                                                                    Gtk.FileChooserAction.OPEN,
-                                                                    Gtk.Stock.CANCEL, Gtk.ResponseType.CANCEL,
-                                                                    Gtk.Stock.OPEN, Gtk.ResponseType.ACCEPT);
-                            dialog.modal = true;
-                            dialog.show_hidden = false;
-                            dialog.local_only = true;
-                            dialog.filter = new Gtk.FileFilter ();
-                            dialog.filter.add_mime_type ("application/x-cd-image");
-                            dialog.response.connect ( (response) => {
-                                if (response == Gtk.ResponseType.ACCEPT) {
-                                    var path = dialog.get_filename ();
-                                    disk_config.set_source (path);
-                                    try {
-                                        domain.update_device (disk_config, DomainUpdateDeviceFlags.CURRENT);
-                                        button_label.set_text (_("Remove"));
-                                        label.set_text (get_utf8_basename (path));
-                                        empty = false;
-                                    } catch (GLib.Error e) {
-                                        got_error (e.message);
-                                    }
-                                }
-                                dialog.destroy ();
-                            });
-                            dialog.show_all ();
-                        } else {
-                            disk_config.set_source ("");
-                            try {
-                                domain.update_device (disk_config, DomainUpdateDeviceFlags.CURRENT);
-                                empty = true;
-                                button_label.set_text (_("Select"));
-                                label.set_markup (Markup.printf_escaped ("<i>%s</i>", _("empty")));
-                            } catch (GLib.Error e) {
-                                got_error (e.message);
-                            }
-                        }
-                    });
-
-                    add_property (ref list, _("CD/DVD"), grid);
-                }
-            }
-
-            bool has_usb_redir = false;
-            bool has_smartcard = false;
-            // We look at the INACTIVE config here, because we want to show the usb
-            // widgetry if usb has been added already but we have not rebooted
-            try {
-                var inactive_config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
-                foreach (var device in inactive_config.get_devices ()) {
-                    if (device is GVirConfig.DomainRedirdev) {
-                        has_usb_redir = true;
-                    }
-                    if (device is GVirConfig.DomainSmartcard) {
-                        has_smartcard = true;
-                    }
-                }
-            } catch (GLib.Error error) {
-                warning ("Failed to fetch configuration for domain '%s': %s", domain.get_name (), error.message);
-            }
-
-            if (!has_usb_redir)
-                flags |= PropertyCreationFlag.NO_USB;
-
-            /* Only add usb support to guests if HAVE_USBREDIR, as older
-             * qemu versions break migration with it. */
-            if (!has_usb_redir && Config.HAVE_USBREDIR) {
-                var button = new Gtk.Button.with_label (_("Add support to guest"));
-                button.halign = Gtk.Align.START;
-                var property = add_property (ref list, _("USB device support"), button);
-                button.clicked.connect (() => {
-                    try {
-                        try_enable_usb_redir ();
-                        update_domain_config ();
-                        property.refresh_properties ();
-                    } catch (GLib.Error error) {
-                        warning ("Failed to enable usb");
-                    }
-                });
-            }
-
-            // Only add smartcart support to guests if HAVE_SMARTCARD, as qemu built
-            // without smartcard support will not start vms with it.
-            if (!has_smartcard && Config.HAVE_SMARTCARD) {
-                var button = new Gtk.Button.with_label (_("Add support to guest"));
-                button.halign = Gtk.Align.START;
-                var property = add_property (ref list, _("Smartcard support"), button);
-                button.clicked.connect (() => {
-                    try {
-                        try_enable_smartcard ();
-                        update_domain_config ();
-                        property.refresh_properties ();
-                    } catch (GLib.Error error) {
-                        warning ("Failed to enable smartcard");
-                    }
-                });
-            }
-
-            break;
-        }
-
-        if (display != null)
-            list.concat (display.get_properties (page,
-                                                 flags));
-
-        return list;
-    }
-
-    private bool update_display () throws GLib.Error {
+    protected override bool update_display () throws GLib.Error {
         update_domain_config ();
 
         var created_display = display == null;
@@ -725,7 +404,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
             domain.suspend ();
     }
 
-    public void force_shutdown (bool confirm = true) {
+    public override void force_shutdown (bool confirm = true) {
         if (confirm) {
             var dialog = new Gtk.MessageDialog (App.app.window,
                                                 Gtk.DialogFlags.DESTROY_WITH_PARENT,
@@ -748,28 +427,12 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
         }
     }
 
-    private GVir.DomainDisk? get_domain_disk () throws GLib.Error {
-        var disk = null as GVir.DomainDisk;
-
-        foreach (var device_config in domain_config.get_devices ()) {
-            if (device_config is GVirConfig.DomainDisk) {
-                var disk_config = device_config as GVirConfig.DomainDisk;
-                var disk_type = disk_config.get_guest_device_type ();
-                var storage_volume_path = (storage_volume != null)? storage_volume.get_path () : null;
-
-                // Prefer Boxes' managed volume over other disks
-                if (disk_type == GVirConfig.DomainDiskGuestDeviceType.DISK &&
-                    disk_config.get_source () == storage_volume_path) {
-                    disk = Object.new (typeof (GVir.DomainDisk),
-                                       "domain", domain,
-                                       "config", device_config) as GVir.DomainDisk;
-
-                    break;
-                }
-            }
+    protected override void try_shutdown () {
+        try {
+            domain.shutdown (0);
+        } catch (GLib.Error error) {
+            warning ("Failed to reboot '%s': %s", domain.get_name (), error.message);
         }
-
-        return disk;
     }
 
     private GVir.DomainInterface? get_domain_network_interface () throws GLib.Error {
@@ -788,69 +451,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
         return net;
     }
 
-    private void update_ram_property (Boxes.Property property) {
-        try {
-            var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
-
-            // we use KiB unit, convert to MiB
-            var actual = domain_config.memory / 1024;
-            var pending = config.memory / 1024;
-
-            debug ("RAM actual: %llu, pending: %llu", actual, pending);
-            // somehow, there are rounded errors, so let's forget about 1Mb diff
-            property.reboot_required = (actual - pending) > 1; // no need for abs()
-
-        } catch (GLib.Error e) {}
-    }
-
-    private void add_ram_property (ref List list) {
-        try {
-            var max_ram = connection.get_node_info ().memory;
-
-            var property = add_size_property (ref list,
-                                              _("Memory"),
-                                              domain_config.memory,
-                                              Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
-                                              max_ram,
-                                              Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
-                                              on_ram_changed);
-
-            this.notify["state"].connect (() => {
-                if (!is_on ())
-                    property.reboot_required = false;
-            });
-
-            update_ram_property (property);
-        } catch (GLib.Error error) {}
-    }
-
-    private void on_ram_changed (Boxes.Property property, uint64 value) {
-        // Ensure that we don't end-up changing RAM like a 1000 times a second while user moves the slider..
-        property.deferred_change = () => {
-            try {
-                var config = domain.get_config (GVir.DomainXMLFlags.INACTIVE);
-                config.memory = value;
-                if (config.get_class ().find_property ("current-memory") != null)
-                    config.set ("current-memory", value);
-                domain.set_config (config);
-                debug ("RAM changed to %llu", value);
-                if (is_on ())
-                    notify_reboot_required ();
-            } catch (GLib.Error error) {
-                warning ("Failed to change RAM of box '%s' to %llu: %s",
-                         domain.get_name (),
-                         value,
-                         error.message);
-            }
-
-            if (is_on ())
-                update_ram_property (property);
-
-            return false;
-        };
-    }
-
-    private async void start () throws GLib.Error {
+    protected override async void start () throws GLib.Error {
         if (state == MachineState.RUNNING)
             return;
 
@@ -868,112 +469,4 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
             yield domain.start_async (0, null);
         }
     }
-
-
-    private void notify_reboot_required () {
-        Notificationbar.OKFunc reboot = () => {
-            debug ("Rebooting '%s'..", name);
-            stay_on_display = true;
-            ulong state_id = 0;
-            Gd.Notification notification = null;
-
-            state_id = this.notify["state"].connect (() => {
-                if (state == MachineState.STOPPED || state == MachineState.FORCE_STOPPED) {
-                    debug ("'%s' stopped.", name);
-                    start.begin ((obj, res) => {
-                        try {
-                            start.end (res);
-                        } catch (GLib.Error error) {
-                            warning ("Failed to start '%s': %s", domain.get_name (), error.message);
-                        }
-                    });
-                    this.disconnect (state_id);
-                    if (shutdown_timeout != 0) {
-                        Source.remove (shutdown_timeout);
-                        shutdown_timeout = 0;
-                    }
-                    if (notification != null) {
-                        notification.dismiss ();
-                        notification = null;
-                    }
-                }
-            });
-
-            shutdown_timeout = Timeout.add_seconds (5, () => {
-                // Seems guest ignored ACPI shutdown, lets force shutdown with user's consent
-                Notificationbar.OKFunc really_force_shutdown = () => {
-                    notification = null;
-                    force_shutdown (false);
-                };
-
-                var message = _("Restart of '%s' is taking too long. Force it to shutdown?").printf (name);
-                notification = App.app.notificationbar.display_for_action (message,
-                                                                           Gtk.Stock.YES,
-                                                                           (owned) really_force_shutdown,
-                                                                           null,
-                                                                           -1);
-                shutdown_timeout = 0;
-
-                return false;
-            });
-
-            try {
-                domain.shutdown (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.app.notificationbar.display_for_action (message, Gtk.Stock.YES, (owned) reboot);
-    }
-
-    private void add_storage_property (ref List list) {
-        if (storage_volume == null)
-            return;
-
-        try {
-            var volume_info = storage_volume.get_info ();
-            var pool = get_storage_pool (connection);
-            var pool_info = pool.get_info ();
-            var max_storage = (volume_info.capacity + pool_info.available)  / Osinfo.KIBIBYTES;
-
-            add_size_property (ref list,
-                               _("Maximum Disk Size"),
-                               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",
-                     storage_volume.get_name (),
-                     error.message);
-        }
-    }
-
-    private void on_storage_changed (Boxes.Property property, uint64 value) {
-        // Ensure that we don't end-up changing storage like a 1000 times a second while user moves the slider..
-        property.deferred_change = () => {
-            if (storage_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
-                    storage_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",
-                         storage_volume.get_name (),
-                         value,
-                         error.message);
-            }
-
-            return false;
-        };
-    }
 }



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