[5/5] dts: add JSON schema generation script

Message ID 20240822163941.1390326-6-luca.vizzarro@arm.com (mailing list archive)
State New
Delegated to: Paul Szczepanek
Headers
Series dts: Pydantic configuration |

Checks

Context Check Description
ci/checkpatch success coding style OK
ci/loongarch-compilation success Compilation OK
ci/loongarch-unit-testing success Unit Testing PASS
ci/Intel-compilation success Compilation OK
ci/intel-Testing success Testing PASS
ci/intel-Functional success Functional PASS
ci/github-robot: build success github build: passed
ci/iol-marvell-Functional success Functional Testing PASS
ci/iol-broadcom-Performance success Performance Testing PASS
ci/iol-broadcom-Functional success Functional Testing PASS
ci/iol-unit-amd64-testing success Testing PASS
ci/iol-sample-apps-testing success Testing PASS
ci/iol-compile-amd64-testing success Testing PASS
ci/iol-unit-arm64-testing success Testing PASS
ci/iol-compile-arm64-testing success Testing PASS
ci/iol-intel-Functional success Functional Testing PASS
ci/iol-intel-Performance success Performance Testing PASS

Commit Message

Luca Vizzarro Aug. 22, 2024, 4:39 p.m. UTC
Adds a new script which automatically re-generates the JSON schema file
based on the Pydantic configuration models.

Moreover, update the JSON schema with this script for the first time.

Signed-off-by: Luca Vizzarro <luca.vizzarro@arm.com>
Reviewed-by: Paul Szczepanek <paul.szczepanek@arm.com>
---
 doc/guides/tools/dts.rst                   |  10 +
 dts/framework/config/conf_yaml_schema.json | 776 ++++++++++++---------
 dts/generate-schema.py                     |  38 +
 3 files changed, 486 insertions(+), 338 deletions(-)
 create mode 100755 dts/generate-schema.py
  

Comments

Juraj Linkeš Sept. 17, 2024, 11:59 a.m. UTC | #1
>   create mode 100755 dts/generate-schema.py

Could it be worth putting this into devtools? It is a devtool.

> 
> diff --git a/doc/guides/tools/dts.rst b/doc/guides/tools/dts.rst

> @@ -430,6 +430,16 @@ Refer to the script for usage: ``devtools/dts-check-format.sh -h``.
>   Configuration Schema
>   --------------------
>   
> +The configuration schema is automatically generated from Pydantic models and can be found
> +at ``dts/framework/config/conf_yaml_schema.json``. Whenever the models are changed, the schema
> +should be regenerated using the dedicated script at ``dts/generate-schema.py``, e.g.:

Should we add this to devtools/dts-check-format.sh? Looks like a good 
candidate.

> +
> +.. code-block:: console
> +
> +   $ poetry shell
> +   (dts-py3.10) $ ./generate-schema.py
> +
> +
>   Definitions
>   ~~~~~~~~~~~

The definition names have changed and maybe there are also some other 
changes or does that not matter? Can these Pydantic changes help us with 
generating this schema description as well?

> diff --git a/dts/generate-schema.py b/dts/generate-schema.py

> @@ -0,0 +1,38 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2024 Arm Limited
> +
> +"""JSON schema generation script."""

This should at least say how to run the script, but we probably want to 
add more, such as from what it's creating the schema and where it's 
going to put it.


> +from framework.config import ConfigurationType
> +

Ah, so it is used elsewhere. Let's just rename it then.

> +DTS_DIR = os.path.dirname(os.path.realpath(__file__))
> +RELATIVE_PATH_TO_SCHEMA = "framework/config/conf_yaml_schema.json"

We're using pathlib everywhere in DTS, so let's use it here as well. Not 
sure if the portability is needed in this script, but why not.


> +class GenerateSchemaWithDialect(GenerateJsonSchema):
> +    """Custom schema generator which adds the schema dialect."""

I'd add that we're adding a reference to the schema dialect.


> +    print("Schema generated successfully!")
> +except Exception as e:
> +    raise Exception("failed to generate schema") from e

Let's unify the message with the print above by capitalizing and adding 
a dot to the end.
  
Nicholas Pratte Oct. 1, 2024, 8:48 p.m. UTC | #2
Seems straightforward. There are actually some intersections here
between my currently-existing config changes and some of the trimming
you provide here which simplifies my upcoming series when I rebase it
to use this series.

Reviewed-by: Nicholas Pratte <npratte@iol.unh.edu>

On Thu, Aug 22, 2024 at 12:40 PM Luca Vizzarro <luca.vizzarro@arm.com> wrote:
>
> Adds a new script which automatically re-generates the JSON schema file
> based on the Pydantic configuration models.
>
> Moreover, update the JSON schema with this script for the first time.
>
> Signed-off-by: Luca Vizzarro <luca.vizzarro@arm.com>
> Reviewed-by: Paul Szczepanek <paul.szczepanek@arm.com>
> ---
>  doc/guides/tools/dts.rst                   |  10 +
>  dts/framework/config/conf_yaml_schema.json | 776 ++++++++++++---------
>  dts/generate-schema.py                     |  38 +
>  3 files changed, 486 insertions(+), 338 deletions(-)
>  create mode 100755 dts/generate-schema.py
>
> diff --git a/doc/guides/tools/dts.rst b/doc/guides/tools/dts.rst
> index 515b15e4d8..317bd0ff99 100644
> --- a/doc/guides/tools/dts.rst
> +++ b/doc/guides/tools/dts.rst
> @@ -430,6 +430,16 @@ Refer to the script for usage: ``devtools/dts-check-format.sh -h``.
>  Configuration Schema
>  --------------------
>
> +The configuration schema is automatically generated from Pydantic models and can be found
> +at ``dts/framework/config/conf_yaml_schema.json``. Whenever the models are changed, the schema
> +should be regenerated using the dedicated script at ``dts/generate-schema.py``, e.g.:
> +
> +.. code-block:: console
> +
> +   $ poetry shell
> +   (dts-py3.10) $ ./generate-schema.py
> +
> +
>  Definitions
>  ~~~~~~~~~~~
>
> diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
> index f02a310bb5..1cf1bb098a 100644
> --- a/dts/framework/config/conf_yaml_schema.json
> +++ b/dts/framework/config/conf_yaml_schema.json
> @@ -1,402 +1,502 @@
>  {
> -  "$schema": "https://json-schema.org/draft-07/schema",
> -  "title": "DTS Config Schema",
> -  "definitions": {
> -    "node_name": {
> -      "type": "string",
> -      "description": "A unique identifier for a node"
> -    },
> -    "NIC": {
> -      "type": "string",
> -      "enum": [
> -        "ALL",
> -        "ConnectX3_MT4103",
> -        "ConnectX4_LX_MT4117",
> -        "ConnectX4_MT4115",
> -        "ConnectX5_MT4119",
> -        "ConnectX5_MT4121",
> -        "I40E_10G-10G_BASE_T_BC",
> -        "I40E_10G-10G_BASE_T_X722",
> -        "I40E_10G-SFP_X722",
> -        "I40E_10G-SFP_XL710",
> -        "I40E_10G-X722_A0",
> -        "I40E_1G-1G_BASE_T_X722",
> -        "I40E_25G-25G_SFP28",
> -        "I40E_40G-QSFP_A",
> -        "I40E_40G-QSFP_B",
> -        "IAVF-ADAPTIVE_VF",
> -        "IAVF-VF",
> -        "IAVF_10G-X722_VF",
> -        "ICE_100G-E810C_QSFP",
> -        "ICE_25G-E810C_SFP",
> -        "ICE_25G-E810_XXV_SFP",
> -        "IGB-I350_VF",
> -        "IGB_1G-82540EM",
> -        "IGB_1G-82545EM_COPPER",
> -        "IGB_1G-82571EB_COPPER",
> -        "IGB_1G-82574L",
> -        "IGB_1G-82576",
> -        "IGB_1G-82576_QUAD_COPPER",
> -        "IGB_1G-82576_QUAD_COPPER_ET2",
> -        "IGB_1G-82580_COPPER",
> -        "IGB_1G-I210_COPPER",
> -        "IGB_1G-I350_COPPER",
> -        "IGB_1G-I354_SGMII",
> -        "IGB_1G-PCH_LPTLP_I218_LM",
> -        "IGB_1G-PCH_LPTLP_I218_V",
> -        "IGB_1G-PCH_LPT_I217_LM",
> -        "IGB_1G-PCH_LPT_I217_V",
> -        "IGB_2.5G-I354_BACKPLANE_2_5GBPS",
> -        "IGC-I225_LM",
> -        "IGC-I226_LM",
> -        "IXGBE_10G-82599_SFP",
> -        "IXGBE_10G-82599_SFP_SF_QP",
> -        "IXGBE_10G-82599_T3_LOM",
> -        "IXGBE_10G-82599_VF",
> -        "IXGBE_10G-X540T",
> -        "IXGBE_10G-X540_VF",
> -        "IXGBE_10G-X550EM_A_SFP",
> -        "IXGBE_10G-X550EM_X_10G_T",
> -        "IXGBE_10G-X550EM_X_SFP",
> -        "IXGBE_10G-X550EM_X_VF",
> -        "IXGBE_10G-X550T",
> -        "IXGBE_10G-X550_VF",
> -        "brcm_57414",
> -        "brcm_P2100G",
> -        "cavium_0011",
> -        "cavium_a034",
> -        "cavium_a063",
> -        "cavium_a064",
> -        "fastlinq_ql41000",
> -        "fastlinq_ql41000_vf",
> -        "fastlinq_ql45000",
> -        "fastlinq_ql45000_vf",
> -        "hi1822",
> -        "virtio"
> -      ]
> -    },
> -
> -    "ARCH": {
> -      "type": "string",
> +  "$defs": {
> +    "Architecture": {
> +      "description": "The supported architectures of :class:`~framework.testbed_model.node.Node`\\s.",
>        "enum": [
> +        "i686",
>          "x86_64",
> +        "x86_32",
>          "arm64",
>          "ppc64le"
> -      ]
> -    },
> -    "OS": {
> -      "type": "string",
> -      "enum": [
> -        "linux"
> -      ]
> -    },
> -    "cpu": {
> -      "type": "string",
> -      "description": "Native should be the default on x86",
> -      "enum": [
> -        "native",
> -        "armv8a",
> -        "dpaa2",
> -        "thunderx",
> -        "xgene1"
> -      ]
> -    },
> -    "compiler": {
> -      "type": "string",
> -      "enum": [
> -        "gcc",
> -        "clang",
> -        "icc",
> -        "mscv"
> -      ]
> +      ],
> +      "title": "Architecture",
> +      "type": "string"
>      },
> -    "build_target": {
> -      "type": "object",
> -      "description": "Targets supported by DTS",
> +    "BuildTargetConfiguration": {
> +      "additionalProperties": false,
> +      "description": "DPDK build configuration.\n\nThe configuration used for building DPDK.\n\nAttributes:\n    arch: The target architecture to build for.\n    os: The target os to build for.\n    cpu: The target CPU to build for.\n    compiler: The compiler executable to use.\n    compiler_wrapper: This string will be put in front of the compiler when\n        executing the build. Useful for adding wrapper commands, such as ``ccache``.",
>        "properties": {
>          "arch": {
> -          "type": "string",
> -          "enum": [
> -            "ALL",
> -            "x86_64",
> -            "arm64",
> -            "ppc64le",
> -            "other"
> -          ]
> +          "$ref": "#/$defs/Architecture"
>          },
>          "os": {
> -          "$ref": "#/definitions/OS"
> +          "$ref": "#/$defs/OS"
>          },
>          "cpu": {
> -          "$ref": "#/definitions/cpu"
> +          "$ref": "#/$defs/CPUType"
>          },
>          "compiler": {
> -          "$ref": "#/definitions/compiler"
> +          "$ref": "#/$defs/Compiler"
>          },
> -          "compiler_wrapper": {
> -          "type": "string",
> -          "description": "This will be added before compiler to the CC variable when building DPDK. Optional."
> +        "compiler_wrapper": {
> +          "default": "",
> +          "title": "Compiler Wrapper",
> +          "type": "string"
>          }
>        },
> -      "additionalProperties": false,
>        "required": [
>          "arch",
>          "os",
>          "cpu",
>          "compiler"
> -      ]
> +      ],
> +      "title": "BuildTargetConfiguration",
> +      "type": "object"
>      },
> -    "hugepages_2mb": {
> -      "type": "object",
> -      "description": "Optional hugepage configuration. If not specified, hugepages won't be configured and DTS will use system configuration.",
> +    "CPUType": {
> +      "description": "The supported CPUs of :class:`~framework.testbed_model.node.Node`\\s.",
> +      "enum": [
> +        "native",
> +        "armv8a",
> +        "dpaa2",
> +        "thunderx",
> +        "xgene1"
> +      ],
> +      "title": "CPUType",
> +      "type": "string"
> +    },
> +    "Compiler": {
> +      "description": "The supported compilers of :class:`~framework.testbed_model.node.Node`\\s.",
> +      "enum": [
> +        "gcc",
> +        "clang",
> +        "icc",
> +        "msvc"
> +      ],
> +      "title": "Compiler",
> +      "type": "string"
> +    },
> +    "HugepageConfiguration": {
> +      "additionalProperties": false,
> +      "description": "The hugepage configuration of :class:`~framework.testbed_model.node.Node`\\s.\n\nAttributes:\n    number_of: The number of hugepages to allocate.\n    force_first_numa: If :data:`True`, the hugepages will be configured on the first NUMA node.",
>        "properties": {
>          "number_of": {
> -          "type": "integer",
> -          "description": "The number of hugepages to configure. Hugepage size will be the system default."
> +          "title": "Number Of",
> +          "type": "integer"
>          },
>          "force_first_numa": {
> -          "type": "boolean",
> -          "description": "Set to True to force configuring hugepages on the first NUMA node. Defaults to False."
> +          "title": "Force First Numa",
> +          "type": "boolean"
>          }
>        },
> -      "additionalProperties": false,
>        "required": [
> -        "number_of"
> -      ]
> -    },
> -    "mac_address": {
> -      "type": "string",
> -      "description": "A MAC address",
> -      "pattern": "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"
> +        "number_of",
> +        "force_first_numa"
> +      ],
> +      "title": "HugepageConfiguration",
> +      "type": "object"
>      },
> -    "pci_address": {
> -      "type": "string",
> -      "pattern": "^[\\da-fA-F]{4}:[\\da-fA-F]{2}:[\\da-fA-F]{2}.\\d:?\\w*$"
> +    "OS": {
> +      "description": "The supported operating systems of :class:`~framework.testbed_model.node.Node`\\s.",
> +      "enum": [
> +        "linux",
> +        "freebsd",
> +        "windows"
> +      ],
> +      "title": "OS",
> +      "type": "string"
>      },
> -    "port_peer_address": {
> -      "description": "Peer is a TRex port, and IXIA port or a PCI address",
> -      "oneOf": [
> -        {
> -          "description": "PCI peer port",
> -          "$ref": "#/definitions/pci_address"
> +    "PortConfig": {
> +      "additionalProperties": false,
> +      "description": "The port configuration of :class:`~framework.testbed_model.node.Node`\\s.\n\nAttributes:\n    pci: The PCI address of the port.\n    os_driver_for_dpdk: The operating system driver name for use with DPDK.\n    os_driver: The operating system driver name when the operating system controls the port.\n    peer_node: The :class:`~framework.testbed_model.node.Node` of the port\n        connected to this port.\n    peer_pci: The PCI address of the port connected to this port.",
> +      "properties": {
> +        "pci": {
> +          "description": "The local PCI address of the port.",
> +          "pattern": "^[\\da-fA-F]{4}:[\\da-fA-F]{2}:[\\da-fA-F]{2}.\\d:?\\w*$",
> +          "title": "Pci",
> +          "type": "string"
> +        },
> +        "os_driver_for_dpdk": {
> +          "description": "The driver that the kernel should bind this device to for DPDK to use it.",
> +          "examples": [
> +            "vfio-pci",
> +            "mlx5_core"
> +          ],
> +          "title": "Os Driver For Dpdk",
> +          "type": "string"
> +        },
> +        "os_driver": {
> +          "description": "The driver normally used by this port",
> +          "examples": [
> +            "i40e",
> +            "ice",
> +            "mlx5_core"
> +          ],
> +          "title": "Os Driver",
> +          "type": "string"
> +        },
> +        "peer_node": {
> +          "description": "The name of the peer node this port is connected to.",
> +          "title": "Peer Node",
> +          "type": "string"
> +        },
> +        "peer_pci": {
> +          "description": "The PCI address of the peer port this port is connected to.",
> +          "pattern": "^[\\da-fA-F]{4}:[\\da-fA-F]{2}:[\\da-fA-F]{2}.\\d:?\\w*$",
> +          "title": "Peer Pci",
> +          "type": "string"
>          }
> -      ]
> +      },
> +      "required": [
> +        "pci",
> +        "os_driver_for_dpdk",
> +        "os_driver",
> +        "peer_node",
> +        "peer_pci"
> +      ],
> +      "title": "PortConfig",
> +      "type": "object"
>      },
> -    "test_suite": {
> -      "type": "string",
> -      "enum": [
> -        "hello_world",
> -        "os_udp",
> -        "pmd_buffer_scatter"
> -      ]
> +    "ScapyTrafficGeneratorConfig": {
> +      "additionalProperties": false,
> +      "description": "Scapy traffic generator specific configuration.",
> +      "properties": {
> +        "type": {
> +          "const": "SCAPY",
> +          "enum": [
> +            "SCAPY"
> +          ],
> +          "title": "Type",
> +          "type": "string"
> +        }
> +      },
> +      "required": [
> +        "type"
> +      ],
> +      "title": "ScapyTrafficGeneratorConfig",
> +      "type": "object"
>      },
> -    "test_target": {
> -      "type": "object",
> +    "SutNodeConfiguration": {
> +      "additionalProperties": false,
> +      "description": ":class:`~framework.testbed_model.sut_node.SutNode` specific configuration.\n\nAttributes:\n    memory_channels: The number of memory channels to use when running DPDK.",
>        "properties": {
> -        "suite": {
> -          "$ref": "#/definitions/test_suite"
> +        "name": {
> +          "description": "A unique identifier for this node.",
> +          "title": "Name",
> +          "type": "string"
> +        },
> +        "hostname": {
> +          "description": "The hostname or IP address of the node.",
> +          "title": "Hostname",
> +          "type": "string"
> +        },
> +        "user": {
> +          "description": "The login user to use to connect to this node.",
> +          "title": "User",
> +          "type": "string"
>          },
> -        "cases": {
> -          "type": "array",
> -          "description": "If specified, only this subset of test suite's test cases will be run.",
> +        "password": {
> +          "anyOf": [
> +            {
> +              "type": "string"
> +            },
> +            {
> +              "type": "null"
> +            }
> +          ],
> +          "default": null,
> +          "description": "The login password to use to connect to this node. SSH keys are STRONGLY preferred, use only as last resort.",
> +          "title": "Password"
> +        },
> +        "use_first_core": {
> +          "default": false,
> +          "description": "DPDK won't use the first physical core if set to False.",
> +          "title": "Use First Core",
> +          "type": "boolean"
> +        },
> +        "hugepages_2mb": {
> +          "anyOf": [
> +            {
> +              "$ref": "#/$defs/HugepageConfiguration"
> +            },
> +            {
> +              "type": "null"
> +            }
> +          ],
> +          "default": null
> +        },
> +        "ports": {
>            "items": {
> -            "type": "string"
> +            "$ref": "#/$defs/PortConfig"
>            },
> -          "minimum": 1
> +          "minItems": 1,
> +          "title": "Ports",
> +          "type": "array"
> +        },
> +        "memory_channels": {
> +          "default": 1,
> +          "description": "Number of memory channels to use when running DPDK.",
> +          "title": "Memory Channels",
> +          "type": "integer"
> +        },
> +        "arch": {
> +          "$ref": "#/$defs/Architecture"
> +        },
> +        "os": {
> +          "$ref": "#/$defs/OS"
> +        },
> +        "lcores": {
> +          "default": "1",
> +          "description": "Comma-separated list of logical cores to use. An empty string means use all lcores.",
> +          "examples": [
> +            "1,2,3,4,5,18-22",
> +            "10-15"
> +          ],
> +          "pattern": "^(([0-9]+|([0-9]+-[0-9]+))(,([0-9]+|([0-9]+-[0-9]+)))*)?$",
> +          "title": "Lcores",
> +          "type": "string"
>          }
>        },
>        "required": [
> -        "suite"
> +        "name",
> +        "hostname",
> +        "user",
> +        "ports",
> +        "arch",
> +        "os"
>        ],
> -      "additionalProperties": false
> -    }
> -  },
> -  "type": "object",
> -  "properties": {
> -    "nodes": {
> -      "type": "array",
> -      "items": {
> -        "type": "object",
> -        "properties": {
> -          "name": {
> -            "type": "string",
> -            "description": "A unique identifier for this node"
> -          },
> -          "hostname": {
> -            "type": "string",
> -            "description": "A hostname from which the node running DTS can access this node. This can also be an IP address."
> -          },
> -          "user": {
> -            "type": "string",
> -            "description": "The user to access this node with."
> -          },
> -          "password": {
> -            "type": "string",
> -            "description": "The password to use on this node. Use only as a last resort. SSH keys are STRONGLY preferred."
> -          },
> -          "arch": {
> -            "$ref": "#/definitions/ARCH"
> -          },
> -          "os": {
> -            "$ref": "#/definitions/OS"
> -          },
> -          "lcores": {
> -            "type": "string",
> -            "pattern": "^(([0-9]+|([0-9]+-[0-9]+))(,([0-9]+|([0-9]+-[0-9]+)))*)?$",
> -            "description": "Optional comma-separated list of logical cores to use, e.g.: 1,2,3,4,5,18-22. Defaults to 1. An empty string means use all lcores."
> +      "title": "SutNodeConfiguration",
> +      "type": "object"
> +    },
> +    "TGNodeConfiguration": {
> +      "additionalProperties": false,
> +      "description": ":class:`~framework.testbed_model.tg_node.TGNode` specific configuration.\n\nAttributes:\n    traffic_generator: The configuration of the traffic generator present on the TG node.",
> +      "properties": {
> +        "name": {
> +          "description": "A unique identifier for this node.",
> +          "title": "Name",
> +          "type": "string"
> +        },
> +        "hostname": {
> +          "description": "The hostname or IP address of the node.",
> +          "title": "Hostname",
> +          "type": "string"
> +        },
> +        "user": {
> +          "description": "The login user to use to connect to this node.",
> +          "title": "User",
> +          "type": "string"
> +        },
> +        "password": {
> +          "anyOf": [
> +            {
> +              "type": "string"
> +            },
> +            {
> +              "type": "null"
> +            }
> +          ],
> +          "default": null,
> +          "description": "The login password to use to connect to this node. SSH keys are STRONGLY preferred, use only as last resort.",
> +          "title": "Password"
> +        },
> +        "use_first_core": {
> +          "default": false,
> +          "description": "DPDK won't use the first physical core if set to False.",
> +          "title": "Use First Core",
> +          "type": "boolean"
> +        },
> +        "hugepages_2mb": {
> +          "anyOf": [
> +            {
> +              "$ref": "#/$defs/HugepageConfiguration"
> +            },
> +            {
> +              "type": "null"
> +            }
> +          ],
> +          "default": null
> +        },
> +        "ports": {
> +          "items": {
> +            "$ref": "#/$defs/PortConfig"
>            },
> -          "use_first_core": {
> -            "type": "boolean",
> -            "description": "Indicate whether DPDK should use the first physical core. It won't be used by default."
> +          "minItems": 1,
> +          "title": "Ports",
> +          "type": "array"
> +        },
> +        "arch": {
> +          "$ref": "#/$defs/Architecture"
> +        },
> +        "os": {
> +          "$ref": "#/$defs/OS"
> +        },
> +        "lcores": {
> +          "default": "1",
> +          "description": "Comma-separated list of logical cores to use. An empty string means use all lcores.",
> +          "examples": [
> +            "1,2,3,4,5,18-22",
> +            "10-15"
> +          ],
> +          "pattern": "^(([0-9]+|([0-9]+-[0-9]+))(,([0-9]+|([0-9]+-[0-9]+)))*)?$",
> +          "title": "Lcores",
> +          "type": "string"
> +        },
> +        "traffic_generator": {
> +          "discriminator": {
> +            "mapping": {
> +              "SCAPY": "#/$defs/ScapyTrafficGeneratorConfig"
> +            },
> +            "propertyName": "type"
>            },
> -          "memory_channels": {
> -            "type": "integer",
> -            "description": "How many memory channels to use. Optional, defaults to 1."
> +          "oneOf": [
> +            {
> +              "$ref": "#/$defs/ScapyTrafficGeneratorConfig"
> +            }
> +          ],
> +          "title": "Traffic Generator"
> +        }
> +      },
> +      "required": [
> +        "name",
> +        "hostname",
> +        "user",
> +        "ports",
> +        "arch",
> +        "os",
> +        "traffic_generator"
> +      ],
> +      "title": "TGNodeConfiguration",
> +      "type": "object"
> +    },
> +    "TestRunConfiguration": {
> +      "additionalProperties": false,
> +      "description": "The configuration of a test run.\n\nThe configuration contains testbed information, what tests to execute\nand with what DPDK build.\n\nAttributes:\n    build_targets: A list of DPDK builds to test.\n    perf: Whether to run performance tests.\n    func: Whether to run functional tests.\n    skip_smoke_tests: Whether to skip smoke tests.\n    test_suites: The names of test suites and/or test cases to execute.\n    system_under_test_node: The SUT node configuration to use in this test run.\n    traffic_generator_node: The TG node name to use in this test run.",
> +      "properties": {
> +        "perf": {
> +          "description": "Enable performance testing.",
> +          "title": "Perf",
> +          "type": "boolean"
> +        },
> +        "func": {
> +          "description": "Enable functional testing.",
> +          "title": "Func",
> +          "type": "boolean"
> +        },
> +        "test_suites": {
> +          "items": {
> +            "$ref": "#/$defs/TestSuiteConfig"
>            },
> -          "hugepages_2mb": {
> -            "$ref": "#/definitions/hugepages_2mb"
> +          "minItems": 1,
> +          "title": "Test Suites",
> +          "type": "array"
> +        },
> +        "build_targets": {
> +          "items": {
> +            "$ref": "#/$defs/BuildTargetConfiguration"
>            },
> -          "ports": {
> -            "type": "array",
> -            "items": {
> -              "type": "object",
> -              "description": "Each port should be described on both sides of the connection. This makes configuration slightly more verbose but greatly simplifies implementation. If there are inconsistencies, then DTS will not run until that issue is fixed. An example inconsistency would be port 1, node 1 says it is connected to port 1, node 2, but port 1, node 2 says it is connected to port 2, node 1.",
> -              "properties": {
> -                "pci": {
> -                  "$ref": "#/definitions/pci_address",
> -                  "description": "The local PCI address of the port"
> -                },
> -                "os_driver_for_dpdk": {
> -                  "type": "string",
> -                  "description": "The driver that the kernel should bind this device to for DPDK to use it. (ex: vfio-pci)"
> -                },
> -                "os_driver": {
> -                  "type": "string",
> -                  "description": "The driver normally used by this port (ex: i40e)"
> -                },
> -                "peer_node": {
> -                  "type": "string",
> -                  "description": "The name of the node the peer port is on"
> -                },
> -                "peer_pci": {
> -                  "$ref": "#/definitions/pci_address",
> -                  "description": "The PCI address of the peer port"
> -                }
> -              },
> -              "additionalProperties": false,
> -              "required": [
> -                "pci",
> -                "os_driver_for_dpdk",
> -                "os_driver",
> -                "peer_node",
> -                "peer_pci"
> -              ]
> -            },
> -            "minimum": 1
> +          "title": "Build Targets",
> +          "type": "array"
> +        },
> +        "skip_smoke_tests": {
> +          "default": false,
> +          "title": "Skip Smoke Tests",
> +          "type": "boolean"
> +        },
> +        "system_under_test_node": {
> +          "$ref": "#/$defs/TestRunSUTNodeConfiguration"
> +        },
> +        "traffic_generator_node": {
> +          "title": "Traffic Generator Node",
> +          "type": "string"
> +        }
> +      },
> +      "required": [
> +        "perf",
> +        "func",
> +        "test_suites",
> +        "build_targets",
> +        "system_under_test_node",
> +        "traffic_generator_node"
> +      ],
> +      "title": "TestRunConfiguration",
> +      "type": "object"
> +    },
> +    "TestRunSUTNodeConfiguration": {
> +      "additionalProperties": false,
> +      "description": "The SUT node configuration of a test run.\n\nAttributes:\n    node_name: The SUT node to use in this test run.\n    vdevs: The names of virtual devices to test.",
> +      "properties": {
> +        "vdevs": {
> +          "items": {
> +            "type": "string"
>            },
> -          "traffic_generator": {
> -            "oneOf": [
> -              {
> -                "type": "object",
> -                "description": "Scapy traffic generator. Used for functional testing.",
> -                "properties": {
> -                  "type": {
> -                    "type": "string",
> -                    "enum": [
> -                      "SCAPY"
> -                    ]
> -                  }
> -                }
> -              }
> -            ]
> -          }
> +          "title": "Vdevs",
> +          "type": "array"
>          },
> -        "additionalProperties": false,
> -        "required": [
> -          "name",
> -          "hostname",
> -          "user",
> -          "arch",
> -          "os"
> -        ]
> +        "node_name": {
> +          "title": "Node Name",
> +          "type": "string"
> +        }
>        },
> -      "minimum": 1
> +      "required": [
> +        "node_name"
> +      ],
> +      "title": "TestRunSUTNodeConfiguration",
> +      "type": "object"
>      },
> -    "test_runs": {
> -      "type": "array",
> -      "items": {
> -        "type": "object",
> -        "properties": {
> -          "build_targets": {
> -            "type": "array",
> -            "items": {
> -              "$ref": "#/definitions/build_target"
> +    "TestSuiteConfig": {
> +      "anyOf": [
> +        {
> +          "additionalProperties": false,
> +          "properties": {
> +            "test_suite": {
> +              "description": "The identifying name of the test suite.",
> +              "title": "Test suite name",
> +              "type": "string"
>              },
> -            "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"
> -                }
> -              ]
> +            "test_cases": {
> +              "description": "The identifying name of the test cases of the test suite.",
> +              "items": {
> +                "type": "string"
> +              },
> +              "title": "Test cases by name",
> +              "type": "array"
>              }
>            },
> -          "skip_smoke_tests": {
> -            "description": "Optional field that allows you to skip smoke testing",
> -            "type": "boolean"
> -          },
> -          "system_under_test_node": {
> -            "type":"object",
> -            "properties": {
> -              "node_name": {
> -                "$ref": "#/definitions/node_name"
> -              },
> -              "vdevs": {
> -                "description": "Optional list of names of vdevs to be used in the test run",
> -                "type": "array",
> -                "items": {
> -                  "type": "string"
> -                }
> -              }
> -            },
> -            "required": [
> -              "node_name"
> -            ]
> +          "required": [
> +            "test_suite"
> +          ],
> +          "type": "object"
> +        },
> +        {
> +          "type": "string"
> +        }
> +      ],
> +      "description": "Test suite configuration.\n\nInformation about a single test suite to be executed. It can be represented and validated as a\nstring type in the form of: ``TEST_SUITE [TEST_CASE, ...]``, in the configuration file.\n\nAttributes:\n    test_suite: The name of the test suite module without the starting ``TestSuite_``.\n    test_cases: The names of test cases from this test suite to execute.\n        If empty, all test cases will be executed.",
> +      "title": "TestSuiteConfig"
> +    }
> +  },
> +  "description": "DTS testbed and test configuration.\n\nAttributes:\n    test_runs: Test run configurations.\n    nodes: Node configurations.",
> +  "properties": {
> +    "test_runs": {
> +      "items": {
> +        "$ref": "#/$defs/TestRunConfiguration"
> +      },
> +      "minItems": 1,
> +      "title": "Test Runs",
> +      "type": "array"
> +    },
> +    "nodes": {
> +      "items": {
> +        "anyOf": [
> +          {
> +            "$ref": "#/$defs/TGNodeConfiguration"
>            },
> -          "traffic_generator_node": {
> -            "$ref": "#/definitions/node_name"
> +          {
> +            "$ref": "#/$defs/SutNodeConfiguration"
>            }
> -        },
> -        "additionalProperties": false,
> -        "required": [
> -          "build_targets",
> -          "perf",
> -          "func",
> -          "test_suites",
> -          "system_under_test_node",
> -          "traffic_generator_node"
>          ]
>        },
> -      "minimum": 1
> +      "minItems": 1,
> +      "title": "Nodes",
> +      "type": "array"
>      }
>    },
>    "required": [
>      "test_runs",
>      "nodes"
>    ],
> -  "additionalProperties": false
> -}
> +  "title": "Configuration",
> +  "type": "object",
> +  "$schema": "https://json-schema.org/draft/2020-12/schema"
> +}
> \ No newline at end of file
> diff --git a/dts/generate-schema.py b/dts/generate-schema.py
> new file mode 100755
> index 0000000000..b41d28492f
> --- /dev/null
> +++ b/dts/generate-schema.py
> @@ -0,0 +1,38 @@
> +#!/usr/bin/env python3
> +# SPDX-License-Identifier: BSD-3-Clause
> +# Copyright(c) 2024 Arm Limited
> +
> +"""JSON schema generation script."""
> +
> +import json
> +import os
> +
> +from pydantic.json_schema import GenerateJsonSchema
> +
> +from framework.config import ConfigurationType
> +
> +DTS_DIR = os.path.dirname(os.path.realpath(__file__))
> +RELATIVE_PATH_TO_SCHEMA = "framework/config/conf_yaml_schema.json"
> +
> +
> +class GenerateSchemaWithDialect(GenerateJsonSchema):
> +    """Custom schema generator which adds the schema dialect."""
> +
> +    def generate(self, schema, mode="validation"):
> +        """Generate JSON schema."""
> +        json_schema = super().generate(schema, mode=mode)
> +        json_schema["$schema"] = self.schema_dialect
> +        return json_schema
> +
> +
> +try:
> +    path = os.path.join(DTS_DIR, RELATIVE_PATH_TO_SCHEMA)
> +
> +    with open(path, "w") as schema_file:
> +        schema_dict = ConfigurationType.json_schema(schema_generator=GenerateSchemaWithDialect)
> +        schema_json = json.dumps(schema_dict, indent=2)
> +        schema_file.write(schema_json)
> +
> +    print("Schema generated successfully!")
> +except Exception as e:
> +    raise Exception("failed to generate schema") from e
> --
> 2.34.1
>
  

Patch

diff --git a/doc/guides/tools/dts.rst b/doc/guides/tools/dts.rst
index 515b15e4d8..317bd0ff99 100644
--- a/doc/guides/tools/dts.rst
+++ b/doc/guides/tools/dts.rst
@@ -430,6 +430,16 @@  Refer to the script for usage: ``devtools/dts-check-format.sh -h``.
 Configuration Schema
 --------------------
 
+The configuration schema is automatically generated from Pydantic models and can be found
+at ``dts/framework/config/conf_yaml_schema.json``. Whenever the models are changed, the schema
+should be regenerated using the dedicated script at ``dts/generate-schema.py``, e.g.:
+
+.. code-block:: console
+
+   $ poetry shell
+   (dts-py3.10) $ ./generate-schema.py
+
+
 Definitions
 ~~~~~~~~~~~
 
diff --git a/dts/framework/config/conf_yaml_schema.json b/dts/framework/config/conf_yaml_schema.json
index f02a310bb5..1cf1bb098a 100644
--- a/dts/framework/config/conf_yaml_schema.json
+++ b/dts/framework/config/conf_yaml_schema.json
@@ -1,402 +1,502 @@ 
 {
-  "$schema": "https://json-schema.org/draft-07/schema",
-  "title": "DTS Config Schema",
-  "definitions": {
-    "node_name": {
-      "type": "string",
-      "description": "A unique identifier for a node"
-    },
-    "NIC": {
-      "type": "string",
-      "enum": [
-        "ALL",
-        "ConnectX3_MT4103",
-        "ConnectX4_LX_MT4117",
-        "ConnectX4_MT4115",
-        "ConnectX5_MT4119",
-        "ConnectX5_MT4121",
-        "I40E_10G-10G_BASE_T_BC",
-        "I40E_10G-10G_BASE_T_X722",
-        "I40E_10G-SFP_X722",
-        "I40E_10G-SFP_XL710",
-        "I40E_10G-X722_A0",
-        "I40E_1G-1G_BASE_T_X722",
-        "I40E_25G-25G_SFP28",
-        "I40E_40G-QSFP_A",
-        "I40E_40G-QSFP_B",
-        "IAVF-ADAPTIVE_VF",
-        "IAVF-VF",
-        "IAVF_10G-X722_VF",
-        "ICE_100G-E810C_QSFP",
-        "ICE_25G-E810C_SFP",
-        "ICE_25G-E810_XXV_SFP",
-        "IGB-I350_VF",
-        "IGB_1G-82540EM",
-        "IGB_1G-82545EM_COPPER",
-        "IGB_1G-82571EB_COPPER",
-        "IGB_1G-82574L",
-        "IGB_1G-82576",
-        "IGB_1G-82576_QUAD_COPPER",
-        "IGB_1G-82576_QUAD_COPPER_ET2",
-        "IGB_1G-82580_COPPER",
-        "IGB_1G-I210_COPPER",
-        "IGB_1G-I350_COPPER",
-        "IGB_1G-I354_SGMII",
-        "IGB_1G-PCH_LPTLP_I218_LM",
-        "IGB_1G-PCH_LPTLP_I218_V",
-        "IGB_1G-PCH_LPT_I217_LM",
-        "IGB_1G-PCH_LPT_I217_V",
-        "IGB_2.5G-I354_BACKPLANE_2_5GBPS",
-        "IGC-I225_LM",
-        "IGC-I226_LM",
-        "IXGBE_10G-82599_SFP",
-        "IXGBE_10G-82599_SFP_SF_QP",
-        "IXGBE_10G-82599_T3_LOM",
-        "IXGBE_10G-82599_VF",
-        "IXGBE_10G-X540T",
-        "IXGBE_10G-X540_VF",
-        "IXGBE_10G-X550EM_A_SFP",
-        "IXGBE_10G-X550EM_X_10G_T",
-        "IXGBE_10G-X550EM_X_SFP",
-        "IXGBE_10G-X550EM_X_VF",
-        "IXGBE_10G-X550T",
-        "IXGBE_10G-X550_VF",
-        "brcm_57414",
-        "brcm_P2100G",
-        "cavium_0011",
-        "cavium_a034",
-        "cavium_a063",
-        "cavium_a064",
-        "fastlinq_ql41000",
-        "fastlinq_ql41000_vf",
-        "fastlinq_ql45000",
-        "fastlinq_ql45000_vf",
-        "hi1822",
-        "virtio"
-      ]
-    },
-
-    "ARCH": {
-      "type": "string",
+  "$defs": {
+    "Architecture": {
+      "description": "The supported architectures of :class:`~framework.testbed_model.node.Node`\\s.",
       "enum": [
+        "i686",
         "x86_64",
+        "x86_32",
         "arm64",
         "ppc64le"
-      ]
-    },
-    "OS": {
-      "type": "string",
-      "enum": [
-        "linux"
-      ]
-    },
-    "cpu": {
-      "type": "string",
-      "description": "Native should be the default on x86",
-      "enum": [
-        "native",
-        "armv8a",
-        "dpaa2",
-        "thunderx",
-        "xgene1"
-      ]
-    },
-    "compiler": {
-      "type": "string",
-      "enum": [
-        "gcc",
-        "clang",
-        "icc",
-        "mscv"
-      ]
+      ],
+      "title": "Architecture",
+      "type": "string"
     },
-    "build_target": {
-      "type": "object",
-      "description": "Targets supported by DTS",
+    "BuildTargetConfiguration": {
+      "additionalProperties": false,
+      "description": "DPDK build configuration.\n\nThe configuration used for building DPDK.\n\nAttributes:\n    arch: The target architecture to build for.\n    os: The target os to build for.\n    cpu: The target CPU to build for.\n    compiler: The compiler executable to use.\n    compiler_wrapper: This string will be put in front of the compiler when\n        executing the build. Useful for adding wrapper commands, such as ``ccache``.",
       "properties": {
         "arch": {
-          "type": "string",
-          "enum": [
-            "ALL",
-            "x86_64",
-            "arm64",
-            "ppc64le",
-            "other"
-          ]
+          "$ref": "#/$defs/Architecture"
         },
         "os": {
-          "$ref": "#/definitions/OS"
+          "$ref": "#/$defs/OS"
         },
         "cpu": {
-          "$ref": "#/definitions/cpu"
+          "$ref": "#/$defs/CPUType"
         },
         "compiler": {
-          "$ref": "#/definitions/compiler"
+          "$ref": "#/$defs/Compiler"
         },
-          "compiler_wrapper": {
-          "type": "string",
-          "description": "This will be added before compiler to the CC variable when building DPDK. Optional."
+        "compiler_wrapper": {
+          "default": "",
+          "title": "Compiler Wrapper",
+          "type": "string"
         }
       },
-      "additionalProperties": false,
       "required": [
         "arch",
         "os",
         "cpu",
         "compiler"
-      ]
+      ],
+      "title": "BuildTargetConfiguration",
+      "type": "object"
     },
-    "hugepages_2mb": {
-      "type": "object",
-      "description": "Optional hugepage configuration. If not specified, hugepages won't be configured and DTS will use system configuration.",
+    "CPUType": {
+      "description": "The supported CPUs of :class:`~framework.testbed_model.node.Node`\\s.",
+      "enum": [
+        "native",
+        "armv8a",
+        "dpaa2",
+        "thunderx",
+        "xgene1"
+      ],
+      "title": "CPUType",
+      "type": "string"
+    },
+    "Compiler": {
+      "description": "The supported compilers of :class:`~framework.testbed_model.node.Node`\\s.",
+      "enum": [
+        "gcc",
+        "clang",
+        "icc",
+        "msvc"
+      ],
+      "title": "Compiler",
+      "type": "string"
+    },
+    "HugepageConfiguration": {
+      "additionalProperties": false,
+      "description": "The hugepage configuration of :class:`~framework.testbed_model.node.Node`\\s.\n\nAttributes:\n    number_of: The number of hugepages to allocate.\n    force_first_numa: If :data:`True`, the hugepages will be configured on the first NUMA node.",
       "properties": {
         "number_of": {
-          "type": "integer",
-          "description": "The number of hugepages to configure. Hugepage size will be the system default."
+          "title": "Number Of",
+          "type": "integer"
         },
         "force_first_numa": {
-          "type": "boolean",
-          "description": "Set to True to force configuring hugepages on the first NUMA node. Defaults to False."
+          "title": "Force First Numa",
+          "type": "boolean"
         }
       },
-      "additionalProperties": false,
       "required": [
-        "number_of"
-      ]
-    },
-    "mac_address": {
-      "type": "string",
-      "description": "A MAC address",
-      "pattern": "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"
+        "number_of",
+        "force_first_numa"
+      ],
+      "title": "HugepageConfiguration",
+      "type": "object"
     },
-    "pci_address": {
-      "type": "string",
-      "pattern": "^[\\da-fA-F]{4}:[\\da-fA-F]{2}:[\\da-fA-F]{2}.\\d:?\\w*$"
+    "OS": {
+      "description": "The supported operating systems of :class:`~framework.testbed_model.node.Node`\\s.",
+      "enum": [
+        "linux",
+        "freebsd",
+        "windows"
+      ],
+      "title": "OS",
+      "type": "string"
     },
-    "port_peer_address": {
-      "description": "Peer is a TRex port, and IXIA port or a PCI address",
-      "oneOf": [
-        {
-          "description": "PCI peer port",
-          "$ref": "#/definitions/pci_address"
+    "PortConfig": {
+      "additionalProperties": false,
+      "description": "The port configuration of :class:`~framework.testbed_model.node.Node`\\s.\n\nAttributes:\n    pci: The PCI address of the port.\n    os_driver_for_dpdk: The operating system driver name for use with DPDK.\n    os_driver: The operating system driver name when the operating system controls the port.\n    peer_node: The :class:`~framework.testbed_model.node.Node` of the port\n        connected to this port.\n    peer_pci: The PCI address of the port connected to this port.",
+      "properties": {
+        "pci": {
+          "description": "The local PCI address of the port.",
+          "pattern": "^[\\da-fA-F]{4}:[\\da-fA-F]{2}:[\\da-fA-F]{2}.\\d:?\\w*$",
+          "title": "Pci",
+          "type": "string"
+        },
+        "os_driver_for_dpdk": {
+          "description": "The driver that the kernel should bind this device to for DPDK to use it.",
+          "examples": [
+            "vfio-pci",
+            "mlx5_core"
+          ],
+          "title": "Os Driver For Dpdk",
+          "type": "string"
+        },
+        "os_driver": {
+          "description": "The driver normally used by this port",
+          "examples": [
+            "i40e",
+            "ice",
+            "mlx5_core"
+          ],
+          "title": "Os Driver",
+          "type": "string"
+        },
+        "peer_node": {
+          "description": "The name of the peer node this port is connected to.",
+          "title": "Peer Node",
+          "type": "string"
+        },
+        "peer_pci": {
+          "description": "The PCI address of the peer port this port is connected to.",
+          "pattern": "^[\\da-fA-F]{4}:[\\da-fA-F]{2}:[\\da-fA-F]{2}.\\d:?\\w*$",
+          "title": "Peer Pci",
+          "type": "string"
         }
-      ]
+      },
+      "required": [
+        "pci",
+        "os_driver_for_dpdk",
+        "os_driver",
+        "peer_node",
+        "peer_pci"
+      ],
+      "title": "PortConfig",
+      "type": "object"
     },
-    "test_suite": {
-      "type": "string",
-      "enum": [
-        "hello_world",
-        "os_udp",
-        "pmd_buffer_scatter"
-      ]
+    "ScapyTrafficGeneratorConfig": {
+      "additionalProperties": false,
+      "description": "Scapy traffic generator specific configuration.",
+      "properties": {
+        "type": {
+          "const": "SCAPY",
+          "enum": [
+            "SCAPY"
+          ],
+          "title": "Type",
+          "type": "string"
+        }
+      },
+      "required": [
+        "type"
+      ],
+      "title": "ScapyTrafficGeneratorConfig",
+      "type": "object"
     },
-    "test_target": {
-      "type": "object",
+    "SutNodeConfiguration": {
+      "additionalProperties": false,
+      "description": ":class:`~framework.testbed_model.sut_node.SutNode` specific configuration.\n\nAttributes:\n    memory_channels: The number of memory channels to use when running DPDK.",
       "properties": {
-        "suite": {
-          "$ref": "#/definitions/test_suite"
+        "name": {
+          "description": "A unique identifier for this node.",
+          "title": "Name",
+          "type": "string"
+        },
+        "hostname": {
+          "description": "The hostname or IP address of the node.",
+          "title": "Hostname",
+          "type": "string"
+        },
+        "user": {
+          "description": "The login user to use to connect to this node.",
+          "title": "User",
+          "type": "string"
         },
-        "cases": {
-          "type": "array",
-          "description": "If specified, only this subset of test suite's test cases will be run.",
+        "password": {
+          "anyOf": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "null"
+            }
+          ],
+          "default": null,
+          "description": "The login password to use to connect to this node. SSH keys are STRONGLY preferred, use only as last resort.",
+          "title": "Password"
+        },
+        "use_first_core": {
+          "default": false,
+          "description": "DPDK won't use the first physical core if set to False.",
+          "title": "Use First Core",
+          "type": "boolean"
+        },
+        "hugepages_2mb": {
+          "anyOf": [
+            {
+              "$ref": "#/$defs/HugepageConfiguration"
+            },
+            {
+              "type": "null"
+            }
+          ],
+          "default": null
+        },
+        "ports": {
           "items": {
-            "type": "string"
+            "$ref": "#/$defs/PortConfig"
           },
-          "minimum": 1
+          "minItems": 1,
+          "title": "Ports",
+          "type": "array"
+        },
+        "memory_channels": {
+          "default": 1,
+          "description": "Number of memory channels to use when running DPDK.",
+          "title": "Memory Channels",
+          "type": "integer"
+        },
+        "arch": {
+          "$ref": "#/$defs/Architecture"
+        },
+        "os": {
+          "$ref": "#/$defs/OS"
+        },
+        "lcores": {
+          "default": "1",
+          "description": "Comma-separated list of logical cores to use. An empty string means use all lcores.",
+          "examples": [
+            "1,2,3,4,5,18-22",
+            "10-15"
+          ],
+          "pattern": "^(([0-9]+|([0-9]+-[0-9]+))(,([0-9]+|([0-9]+-[0-9]+)))*)?$",
+          "title": "Lcores",
+          "type": "string"
         }
       },
       "required": [
-        "suite"
+        "name",
+        "hostname",
+        "user",
+        "ports",
+        "arch",
+        "os"
       ],
-      "additionalProperties": false
-    }
-  },
-  "type": "object",
-  "properties": {
-    "nodes": {
-      "type": "array",
-      "items": {
-        "type": "object",
-        "properties": {
-          "name": {
-            "type": "string",
-            "description": "A unique identifier for this node"
-          },
-          "hostname": {
-            "type": "string",
-            "description": "A hostname from which the node running DTS can access this node. This can also be an IP address."
-          },
-          "user": {
-            "type": "string",
-            "description": "The user to access this node with."
-          },
-          "password": {
-            "type": "string",
-            "description": "The password to use on this node. Use only as a last resort. SSH keys are STRONGLY preferred."
-          },
-          "arch": {
-            "$ref": "#/definitions/ARCH"
-          },
-          "os": {
-            "$ref": "#/definitions/OS"
-          },
-          "lcores": {
-            "type": "string",
-            "pattern": "^(([0-9]+|([0-9]+-[0-9]+))(,([0-9]+|([0-9]+-[0-9]+)))*)?$",
-            "description": "Optional comma-separated list of logical cores to use, e.g.: 1,2,3,4,5,18-22. Defaults to 1. An empty string means use all lcores."
+      "title": "SutNodeConfiguration",
+      "type": "object"
+    },
+    "TGNodeConfiguration": {
+      "additionalProperties": false,
+      "description": ":class:`~framework.testbed_model.tg_node.TGNode` specific configuration.\n\nAttributes:\n    traffic_generator: The configuration of the traffic generator present on the TG node.",
+      "properties": {
+        "name": {
+          "description": "A unique identifier for this node.",
+          "title": "Name",
+          "type": "string"
+        },
+        "hostname": {
+          "description": "The hostname or IP address of the node.",
+          "title": "Hostname",
+          "type": "string"
+        },
+        "user": {
+          "description": "The login user to use to connect to this node.",
+          "title": "User",
+          "type": "string"
+        },
+        "password": {
+          "anyOf": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "null"
+            }
+          ],
+          "default": null,
+          "description": "The login password to use to connect to this node. SSH keys are STRONGLY preferred, use only as last resort.",
+          "title": "Password"
+        },
+        "use_first_core": {
+          "default": false,
+          "description": "DPDK won't use the first physical core if set to False.",
+          "title": "Use First Core",
+          "type": "boolean"
+        },
+        "hugepages_2mb": {
+          "anyOf": [
+            {
+              "$ref": "#/$defs/HugepageConfiguration"
+            },
+            {
+              "type": "null"
+            }
+          ],
+          "default": null
+        },
+        "ports": {
+          "items": {
+            "$ref": "#/$defs/PortConfig"
           },
-          "use_first_core": {
-            "type": "boolean",
-            "description": "Indicate whether DPDK should use the first physical core. It won't be used by default."
+          "minItems": 1,
+          "title": "Ports",
+          "type": "array"
+        },
+        "arch": {
+          "$ref": "#/$defs/Architecture"
+        },
+        "os": {
+          "$ref": "#/$defs/OS"
+        },
+        "lcores": {
+          "default": "1",
+          "description": "Comma-separated list of logical cores to use. An empty string means use all lcores.",
+          "examples": [
+            "1,2,3,4,5,18-22",
+            "10-15"
+          ],
+          "pattern": "^(([0-9]+|([0-9]+-[0-9]+))(,([0-9]+|([0-9]+-[0-9]+)))*)?$",
+          "title": "Lcores",
+          "type": "string"
+        },
+        "traffic_generator": {
+          "discriminator": {
+            "mapping": {
+              "SCAPY": "#/$defs/ScapyTrafficGeneratorConfig"
+            },
+            "propertyName": "type"
           },
-          "memory_channels": {
-            "type": "integer",
-            "description": "How many memory channels to use. Optional, defaults to 1."
+          "oneOf": [
+            {
+              "$ref": "#/$defs/ScapyTrafficGeneratorConfig"
+            }
+          ],
+          "title": "Traffic Generator"
+        }
+      },
+      "required": [
+        "name",
+        "hostname",
+        "user",
+        "ports",
+        "arch",
+        "os",
+        "traffic_generator"
+      ],
+      "title": "TGNodeConfiguration",
+      "type": "object"
+    },
+    "TestRunConfiguration": {
+      "additionalProperties": false,
+      "description": "The configuration of a test run.\n\nThe configuration contains testbed information, what tests to execute\nand with what DPDK build.\n\nAttributes:\n    build_targets: A list of DPDK builds to test.\n    perf: Whether to run performance tests.\n    func: Whether to run functional tests.\n    skip_smoke_tests: Whether to skip smoke tests.\n    test_suites: The names of test suites and/or test cases to execute.\n    system_under_test_node: The SUT node configuration to use in this test run.\n    traffic_generator_node: The TG node name to use in this test run.",
+      "properties": {
+        "perf": {
+          "description": "Enable performance testing.",
+          "title": "Perf",
+          "type": "boolean"
+        },
+        "func": {
+          "description": "Enable functional testing.",
+          "title": "Func",
+          "type": "boolean"
+        },
+        "test_suites": {
+          "items": {
+            "$ref": "#/$defs/TestSuiteConfig"
           },
-          "hugepages_2mb": {
-            "$ref": "#/definitions/hugepages_2mb"
+          "minItems": 1,
+          "title": "Test Suites",
+          "type": "array"
+        },
+        "build_targets": {
+          "items": {
+            "$ref": "#/$defs/BuildTargetConfiguration"
           },
-          "ports": {
-            "type": "array",
-            "items": {
-              "type": "object",
-              "description": "Each port should be described on both sides of the connection. This makes configuration slightly more verbose but greatly simplifies implementation. If there are inconsistencies, then DTS will not run until that issue is fixed. An example inconsistency would be port 1, node 1 says it is connected to port 1, node 2, but port 1, node 2 says it is connected to port 2, node 1.",
-              "properties": {
-                "pci": {
-                  "$ref": "#/definitions/pci_address",
-                  "description": "The local PCI address of the port"
-                },
-                "os_driver_for_dpdk": {
-                  "type": "string",
-                  "description": "The driver that the kernel should bind this device to for DPDK to use it. (ex: vfio-pci)"
-                },
-                "os_driver": {
-                  "type": "string",
-                  "description": "The driver normally used by this port (ex: i40e)"
-                },
-                "peer_node": {
-                  "type": "string",
-                  "description": "The name of the node the peer port is on"
-                },
-                "peer_pci": {
-                  "$ref": "#/definitions/pci_address",
-                  "description": "The PCI address of the peer port"
-                }
-              },
-              "additionalProperties": false,
-              "required": [
-                "pci",
-                "os_driver_for_dpdk",
-                "os_driver",
-                "peer_node",
-                "peer_pci"
-              ]
-            },
-            "minimum": 1
+          "title": "Build Targets",
+          "type": "array"
+        },
+        "skip_smoke_tests": {
+          "default": false,
+          "title": "Skip Smoke Tests",
+          "type": "boolean"
+        },
+        "system_under_test_node": {
+          "$ref": "#/$defs/TestRunSUTNodeConfiguration"
+        },
+        "traffic_generator_node": {
+          "title": "Traffic Generator Node",
+          "type": "string"
+        }
+      },
+      "required": [
+        "perf",
+        "func",
+        "test_suites",
+        "build_targets",
+        "system_under_test_node",
+        "traffic_generator_node"
+      ],
+      "title": "TestRunConfiguration",
+      "type": "object"
+    },
+    "TestRunSUTNodeConfiguration": {
+      "additionalProperties": false,
+      "description": "The SUT node configuration of a test run.\n\nAttributes:\n    node_name: The SUT node to use in this test run.\n    vdevs: The names of virtual devices to test.",
+      "properties": {
+        "vdevs": {
+          "items": {
+            "type": "string"
           },
-          "traffic_generator": {
-            "oneOf": [
-              {
-                "type": "object",
-                "description": "Scapy traffic generator. Used for functional testing.",
-                "properties": {
-                  "type": {
-                    "type": "string",
-                    "enum": [
-                      "SCAPY"
-                    ]
-                  }
-                }
-              }
-            ]
-          }
+          "title": "Vdevs",
+          "type": "array"
         },
-        "additionalProperties": false,
-        "required": [
-          "name",
-          "hostname",
-          "user",
-          "arch",
-          "os"
-        ]
+        "node_name": {
+          "title": "Node Name",
+          "type": "string"
+        }
       },
-      "minimum": 1
+      "required": [
+        "node_name"
+      ],
+      "title": "TestRunSUTNodeConfiguration",
+      "type": "object"
     },
-    "test_runs": {
-      "type": "array",
-      "items": {
-        "type": "object",
-        "properties": {
-          "build_targets": {
-            "type": "array",
-            "items": {
-              "$ref": "#/definitions/build_target"
+    "TestSuiteConfig": {
+      "anyOf": [
+        {
+          "additionalProperties": false,
+          "properties": {
+            "test_suite": {
+              "description": "The identifying name of the test suite.",
+              "title": "Test suite name",
+              "type": "string"
             },
-            "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"
-                }
-              ]
+            "test_cases": {
+              "description": "The identifying name of the test cases of the test suite.",
+              "items": {
+                "type": "string"
+              },
+              "title": "Test cases by name",
+              "type": "array"
             }
           },
-          "skip_smoke_tests": {
-            "description": "Optional field that allows you to skip smoke testing",
-            "type": "boolean"
-          },
-          "system_under_test_node": {
-            "type":"object",
-            "properties": {
-              "node_name": {
-                "$ref": "#/definitions/node_name"
-              },
-              "vdevs": {
-                "description": "Optional list of names of vdevs to be used in the test run",
-                "type": "array",
-                "items": {
-                  "type": "string"
-                }
-              }
-            },
-            "required": [
-              "node_name"
-            ]
+          "required": [
+            "test_suite"
+          ],
+          "type": "object"
+        },
+        {
+          "type": "string"
+        }
+      ],
+      "description": "Test suite configuration.\n\nInformation about a single test suite to be executed. It can be represented and validated as a\nstring type in the form of: ``TEST_SUITE [TEST_CASE, ...]``, in the configuration file.\n\nAttributes:\n    test_suite: The name of the test suite module without the starting ``TestSuite_``.\n    test_cases: The names of test cases from this test suite to execute.\n        If empty, all test cases will be executed.",
+      "title": "TestSuiteConfig"
+    }
+  },
+  "description": "DTS testbed and test configuration.\n\nAttributes:\n    test_runs: Test run configurations.\n    nodes: Node configurations.",
+  "properties": {
+    "test_runs": {
+      "items": {
+        "$ref": "#/$defs/TestRunConfiguration"
+      },
+      "minItems": 1,
+      "title": "Test Runs",
+      "type": "array"
+    },
+    "nodes": {
+      "items": {
+        "anyOf": [
+          {
+            "$ref": "#/$defs/TGNodeConfiguration"
           },
-          "traffic_generator_node": {
-            "$ref": "#/definitions/node_name"
+          {
+            "$ref": "#/$defs/SutNodeConfiguration"
           }
-        },
-        "additionalProperties": false,
-        "required": [
-          "build_targets",
-          "perf",
-          "func",
-          "test_suites",
-          "system_under_test_node",
-          "traffic_generator_node"
         ]
       },
-      "minimum": 1
+      "minItems": 1,
+      "title": "Nodes",
+      "type": "array"
     }
   },
   "required": [
     "test_runs",
     "nodes"
   ],
-  "additionalProperties": false
-}
+  "title": "Configuration",
+  "type": "object",
+  "$schema": "https://json-schema.org/draft/2020-12/schema"
+}
\ No newline at end of file
diff --git a/dts/generate-schema.py b/dts/generate-schema.py
new file mode 100755
index 0000000000..b41d28492f
--- /dev/null
+++ b/dts/generate-schema.py
@@ -0,0 +1,38 @@ 
+#!/usr/bin/env python3
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2024 Arm Limited
+
+"""JSON schema generation script."""
+
+import json
+import os
+
+from pydantic.json_schema import GenerateJsonSchema
+
+from framework.config import ConfigurationType
+
+DTS_DIR = os.path.dirname(os.path.realpath(__file__))
+RELATIVE_PATH_TO_SCHEMA = "framework/config/conf_yaml_schema.json"
+
+
+class GenerateSchemaWithDialect(GenerateJsonSchema):
+    """Custom schema generator which adds the schema dialect."""
+
+    def generate(self, schema, mode="validation"):
+        """Generate JSON schema."""
+        json_schema = super().generate(schema, mode=mode)
+        json_schema["$schema"] = self.schema_dialect
+        return json_schema
+
+
+try:
+    path = os.path.join(DTS_DIR, RELATIVE_PATH_TO_SCHEMA)
+
+    with open(path, "w") as schema_file:
+        schema_dict = ConfigurationType.json_schema(schema_generator=GenerateSchemaWithDialect)
+        schema_json = json.dumps(schema_dict, indent=2)
+        schema_file.write(schema_json)
+
+    print("Schema generated successfully!")
+except Exception as e:
+    raise Exception("failed to generate schema") from e