diff mbox series

Devtool: Make qmp detection for qemurunner more robust.

Message ID 20260106233539.1886869-2-t.f.g.geelen@gmail.com
State Changes Requested
Headers show
Series Devtool: Make qmp detection for qemurunner more robust. | expand

Commit Message

Tom Geelen Jan. 6, 2026, 11:35 p.m. UTC
As discussed here: https://lists.yoctoproject.org/g/yocto-patches/message/2762 the detection of QMP is currently based on the fact that it expects `qmp` 
to always be available under the same directory as where the logfiles are stored. 
However tools like AUH might adjust TEST_LOG_DIR to another location. This causes qemurunner to no longer boot as it cannot find qmp anymore.

This patch addresses this by using the available information with qemurunner:
- we have the rootfs name for the qemu image
- we have the folder structure where we expect qmp to be present.
Based on this we can construct the path where qmp is actually located.

For backwards-compatibility the old path detection is:
- still working
- always tried first before the new method.

Signed-off-by: Tom Geelen <t.f.g.geelen@gmail.com>
---
 meta/lib/oeqa/utils/qemurunner.py | 39 +++++++++++++++++++++++--------
 1 file changed, 29 insertions(+), 10 deletions(-)

Comments

Alexander Kanavin Jan. 7, 2026, 11:05 a.m. UTC | #1
On Wed, 7 Jan 2026 at 00:36, Tom Geelen <t.f.g.geelen@gmail.com> wrote:
>      def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, env = None):
> -        # use logfile to determine the recipe-sysroot-native path and
> -        # then add in the site-packages path components and add that
> -        # to the python sys.path so the qmp module can be found.
> -        python_path = os.path.dirname(os.path.dirname(self.logfile))
> -        python_path += "/recipe-sysroot-native/usr/lib/qemu-python"
> -        sys.path.append(python_path)
> +        # Discover qmp module location robustly.
> +        # 1) Try path derived from logfile (historical behavior)
> +        # 2) Fallback: search TMPDIR/work/*/<rootfs>/recipe-sysroot-4native for qemu-python or site-packages
> +        candidates = []
> +        base_from_log = os.path.dirname(os.path.dirname(self.logfile))
> +        candidates.append(os.path.join(base_from_log, "recipe-sysroot-native", "usr", "lib", "qemu-python"))
> +
> +        # We can deduce the location for qmp also from the image which is being called as we know:
> +        # - this will have qmp in recipe-sysroot-native
> +        # - this will need to be called as this will derive TESTIMAGE
> +        for p in glob.glob(os.path.join(self.tmpdir, "work", "*", os.path.basename(self.rootfs).split('.')[0], "*", "recipe-sysroot-native", "usr", "lib", "qemu-python")):
> +            candidates.append(p)
> +        # Print all candidates to the debugger
> +        for candidate in candidates:
> +            self.logger.debug("qmp candidates : %s", candidate)

Apologies, but I can't agree to this. It's a lot of guessing against
hardcoded path elements, which can go wrong in various ways. The
correct fix is to pass in the correct native sysroot path explicitly
from further up, how to start with doing that was previously
discussed:

https://lists.yoctoproject.org/g/yocto-patches/message/2762

This bit:

"I'd say in this situation AUH is right, and the code in point 1 is
wrong. It needs to figure out the path to qmp module directly from
WORKDIR, but I don't know yet which of the many parameters to
QemuRunner's constructor and launch() method can be used. Can you look
into it? You can tweak the code to print them all, and see if
something suitable is in there."

Alex
Tom Geelen Jan. 7, 2026, 11:31 a.m. UTC | #2
Okay, no problem. I'll think of an alternative. I was under the impression
that the directory structure onder work was fixed enough for this solution
as the only 'guesing' it does is the folder directly under work (arch).


The launch method does not have anything useful to tackle this, so I'll
start to see if I can inject something from the calling function.

I'll send an update soon

Tom

Op wo 7 jan 2026, 12:05 schreef Alexander Kanavin <alex.kanavin@gmail.com>:

> On Wed, 7 Jan 2026 at 00:36, Tom Geelen <t.f.g.geelen@gmail.com> wrote:
> >      def launch(self, launch_cmd, get_ip = True, qemuparams = None,
> extra_bootparams = None, env = None):
> > -        # use logfile to determine the recipe-sysroot-native path and
> > -        # then add in the site-packages path components and add that
> > -        # to the python sys.path so the qmp module can be found.
> > -        python_path = os.path.dirname(os.path.dirname(self.logfile))
> > -        python_path += "/recipe-sysroot-native/usr/lib/qemu-python"
> > -        sys.path.append(python_path)
> > +        # Discover qmp module location robustly.
> > +        # 1) Try path derived from logfile (historical behavior)
> > +        # 2) Fallback: search
> TMPDIR/work/*/<rootfs>/recipe-sysroot-4native for qemu-python or
> site-packages
> > +        candidates = []
> > +        base_from_log = os.path.dirname(os.path.dirname(self.logfile))
> > +        candidates.append(os.path.join(base_from_log,
> "recipe-sysroot-native", "usr", "lib", "qemu-python"))
> > +
> > +        # We can deduce the location for qmp also from the image which
> is being called as we know:
> > +        # - this will have qmp in recipe-sysroot-native
> > +        # - this will need to be called as this will derive TESTIMAGE
> > +        for p in glob.glob(os.path.join(self.tmpdir, "work", "*",
> os.path.basename(self.rootfs).split('.')[0], "*", "recipe-sysroot-native",
> "usr", "lib", "qemu-python")):
> > +            candidates.append(p)
> > +        # Print all candidates to the debugger
> > +        for candidate in candidates:
> > +            self.logger.debug("qmp candidates : %s", candidate)
>
> Apologies, but I can't agree to this. It's a lot of guessing against
> hardcoded path elements, which can go wrong in various ways. The
> correct fix is to pass in the correct native sysroot path explicitly
> from further up, how to start with doing that was previously
> discussed:
>
> https://lists.yoctoproject.org/g/yocto-patches/message/2762
>
> This bit:
>
> "I'd say in this situation AUH is right, and the code in point 1 is
> wrong. It needs to figure out the path to qmp module directly from
> WORKDIR, but I don't know yet which of the many parameters to
> QemuRunner's constructor and launch() method can be used. Can you look
> into it? You can tweak the code to print them all, and see if
> something suitable is in there."
>
> Alex
>
diff mbox series

Patch

diff --git a/meta/lib/oeqa/utils/qemurunner.py b/meta/lib/oeqa/utils/qemurunner.py
index c4db0cf038..579f7a572b 100644
--- a/meta/lib/oeqa/utils/qemurunner.py
+++ b/meta/lib/oeqa/utils/qemurunner.py
@@ -20,6 +20,7 @@  import string
 import threading
 import codecs
 import tempfile
+import glob
 from collections import defaultdict
 from contextlib import contextmanager
 import importlib
@@ -186,17 +187,35 @@  class QemuRunner:
         return self.launch(launch_cmd, qemuparams=qemuparams, get_ip=get_ip, extra_bootparams=extra_bootparams, env=env)
 
     def launch(self, launch_cmd, get_ip = True, qemuparams = None, extra_bootparams = None, env = None):
-        # use logfile to determine the recipe-sysroot-native path and
-        # then add in the site-packages path components and add that
-        # to the python sys.path so the qmp module can be found.
-        python_path = os.path.dirname(os.path.dirname(self.logfile))
-        python_path += "/recipe-sysroot-native/usr/lib/qemu-python"
-        sys.path.append(python_path)
+        # Discover qmp module location robustly.
+        # 1) Try path derived from logfile (historical behavior)
+        # 2) Fallback: search TMPDIR/work/*/<rootfs>/recipe-sysroot-4native for qemu-python or site-packages
+        candidates = []
+        base_from_log = os.path.dirname(os.path.dirname(self.logfile))
+        candidates.append(os.path.join(base_from_log, "recipe-sysroot-native", "usr", "lib", "qemu-python"))
+
+        # We can deduce the location for qmp also from the image which is being called as we know:
+        # - this will have qmp in recipe-sysroot-native
+        # - this will need to be called as this will derive TESTIMAGE
+        for p in glob.glob(os.path.join(self.tmpdir, "work", "*", os.path.basename(self.rootfs).split('.')[0], "*", "recipe-sysroot-native", "usr", "lib", "qemu-python")):
+            candidates.append(p)
+        # Print all candidates to the debugger
+        for candidate in candidates:
+            self.logger.debug("qmp candidates : %s", candidate)
+
         importlib.invalidate_caches()
-        try:
-            qmp = importlib.import_module("qmp")
-        except Exception as e:
-            self.logger.error("qemurunner: qmp module missing, please ensure it's installed in %s (%s)" % (python_path, str(e)))
+        qmp = None
+        for candidate in candidates:
+            if os.path.isdir(candidate):
+                try:
+                    sys.path.append(candidate)
+                    qmp = importlib.import_module("qmp")
+                    self.logger.debug("qemurunner: loaded qmp from %s" % candidate)
+                    break
+                except Exception:
+                    continue
+        if not qmp:
+            self.logger.error("qemurunner: qmp module missing; tried paths: %s" % ", ".join(candidates))
             return False
         # Path relative to tmpdir used as cwd for qemu below to avoid unix socket path length issues
         qmp_file = "." + next(tempfile._get_candidate_names())