[virt-tools-list] [PATCH 1/4] Add inspection thread.
Richard W.M. Jones
rjones at redhat.com
Mon Jul 18 18:53:54 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 | 31 ++++++
src/virtManager/engine.py | 17 ++++
src/virtManager/inspection.py | 204 +++++++++++++++++++++++++++++++++++++++++
src/virtManager/manager.py | 3 +
4 files changed, 255 insertions(+), 0 deletions(-)
create mode 100644 src/virtManager/inspection.py
diff --git a/src/virtManager/config.py b/src/virtManager/config.py
index d28b083..d7da236 100644
--- a/src/virtManager/config.py
+++ b/src/virtManager/config.py
@@ -104,8 +104,39 @@ class vmmConfig(object):
self._objects = []
self.support_threading = virtinst.support.support_threading()
+
+ self.support_inspection = self.check_inspection(self.support_threading)
+
self._spice_error = None
+ def check_inspection(self, support_threading):
+ if not support_threading:
+ return False
+
+ try:
+ # Check we can open the Python guestfs module.
+ from guestfs import GuestFS
+ g = GuestFS()
+
+ # Check for the first version which fixed Python GIL bug.
+ version = g.version()
+ if version["major"] == 1: # major must be 1
+ if version["minor"] == 8:
+ if version["release"] >= 6: # >= 1.8.6
+ return True
+ elif version["minor"] == 10:
+ if version["release"] >= 1: # >= 1.10.1
+ return True
+ elif version["minor"] == 11:
+ if version["release"] >= 2: # >= 1.11.2
+ return True
+ elif version["minor"] >= 12: # >= 1.12, 1.13, etc.
+ return True
+ except:
+ pass
+
+ return False
+
# General app wide helpers (gconf agnostic)
def get_shutdown_icon_name(self):
diff --git a/src/virtManager/engine.py b/src/virtManager/engine.py
index dbbb8bd..a310b8e 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
@@ -531,6 +534,20 @@ 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()
+ self.connect("connection-added", self.inspection_thread.conn_added)
+ self.connect("connection-removed", self.inspection_thread.conn_removed)
+ return
+
def add_connection(self, uri):
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..99b225f
--- /dev/null
+++ b/src/virtManager/inspection.py
@@ -0,0 +1,204 @@
+#
+# 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 time
+from Queue import Queue, Empty
+from threading import Thread
+
+from guestfs import GuestFS
+
+import logging
+
+class vmmInspection(Thread):
+ _name = "inspection thread"
+ _wait = 15 # seconds
+
+ def __init__(self):
+ Thread.__init__(self, name=self._name)
+ self._q = Queue()
+ self._conns = dict()
+ self._vmseen = dict()
+
+ # Called by the main thread whenever a connection is added or
+ # removed. We tell the inspection thread, so it can track
+ # connections.
+ def conn_added(self, engine_ignore, conn):
+ obj = ("conn_added", conn)
+ self._q.put(obj)
+
+ def conn_removed(self, engine_ignore, uri):
+ obj = ("conn_removed", uri)
+ self._q.put(obj)
+
+ # Called by the main thread whenever a VM is added to vmlist.
+ def vm_added(self):
+ obj = ("vm_added")
+ 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)
+
+ while True:
+ logging.debug("%s: ready" % self._name)
+ self._process_queue()
+ self._process_vms()
+
+ # Process everything on the queue. If the queue is empty when
+ # called, block.
+ def _process_queue(self):
+ first_obj = self._q.get()
+ self._process_queue_item(first_obj)
+ self._q.task_done()
+ try:
+ while True:
+ obj = self._q.get(False)
+ self._process_queue_item(obj)
+ self._q.task_done()
+ except Empty:
+ pass
+
+ def _process_queue_item(self, obj):
+ if obj[0] == "conn_added":
+ conn = obj[1]
+ if conn and not (conn.is_remote()):
+ uri = conn.get_uri()
+ self._conns[uri] = conn
+ elif obj[0] == "conn_removed":
+ uri = obj[1]
+ del self._conns[uri]
+ elif obj[0] == "vm_added":
+ # Nothing - just a signal for the inspection thread to wake up.
+ pass
+
+ # Any VMs we've not seen yet? If so, process them.
+ def _process_vms(self):
+ for conn in self._conns.itervalues():
+ for vmuuid in conn.list_vm_uuids():
+ if not (vmuuid in self._vmseen):
+ logging.debug("%s: processing started on '%s'" %
+ (self._name, vmuuid))
+ try:
+ vm = conn.get_vm(vmuuid)
+ # Whether success or failure, we've "seen" this VM now.
+ self._vmseen[vmuuid] = True
+ self._process(conn, vm, vmuuid)
+ except:
+ logging.exception("%s: exception while processing '%s'" %
+ (self._name, vmuuid))
+ logging.debug("%s: processing done on '%s'" %
+ (self._name, vmuuid))
+
+ def _process(self, conn_ignore, vm, vmuuid):
+ # 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:
+ product_variant = None
+ if hasattr(g, "inspect_get_product_variant"):
+ product_variant = g.inspect_get_product_variant(root) # string
+
+ # 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.exception("%s: exception mounting %s on %s "
+ "(ignored)" % (
+ self._name,
+ mp_dev[1], mp_dev[0]))
+
+ filesystems_mounted = True
+ except:
+ logging.exception("%s: exception while mounting disks (ignored)" %
+ self._name)
+
+ icon = None
+ apps = None
+ if filesystems_mounted:
+ # Added in libguestfs 1.11.12:
+ if hasattr(g, "inspect_get_icon"):
+ # string containing PNG data
+ icon = g.inspect_get_icon(root, favicon=0, highquality=1)
+ if icon == "":
+ icon = None
+
+ # Inspection applications.
+ apps = g.inspect_list_applications(root)
+
+ # Force the libguestfs handle to close right now.
+ del g
+
+ # 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 0cffa21..f10879d 100644
--- a/src/virtManager/manager.py
+++ b/src/virtManager/manager.py
@@ -682,6 +682,9 @@ class vmmManager(vmmGObjectUI):
self._append_vm(model, vm, connection)
+ if self.engine.inspection_thread:
+ self.engine.inspection_thread.vm_added()
+
def vm_removed(self, connection, uri_ignore, vmuuid):
vmlist = self.widget("vm-list")
model = vmlist.get_model()
--
1.7.5.2
More information about the virt-tools-list
mailing list