@@ -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
@@ -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:
@@ -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