[gnome-boxes] Add LibvirtMachineProperties
- From: Zeeshan Ali Khattak <zeeshanak src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-boxes] Add LibvirtMachineProperties
- Date: Fri, 15 Feb 2013 14:29:02 +0000 (UTC)
commit e176e5740c5c64c7cb9d8fedeacc519bd279bf17
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 52%.
https://bugzilla.gnome.org/show_bug.cgi?id=688333
src/Makefile.am | 1 +
src/libvirt-machine-properties.vala | 497 ++++++++++++++++++++++++++++++++++
src/libvirt-machine.vala | 512 ++---------------------------------
3 files changed, 519 insertions(+), 491 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index eb1323b..421aa5c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -90,6 +90,7 @@ gnome_boxes_SOURCES = \
iso-extractor.vala \
libvirt-broker.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..fc8e0fe
--- /dev/null
+++ b/src/libvirt-machine-properties.vala
@@ -0,0 +1,497 @@
+// This file is part of GNOME Boxes. License: LGPLv2+
+using GVir;
+using Gtk;
+
+private class Boxes.LibvirtMachineProperties: GLib.Object, Boxes.IPropertiesProvider {
+ private weak LibvirtMachine machine; // Weak ref for avoiding cyclic ref */
+ private uint shutdown_timeout;
+
+ public LibvirtMachineProperties (LibvirtMachine machine) {
+ this.machine = machine;
+ }
+
+ public void try_change_name (string name) throws Boxes.Error {
+ try {
+ var config = machine.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
+ machine.domain.set_config (config);
+
+ machine.name = name;
+ } catch (GLib.Error error) {
+ warning ("Failed to change title of box '%s' to '%s': %s",
+ machine.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 = machine.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
+ machine.domain.set_config (config);
+ if (machine.is_on ())
+ notify_reboot_required ();
+ }
+
+ public void try_enable_smartcard () throws GLib.Error {
+ var config = machine.domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+
+ VMConfigurator.add_smartcard_support (config);
+
+ // This will take effect only after next reboot
+ machine.domain.set_config (config);
+ if (machine.is_on ())
+ notify_reboot_required ();
+ }
+
+ private string collect_logs () {
+ var builder = new StringBuilder ();
+
+ builder.append_printf ("Domain: %s\n", machine.domain.get_name ());
+ builder.append_printf ("UUID: %s\n", machine.domain.get_uuid ());
+ builder.append_printf ("Persistent: %s\n", machine.domain.get_persistent () ? "yes" : "no");
+ try {
+ var info = machine.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 (machine.display != null)
+ machine.display.collect_logs (builder);
+
+
+ try {
+ var conf = machine.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",
+ machine.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 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 (machine.display == null)
+ try {
+ machine.update_display ();
+ } catch (GLib.Error e) {
+ warning (e.message);
+ }
+
+ switch (page) {
+ case PropertiesPage.LOGIN:
+ add_string_property (ref list, _("Name"), machine.name, (property, name) => {
+ try_change_name (name);
+ });
+ add_string_property (ref list, _("Virtualizer"), machine.source.uri);
+ if (machine.display != null)
+ add_string_property (ref list, _("URI"), machine.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 (machine.display != null)
+ add_string_property (ref list, _("Protocol"), machine.display.protocol);
+ break;
+
+ case PropertiesPage.DEVICES:
+ foreach (var device_config in machine.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 {
+ machine.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) {
+ machine.got_error (e.message);
+ }
+ }
+ dialog.destroy ();
+ });
+ dialog.show_all ();
+ } else {
+ disk_config.set_source ("");
+ try {
+ machine.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) {
+ machine.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 = machine.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", machine.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 ();
+ machine.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 ();
+ machine.update_domain_config ();
+ property.refresh_properties ();
+ } catch (GLib.Error error) {
+ warning ("Failed to enable smartcard");
+ }
+ });
+ }
+
+ break;
+ }
+
+ return list;
+ }
+
+ private void update_ram_property (Boxes.Property property) {
+ try {
+ var config = machine.domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+
+ // we use KiB unit, convert to MiB
+ var actual = machine.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 = machine.connection.get_node_info ().memory;
+
+ var property = add_size_property (ref list,
+ _("Memory"),
+ machine.domain_config.memory,
+ Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
+ max_ram,
+ Osinfo.MEBIBYTES / Osinfo.KIBIBYTES,
+ on_ram_changed);
+
+ this.notify["state"].connect (() => {
+ if (!machine.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 = machine.domain.get_config (GVir.DomainXMLFlags.INACTIVE);
+ config.memory = value;
+ if (config.get_class ().find_property ("current-memory") != null)
+ config.set ("current-memory", value);
+ machine.domain.set_config (config);
+ debug ("RAM changed to %llu", value);
+ if (machine.is_on ())
+ notify_reboot_required ();
+ } catch (GLib.Error error) {
+ warning ("Failed to change RAM of box '%s' to %llu: %s",
+ machine.domain.get_name (),
+ value,
+ error.message);
+ }
+
+ if (machine.is_on ())
+ update_ram_property (property);
+
+ return false;
+ };
+ }
+
+ private void notify_reboot_required () {
+ Notificationbar.OKFunc reboot = () => {
+ debug ("Rebooting '%s'..", machine.name);
+ machine.stay_on_display = true;
+ ulong state_id = 0;
+ Gd.Notification notification = null;
+
+ state_id = this.notify["state"].connect (() => {
+ if (machine.state == Machine.MachineState.STOPPED ||
+ machine.state == Machine.MachineState.FORCE_STOPPED) {
+ debug ("'%s' stopped.", machine.name);
+ machine.start.begin ((obj, res) => {
+ try {
+ machine.start.end (res);
+ } catch (GLib.Error error) {
+ warning ("Failed to start '%s': %s", machine.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;
+ machine.force_shutdown (false);
+ };
+
+ var message = _("Restart of '%s' is taking too long. Force it to shutdown?").printf
(machine.name);
+ notification = App.app.notificationbar.display_for_action (message,
+ Gtk.Stock.YES,
+ (owned) really_force_shutdown,
+ null,
+ -1);
+ shutdown_timeout = 0;
+
+ return false;
+ });
+
+ machine.try_shutdown ();
+ };
+ var message = _("Changes require restart of '%s'. Attempt restart?").printf (machine.name);
+ App.app.notificationbar.display_for_action (message, Gtk.Stock.YES, (owned) reboot);
+ }
+
+ private void add_storage_property (ref List list) {
+ if (machine.storage_volume == null)
+ return;
+
+ try {
+ var volume_info = machine.storage_volume.get_info ();
+ var pool = get_storage_pool (machine.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",
+ machine.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 (machine.storage_volume == null)
+ return false;
+
+ try {
+ if (machine.is_running ()) {
+ var disk = machine.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
+ 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",
+ machine.storage_volume.get_name (),
+ value,
+ error.message);
+ }
+
+ return false;
+ };
+ }
+}
diff --git a/src/libvirt-machine.vala b/src/libvirt-machine.vala
index 2a9a527..561cc53 100644
--- a/src/libvirt-machine.vala
+++ b/src/libvirt-machine.vala
@@ -3,11 +3,13 @@ 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
+ public GVir.Domain domain { get; set; }
+ public GVirConfig.Domain domain_config { get; set; }
+ public GVir.Connection connection { get; set; }
+ public GVir.StorageVol? storage_volume { get; set; }
+ public VMCreator? vm_creator { get; set; } // Under installation if this is set to non-null
+
+ private LibvirtMachineProperties properties;
public bool save_on_quit {
get { return source.get_boolean ("source", "save-on-quit"); }
@@ -61,7 +63,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
can_save = true;
}
- private void update_domain_config () {
+ public void update_domain_config () {
try {
domain_config = domain.get_config (GVir.DomainXMLFlags.NONE);
storage_volume = get_storage_volume (connection, domain);
@@ -90,6 +92,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
create_display_config (domain.get_uuid ());
this.connection = connection;
this.domain = domain;
+ this.properties = new LibvirtMachineProperties (this);
try {
var s = domain.get_info ().state;
@@ -290,319 +293,8 @@ 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;
- }
+ var list = properties.get_properties (page, flags);
if (display != null)
list.concat (display.get_properties (page,
@@ -611,7 +303,7 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
return list;
}
- private bool update_display () throws GLib.Error {
+ public bool update_display () throws GLib.Error {
update_domain_config ();
var created_display = display == null;
@@ -748,7 +440,15 @@ private class Boxes.LibvirtMachine: Boxes.Machine {
}
}
- private GVir.DomainDisk? get_domain_disk () throws GLib.Error {
+ public void try_shutdown () {
+ try {
+ domain.shutdown (0);
+ } catch (GLib.Error error) {
+ warning ("Failed to reboot '%s': %s", domain.get_name (), error.message);
+ }
+ }
+
+ public GVir.DomainDisk? get_domain_disk () throws GLib.Error {
var disk = null as GVir.DomainDisk;
foreach (var device_config in domain_config.get_devices ()) {
@@ -788,69 +488,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 {
+ public async void start () throws GLib.Error {
if (state == MachineState.RUNNING)
return;
@@ -868,112 +506,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]