diff --git a/meta/lib/oeqa/runtime/cases/ltp.py b/meta/lib/oeqa/runtime/cases/ltp.py
index b855e76907f..4a7400cbc15 100644
--- a/meta/lib/oeqa/runtime/cases/ltp.py
+++ b/meta/lib/oeqa/runtime/cases/ltp.py
@@ -5,57 +5,18 @@
 # SPDX-License-Identifier: GPL-2.0-only
 #
 
+import os
 import time
-import datetime
 import pprint
 
-from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.runtime.utils.ltp import LtpTestBase
 from oeqa.core.decorator.depends import OETestDepends
 from oeqa.runtime.decorator.package import OEHasPackage
 from oeqa.utils.logparser import LtpParser
 
-class LtpTestBase(OERuntimeTestCase):
-
-    @classmethod
-    def setUpClass(cls):
-        cls.ltp_startup()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.ltp_finishup()
-
-    @classmethod
-    def ltp_startup(cls):
-        cls.sections = {}
-        cls.failmsg = ""
-        test_log_dir = os.path.join(cls.td.get('WORKDIR', ''), 'testimage')
-        timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
-
-        cls.ltptest_log_dir_link = os.path.join(test_log_dir, 'ltp_log')
-        cls.ltptest_log_dir = '%s.%s' % (cls.ltptest_log_dir_link, timestamp)
-        os.makedirs(cls.ltptest_log_dir)
-
-        cls.tc.target.run("mkdir -p /opt/ltp/results")
-
-        if not hasattr(cls.tc, "extraresults"):
-            cls.tc.extraresults = {}
-        cls.extras = cls.tc.extraresults
-        cls.extras['ltpresult.rawlogs'] = {'log': ""}
-
- 
-    @classmethod
-    def ltp_finishup(cls):
-        cls.extras['ltpresult.sections'] =  cls.sections
-
-        # update symlink to ltp_log
-        if os.path.exists(cls.ltptest_log_dir_link):
-            os.remove(cls.ltptest_log_dir_link)
-        os.symlink(os.path.basename(cls.ltptest_log_dir), cls.ltptest_log_dir_link)
-
-        if cls.failmsg:
-            cls.fail(cls.failmsg)
-
 class LtpTest(LtpTestBase):
+    ltp_log_name = 'ltp_log'
+    ltp_result_prefix = 'ltpresult'
 
     ltp_groups = ["math", "syscalls", "dio", "mm", "sched", "nptl", "containers", "controllers", "fcntl-locktests", "commands", "net.ipv6_lib", "input","fs_perms_simple", "cve", "crypto", "ima", "net.nfs", "net_stress.ipsec_icmp", "net.ipv6", "numa", "uevent", "ltp-aiodio.part1", "ltp-aiodio.part2", "ltp-aiodio.part3", "ltp-aiodio.part4"]
 
diff --git a/meta/lib/oeqa/runtime/cases/ltp_compliance.py b/meta/lib/oeqa/runtime/cases/ltp_compliance.py
index ba47c78fd46..f16750a791c 100644
--- a/meta/lib/oeqa/runtime/cases/ltp_compliance.py
+++ b/meta/lib/oeqa/runtime/cases/ltp_compliance.py
@@ -5,58 +5,19 @@
 # SPDX-License-Identifier: GPL-2.0-only
 #
 
+import os
 import time
-import datetime
 import pprint
 
-from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.runtime.utils.ltp import LtpTestBase
 from oeqa.core.decorator.depends import OETestDepends
 from oeqa.runtime.decorator.package import OEHasPackage
 from oeqa.utils.logparser import LtpComplianceParser
 
-class LtpPosixBase(OERuntimeTestCase):
+class LtpPosixTest(LtpTestBase):
+    ltp_log_name = 'ltpcomp_log'
+    ltp_result_prefix = 'ltpposixresult'
 
-    @classmethod
-    def setUpClass(cls):
-        cls.ltp_startup()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.ltp_finishup()
-
-    @classmethod
-    def ltp_startup(cls):
-        cls.sections = {}
-        cls.failmsg = ""
-        test_log_dir = os.path.join(cls.td.get('WORKDIR', ''), 'testimage')
-        timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
-
-        cls.ltptest_log_dir_link = os.path.join(test_log_dir, 'ltpcomp_log')
-        cls.ltptest_log_dir = '%s.%s' % (cls.ltptest_log_dir_link, timestamp)
-        os.makedirs(cls.ltptest_log_dir)
-
-        cls.tc.target.run("mkdir -p /opt/ltp/results")
-
-        if not hasattr(cls.tc, "extraresults"):
-            cls.tc.extraresults = {}
-        cls.extras = cls.tc.extraresults
-        cls.extras['ltpposixresult.rawlogs'] = {'log': ""}
-
- 
-    @classmethod
-    def ltp_finishup(cls):
-        cls.extras['ltpposixresult.sections'] =  cls.sections
-
-        # update symlink to ltp_log
-        if os.path.exists(cls.ltptest_log_dir_link):
-            os.remove(cls.ltptest_log_dir_link)
-
-        os.symlink(os.path.basename(cls.ltptest_log_dir), cls.ltptest_log_dir_link)
-
-        if cls.failmsg:
-            cls.fail(cls.failmsg)
-
-class LtpPosixTest(LtpPosixBase):
     posix_groups = ["AIO", "MEM", "MSG", "SEM", "SIG",  "THR", "TMR", "TPS"]
 
     def runltp(self, posix_group):
diff --git a/meta/lib/oeqa/runtime/cases/ltp_stress.py b/meta/lib/oeqa/runtime/cases/ltp_stress.py
index c902486eabf..e52d64c662b 100644
--- a/meta/lib/oeqa/runtime/cases/ltp_stress.py
+++ b/meta/lib/oeqa/runtime/cases/ltp_stress.py
@@ -5,59 +5,19 @@
 # SPDX-License-Identifier: MIT
 #
 
+import os
 import time
-import datetime
 import pprint
 
-from oeqa.runtime.case import OERuntimeTestCase
+from oeqa.runtime.utils.ltp import LtpTestBase
 from oeqa.core.decorator.depends import OETestDepends
 from oeqa.runtime.decorator.package import OEHasPackage
 from oeqa.core.decorator.data import skipIfQemu
 from oeqa.utils.logparser import LtpParser
 
-class LtpStressBase(OERuntimeTestCase):
-
-    @classmethod
-    def setUpClass(cls):
-        cls.ltp_startup()
-
-    @classmethod
-    def tearDownClass(cls):
-        cls.ltp_finishup()
-
-    @classmethod
-    def ltp_startup(cls):
-        cls.sections = {}
-        cls.failmsg = ""
-        test_log_dir = os.path.join(cls.td.get('WORKDIR', ''), 'testimage')
-        timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
-
-        cls.ltptest_log_dir_link = os.path.join(test_log_dir, 'ltpstress_log')
-        cls.ltptest_log_dir = '%s.%s' % (cls.ltptest_log_dir_link, timestamp)
-        os.makedirs(cls.ltptest_log_dir)
-
-        cls.tc.target.run("mkdir -p /opt/ltp/results")
-
-        if not hasattr(cls.tc, "extraresults"):
-            cls.tc.extraresults = {}
-        cls.extras = cls.tc.extraresults
-        cls.extras['ltpstressresult.rawlogs'] = {'log': ""}
-
- 
-    @classmethod
-    def ltp_finishup(cls):
-        cls.extras['ltpstressresult.sections'] =  cls.sections
-
-        # update symlink to ltp_log
-        if os.path.exists(cls.ltptest_log_dir_link):
-            os.remove(cls.ltptest_log_dir_link)
-
-        os.symlink(os.path.basename(cls.ltptest_log_dir), cls.ltptest_log_dir_link)
-
-        if cls.failmsg:
-            cls.fail(cls.failmsg)
-
-class LtpStressTest(LtpStressBase):
+class LtpStressTest(LtpTestBase):
+    ltp_log_name = 'ltpstress_log'
+    ltp_result_prefix = 'ltpstressresult'
 
     def runltp(self, stress_group):
             cmd = 'kirk --run-suite %s --json-report /opt/ltp/results/%s.json -n -d /opt/ltp' % (stress_group, stress_group)
diff --git a/meta/lib/oeqa/runtime/utils/ltp.py b/meta/lib/oeqa/runtime/utils/ltp.py
new file mode 100644
index 00000000000..e3aea4ead2f
--- /dev/null
+++ b/meta/lib/oeqa/runtime/utils/ltp.py
@@ -0,0 +1,59 @@
+#
+# Copyright OpenEmbedded Contributors
+#
+# SPDX-License-Identifier: MIT
+#
+
+import datetime
+import os
+
+from oeqa.runtime.case import OERuntimeTestCase
+
+class LtpTestBase(OERuntimeTestCase):
+    """
+    Common setup and teardown for the LTP based test cases. Subclasses set
+    ltp_log_name, the name of the log directory symlink in the test log
+    directory, and ltp_result_prefix, the prefix of their extra test result
+    entries.
+    """
+    ltp_log_name = None
+    ltp_result_prefix = None
+
+    @classmethod
+    def setUpClass(cls):
+        cls.ltp_startup()
+
+    @classmethod
+    def tearDownClass(cls):
+        cls.ltp_finishup()
+
+    @classmethod
+    def ltp_startup(cls):
+        cls.sections = {}
+        cls.failmsg = ""
+        test_log_dir = os.path.join(cls.td.get('WORKDIR', ''), 'testimage')
+        timestamp = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
+
+        cls.ltptest_log_dir_link = os.path.join(test_log_dir, cls.ltp_log_name)
+        cls.ltptest_log_dir = '%s.%s' % (cls.ltptest_log_dir_link, timestamp)
+        os.makedirs(cls.ltptest_log_dir)
+
+        cls.tc.target.run("mkdir -p /opt/ltp/results")
+
+        if not hasattr(cls.tc, "extraresults"):
+            cls.tc.extraresults = {}
+        cls.extras = cls.tc.extraresults
+        cls.extras['%s.rawlogs' % cls.ltp_result_prefix] = {'log': ""}
+
+    @classmethod
+    def ltp_finishup(cls):
+        cls.extras['%s.sections' % cls.ltp_result_prefix] = cls.sections
+
+        # update symlink to the log directory
+        if os.path.exists(cls.ltptest_log_dir_link):
+            os.remove(cls.ltptest_log_dir_link)
+        os.symlink(os.path.basename(cls.ltptest_log_dir), cls.ltptest_log_dir_link)
+
+        if cls.failmsg:
+            # fail() is an instance method so can't be used from a classmethod
+            raise cls.failureException(cls.failmsg)
