mirror of
https://github.com/virt-manager/virt-manager.git
synced 2025-02-25 18:55:27 -06:00
prefs: Add a setting to enable/disable libguestfs inspection
Rather than key it on the library being available. Makes it much easier to test both modes of behavior. Fix up a few inspection bugs while I'm in the area, and convert it to be more singleton like.
This commit is contained in:
parent
6959a41ff2
commit
6b1278ccda
@ -47,6 +47,12 @@
|
|||||||
<description>Show system tray icon while app is running</description>
|
<description>Show system tray icon while app is running</description>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
|
<key name="enable-libguestfs-vm-inspection" type="b">
|
||||||
|
<default>true</default>
|
||||||
|
<summary>Enable libguestfs VM inspection</summary>
|
||||||
|
<description>Enable libguestfs VM inspection for things like OS icons, installed applications, etc. This only works if python libguestfs bindings are installed.</description>
|
||||||
|
</key>
|
||||||
|
|
||||||
<key name="manager-window-height" type="i">
|
<key name="manager-window-height" type="i">
|
||||||
<default>0</default>
|
<default>0</default>
|
||||||
<summary>Default manager window height</summary>
|
<summary>Default manager window height</summary>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!-- Generated with glade 3.20.2 -->
|
<!-- Generated with glade 3.20.3 -->
|
||||||
<interface>
|
<interface>
|
||||||
<requires lib="gtk+" version="3.14"/>
|
<requires lib="gtk+" version="3.14"/>
|
||||||
<object class="GtkAdjustment" id="adjustment1">
|
<object class="GtkAdjustment" id="adjustment1">
|
||||||
@ -49,6 +49,7 @@
|
|||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="can_focus">True</property>
|
<property name="can_focus">True</property>
|
||||||
<property name="receives_default">False</property>
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
<property name="use_underline">True</property>
|
<property name="use_underline">True</property>
|
||||||
<property name="draw_indicator">True</property>
|
<property name="draw_indicator">True</property>
|
||||||
<signal name="toggled" handler="on_prefs_system_tray_toggled" swapped="no"/>
|
<signal name="toggled" handler="on_prefs_system_tray_toggled" swapped="no"/>
|
||||||
@ -58,6 +59,68 @@
|
|||||||
<property name="top_attach">0</property>
|
<property name="top_attach">0</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkGrid">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="row_spacing">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkCheckButton" id="prefs-libguestfs">
|
||||||
|
<property name="label" translatable="yes">Enable libgues_tfs VM introspection</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">True</property>
|
||||||
|
<property name="receives_default">False</property>
|
||||||
|
<property name="halign">start</property>
|
||||||
|
<property name="use_underline">True</property>
|
||||||
|
<property name="draw_indicator">True</property>
|
||||||
|
<signal name="toggled" handler="on_prefs_libguestfs_toggled" swapped="no"/>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox" id="prefs-libguestfs-warn-box">
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="spacing">3</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkImage">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="stock">gtk-dialog-warning</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel" id="prefs-libguestfs-warn-label">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can_focus">False</property>
|
||||||
|
<property name="label"><small>You must restart the application for this change to take effect</small></property>
|
||||||
|
<property name="use_markup">True</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="left_attach">0</property>
|
||||||
|
<property name="top_attach">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
@ -25,6 +25,8 @@ from gi.repository import GLib
|
|||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
|
|
||||||
from virtinst import CPU
|
from virtinst import CPU
|
||||||
|
|
||||||
|
from .inspection import vmmInspection
|
||||||
from .keyring import vmmKeyring, vmmSecret
|
from .keyring import vmmKeyring, vmmSecret
|
||||||
|
|
||||||
|
|
||||||
@ -215,19 +217,6 @@ class vmmConfig(object):
|
|||||||
|
|
||||||
self._objects = []
|
self._objects = []
|
||||||
|
|
||||||
self.support_inspection = self.check_inspection()
|
|
||||||
|
|
||||||
self._spice_error = None
|
|
||||||
|
|
||||||
|
|
||||||
def check_inspection(self):
|
|
||||||
try:
|
|
||||||
# Check we can open the Python guestfs module.
|
|
||||||
from guestfs import GuestFS # pylint: disable=import-error
|
|
||||||
g = GuestFS(close_on_exit=False)
|
|
||||||
return bool(getattr(g, "add_libvirt_dom", None))
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# General app wide helpers (gsettings agnostic)
|
# General app wide helpers (gsettings agnostic)
|
||||||
|
|
||||||
@ -242,6 +231,11 @@ class vmmConfig(object):
|
|||||||
ret = ["vnc", "spice"]
|
ret = ["vnc", "spice"]
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def inspection_supported(self):
|
||||||
|
if not vmmInspection.libguestfs_installed():
|
||||||
|
return False
|
||||||
|
return self.get_libguestfs_inspect_vms()
|
||||||
|
|
||||||
def remove_notifier(self, h):
|
def remove_notifier(self, h):
|
||||||
self.conf.notify_remove(h)
|
self.conf.notify_remove(h)
|
||||||
|
|
||||||
@ -379,7 +373,6 @@ class vmmConfig(object):
|
|||||||
def get_confirm_delstorage(self):
|
def get_confirm_delstorage(self):
|
||||||
return self.conf.get("/confirm/delete-storage")
|
return self.conf.get("/confirm/delete-storage")
|
||||||
|
|
||||||
|
|
||||||
def set_confirm_forcepoweroff(self, val):
|
def set_confirm_forcepoweroff(self, val):
|
||||||
self.conf.set("/confirm/forcepoweroff", val)
|
self.conf.set("/confirm/forcepoweroff", val)
|
||||||
def set_confirm_poweroff(self, val):
|
def set_confirm_poweroff(self, val):
|
||||||
@ -404,6 +397,14 @@ class vmmConfig(object):
|
|||||||
def set_view_system_tray(self, val):
|
def set_view_system_tray(self, val):
|
||||||
self.conf.set("/system-tray", val)
|
self.conf.set("/system-tray", val)
|
||||||
|
|
||||||
|
# Libguestfs VM inspection
|
||||||
|
def on_libguestfs_inspect_vms_changed(self, cb):
|
||||||
|
return self.conf.notify_add("/enable-libguestfs-vm-inspection", cb)
|
||||||
|
def get_libguestfs_inspect_vms(self):
|
||||||
|
return self.conf.get("/enable-libguestfs-vm-inspection")
|
||||||
|
def set_libguestfs_inspect_vms(self, val):
|
||||||
|
self.conf.set("/enable-libguestfs-vm-inspection", val)
|
||||||
|
|
||||||
|
|
||||||
# Stats history and interval length
|
# Stats history and interval length
|
||||||
def get_stats_history_length(self):
|
def get_stats_history_length(self):
|
||||||
|
@ -2370,61 +2370,62 @@ class vmmDetails(vmmGObjectUI):
|
|||||||
self.widget(name).set_value(int(IdMap_proper))
|
self.widget(name).set_value(int(IdMap_proper))
|
||||||
|
|
||||||
def refresh_inspection_page(self):
|
def refresh_inspection_page(self):
|
||||||
inspection_supported = self.config.support_inspection
|
inspection_supported = self.config.inspection_supported()
|
||||||
uiutil.set_grid_row_visible(self.widget("details-overview-error"),
|
uiutil.set_grid_row_visible(self.widget("details-overview-error"),
|
||||||
self.vm.inspection.error)
|
self.vm.inspection.error)
|
||||||
if self.vm.inspection.error:
|
if self.vm.inspection.error:
|
||||||
msg = _("Error while inspecting the guest configuration")
|
msg = _("Error while inspecting the guest configuration")
|
||||||
self.widget("details-overview-error").set_text(msg)
|
self.widget("details-overview-error").set_text(msg)
|
||||||
|
|
||||||
# Operating System (ie. inspection data)
|
|
||||||
self.widget("details-inspection-os").set_visible(inspection_supported)
|
self.widget("details-inspection-os").set_visible(inspection_supported)
|
||||||
if inspection_supported:
|
self.widget("details-inspection-apps").set_visible(inspection_supported)
|
||||||
hostname = self.vm.inspection.hostname
|
if not inspection_supported:
|
||||||
if not hostname:
|
return
|
||||||
hostname = _("unknown")
|
|
||||||
self.widget("inspection-hostname").set_text(hostname)
|
# Operating System (ie. inspection data)
|
||||||
os_type = self.vm.inspection.os_type
|
hostname = self.vm.inspection.hostname
|
||||||
if not os_type:
|
if not hostname:
|
||||||
os_type = "unknown"
|
hostname = _("unknown")
|
||||||
self.widget("inspection-type").set_text(_label_for_os_type(os_type))
|
self.widget("inspection-hostname").set_text(hostname)
|
||||||
product_name = self.vm.inspection.product_name
|
os_type = self.vm.inspection.os_type
|
||||||
if not product_name:
|
if not os_type:
|
||||||
product_name = _("unknown")
|
os_type = "unknown"
|
||||||
self.widget("inspection-product-name").set_text(product_name)
|
self.widget("inspection-type").set_text(_label_for_os_type(os_type))
|
||||||
|
product_name = self.vm.inspection.product_name
|
||||||
|
if not product_name:
|
||||||
|
product_name = _("unknown")
|
||||||
|
self.widget("inspection-product-name").set_text(product_name)
|
||||||
|
|
||||||
# Applications (also inspection data)
|
# Applications (also inspection data)
|
||||||
self.widget("details-inspection-apps").set_visible(inspection_supported)
|
apps = self.vm.inspection.applications or []
|
||||||
if inspection_supported:
|
apps_list = self.widget("inspection-apps")
|
||||||
apps = self.vm.inspection.applications or []
|
apps_model = apps_list.get_model()
|
||||||
apps_list = self.widget("inspection-apps")
|
apps_model.clear()
|
||||||
apps_model = apps_list.get_model()
|
for app in apps:
|
||||||
apps_model.clear()
|
name = ""
|
||||||
for app in apps:
|
if app["app_name"]:
|
||||||
name = ""
|
name = app["app_name"]
|
||||||
if app["app_name"]:
|
if app["app_display_name"]:
|
||||||
name = app["app_name"]
|
name = app["app_display_name"]
|
||||||
if app["app_display_name"]:
|
version = ""
|
||||||
name = app["app_display_name"]
|
if app["app_epoch"] > 0:
|
||||||
version = ""
|
version += str(app["app_epoch"]) + ":"
|
||||||
if app["app_epoch"] > 0:
|
if app["app_version"]:
|
||||||
version += str(app["app_epoch"]) + ":"
|
version += app["app_version"]
|
||||||
if app["app_version"]:
|
if app["app_release"]:
|
||||||
version += app["app_version"]
|
version += "-" + app["app_release"]
|
||||||
if app["app_release"]:
|
summary = ""
|
||||||
version += "-" + app["app_release"]
|
if app["app_summary"]:
|
||||||
summary = ""
|
summary = app["app_summary"]
|
||||||
if app["app_summary"]:
|
elif app["app_description"]:
|
||||||
summary = app["app_summary"]
|
summary = app["app_description"]
|
||||||
elif app["app_description"]:
|
pos = summary.find("\n")
|
||||||
summary = app["app_description"]
|
if pos > -1:
|
||||||
pos = summary.find("\n")
|
summary = _("%(summary)s ...") % {
|
||||||
if pos > -1:
|
"summary": summary[0:pos]
|
||||||
summary = _("%(summary)s ...") % {
|
}
|
||||||
"summary": summary[0:pos]
|
|
||||||
}
|
|
||||||
|
|
||||||
apps_model.append([name, version, summary])
|
apps_model.append([name, version, summary])
|
||||||
|
|
||||||
def refresh_stats_page(self):
|
def refresh_stats_page(self):
|
||||||
def _multi_color(text1, text2):
|
def _multi_color(text1, text2):
|
||||||
@ -3070,7 +3071,7 @@ class vmmDetails(vmmGObjectUI):
|
|||||||
|
|
||||||
add_hw_list_option(_("Overview"), HW_LIST_TYPE_GENERAL, "computer")
|
add_hw_list_option(_("Overview"), HW_LIST_TYPE_GENERAL, "computer")
|
||||||
if not self.is_customize_dialog:
|
if not self.is_customize_dialog:
|
||||||
if self.config.support_inspection:
|
if self.config.inspection_supported():
|
||||||
add_hw_list_option(_("OS information"),
|
add_hw_list_option(_("OS information"),
|
||||||
HW_LIST_TYPE_INSPECTION, "computer")
|
HW_LIST_TYPE_INSPECTION, "computer")
|
||||||
add_hw_list_option(_("Performance"), HW_LIST_TYPE_STATS,
|
add_hw_list_option(_("Performance"), HW_LIST_TYPE_STATS,
|
||||||
|
@ -34,14 +34,15 @@ from .baseclass import vmmGObject
|
|||||||
from .clone import vmmCloneVM
|
from .clone import vmmCloneVM
|
||||||
from .connect import vmmConnect
|
from .connect import vmmConnect
|
||||||
from .connection import vmmConnection
|
from .connection import vmmConnection
|
||||||
|
from .create import vmmCreate
|
||||||
|
from .delete import vmmDeleteDialog
|
||||||
|
from .details import vmmDetails
|
||||||
|
from .error import vmmErrorDialog
|
||||||
|
from .host import vmmHost
|
||||||
|
from .inspection import vmmInspection
|
||||||
from .manager import vmmManager
|
from .manager import vmmManager
|
||||||
from .migrate import vmmMigrateDialog
|
from .migrate import vmmMigrateDialog
|
||||||
from .details import vmmDetails
|
|
||||||
from .create import vmmCreate
|
|
||||||
from .host import vmmHost
|
|
||||||
from .error import vmmErrorDialog
|
|
||||||
from .systray import vmmSystray
|
from .systray import vmmSystray
|
||||||
from .delete import vmmDeleteDialog
|
|
||||||
|
|
||||||
DETAILS_PERF = 1
|
DETAILS_PERF = 1
|
||||||
DETAILS_CONFIG = 2
|
DETAILS_CONFIG = 2
|
||||||
@ -93,8 +94,7 @@ class vmmEngine(vmmGObject):
|
|||||||
self._tick_thread.daemon = True
|
self._tick_thread.daemon = True
|
||||||
self._tick_queue = queue.PriorityQueue(100)
|
self._tick_queue = queue.PriorityQueue(100)
|
||||||
|
|
||||||
self.inspection = None
|
vmmInspection.get_instance(self)
|
||||||
self._create_inspection_thread()
|
|
||||||
|
|
||||||
# Counter keeping track of how many manager and details windows
|
# Counter keeping track of how many manager and details windows
|
||||||
# are open. When it is decremented to 0, close the app or
|
# are open. When it is decremented to 0, close the app or
|
||||||
@ -394,10 +394,6 @@ class vmmEngine(vmmGObject):
|
|||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
self.err = None
|
self.err = None
|
||||||
|
|
||||||
if self.inspection:
|
|
||||||
self.inspection.cleanup()
|
|
||||||
self.inspection = None
|
|
||||||
|
|
||||||
if self.timer is not None:
|
if self.timer is not None:
|
||||||
GLib.source_remove(self.timer)
|
GLib.source_remove(self.timer)
|
||||||
|
|
||||||
@ -462,19 +458,6 @@ class vmmEngine(vmmGObject):
|
|||||||
logging.debug("Exiting app normally.")
|
logging.debug("Exiting app normally.")
|
||||||
self._application.quit()
|
self._application.quit()
|
||||||
|
|
||||||
def _create_inspection_thread(self):
|
|
||||||
logging.debug("libguestfs inspection support: %s",
|
|
||||||
self.config.support_inspection)
|
|
||||||
if not self.config.support_inspection:
|
|
||||||
return
|
|
||||||
|
|
||||||
from .inspection import vmmInspection
|
|
||||||
self.inspection = vmmInspection()
|
|
||||||
self.inspection.start()
|
|
||||||
self.connect("conn-added", self.inspection.conn_added)
|
|
||||||
self.connect("conn-removed", self.inspection.conn_removed)
|
|
||||||
return
|
|
||||||
|
|
||||||
def _find_error_parent_cb(self):
|
def _find_error_parent_cb(self):
|
||||||
"""
|
"""
|
||||||
Search over the toplevel windows for any that are visible or have
|
Search over the toplevel windows for any that are visible or have
|
||||||
@ -858,12 +841,14 @@ class vmmEngine(vmmGObject):
|
|||||||
src.err.show_err(_("Error setting clone parameters: %s") % str(e))
|
src.err.show_err(_("Error setting clone parameters: %s") % str(e))
|
||||||
|
|
||||||
def _do_refresh_inspection(self, src_ignore, uri, connkey):
|
def _do_refresh_inspection(self, src_ignore, uri, connkey):
|
||||||
if not self.inspection:
|
inspection = vmmInspection.get_instance(self)
|
||||||
|
if not inspection:
|
||||||
return
|
return
|
||||||
|
|
||||||
conn = self._lookup_conn(uri)
|
conn = self._lookup_conn(uri)
|
||||||
vm = conn.get_vm(connkey)
|
vm = conn.get_vm(connkey)
|
||||||
self.inspection.vm_refresh(vm)
|
inspection.vm_refresh(vm)
|
||||||
|
|
||||||
|
|
||||||
##########################################
|
##########################################
|
||||||
# Window launchers from virt-manager cli #
|
# Window launchers from virt-manager cli #
|
||||||
|
@ -17,11 +17,10 @@
|
|||||||
# MA 02110-1301 USA.
|
# MA 02110-1301 USA.
|
||||||
#
|
#
|
||||||
|
|
||||||
from queue import Queue
|
import functools
|
||||||
from threading import Thread
|
|
||||||
import logging
|
import logging
|
||||||
|
import queue
|
||||||
from guestfs import GuestFS # pylint: disable=import-error
|
import threading
|
||||||
|
|
||||||
from .baseclass import vmmGObject
|
from .baseclass import vmmGObject
|
||||||
from .domain import vmmInspectionData
|
from .domain import vmmInspectionData
|
||||||
@ -30,22 +29,55 @@ from .domain import vmmInspectionData
|
|||||||
class vmmInspection(vmmGObject):
|
class vmmInspection(vmmGObject):
|
||||||
# Can't find a way to make Thread release our reference
|
# Can't find a way to make Thread release our reference
|
||||||
_leak_check = False
|
_leak_check = False
|
||||||
|
_instance = None
|
||||||
|
_libguestfs_installed = None
|
||||||
|
|
||||||
def __init__(self):
|
@classmethod
|
||||||
|
def get_instance(cls, engine):
|
||||||
|
if not cls._instance:
|
||||||
|
if not cls.libguestfs_installed():
|
||||||
|
return None
|
||||||
|
cls._instance = cls(engine)
|
||||||
|
return cls._instance
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def libguestfs_installed(cls):
|
||||||
|
if cls._libguestfs_installed is None:
|
||||||
|
try:
|
||||||
|
import guestfs as ignore # pylint: disable=import-error
|
||||||
|
logging.debug("python guestfs is installed")
|
||||||
|
cls._libguestfs_installed = True
|
||||||
|
except ImportError:
|
||||||
|
logging.debug("python guestfs is not installed")
|
||||||
|
cls._libguestfs_installed = False
|
||||||
|
except Exception:
|
||||||
|
logging.debug("error importing guestfs",
|
||||||
|
exc_info=True)
|
||||||
|
cls._libguestfs_installed = False
|
||||||
|
return cls._libguestfs_installed
|
||||||
|
|
||||||
|
def __init__(self, engine):
|
||||||
vmmGObject.__init__(self)
|
vmmGObject.__init__(self)
|
||||||
|
|
||||||
self._thread = Thread(name="inspection thread", target=self._run)
|
self._thread = None
|
||||||
self._thread.daemon = True
|
|
||||||
self._wait = 5 * 1000 # 5 seconds
|
self._wait = 5 * 1000 # 5 seconds
|
||||||
|
|
||||||
self._q = Queue()
|
self._q = queue.Queue()
|
||||||
self._conns = {}
|
self._conns = {}
|
||||||
self._vmseen = {}
|
self._vmseen = {}
|
||||||
self._cached_data = {}
|
self._cached_data = {}
|
||||||
|
|
||||||
|
val = self.config.get_libguestfs_inspect_vms()
|
||||||
|
logging.debug("libguestfs gsetting enabled=%s", str(val))
|
||||||
|
if not val:
|
||||||
|
return
|
||||||
|
engine.connect("conn-added", self._conn_added)
|
||||||
|
engine.connect("conn-removed", self._conn_removed)
|
||||||
|
self._start()
|
||||||
|
|
||||||
def _cleanup(self):
|
def _cleanup(self):
|
||||||
self._thread = None
|
self._stop()
|
||||||
self._q = Queue()
|
self._q = queue.Queue()
|
||||||
self._conns = {}
|
self._conns = {}
|
||||||
self._vmseen = {}
|
self._vmseen = {}
|
||||||
self._cached_data = {}
|
self._cached_data = {}
|
||||||
@ -53,16 +85,16 @@ class vmmInspection(vmmGObject):
|
|||||||
# Called by the main thread whenever a connection is added or
|
# Called by the main thread whenever a connection is added or
|
||||||
# removed. We tell the inspection thread, so it can track
|
# removed. We tell the inspection thread, so it can track
|
||||||
# connections.
|
# connections.
|
||||||
def conn_added(self, engine_ignore, conn):
|
def _conn_added(self, engine_ignore, conn):
|
||||||
obj = ("conn_added", conn)
|
obj = ("conn_added", conn)
|
||||||
self._q.put(obj)
|
self._q.put(obj)
|
||||||
|
|
||||||
def conn_removed(self, engine_ignore, uri):
|
def _conn_removed(self, engine_ignore, uri):
|
||||||
obj = ("conn_removed", uri)
|
obj = ("conn_removed", uri)
|
||||||
self._q.put(obj)
|
self._q.put(obj)
|
||||||
|
|
||||||
# Called by the main thread whenever a VM is added to vmlist.
|
# Called by the main thread whenever a VM is added to vmlist.
|
||||||
def vm_added(self, conn, connkey):
|
def _vm_added(self, conn, connkey):
|
||||||
if connkey.startswith("guestfs-"):
|
if connkey.startswith("guestfs-"):
|
||||||
logging.debug("ignore libvirt/guestfs temporary VM %s",
|
logging.debug("ignore libvirt/guestfs temporary VM %s",
|
||||||
connkey)
|
connkey)
|
||||||
@ -75,58 +107,80 @@ class vmmInspection(vmmGObject):
|
|||||||
obj = ("vm_refresh", vm.conn.get_uri(), vm.get_name(), vm.get_uuid())
|
obj = ("vm_refresh", vm.conn.get_uri(), vm.get_name(), vm.get_uuid())
|
||||||
self._q.put(obj)
|
self._q.put(obj)
|
||||||
|
|
||||||
def start(self):
|
def _start(self):
|
||||||
# Wait a few seconds before we do anything. This prevents
|
if self._thread:
|
||||||
# inspection from being a burden for initial virt-manager
|
return
|
||||||
# interactivity (although it shouldn't affect interactivity at
|
|
||||||
# all).
|
|
||||||
def cb():
|
def cb():
|
||||||
self._thread.start()
|
if self._thread:
|
||||||
|
self._thread.start()
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
logging.debug("waiting")
|
self._thread = threading.Thread(
|
||||||
|
name="inspection thread", target=self._run)
|
||||||
|
self._thread.daemon = True
|
||||||
|
|
||||||
|
# Wait a few seconds before we do anything. This prevents
|
||||||
|
# inspection from being a burden for initial virt-manager
|
||||||
|
# interactivity (although it shouldn't affect interactivity at all)
|
||||||
|
logging.debug("waiting before startup wait=%s", self._wait)
|
||||||
self.timeout_add(self._wait, cb)
|
self.timeout_add(self._wait, cb)
|
||||||
|
|
||||||
|
def _stop(self):
|
||||||
|
if self._thread is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self._q.put(None)
|
||||||
|
self._thread = None
|
||||||
|
|
||||||
def _run(self):
|
def _run(self):
|
||||||
# Process everything on the queue. If the queue is empty when
|
# Process everything on the queue. If the queue is empty when
|
||||||
# called, block.
|
# called, block.
|
||||||
while True:
|
while True:
|
||||||
obj = self._q.get()
|
obj = self._q.get()
|
||||||
|
if obj is None:
|
||||||
|
logging.debug("libguestfs queue obj=None, exiting thread")
|
||||||
|
return
|
||||||
self._process_queue_item(obj)
|
self._process_queue_item(obj)
|
||||||
self._q.task_done()
|
self._q.task_done()
|
||||||
|
|
||||||
def _process_queue_item(self, obj):
|
def _process_queue_item(self, obj):
|
||||||
if obj[0] == "conn_added":
|
cmd = obj[0]
|
||||||
|
if cmd == "conn_added":
|
||||||
conn = obj[1]
|
conn = obj[1]
|
||||||
uri = conn.get_uri()
|
uri = conn.get_uri()
|
||||||
if conn and not (conn.is_remote()) and not (uri in self._conns):
|
if conn.is_remote() or uri in self._conns:
|
||||||
self._conns[uri] = conn
|
return
|
||||||
conn.connect("vm-added", self.vm_added)
|
|
||||||
for vm in conn.list_vms():
|
self._conns[uri] = conn
|
||||||
self.vm_added(conn, vm.get_connkey())
|
conn.connect("vm-added", self._vm_added)
|
||||||
elif obj[0] == "conn_removed":
|
for vm in conn.list_vms():
|
||||||
|
self._vm_added(conn, vm.get_connkey())
|
||||||
|
|
||||||
|
elif cmd == "conn_removed":
|
||||||
uri = obj[1]
|
uri = obj[1]
|
||||||
del self._conns[uri]
|
self._conns.pop(uri)
|
||||||
elif obj[0] == "vm_added" or obj[0] == "vm_refresh":
|
|
||||||
|
elif cmd == "vm_added" or cmd == "vm_refresh":
|
||||||
uri = obj[1]
|
uri = obj[1]
|
||||||
if not (uri in self._conns):
|
if uri not in self._conns:
|
||||||
# This connection disappeared in the meanwhile.
|
# This connection disappeared in the meanwhile.
|
||||||
return
|
return
|
||||||
|
|
||||||
conn = self._conns[uri]
|
conn = self._conns[uri]
|
||||||
if not conn.is_active():
|
vm = conn.get_vm(obj[2])
|
||||||
return
|
|
||||||
connkey = obj[2]
|
|
||||||
vm = conn.get_vm(connkey)
|
|
||||||
if not vm:
|
if not vm:
|
||||||
# The VM was removed in the meanwhile.
|
# The VM was removed in the meanwhile.
|
||||||
return
|
return
|
||||||
if obj[0] == "vm_refresh":
|
|
||||||
|
if cmd == "vm_refresh":
|
||||||
vmuuid = obj[3]
|
vmuuid = obj[3]
|
||||||
# When refreshing the inspection data of a VM,
|
# When refreshing the inspection data of a VM,
|
||||||
# all we need is to remove it from the "seen" cache,
|
# all we need is to remove it from the "seen" cache,
|
||||||
# as the data itself will be replaced once the new
|
# as the data itself will be replaced once the new
|
||||||
# results are available.
|
# results are available.
|
||||||
del self._vmseen[vmuuid]
|
self._vmseen.pop(vmuuid)
|
||||||
|
|
||||||
self._process_vm(conn, vm)
|
self._process_vm(conn, vm)
|
||||||
|
|
||||||
# Try processing a single VM, keeping into account whether it was
|
# Try processing a single VM, keeping into account whether it was
|
||||||
@ -167,12 +221,21 @@ class vmmInspection(vmmGObject):
|
|||||||
logging.exception("%s: exception while processing", prettyvm)
|
logging.exception("%s: exception while processing", prettyvm)
|
||||||
|
|
||||||
def _inspect_vm(self, conn, vm):
|
def _inspect_vm(self, conn, vm):
|
||||||
g = GuestFS(close_on_exit=False)
|
if self._thread is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
import guestfs # pylint: disable=import-error
|
||||||
|
|
||||||
|
g = guestfs.GuestFS(close_on_exit=False)
|
||||||
prettyvm = conn.get_uri() + ":" + vm.get_name()
|
prettyvm = conn.get_uri() + ":" + vm.get_name()
|
||||||
|
try:
|
||||||
g.add_libvirt_dom(vm.get_backend(), readonly=1)
|
g.add_libvirt_dom(vm.get_backend(), readonly=1)
|
||||||
|
g.launch()
|
||||||
g.launch()
|
except Exception as e:
|
||||||
|
logging.debug("%s: Error launching libguestfs appliance: %s",
|
||||||
|
prettyvm, str(e))
|
||||||
|
return None
|
||||||
|
logging.debug("%s: inspection appliance connected", prettyvm)
|
||||||
|
|
||||||
# Inspect the operating system.
|
# Inspect the operating system.
|
||||||
roots = g.inspect_os()
|
roots = g.inspect_os()
|
||||||
@ -210,8 +273,8 @@ class vmmInspection(vmmGObject):
|
|||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
return -1
|
return -1
|
||||||
mps.sort(compare)
|
|
||||||
|
|
||||||
|
mps.sort(key=functools.cmp_to_key(compare))
|
||||||
for mp_dev in mps:
|
for mp_dev in mps:
|
||||||
try:
|
try:
|
||||||
g.mount_ro(mp_dev[1], mp_dev[0])
|
g.mount_ro(mp_dev[1], mp_dev[0])
|
||||||
@ -261,7 +324,7 @@ class vmmInspection(vmmGObject):
|
|||||||
data.product_name = str(product_name)
|
data.product_name = str(product_name)
|
||||||
data.product_variant = str(product_variant)
|
data.product_variant = str(product_variant)
|
||||||
data.icon = icon
|
data.icon = icon
|
||||||
data.applications = list(apps)
|
data.applications = list(apps or [])
|
||||||
data.error = False
|
data.error = False
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -25,6 +25,7 @@ from gi.repository import Gdk
|
|||||||
|
|
||||||
from . import uiutil
|
from . import uiutil
|
||||||
from .baseclass import vmmGObjectUI
|
from .baseclass import vmmGObjectUI
|
||||||
|
from .inspection import vmmInspection
|
||||||
|
|
||||||
|
|
||||||
class vmmPreferences(vmmGObjectUI):
|
class vmmPreferences(vmmGObjectUI):
|
||||||
@ -45,7 +46,10 @@ class vmmPreferences(vmmGObjectUI):
|
|||||||
|
|
||||||
self._init_ui()
|
self._init_ui()
|
||||||
|
|
||||||
|
self._orig_libguestfs_val = None
|
||||||
|
|
||||||
self.refresh_view_system_tray()
|
self.refresh_view_system_tray()
|
||||||
|
self.refresh_libguestfs()
|
||||||
self.refresh_update_interval()
|
self.refresh_update_interval()
|
||||||
self.refresh_console_accels()
|
self.refresh_console_accels()
|
||||||
self.refresh_console_scaling()
|
self.refresh_console_scaling()
|
||||||
@ -73,6 +77,7 @@ class vmmPreferences(vmmGObjectUI):
|
|||||||
"on_prefs_close_clicked": self.close,
|
"on_prefs_close_clicked": self.close,
|
||||||
|
|
||||||
"on_prefs_system_tray_toggled": self.change_view_system_tray,
|
"on_prefs_system_tray_toggled": self.change_view_system_tray,
|
||||||
|
"on_prefs_libguestfs_toggled": self.change_libguestfs,
|
||||||
"on_prefs_stats_update_interval_changed": self.change_update_interval,
|
"on_prefs_stats_update_interval_changed": self.change_update_interval,
|
||||||
"on_prefs_console_accels_toggled": self.change_console_accels,
|
"on_prefs_console_accels_toggled": self.change_console_accels,
|
||||||
"on_prefs_console_scaling_changed": self.change_console_scaling,
|
"on_prefs_console_scaling_changed": self.change_console_scaling,
|
||||||
@ -181,6 +186,11 @@ class vmmPreferences(vmmGObjectUI):
|
|||||||
combo.set_model(model)
|
combo.set_model(model)
|
||||||
uiutil.init_combo_text_column(combo, 1)
|
uiutil.init_combo_text_column(combo, 1)
|
||||||
|
|
||||||
|
if not vmmInspection.libguestfs_installed():
|
||||||
|
self.widget("prefs-libguestfs").set_sensitive(False)
|
||||||
|
self.widget("prefs-libguestfs").set_tooltip_text(
|
||||||
|
_("python libguestfs support is not installed"))
|
||||||
|
|
||||||
|
|
||||||
#########################
|
#########################
|
||||||
# Config Change Options #
|
# Config Change Options #
|
||||||
@ -190,6 +200,12 @@ class vmmPreferences(vmmGObjectUI):
|
|||||||
val = self.config.get_view_system_tray()
|
val = self.config.get_view_system_tray()
|
||||||
self.widget("prefs-system-tray").set_active(bool(val))
|
self.widget("prefs-system-tray").set_active(bool(val))
|
||||||
|
|
||||||
|
def refresh_libguestfs(self):
|
||||||
|
val = self.config.get_libguestfs_inspect_vms()
|
||||||
|
if self._orig_libguestfs_val is None:
|
||||||
|
self._orig_libguestfs_val = val
|
||||||
|
self.widget("prefs-libguestfs").set_active(bool(val))
|
||||||
|
|
||||||
def refresh_update_interval(self):
|
def refresh_update_interval(self):
|
||||||
self.widget("prefs-stats-update-interval").set_value(
|
self.widget("prefs-stats-update-interval").set_value(
|
||||||
self.config.get_stats_update_interval())
|
self.config.get_stats_update_interval())
|
||||||
@ -341,6 +357,14 @@ class vmmPreferences(vmmGObjectUI):
|
|||||||
|
|
||||||
def change_view_system_tray(self, src):
|
def change_view_system_tray(self, src):
|
||||||
self.config.set_view_system_tray(src.get_active())
|
self.config.set_view_system_tray(src.get_active())
|
||||||
|
def change_libguestfs(self, src):
|
||||||
|
val = src.get_active()
|
||||||
|
self.config.set_libguestfs_inspect_vms(val)
|
||||||
|
|
||||||
|
vis = (val != self._orig_libguestfs_val and
|
||||||
|
self.widget("prefs-libguestfs").get_sensitive())
|
||||||
|
uiutil.set_grid_row_visible(
|
||||||
|
self.widget("prefs-libguestfs-warn-box"), vis)
|
||||||
|
|
||||||
def change_update_interval(self, src):
|
def change_update_interval(self, src):
|
||||||
self.config.set_stats_update_interval(src.get_value_as_int())
|
self.config.set_stats_update_interval(src.get_value_as_int())
|
||||||
|
Loading…
Reference in New Issue
Block a user