[virt-tools-list] [PATCH 1/4] Add inspection thread.

Richard W.M. Jones rjones at redhat.com
Thu Jun 30 08:02:22 UTC 2011


From: "Richard W.M. Jones" <rjones at redhat.com>

This commit adds an inspection daemon thread which performs libguestfs
inspection on domains when they first appear in the manager UI.

python-guestfs is not required.
---
 src/virtManager/config.py     |   25 ++++++
 src/virtManager/engine.py     |   15 ++++
 src/virtManager/inspection.py |  182 +++++++++++++++++++++++++++++++++++++++++
 src/virtManager/manager.py    |    3 +
 4 files changed, 225 insertions(+), 0 deletions(-)
 create mode 100644 src/virtManager/inspection.py

diff --git a/src/virtManager/config.py b/src/virtManager/config.py
index 791ef4d..d3c3155 100644
--- a/src/virtManager/config.py
+++ b/src/virtManager/config.py
@@ -113,6 +113,31 @@ class vmmConfig(object):
         self._objects = []
 
         self.support_threading = virtinst.support.support_threading()
+
+        # Check that we have the guestfs module with the non-broken
+        # support for Python threads.  Also libvirt must be
+        # thread-safe too.
+        self.support_inspection = False
+        if self.support_threading:
+            try:
+                from guestfs import GuestFS
+                g = GuestFS()
+                version = g.version()
+                if version["major"] == 1: # major must be 1
+                    if version["minor"] == 8:
+                        if version["release"] >= 6: # >= 1.8.6
+                            self.support_inspection = True
+                    elif version["minor"] == 10:
+                        if version["release"] >= 1: # >= 1.10.1
+                            self.support_inspection = True
+                    elif version["minor"] == 11:
+                        if version["release"] >= 2: # >= 1.11.2
+                            self.support_inspection = True
+                    elif version["minor"] >= 12:    # >= 1.12.x
+                        self.support_inspection = True
+            except:
+                pass
+
         self._spice_error = None
 
         self.status_icons = {
diff --git a/src/virtManager/engine.py b/src/virtManager/engine.py
index 3ae1f7a..3d0cbff 100644
--- a/src/virtManager/engine.py
+++ b/src/virtManager/engine.py
@@ -236,6 +236,9 @@ class vmmEngine(vmmGObject):
         if not self.config.support_threading:
             logging.debug("Libvirt doesn't support threading, skipping.")
 
+        self.inspection_thread = None
+        self._create_inspection_thread()
+
         # Counter keeping track of how many manager and details windows
         # are open. When it is decremented to 0, close the app or
         # keep running in system tray if enabled
@@ -529,6 +532,18 @@ class vmmEngine(vmmGObject):
         logging.debug("Exiting app normally.")
         gtk.main_quit()
 
+    def _create_inspection_thread(self):
+        if not self.config.support_inspection:
+            logging.debug("No inspection thread because "
+                          "libguestfs is too old, not available, "
+                          "or libvirt is not thread safe.")
+            return
+        from virtManager.inspection import vmmInspection
+        self.inspection_thread = vmmInspection()
+        self.inspection_thread.daemon = True
+        self.inspection_thread.start()
+        return
+
     def add_connection(self, uri, readOnly=None, autoconnect=False):
         conn = self._check_connection(uri)
         if conn:
diff --git a/src/virtManager/inspection.py b/src/virtManager/inspection.py
new file mode 100644
index 0000000..146e021
--- /dev/null
+++ b/src/virtManager/inspection.py
@@ -0,0 +1,182 @@
+#
+# Copyright (C) 2011 Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+# MA 02110-1301 USA.
+#
+
+import sys
+import time
+import traceback
+from Queue import Queue
+from threading import Thread
+
+from guestfs import GuestFS
+
+import logging
+import util
+
+class vmmInspection(Thread):
+    _name = "inspection thread"
+    _wait = 15 # seconds
+
+    def __init__(self):
+        Thread.__init__(self, name=self._name)
+        self._q = Queue()
+        self._vmseen = dict()
+
+    # Called by the main thread whenever a VM is added to vmlist.
+    def vm_added(self, connection, vmuuid):
+        obj = (connection, vmuuid)
+        self._q.put(obj)
+
+    def run(self):
+        # 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("%s: waiting" % self._name)
+        time.sleep(self._wait)
+
+        logging.debug("%s: ready" % self._name)
+
+        while True:
+            obj = self._q.get(True)
+            (connection, vmuuid) = obj
+
+            logging.debug("%s: processing started on '%s'" %
+                          (self._name, vmuuid))
+            try:
+                self._process(connection, vmuuid)
+            except:
+                logging.debug("%s: exception while processing '%s':\n%s" %
+                              (self._name, vmuuid, traceback.format_exc()))
+
+            self._q.task_done()
+            logging.debug("%s: processing done on '%s'" %
+                          (self._name, vmuuid))
+
+    def _process(self, connection, vmuuid):
+        if not connection or connection.is_remote():
+            logging.debug("%s: %s: no connection object or "
+                          "connection is remote" % (self._name, vmuuid))
+            return
+        vm = connection.get_vm(vmuuid)
+
+        # If we've already inspected this VM, don't do it again.  Note
+        # that we must filter here (not before adding to the queue in
+        # the main thread) because of the case where the VM has
+        # already been enqueued but the results are not yet in the
+        # cache.
+        if vmuuid in self._vmseen: return
+
+        xml = vm.get_xml()
+
+        # Add the disks.  Note they *must* be added with readonly flag set.
+        g = GuestFS()
+        for disk in vm.get_disk_devices():
+            path = disk.path
+            driver_type = disk.driver_type
+            if (disk.type == "block" or disk.type == "file") and path != None:
+                g.add_drive_opts(path, readonly=1, format=driver_type)
+
+        g.launch()
+
+        # Inspect the operating system.
+        roots = g.inspect_os()
+        if len(roots) == 0:
+            logging.debug("%s: %s: no operating systems found" %
+                          (self._name, vmuuid))
+            return
+
+        # Arbitrarily pick the first root device.
+        root = roots[0]
+
+        # Inspection results.
+        typ = g.inspect_get_type(root) # eg. "linux"
+        distro = g.inspect_get_distro(root) # eg. "fedora"
+        major_version = g.inspect_get_major_version(root) # eg. 14
+        minor_version = g.inspect_get_minor_version(root) # eg. 0
+        hostname = g.inspect_get_hostname(root) # string
+        product_name = g.inspect_get_product_name(root) # string
+
+        # Added in libguestfs 1.9.13:
+        try:
+            product_variant = g.inspect_get_product_variant(root) # string
+        except AttributeError:
+            pass
+
+        # For inspect_list_applications and inspect_get_icon we
+        # require that the guest filesystems are mounted.  However
+        # don't fail if this is not possible (I'm looking at you,
+        # FreeBSD).
+        filesystems_mounted = False
+        try:
+            # Mount up the disks, like guestfish --ro -i.
+
+            # Sort keys by length, shortest first, so that we end up
+            # mounting the filesystems in the correct order.
+            mps = g.inspect_get_mountpoints (root)
+            def compare (a, b):
+                if len(a[0]) > len(b[0]):
+                    return 1
+                elif len(a[0]) == len(b[0]):
+                    return 0
+                else:
+                    return -1
+            mps.sort (compare)
+
+            for mp_dev in mps:
+                try:
+                    g.mount_ro (mp_dev[1], mp_dev[0])
+                except:
+                    logging.debug ("%s: exception mounting %s on %s "
+                                   "(ignored):\n%s" % (
+                            self._name,
+                            mp_dev[1], mp_dev[0],
+                            traceback.format_exc()))
+
+            filesystems_mounted = True
+        except:
+            logging.debug("%s: exception while mounting disks (ignored):\n%s" %
+                          (self._name, vmuuid, traceback.format_exc()))
+
+        if filesystems_mounted:
+            # Added in libguestfs 1.11.12:
+            try:
+                # string containing PNG data
+                icon = g.inspect_get_icon(root, favicon=0, highquality=1)
+                if icon == "":
+                    icon = None
+            except AttributeError:
+                pass
+
+            # Inspection applications.
+            apps = g.inspect_list_applications(root)
+
+        # Force the libguestfs handle to close right now.
+        del g
+
+        self._vmseen[vmuuid] = True
+
+        # Log what we found.
+        logging.debug("%s: detected operating system: %s %s %d.%d (%s)",
+                      self._name, typ, distro, major_version, minor_version,
+                      product_name)
+        logging.debug("%s: hostname: %s", self._name, hostname)
+        if icon:
+            logging.debug("%s: icon: %d bytes", self._name, len(icon))
+        if apps:
+            logging.debug("%s: # apps: %d", self._name, len(apps))
diff --git a/src/virtManager/manager.py b/src/virtManager/manager.py
index b1837ee..c7cca67 100644
--- a/src/virtManager/manager.py
+++ b/src/virtManager/manager.py
@@ -685,6 +685,9 @@ class vmmManager(vmmGObjectUI):
 
         self._append_vm(model, vm, connection)
 
+        if self.engine.inspection_thread:
+            self.engine.inspection_thread.vm_added(connection, vmuuid)
+
     def vm_removed(self, connection, uri_ignore, vmuuid):
         vmlist = self.window.get_widget("vm-list")
         model = vmlist.get_model()
-- 
1.7.5.2




More information about the virt-tools-list mailing list