[RFC,v1,01/10] dts: hello world config options

Message ID 20220824162454.394285-2-juraj.linkes@pantheon.tech (mailing list archive)
State Superseded, archived
Delegated to: Thomas Monjalon
Headers
Series dts: add hello world testcase |

Checks

Context Check Description
ci/checkpatch success coding style OK

Commit Message

Juraj Linkeš Aug. 24, 2022, 4:24 p.m. UTC
  There are two categories of new config options: DPDK build/config
options and testing options.

Signed-off-by: Juraj Linkeš <juraj.linkes@pantheon.tech>
---
 dts/conf.yaml                              |  20 ++-
 dts/framework/config/__init__.py           | 141 ++++++++++++++++++++-
 dts/framework/config/conf_yaml_schema.json | 139 +++++++++++++++++++-
 3 files changed, 289 insertions(+), 11 deletions(-)
  

Patch

diff --git a/dts/conf.yaml b/dts/conf.yaml
index cb12ea3d0f..36399c6e74 100644
--- a/dts/conf.yaml
+++ b/dts/conf.yaml
@@ -1,7 +1,21 @@ 
 executions:
-  - system_under_test: "SUT 1"
+  - target_descriptions:
+      - cpu: native
+        compiler: gcc
+        arch: x86_64
+        os: linux
+    perf: false
+    func: true
+    test_suites:
+      - hello_world
+    system_under_test: "SUT 1"
 nodes:
   - name: "SUT 1"
-    hostname: "SUT IP address or hostname"
+    hostname: sut1.change.me.localhost
+    os: linux
     user: root
-    password: "Leave blank to use SSH keys"
+    password: a
+    arch: x86_64
+    bypass_core0: true
+    cores: 1
+    memory_channels: 4
diff --git a/dts/framework/config/__init__.py b/dts/framework/config/__init__.py
index a0fdffcd77..158baac143 100644
--- a/dts/framework/config/__init__.py
+++ b/dts/framework/config/__init__.py
@@ -6,11 +6,14 @@ 
 """
 Generic port and topology nodes configuration file load function
 """
+import abc
 import json
 import os.path
 import pathlib
+from abc import abstractmethod
 from dataclasses import dataclass
-from typing import Any, Optional
+from enum import Enum, auto, unique
+from typing import Any, Optional, TypedDict, Union
 
 import warlock
 import yaml
@@ -18,6 +21,53 @@ 
 from framework.settings import SETTINGS
 
 
+class StrEnum(Enum):
+    @staticmethod
+    def _generate_next_value_(
+        name: str, start: int, count: int, last_values: object
+    ) -> str:
+        return name
+
+
+@unique
+class OS(StrEnum):
+    linux = auto()
+    freebsd = auto()
+    windows = auto()
+
+
+@unique
+class Architecture(StrEnum):
+    i686 = auto()
+    x86_64 = auto()
+    x86_32 = auto()
+    arm64 = auto()
+    ppc64le = auto()
+
+
+@unique
+class Compiler(StrEnum):
+    gcc = auto()
+    clang = auto()
+    icc = auto()
+    msvc = auto()
+
+
+@unique
+class CPU(StrEnum):
+    native = auto()
+    armv8a = auto()
+    dpaa2 = auto()
+    thunderx = auto()
+    xgene1 = auto()
+
+
+@unique
+class NodeType(StrEnum):
+    physical = auto()
+    virtual = auto()
+
+
 # Slots enables some optimizations, by pre-allocating space for the defined
 # attributes in the underlying data structure.
 #
@@ -28,7 +78,12 @@  class NodeConfiguration:
     name: str
     hostname: str
     user: str
+    os: OS
+    arch: Architecture
     password: Optional[str]
+    bypass_core0: bool
+    cores: str
+    memory_channels: int
 
     @staticmethod
     def from_dict(d: dict) -> "NodeConfiguration":
@@ -36,20 +91,101 @@  def from_dict(d: dict) -> "NodeConfiguration":
             name=d["name"],
             hostname=d["hostname"],
             user=d["user"],
+            os=OS(d["os"]),
+            arch=Architecture(d["arch"]),
             password=d.get("password"),
+            bypass_core0=d.get("bypass_core0", False),
+            cores=d["cores"],
+            memory_channels=d["memory_channels"],
         )
 
 
+@dataclass(slots=True, frozen=True)
+class TargetDescription:
+    cpu: CPU
+    compiler: Compiler
+    arch: Architecture
+    os: OS
+
+    @staticmethod
+    def from_dict(d: dict) -> "TargetDescription":
+        return TargetDescription(
+            cpu=CPU(d["cpu"]),
+            compiler=Compiler(d["compiler"]),
+            arch=Architecture(d["arch"]),
+            os=OS(d["os"]),
+        )
+
+    def __str__(self):
+        return f"{self.arch}-{self.os}-{self.cpu}-{self.compiler}"
+
+
+class TestSuiteConfigDict(TypedDict):
+    suite: str
+    cases: list[str]
+
+
+# https://github.com/python/mypy/issues/5374
+@dataclass(slots=True, frozen=True)  # type: ignore
+class TestSuiteConfig(abc.ABC):
+    test_suite: str
+
+    @staticmethod
+    def from_dict(
+        entry: str | TestSuiteConfigDict,
+    ) -> Union["AllTestCasesTestSuiteConfig", "SelectedTestCasesTestSuiteConfig"]:
+        if isinstance(entry, str):
+            return AllTestCasesTestSuiteConfig(test_suite=entry)
+        elif isinstance(entry, dict):
+            return SelectedTestCasesTestSuiteConfig(
+                test_suite=entry["suite"], test_cases=entry["cases"]
+            )
+        else:
+            raise TypeError(f"{type(entry)} is not valid for a test suite config.")
+
+    @abstractmethod
+    def get_requested_test_cases(self) -> Optional[list[str]]:
+        raise NotImplementedError()
+
+
+@dataclass(slots=True, frozen=True)
+class AllTestCasesTestSuiteConfig(TestSuiteConfig):
+    def get_requested_test_cases(self) -> Optional[list[str]]:
+        return None
+
+
+@dataclass(slots=True, frozen=True)
+class SelectedTestCasesTestSuiteConfig(TestSuiteConfig):
+    test_cases: list[str]
+
+    def get_requested_test_cases(self) -> Optional[list[str]]:
+        return self.test_cases
+
+
 @dataclass(slots=True, frozen=True)
 class ExecutionConfiguration:
+    target_descriptions: list[TargetDescription]
+    perf: bool
+    func: bool
+    test_suites: list[TestSuiteConfig]
     system_under_test: NodeConfiguration
 
     @staticmethod
     def from_dict(d: dict, node_map: dict) -> "ExecutionConfiguration":
+        target_descriptions: list[TargetDescription] = list(
+            map(TargetDescription.from_dict, d["target_descriptions"])
+        )
+        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(
+            target_descriptions=target_descriptions,
+            perf=d["perf"],
+            func=d["func"],
+            test_suites=test_suites,
             system_under_test=node_map[sut_name],
         )
 
@@ -57,6 +193,7 @@  def from_dict(d: dict, node_map: dict) -> "ExecutionConfiguration":
 @dataclass(slots=True, frozen=True)
 class Configuration:
     executions: list[ExecutionConfiguration]
+    nodes: list[NodeConfiguration]
 
     @staticmethod
     def from_dict(d: dict) -> "Configuration":
@@ -74,7 +211,7 @@  def from_dict(d: dict) -> "Configuration":
             )
         )
 
-        return Configuration(executions=executions)
+        return Configuration(executions=executions, nodes=nodes)
 
 
 def load_config() -> Configuration:
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index 04b2bec3a5..d1cc990fd5 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -6,13 +6,88 @@ 
       "type": "string",
       "description": "A unique identifier for a node"
     },
-    "node_role": {
+    "OS": {
       "type": "string",
-      "description": "The role a node plays in DTS",
       "enum": [
-        "system_under_test",
-        "traffic_generator"
+        "linux"
       ]
+    },
+    "ARCH": {
+      "type": "string",
+      "enum": [
+        "x86_64"
+      ]
+    },
+    "compiler": {
+      "type": "string",
+      "enum": [
+        "gcc",
+        "clang",
+        "icc",
+        "mscv"
+      ]
+    },
+    "cpu": {
+      "type": "string",
+      "description": "Native should be the default on x86",
+      "enum": [
+        "native",
+        "armv8a",
+        "dpaa2",
+        "thunderx",
+        "xgene1"
+      ]
+    },
+    "target": {
+      "type": "object",
+      "description": "Targets supported by DTS",
+      "properties": {
+        "arch": {
+          "type": "string",
+          "enum": [
+            "ALL",
+            "x86_64",
+            "arm64",
+            "ppc64le",
+            "other"
+          ]
+        },
+        "cpu": {
+          "$ref": "#/definitions/cpu"
+        },
+        "os": {
+          "$ref": "#/definitions/OS"
+        },
+        "compiler": {
+          "$ref": "#/definitions/compiler"
+        }
+      },
+      "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",
@@ -34,16 +109,36 @@ 
             "type": "string",
             "description": "The user to access this node with."
           },
+          "os": {
+            "$ref": "#/definitions/OS"
+          },
+          "arch": {
+            "$ref": "#/definitions/ARCH"
+          },
           "password": {
             "type": "string",
             "description": "The password to use on this node. SSH keys are preferred."
+          },
+          "bypass_core0": {
+            "type": "boolean",
+            "description": "Indicate whether DPDK should omit using the first core or not."
+          },
+	  "cores": {
+            "type": "string",
+            "description": "Comma-separated list of cores to use, e.g.: 1,2,3,4,5,18-22"
+          },
+	  "memory_channels": {
+            "type": "integer",
+            "description": "How many memory channels to use."
           }
         },
         "additionalProperties": false,
         "required": [
           "name",
+          "os",
+          "user",
           "hostname",
-          "user"
+          "arch"
         ]
       },
       "minimum": 1
@@ -55,11 +150,43 @@ 
         "properties": {
           "system_under_test": {
             "$ref": "#/definitions/node_name"
+          },
+          "target_descriptions": {
+            "type": "array",
+            "items": {
+              "$ref": "#/definitions/target"
+            },
+            "minimum": 1
+          },
+          "test_suites": {
+            "type": "array",
+            "items": {
+              "oneOf": [
+                {
+                  "$ref": "#/definitions/test_suite"
+                },
+                {
+                  "$ref": "#/definitions/test_target"
+                }
+              ]
+            }
+          },
+          "perf": {
+            "type": "boolean",
+            "description": "Enable performance testing"
+          },
+          "func": {
+            "type": "boolean",
+            "description": "Enable functional testing"
           }
         },
         "additionalProperties": false,
         "required": [
-          "system_under_test"
+          "system_under_test",
+          "target_descriptions",
+          "perf",
+          "func",
+          "test_suites"
         ]
       },
       "minimum": 1