diff --git a/dts/conf.yaml b/dts/conf.yaml
index 976888a88e..0b0f2c59b0 100644
--- a/dts/conf.yaml
+++ b/dts/conf.yaml
@@ -7,6 +7,10 @@ executions:
         os: linux
         cpu: native
         compiler: gcc
+    perf: false
+    func: true
+    test_suites:
+      - hello_world
     system_under_test: "SUT 1"
 nodes:
   - name: "SUT 1"
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index 344d697a69..8874b10030 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -11,7 +11,7 @@
 import pathlib
 from dataclasses import dataclass
 from enum import Enum, auto, unique
-from typing import Any, Iterable
+from typing import Any, Iterable, TypedDict
 
 import warlock  # type: ignore
 import yaml
@@ -186,9 +186,34 @@ def from_dict(d: dict) -> "BuildTargetConfiguration":
         )
 
 
+class TestSuiteConfigDict(TypedDict):
+    suite: str
+    cases: list[str]
+
+
+@dataclass(slots=True, frozen=True)
+class TestSuiteConfig:
+    test_suite: str
+    test_cases: list[str]
+
+    @staticmethod
+    def from_dict(
+        entry: str | TestSuiteConfigDict,
+    ) -> "TestSuiteConfig":
+        if isinstance(entry, str):
+            return TestSuiteConfig(test_suite=entry, test_cases=[])
+        elif isinstance(entry, dict):
+            return TestSuiteConfig(test_suite=entry["suite"], test_cases=entry["cases"])
+        else:
+            raise TypeError(f"{type(entry)} is not valid for a test suite config.")
+
+
 @dataclass(slots=True, frozen=True)
 class ExecutionConfiguration:
     build_targets: list[BuildTargetConfiguration]
+    perf: bool
+    func: bool
+    test_suites: list[TestSuiteConfig]
     system_under_test: NodeConfiguration
 
     @staticmethod
@@ -196,11 +221,17 @@ def from_dict(d: dict, node_map: dict) -> "ExecutionConfiguration":
         build_targets: list[BuildTargetConfiguration] = list(
             map(BuildTargetConfiguration.from_dict, d["build_targets"])
         )
+        test_suites: list[TestSuiteConfig] = list(
+            map(TestSuiteConfig.from_dict, d["test_suites"])
+        )
         sut_name = d["system_under_test"]
         assert sut_name in node_map, f"Unknown SUT {sut_name} in execution {d}"
 
         return ExecutionConfiguration(
             build_targets=build_targets,
+            perf=d["perf"],
+            func=d["func"],
+            test_suites=test_suites,
             system_under_test=node_map[sut_name],
         )
 
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index c59d3e30e6..e37ced65fe 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -63,6 +63,31 @@
         }
       },
       "additionalProperties": false
+    },
+    "test_suite": {
+      "type": "string",
+      "enum": [
+        "hello_world"
+      ]
+    },
+    "test_target": {
+      "type": "object",
+      "properties": {
+        "suite": {
+          "$ref": "#/definitions/test_suite"
+        },
+        "cases": {
+          "type": "array",
+          "items": {
+            "type": "string"
+          },
+          "minimum": 1
+        }
+      },
+      "required": [
+        "suite"
+      ],
+      "additionalProperties": false
     }
   },
   "type": "object",
@@ -130,6 +155,27 @@
             },
             "minimum": 1
           },
+          "perf": {
+            "type": "boolean",
+            "description": "Enable performance testing"
+          },
+          "func": {
+            "type": "boolean",
+            "description": "Enable functional testing"
+          },
+          "test_suites": {
+            "type": "array",
+            "items": {
+              "oneOf": [
+                {
+                  "$ref": "#/definitions/test_suite"
+                },
+                {
+                  "$ref": "#/definitions/test_target"
+                }
+              ]
+            }
+          },
           "system_under_test": {
             "$ref": "#/definitions/node_name"
           }
@@ -137,6 +183,9 @@
         "additionalProperties": false,
         "required": [
           "build_targets",
+          "perf",
+          "func",
+          "test_suites",
           "system_under_test"
         ]
       },
diff --git a/dts/framework/dts.py b/dts/framework/dts.py
index a7c243a5c3..ba3f4b4168 100644
--- a/dts/framework/dts.py
+++ b/dts/framework/dts.py
@@ -15,6 +15,7 @@
 from .logger import DTSLOG, getLogger
 from .settings import SETTINGS
 from .stats_reporter import TestStats
+from .test_case import TestCase
 from .test_result import Result
 from .utils import check_dts_python_version
 
@@ -129,6 +130,34 @@ def run_suite(
     Use the given build_target to run the test suite with possibly only a subset
     of tests. If no subset is specified, run all tests.
     """
+    for test_suite_config in execution.test_suites:
+        result.test_suite = test_suite_config.test_suite
+        full_suite_path = f"tests.TestSuite_{test_suite_config.test_suite}"
+        testcase_classes = TestCase.get_testcases(full_suite_path)
+        dts_logger.debug(
+            f"Found testcase classes '{testcase_classes}' in '{full_suite_path}'"
+        )
+        for testcase_class in testcase_classes:
+            testcase = testcase_class(
+                sut_node, test_suite_config.test_suite, build_target, execution
+            )
+
+            testcase.init_log()
+            testcase.set_requested_cases(SETTINGS.test_cases)
+            testcase.set_requested_cases(test_suite_config.test_cases)
+
+            dts_logger.info(f"Running test suite '{testcase_class.__name__}'")
+            try:
+                testcase.execute_setup_all()
+                testcase.execute_test_cases()
+                dts_logger.info(
+                    f"Finished running test suite '{testcase_class.__name__}'"
+                )
+                result.copy_suite(testcase.get_result())
+                test_stats.save(result)  # this was originally after teardown
+
+            finally:
+                testcase.execute_tear_downall()
 
 
 def quit_execution(nodes: Iterable[Node], return_code: ReturnCode) -> None:
diff --git a/dts/framework/exception.py b/dts/framework/exception.py
index 93d99432ae..a35eeff640 100644
--- a/dts/framework/exception.py
+++ b/dts/framework/exception.py
@@ -29,6 +29,10 @@ class ReturnCode(IntEnum):
     DPDK_BUILD_ERR = 10
     NODE_SETUP_ERR = 20
     NODE_CLEANUP_ERR = 21
+    SUITE_SETUP_ERR = 30
+    SUITE_EXECUTION_ERR = 31
+    TESTCASE_VERIFY_ERR = 32
+    SUITE_CLEANUP_ERR = 33
 
 
 class DTSError(Exception):
@@ -153,6 +157,67 @@ def __init__(self):
         )
 
 
+class TestSuiteNotFound(DTSError):
+    """
+    Raised when a configured test suite cannot be imported.
+    """
+
+    return_code: ClassVar[ReturnCode] = ReturnCode.SUITE_SETUP_ERR
+
+
+class SuiteSetupError(DTSError):
+    """
+    Raised when an error occurs during suite setup.
+    """
+
+    return_code: ClassVar[ReturnCode] = ReturnCode.SUITE_SETUP_ERR
+
+    def __init__(self):
+        super(SuiteSetupError, self).__init__("An error occurred during suite setup.")
+
+
+class SuiteExecutionError(DTSError):
+    """
+    Raised when an error occurs during suite execution.
+    """
+
+    return_code: ClassVar[ReturnCode] = ReturnCode.SUITE_EXECUTION_ERR
+
+    def __init__(self):
+        super(SuiteExecutionError, self).__init__(
+            "An error occurred during suite execution."
+        )
+
+
+class VerifyError(DTSError):
+    """
+    To be used within the test cases to verify if a command output
+    is as it was expected.
+    """
+
+    value: str
+    return_code: ClassVar[ReturnCode] = ReturnCode.TESTCASE_VERIFY_ERR
+
+    def __init__(self, value: str):
+        self.value = value
+
+    def __str__(self) -> str:
+        return repr(self.value)
+
+
+class SuiteCleanupError(DTSError):
+    """
+    Raised when an error occurs during suite cleanup.
+    """
+
+    return_code: ClassVar[ReturnCode] = ReturnCode.SUITE_CLEANUP_ERR
+
+    def __init__(self):
+        super(SuiteCleanupError, self).__init__(
+            "An error occurred during suite cleanup."
+        )
+
+
 def convert_exception(exception: type[DTSError]) -> Callable[..., Callable[..., None]]:
     """
     When a non-DTS exception is raised while executing the decorated function,
diff --git a/dts/framework/settings.py b/dts/framework/settings.py
index e2bf3d2ce4..069f28ce81 100644
--- a/dts/framework/settings.py
+++ b/dts/framework/settings.py
@@ -64,6 +64,8 @@ class _Settings:
     skip_setup: bool
     dpdk_ref: Path
     compile_timeout: float
+    test_cases: list
+    re_run: int
 
 
 def _get_parser() -> argparse.ArgumentParser:
@@ -138,6 +140,25 @@ def _get_parser() -> argparse.ArgumentParser:
         help="[DTS_COMPILE_TIMEOUT] The timeout for compiling DPDK.",
     )
 
+    parser.add_argument(
+        "--test-cases",
+        action=_env_arg("DTS_TESTCASES"),
+        default="",
+        required=False,
+        help="[DTS_TESTCASES] Comma-separated list of testcases to execute",
+    )
+
+    parser.add_argument(
+        "--re-run",
+        "--re_run",
+        action=_env_arg("DTS_RERUN"),
+        default=0,
+        type=int,
+        required=False,
+        help="[DTS_RERUN] Re-run tests the specified amount of times if a test failure "
+        "occurs",
+    )
+
     return parser
 
 
@@ -151,6 +172,10 @@ def _get_settings() -> _Settings:
         skip_setup=(parsed_args.skip_setup == "Y"),
         dpdk_ref=parsed_args.dpdk_ref,
         compile_timeout=parsed_args.compile_timeout,
+        test_cases=parsed_args.test_cases.split(",")
+        if parsed_args.test_cases != ""
+        else [],
+        re_run=parsed_args.re_run,
     )
 
 
diff --git a/dts/framework/test_case.py b/dts/framework/test_case.py
new file mode 100644
index 0000000000..0479f795bb
--- /dev/null
+++ b/dts/framework/test_case.py
@@ -0,0 +1,246 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2010-2014 Intel Corporation
+# Copyright(c) 2022 PANTHEON.tech s.r.o.
+
+"""
+A base class for creating DTS test cases.
+"""
+
+import importlib
+import inspect
+import re
+import time
+import traceback
+
+from .exception import (
+    SSHTimeoutError,
+    SuiteCleanupError,
+    SuiteExecutionError,
+    SuiteSetupError,
+    TestSuiteNotFound,
+    VerifyError,
+    convert_exception,
+)
+from .logger import getLogger
+from .settings import SETTINGS
+from .test_result import Result
+
+
+class TestCase(object):
+    def __init__(self, sut_node, suitename, target, execution):
+        self.sut_node = sut_node
+        self.suite_name = suitename
+        self.target = target
+
+        # local variable
+        self._requested_tests = []
+        self._subtitle = None
+
+        # result object for save suite result
+        self._suite_result = Result()
+        self._suite_result.sut = self.sut_node.config.hostname
+        self._suite_result.target = target
+        self._suite_result.test_suite = self.suite_name
+        if self._suite_result is None:
+            raise ValueError("Result object should not None")
+
+        self._enable_func = execution.func
+
+        # command history
+        self.setup_history = list()
+        self.test_history = list()
+
+    def init_log(self):
+        # get log handler
+        class_name = self.__class__.__name__
+        self.logger = getLogger(class_name)
+
+    def set_up_all(self):
+        pass
+
+    def set_up(self):
+        pass
+
+    def tear_down(self):
+        pass
+
+    def tear_down_all(self):
+        pass
+
+    def verify(self, passed, description):
+        if not passed:
+            raise VerifyError(description)
+
+    def _get_functional_cases(self):
+        """
+        Get all functional test cases.
+        """
+        return self._get_test_cases(r"test_(?!perf_)")
+
+    def _has_it_been_requested(self, test_case, test_name_regex):
+        """
+        Check whether test case has been requested for validation.
+        """
+        name_matches = re.match(test_name_regex, test_case.__name__)
+        if self._requested_tests:
+            return name_matches and test_case.__name__ in self._requested_tests
+
+        return name_matches
+
+    def set_requested_cases(self, case_list):
+        """
+        Pass down input cases list for check
+        """
+        self._requested_tests += case_list
+
+    def _get_test_cases(self, test_name_regex):
+        """
+        Return case list which name matched regex.
+        """
+        self.logger.debug(f"Searching for testcases in {self.__class__}")
+        for test_case_name in dir(self):
+            test_case = getattr(self, test_case_name)
+            if callable(test_case) and self._has_it_been_requested(
+                test_case, test_name_regex
+            ):
+                yield test_case
+
+    @convert_exception(SuiteSetupError)
+    def execute_setup_all(self):
+        """
+        Execute suite setup_all function before cases.
+        """
+        try:
+            self.set_up_all()
+            return True
+        except Exception as v:
+            self.logger.error("set_up_all failed:\n" + traceback.format_exc())
+            # record all cases blocked
+            if self._enable_func:
+                for case_obj in self._get_functional_cases():
+                    self._suite_result.test_case = case_obj.__name__
+                    self._suite_result.test_case_blocked(
+                        "set_up_all failed: {}".format(str(v))
+                    )
+            return False
+
+    def _execute_test_case(self, case_obj):
+        """
+        Execute specified test case in specified suite. If any exception occurred in
+        validation process, save the result and tear down this case.
+        """
+        case_name = case_obj.__name__
+        self._suite_result.test_case = case_obj.__name__
+
+        case_result = True
+        try:
+            self.logger.info("Test Case %s Begin" % case_name)
+
+            self.running_case = case_name
+            # run set_up function for each case
+            self.set_up()
+            # run test case
+            case_obj()
+
+            self._suite_result.test_case_passed()
+
+            self.logger.info("Test Case %s Result PASSED:" % case_name)
+
+        except VerifyError as v:
+            case_result = False
+            self._suite_result.test_case_failed(str(v))
+            self.logger.error("Test Case %s Result FAILED: " % (case_name) + str(v))
+        except KeyboardInterrupt:
+            self._suite_result.test_case_blocked("Skipped")
+            self.logger.error("Test Case %s SKIPPED: " % (case_name))
+            self.tear_down()
+            raise KeyboardInterrupt("Stop DTS")
+        except SSHTimeoutError as e:
+            case_result = False
+            self._suite_result.test_case_failed(str(e))
+            self.logger.error("Test Case %s Result FAILED: " % (case_name) + str(e))
+            self.logger.error("%s" % (e.get_output()))
+        except Exception:
+            case_result = False
+            trace = traceback.format_exc()
+            self._suite_result.test_case_failed(trace)
+            self.logger.error("Test Case %s Result ERROR: " % (case_name) + trace)
+        finally:
+            self.execute_tear_down()
+            return case_result
+
+    @convert_exception(SuiteExecutionError)
+    def execute_test_cases(self):
+        """
+        Execute all test cases in one suite.
+        """
+        # prepare debugger rerun case environment
+        if self._enable_func:
+            for case_obj in self._get_functional_cases():
+                for i in range(SETTINGS.re_run + 1):
+                    ret = self.execute_test_case(case_obj)
+
+                    if ret is False and SETTINGS.re_run:
+                        self.sut_node.get_session_output(timeout=0.5 * (i + 1))
+                        time.sleep(i + 1)
+                        self.logger.info(
+                            " Test case %s failed and re-run %d time"
+                            % (case_obj.__name__, i + 1)
+                        )
+                    else:
+                        break
+
+    def execute_test_case(self, case_obj):
+        """
+        Execute test case or enter into debug mode.
+        """
+        return self._execute_test_case(case_obj)
+
+    def get_result(self):
+        """
+        Return suite test result
+        """
+        return self._suite_result
+
+    @convert_exception(SuiteCleanupError)
+    def execute_tear_downall(self):
+        """
+        execute suite tear_down_all function
+        """
+        self.tear_down_all()
+
+        self.sut_node.kill_cleanup_dpdk_apps()
+
+    def execute_tear_down(self):
+        """
+        execute suite tear_down function
+        """
+        try:
+            self.tear_down()
+        except Exception:
+            self.logger.error("tear_down failed:\n" + traceback.format_exc())
+            self.logger.warning(
+                "tear down %s failed, might iterfere next case's result!"
+                % self.running_case
+            )
+
+    @staticmethod
+    def get_testcases(testsuite_module_path: str) -> list[type["TestCase"]]:
+        def is_testcase(object) -> bool:
+            try:
+                if issubclass(object, TestCase) and object != TestCase:
+                    return True
+            except TypeError:
+                return False
+            return False
+
+        try:
+            testcase_module = importlib.import_module(testsuite_module_path)
+        except ModuleNotFoundError as e:
+            raise TestSuiteNotFound(
+                f"Testsuite '{testsuite_module_path}' not found."
+            ) from e
+        return [
+            testcase_class
+            for _, testcase_class in inspect.getmembers(testcase_module, is_testcase)
+        ]
