[gnome-boxes] installer: More reliable way to track installations



commit be2d57c19a534e458f0042aabe0ab95613060125
Author: Zeeshan Ali (Khattak) <zeeshanak gnome org>
Date:   Wed Jan 18 22:02:53 2012 +0200

    installer: More reliable way to track installations
    
    * Don't create transient domains for live case but rather delete the
      domain (and its volume) ourselves. The issue with transient domains is
      that once they are shutdown, there is no way to get their configuration
      to base a new permanent configuration to base on.
    * Make use of custom XML in domain configuration to keep track of an
      installation/live session in progress. Without this change, Boxes is not
      able to do the necessary post-installation setup if user quits Boxes
      during installation in progress.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=668211

 configure.ac             |    2 +-
 src/app.vala             |    2 +-
 src/libvirt-machine.vala |   27 ++++++--------
 src/vm-configurator.vala |   46 ++++++++++++++++++++++++
 src/vm-creator.vala      |   89 ++++++++++++++++++++++++++++-----------------
 5 files changed, 114 insertions(+), 52 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1b99bcb..77ba0ac 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,7 +46,7 @@ GOBJECT_INTROSPECTION_MIN_VERSION=0.9.6
 GTK_MIN_VERSION=3.3.5
 GTK_VNC_MIN_VERSION=0.4.4
 LIBVIRT_GLIB_MIN_VERSION=0.0.4
-LIBVIRT_GCONFIG_MIN_VERSION=0.0.4
+LIBVIRT_GCONFIG_MIN_VERSION=0.0.5
 LIBXML2_MIN_VERSION=2.7.8
 SPICE_GTK_MIN_VERSION=0.9
 GUDEV_MIN_VERSION=147
diff --git a/src/app.vala b/src/app.vala
index 983d97c..2ebfa18 100644
--- a/src/app.vala
+++ b/src/app.vala
@@ -114,10 +114,10 @@ private class Boxes.App: Boxes.UI {
 
             application.set_app_menu (menu);
 
+            collection = new Collection (this);
             duration = settings.get_int ("animation-duration");
             setup_ui ();
 
-            collection = new Collection (this);
             collection.item_added.connect ((item) => {
                 view.add_item (item);
             });
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index bd58b7f..165ec8e 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -337,24 +337,19 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
 
         if (by_user) {
             try {
-                // The reason we fetch the volume before stopping the domain is that we need the domain's
-                // configuration for fechting its volume and transient domains stop existing after they are stopped.
-                // OTOH we can't just delete the volume from a running domain.
-                StorageVol volume = null;
-                if (connection == app.default_connection)
-                    volume = get_storage_volume (connection, domain);
+                if (is_running ())
+                    domain.stop (0);
+            } catch (GLib.Error err) {
+                // ignore stop error
+            }
 
-                try {
-                    if (is_running ())
-                        domain.stop (0);
-                } catch (GLib.Error err) {
-                    // ignore stop error
+            try {
+                if (connection == app.default_connection) {
+                    var volume = get_storage_volume (connection, domain);
+                    if (volume != null)
+                        volume.delete (0);
                 }
-
-                if (volume != null)
-                    volume.delete (0);
-                if (domain.persistent)
-                    domain.delete (0);
+                domain.delete (0);
             } catch (GLib.Error err) {
                 warning (err.message);
             }
diff --git a/src/vm-configurator.vala b/src/vm-configurator.vala
index a51e8e7..0f66155 100644
--- a/src/vm-configurator.vala
+++ b/src/vm-configurator.vala
@@ -4,9 +4,25 @@ using Osinfo;
 using GVirConfig;
 
 private class Boxes.VMConfigurator {
+    private const string BOXES_NS = "boxes";
+    private const string BOXES_NS_URI = "http://live.gnome.org/Boxes/";;
+    private const string LIVE_STATE = "live";
+    private const string INSTALLATION_STATE = "installation";
+    private const string INSTALLED_STATE = "installed";
+    private const string LIVE_XML = "<os-state>" + LIVE_STATE + "</os-state>";
+    private const string INSTALLATION_XML = "<os-state>" + INSTALLATION_STATE + "</os-state>";
+    private const string INSTALLED_XML = "<os-state>" + INSTALLED_STATE + "</os-state>";
+
     public Domain create_domain_config (InstallerMedia install_media, string name, string target_path) {
         var domain = new Domain ();
         domain.name = name;
+
+        var xml = (install_media.live) ? LIVE_XML : INSTALLATION_XML;
+
+        try {
+            domain.set_custom_xml (xml, BOXES_NS, BOXES_NS_URI);
+        } catch (GLib.Error error) { assert_not_reached (); /* We are so screwed if this happens */ }
+
         domain.memory = install_media.resources.ram / KIBIBYTES;
         domain.vcpu = install_media.resources.n_cpus;
         domain.set_virt_type (DomainVirtType.KVM);
@@ -54,10 +70,21 @@ private class Boxes.VMConfigurator {
     }
 
     public void post_install_setup (Domain domain) {
+        try {
+            domain.set_custom_xml (INSTALLED_XML, BOXES_NS, BOXES_NS_URI);
+        } catch (GLib.Error error) { assert_not_reached (); /* We are so screwed if this happens */ }
         set_os_config (domain);
         domain.set_lifecycle (DomainLifecycleEvent.ON_REBOOT, DomainLifecycleAction.RESTART);
     }
 
+    public bool is_install_config (Domain domain) {
+        return get_os_state (domain) == INSTALLATION_STATE;
+    }
+
+    public bool is_live_config (Domain domain) {
+        return get_os_state (domain) == LIVE_STATE;
+    }
+
     public StorageVol create_volume_config (string name, int64 storage) throws GLib.Error {
         var volume = new StorageVol ();
         volume.set_name (name);
@@ -205,4 +232,23 @@ private class Boxes.VMConfigurator {
 
         return permissions;
     }
+
+    private string? get_os_state (Domain domain) {
+        var xml = domain.get_custom_xml (BOXES_NS_URI);
+        if (xml != null) {
+            var reader = new Xml.TextReader.for_memory ((char []) xml.data,
+                                                        xml.length,
+                                                        BOXES_NS_URI,
+                                                        null,
+                                                        Xml.ParserOption.COMPACT);
+            do {
+                if (reader.name () == "boxes:os-state")
+                    return reader.read_string ();
+            } while (reader.next () == 1);
+        }
+
+        warning ("Failed to find OS state for domain '%s'.", domain.get_name ());
+
+        return null;
+    }
 }
diff --git a/src/vm-creator.vala b/src/vm-creator.vala
index 5a9aa2d..cb28060 100644
--- a/src/vm-creator.vala
+++ b/src/vm-creator.vala
@@ -11,6 +11,31 @@ private class Boxes.VMCreator {
     public VMCreator (App app) {
         configurator = new VMConfigurator ();
         this.app = app;
+
+        app.collection.item_added.connect (on_item_added);
+    }
+
+    private void on_item_added (Collection collection, CollectionItem item) {
+        if (!(item is LibvirtMachine))
+            return;
+
+        var machine = item as LibvirtMachine;
+        if (machine.connection != connection)
+            return;
+
+        try {
+            var config = machine.domain.get_config (0);
+            if (!configurator.is_install_config (config) && !configurator.is_live_config (config))
+                return;
+
+            var state = machine.domain.get_info ().state;
+            if (state == DomainState.SHUTOFF || state == DomainState.CRASHED || state == DomainState.NONE)
+                on_domain_stopped (machine.domain);
+            else
+                machine.domain.stopped.connect (on_domain_stopped);
+        } catch (GLib.Error error) {
+            warning ("Failed to get information on domain '%s': %s", machine.domain.get_name (), error.message);
+        }
     }
 
     public async Domain create_and_launch_vm (InstallerMedia install_media,
@@ -22,46 +47,42 @@ private class Boxes.VMCreator {
         var volume = yield create_target_volume (name, install_media.resources.storage);
         var config = configurator.create_domain_config (install_media, name, volume.get_path ());
 
-        Domain domain;
-        if (install_media.live)
-            // We create a (initially) transient domain for live and unknown media
-            domain = connection.start_domain (config, 0);
-        else {
-            domain = connection.create_domain (config);
-            domain.start (0);
-            config = domain.get_config (0);
-        }
-
-        ulong id = 0;
-        id = domain.stopped.connect (() => {
-            if (guest_installed_os (volume)) {
-                post_install_setup (domain, config, !install_media.live);
-                domain.disconnect (id);
-            } else if (install_media.live) {
-                domain.disconnect (id);
-                // Domain is gone then so we should delete associated storage volume.
-                try {
-                    volume.delete (0);
-                } catch (GLib.Error error) {
-                    warning ("Failed to delete volume '%s': %s", volume.get_name (), error.message);
-                }
-            }
-        });
+        var domain = connection.create_domain (config);
+        domain.start (0);
 
         return domain;
     }
 
-    private void post_install_setup (Domain domain, GVirConfig.Domain config, bool permanent) {
-        configurator.post_install_setup (config);
+    private void on_domain_stopped (Domain domain) {
+        var volume = get_storage_volume (connection, domain);
+
+        if (guest_installed_os (volume)) {
+            post_install_setup (domain);
+            domain.stopped.disconnect (on_domain_stopped);
+        } else {
+            try {
+                var config = domain.get_config (0);
+
+                if (!configurator.is_live_config (config))
+                    return;
+
+                // No installation during live session, so lets delete the domain and its storage volume.
+                domain.stopped.disconnect (on_domain_stopped);
+                domain.delete (0);
+                volume.delete (0);
+            } catch (GLib.Error error) {
+                warning ("Failed to delete domain '%s' or its volume: %s", domain.get_name (), error.message);
+            }
+        }
+    }
 
+    private void post_install_setup (Domain domain) {
         try {
-            if (permanent) {
-                domain.set_config (config);
-                domain.start (0);
-            } else {
-                var new_domain = connection.create_domain (config);
-                new_domain.start (0);
-            }
+            var config = domain.get_config (0);
+            configurator.post_install_setup (config);
+
+            domain.set_config (config);
+            domain.start (0);
         } catch (GLib.Error error) {
             warning ("Post-install setup failed for domain '%s': %s", domain.get_uuid (), error.message);
         }



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